Files
podkop/podkop/files/usr/lib/sing_box_config_manager.sh

1432 lines
48 KiB
Bash

#
# Module: sing_box_config_manager.sh
#
# Purpose:
# This script provides a set of helper functions to simplify creation and management
# of sing-box configuration.
#
# Conventions:
# - All functions are prefixed with: sing_box_cm_*
# - "cm" stands for "config manager", shortened by convention
#
# Usage:
# Include this script in your ash script with:
# . /usr/lib/podkop/sing_box_config_manager.sh
#
# After that, sing_box_cm_* functions are available for generating
# and modifying sing-box JSON configuration.
SERVICE_TAG="__service_tag"
#######################################
# Configure the logging section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# disabled: boolean, true to disable logging
# level: string, e.g., "info", "debug", "warn"
# timestamp: boolean, true to include timestamps
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_configure_log "$CONFIG" false "info" true)
#######################################
sing_box_cm_configure_log() {
local config="$1"
local disabled="$2"
local level="$3"
local timestamp="$4"
echo "$config" | jq \
--argjson disabled "$disabled" \
--arg level "$level" \
--argjson timestamp "$timestamp" \
'.log = {
disabled: $disabled,
level: $level,
timestamp: $timestamp
}'
}
#######################################
# Configure the DNS section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# final: string, default dns server tag
# strategy: string, default domain strategy for resolving the domain names
# independent_cache: boolean, whether to use an independent DNS cache
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_configure_dns "$CONFIG" "direct-out" "ipv4_only" true)
#######################################
sing_box_cm_configure_dns() {
local config="$1"
local final="$2"
local strategy="$3"
local independent_cache="$4"
echo "$config" | jq \
--arg final "$final" \
--arg strategy "$strategy" \
--argjson independent_cache "$independent_cache" \
'.dns = {
servers: (.dns.servers // []),
rules: (.dns.rules // []),
final: $final,
strategy: $strategy,
independent_cache: $independent_cache
}'
}
#######################################
# Add a UDP DNS server to the DNS section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the DNS server
# server_address: string, IP address or hostname of the DNS server
# server_port: string or number, port of the DNS server
# domain_resolver: string, domain resolver to use for resolving domain names
# detour: string, tag of the upstream outbound
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_udp_dns_server "$CONFIG" "udp-server" "8.8.8.8" 53)
#######################################
sing_box_cm_add_udp_dns_server() {
local config="$1"
local tag="$2"
local server_address="$3"
local server_port="$4"
local domain_resolver="$5"
local detour="$6"
echo "$config" | jq \
--arg tag "$tag" \
--arg server_address "$server_address" \
--arg server_port "$server_port" \
--arg domain_resolver "$domain_resolver" \
--arg detour "$detour" \
'.dns.servers += [(
{
type: "udp",
tag: $tag,
server: $server_address,
server_port: ($server_port | tonumber)
}
+ (if $detour != "" then { detour: $detour } else {} end)
+ (if $domain_resolver != "" then { domain_resolver: $domain_resolver } else {} end)
)]'
}
#######################################
# Add a TLS DNS server to the DNS section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the DNS server
# server_address: string, IP address or hostname of the DNS server
# server_port: string or number, port of the DNS server
# domain_resolver: string, domain resolver to use for resolving domain names
# detour: string, tag of the upstream outbound
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_tls_dns_server "$CONFIG" "dot-server" "1.1.1.1" 853)
#######################################
sing_box_cm_add_tls_dns_server() {
local config="$1"
local tag="$2"
local server_address="$3"
local server_port="$4"
local domain_resolver="$5"
local detour="$6"
echo "$config" | jq \
--arg tag "$tag" \
--arg server_address "$server_address" \
--arg server_port "$server_port" \
--arg domain_resolver "$domain_resolver" \
--arg detour "$detour" \
'.dns.servers += [(
{
type: "tls",
tag: $tag,
server: $server_address,
server_port: ($server_port | tonumber)
}
+ (if $detour != "" then { detour: $detour } else {} end)
+ (if $domain_resolver != "" then { domain_resolver: $domain_resolver } else {} end)
)]'
}
#######################################
# Add an HTTPS DNS server to the DNS section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the DNS server
# server_address: string, IP address or hostname of the DNS server
# server_port: string or number, port of the DNS server
# path: string, optional URL path for HTTPS DNS requests
# headers: string, optional additional headers for HTTPS DNS requests
# domain_resolver: string, domain resolver to use for resolving domain names
# detour: string, tag of the upstream outbound
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_https_dns_server "$CONFIG" "doh-server" "1.1.1.1" 443)
#######################################
sing_box_cm_add_https_dns_server() {
local config="$1"
local tag="$2"
local server_address="$3"
local server_port="$4"
local path="$5"
local headers="$6"
local domain_resolver="$7"
local detour="$8"
echo "$config" | jq \
--arg tag "$tag" \
--arg server_address "$server_address" \
--arg server_port "$server_port" \
--arg path "$path" \
--arg headers "$headers" \
--arg domain_resolver "$domain_resolver" \
--arg detour "$detour" \
'.dns.servers += [(
{
type: "https",
tag: $tag,
server: $server_address,
server_port: ($server_port |tonumber)
}
+ (if $path != "" then { path: $path } else {} end)
+ (if $headers != "" then { headers: $headers } else {} end)
+ (if $detour != "" then { detour: $detour } else {} end)
+ (if $domain_resolver != "" then { domain_resolver: $domain_resolver } else {} end)
)]'
}
#######################################
# Add a FakeIP DNS server to the DNS section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the DNS server
# inet4_range: string, IPv4 range used for fake IP mapping
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_fakeip_dns_server "$CONFIG" "fakeip-server" "198.18.0.0/15")
#######################################
sing_box_cm_add_fakeip_dns_server() {
local config="$1"
local tag="$2"
local inet4_range="$3"
echo "$config" | jq \
--arg tag "$tag" \
--arg inet4_range "$inet4_range" \
'.dns.servers += [{
type: "fakeip",
tag: $tag,
inet4_range: $inet4_range,
}]'
}
#######################################
# Add a DNS routing rule to the DNS section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# server: string, target DNS server for the rule
# tag: string, identifier for the route rule
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_dns_route_rule "$CONFIG" "fakeip-server" "fakeip-dns-rule-id")
#######################################
sing_box_cm_add_dns_route_rule() {
local config="$1"
local server="$2"
local tag="$3"
echo "$config" | jq \
--arg server "$server" \
--arg service_tag "$SERVICE_TAG" \
--arg tag "$tag" \
'.dns.rules += [{
action: "route",
server: $server,
$service_tag: $tag
}]'
}
#######################################
# Patch a DNS routing rule in the DNS section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier of the rule to patch
# key: string, the key in the rule to update or add
# value: JSON value to assign to the key
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_patch_dns_route_rule "$CONFIG" "fakeip-dns-rule-id" "rule_set" "main")
# CONFIG=$(sing_box_cm_patch_dns_route_rule "$CONFIG" "fakeip-dns-rule-id" "rule_set" '["main","second"]')
# CONFIG=$(sing_box_cm_patch_dns_route_rule "$CONFIG" "fakeip-dns-rule-id" "domain" "example.com")
#######################################
sing_box_cm_patch_dns_route_rule() {
local config="$1"
local tag="$2"
local key="$3"
local value="$4"
value=$(_normalize_arg "$value")
echo "$config" | jq \
--arg service_tag "$SERVICE_TAG" \
--arg tag "$tag" \
--arg key "$key" \
--argjson value "$value" \
'import "helpers" as h {"search": "/usr/lib/podkop"};
.dns.rules |= map(
if .[$service_tag] == $tag then
if has($key) then
.[$key] |= h::extend_key_value(.; $value)
else
. + { ($key): $value }
end
else
.
end
)'
}
#######################################
# Add a DNS reject rule to the DNS section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# key: string, the key to set for the reject rule
# value: JSON value to assign to the key
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_dns_reject_rule "$CONFIG" "query_type" "HTTPS")
#######################################
sing_box_cm_add_dns_reject_rule() {
local config="$1"
local key="$2"
local value="$3"
value=$(_normalize_arg "$value")
echo "$config" | jq \
--arg key "$key" \
--argjson value "$value" \
'.dns.rules += [{
action: "reject",
($key): $value
}]'
}
#######################################
# Add a TProxy inbound to the inbounds section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the inbound
# listen_address: string, IP address to listen on
# listen_port: number, port to listen on
# tcp_fast_open: boolean, enable or disable TCP Fast Open
# udp_fragment: boolean, enable or disable UDP fragmentation
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_tproxy_inbound "$CONFIG" "tproxy-in" "127.0.0.1" 6969 true true)
#######################################
sing_box_cm_add_tproxy_inbound() {
local config="$1"
local tag="$2"
local listen_address="$3"
local listen_port="$4"
local tcp_fast_open="$5"
local udp_fragment="$6"
echo "$config" | jq \
--arg tag "$tag" \
--arg listen_address "$listen_address" \
--argjson listen_port "$listen_port" \
--argjson tcp_fast_open "$tcp_fast_open" \
--argjson udp_fragment "$udp_fragment" \
'.inbounds += [{
type: "tproxy",
tag: $tag,
listen: $listen_address,
listen_port: $listen_port,
tcp_fast_open: $tcp_fast_open,
udp_fragment: $udp_fragment
}]'
}
#######################################
# Add a Direct inbound to the inbounds section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the inbound
# listen_address: string, IP address to listen on
# listen_port: number, port to listen on
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_direct_inbound "$CONFIG" "dns-in" "127.0.0.42" 53)
#######################################
sing_box_cm_add_direct_inbound() {
local config="$1"
local tag="$2"
local listen_address="$3"
local listen_port="$4"
echo "$config" | jq \
--arg tag "$tag" \
--arg listen_address "$listen_address" \
--argjson listen_port "$listen_port" \
'.inbounds += [{
type: "direct",
tag: $tag,
listen: $listen_address,
listen_port: $listen_port,
}]'
}
#######################################
# Add a Mixed inbound to the inbounds section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the inbound
# listen_address: string, IP address to listen on
# listen_port: number, port to listen on
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_mixed_inbound "$CONFIG" "tproxy-in" "192.168.1.1" 2080)
#######################################
sing_box_cm_add_mixed_inbound() {
local config="$1"
local tag="$2"
local listen_address="$3"
local listen_port="$4"
echo "$config" | jq \
--arg tag "$tag" \
--arg listen_address "$listen_address" \
--argjson listen_port "$listen_port" \
'.inbounds += [{
type: "mixed",
tag: $tag,
listen: $listen_address,
listen_port: $listen_port,
}]'
}
#######################################
# Add a Direct outbound to the outbounds section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the outbound
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_direct_outbound "$CONFIG" "direct-out")
#######################################
sing_box_cm_add_direct_outbound() {
local config="$1"
local tag="$2"
echo "$config" | jq \
--arg tag "$tag" \
'.outbounds += [{
type: "direct",
tag: $tag
}]'
}
#######################################
# Add a SOCKS5 outbound to the outbounds section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the outbound
# server_address: string, IP address or hostname of the SOCKS5 server
# server_port: number, port of the SOCKS5 server
# version: string, optional SOCKS version
# username: string, optional username for authentication
# password: string, optional password for authentication
# network: string, optional network type (e.g., "tcp")
# udp_over_tcp: number, optional version for UDP over TCP
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_socks5_outbound "$CONFIG" "socks5-out" "192.168.1.10" 1080)
#######################################
sing_box_cm_add_socks5_outbound() {
local config="$1"
local tag="$2"
local server_address="$3"
local server_port="$4"
local version="$5"
local username="$6"
local password="$7"
local network="$8"
local udp_over_tcp="$9"
echo "$config" | jq \
--arg tag "$tag" \
--arg server_address "$server_address" \
--arg server_port "$server_port" \
--arg version "$version" \
--arg username "$username" \
--arg password "$password" \
--arg network "$network" \
--arg udp_over_tcp "$udp_over_tcp" \
'.outbounds += [(
{
type: "socks",
tag: $tag,
server: $server_address,
server_port: ($server_port | tonumber)
}
+ (if $version != "" then {version: $version} else {} end)
+ (if $username != "" then {username: $username} else {} end)
+ (if $password != "" then {password: $password} else {} end)
+ (if $network != "" then {network: $network} else {} end)
+ (if $udp_over_tcp != "" then {
udp_over_tcp: {
enabled: true,
version: ($udp_over_tcp | tonumber)
}
} else {} end)
)]'
}
#######################################
# Add a Shadowsocks outbound to the outbounds section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the outbound
# server_address: string, IP address or hostname of the Shadowsocks server
# server_port: number, port of the Shadowsocks server
# method: string, encryption method
# password: string, password for encryption
# network: string, optional network type (e.g., "tcp")
# udp_over_tcp: number, optional version for UDP over TCP
# plugin: string, optional plugin name
# plugin_opts: string, optional plugin options
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(
# sing_box_cm_add_shadowsocks_outbound "$CONFIG" "ss-out" "127.0.0.1" 443 \
# "2022-blake3-aes-128-gcm" "8JCsPssfgS8tiRwiMlhARg=="
# )
#######################################
sing_box_cm_add_shadowsocks_outbound() {
local config="$1"
local tag="$2"
local server_address="$3"
local server_port="$4"
local method="$5"
local password="$6"
local network="$7"
local udp_over_tcp="$8"
local plugin="$9"
local plugin_opts="${10}"
echo "$config" | jq \
--arg tag "$tag" \
--arg server_address "$server_address" \
--arg server_port "$server_port" \
--arg method "$method" \
--arg password "$password" \
--arg plugin "$plugin" \
--arg plugin_opts "$plugin_opts" \
--arg network "$network" \
--arg udp_over_tcp "$udp_over_tcp" \
'.outbounds += [(
{
type: "shadowsocks",
tag: $tag,
server: $server_address,
server_port: ($server_port | tonumber),
method: $method,
password: $password
}
+ (if $plugin != "" then {plugin: $plugin} else {} end)
+ (if $plugin_opts != "" then {plugin_opts: $plugin_opts} else {} end)
+ (if $network != "" then {network: $network} else {} end)
+ (if $udp_over_tcp != "" then {
udp_over_tcp: {
enabled: true,
version: ($udp_over_tcp | tonumber)
}
} else {} end)
)]'
}
#######################################
# Add a VLESS outbound to the outbounds section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the outbound
# server_address: string, IP address or hostname of the VLESS server
# server_port: number, port of the VLESS server
# uuid: string, user UUID
# flow: string, optional flow setting
# network: string, optional network type (e.g., "tcp")
# packet_encoding: string, optional packet encoding method
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(
# sing_box_cm_add_vless_outbound "$CONFIG" "vless-reality-out" "example.com" 443 \
# "bf000d23-0752-40b4-affe-68f7707a9661" "xtls-rprx-vision"
# )
#######################################
sing_box_cm_add_vless_outbound() {
local config="$1"
local tag="$2"
local server_address="$3"
local server_port="$4"
local uuid="$5"
local flow="$6"
local network="$7"
local packet_encoding="$8"
echo "$config" | jq \
--arg tag "$tag" \
--arg server_address "$server_address" \
--arg server_port "$server_port" \
--arg uuid "$uuid" \
--arg flow "$flow" \
--arg network "$network" \
--arg packet_encoding "$packet_encoding" \
'.outbounds += [(
{
type: "vless",
tag: $tag,
server: $server_address,
server_port: ($server_port | tonumber),
uuid: $uuid
}
+ (if $flow != "" then {flow: $flow} else {} end)
+ (if $network != "" then {network: $network} else {} end)
+ (if $packet_encoding != "" then {packet_encoding: $packet_encoding} else {} end)
)]'
}
#######################################
# Add a Trojan outbound to the outbounds section of a sing-box JSON configuration.
# Arguments:
# config: string, JSON configuration
# tag: string, identifier for the outbound
# server_address: string, IP address or hostname of the Trojan server
# server_port: number, port of the Trojan server
# password: string, password for authentication
# network: string, optional network type (e.g., "tcp")
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_trojan_outbound "$CONFIG" "trojan-out" "example.com" 443 "supersecretpassword" "tcp")
#######################################
sing_box_cm_add_trojan_outbound() {
local config="$1"
local tag="$2"
local server_address="$3"
local server_port="$4"
local password="$5"
local network="$6"
echo "$config" | jq \
--arg tag "$tag" \
--arg server_address "$server_address" \
--arg server_port "$server_port" \
--arg password "$password" \
--arg network "$network" \
'.outbounds += [(
{
type: "trojan",
tag: $tag,
server: $server_address,
server_port: ($server_port | tonumber),
password: $password
}
+ (if $network != "" then {network: $network} else {} end)
)]'
}
#######################################
# Set gRPC transport settings for an outbound in a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier of the outbound to modify
# service_name: string, optional gRPC service name
# idle_timeout: string or number, optional idle timeout
# ping_timeout: string or number, optional ping timeout
# permit_without_stream: boolean, optional flag for permitting without stream
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_set_grpc_transport_for_outbound "$CONFIG" "vless-tls-grpc-out")
#######################################
sing_box_cm_set_grpc_transport_for_outbound() {
local config="$1"
local tag="$2"
local service_name="$3"
local idle_timeout="$4"
local ping_timeout="$5"
local permit_without_stream="$6"
echo "$config" | jq \
--arg tag "$tag" \
--arg service_name "$service_name" \
--arg idle_timeout "$idle_timeout" \
--arg ping_timeout "$ping_timeout" \
--arg permit_without_stream "$permit_without_stream" \
'.outbounds |= map(
if .tag == $tag then
. + {
transport: (
{ type: "grpc" }
+ (if $service_name != "" then {service_name: $service_name} else {} end)
+ (if $idle_timeout != "" then {idle_timeout: $idle_timeout} else {} end)
+ (if $ping_timeout != "" then {ping_timeout: $ping_timeout} else {} end)
+ (if $permit_without_stream != "" then {permit_without_stream: $permit_without_stream} else {} end)
)
}
else
.
end
)'
}
#######################################
# Set WebSocket transport settings for an outbound in a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier of the outbound to modify
# path: string, WebSocket path
# host: string, optional Host header for WebSocket
# max_early_data: number, optional maximum early data
# early_data_header_name: string, optional header name for early data
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_set_ws_transport_for_outbound "$CONFIG" "vless-tls-ws-out" "/path" "example.com")
#######################################
sing_box_cm_set_ws_transport_for_outbound() {
local config="$1"
local tag="$2"
local path="$3"
local host="$4"
local max_early_data="$5"
local early_data_header_name="$6"
# Not sure if the "Host" parameter in headers is correct — needs verification
echo "$config" | jq \
--arg tag "$tag" \
--arg path "$path" \
--arg host "$host" \
--arg max_early_data "$max_early_data" \
--arg early_data_header_name "$early_data_header_name" \
'.outbounds |= map(
if .tag == $tag then
. + {
transport: (
{
type: "ws",
path: $path
}
+ (if $host != "" then {headers: {Host: $host}} else {} end)
+ (if $max_early_data != "" then {max_early_data: $max_early_data | tonumber} else {} end)
+ (if $early_data_header_name != "" then
{early_data_header_name: $early_data_header_name}
else {} end)
)
}
else
.
end
)'
}
#######################################
# Set TLS settings for an outbound in a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier of the outbound to modify
# server_name: string, optional, used to verify the hostname on the returned certificates
# insecure: boolean, accept any server certificate
# alpn: JSON value or null, optional supported application level protocols
# utls_fingerprint: string, optional uTLS fingerprint
# reality_public_key: string, optional Reality public key
# reality_short_id: string, optional Reality short ID
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(
# sing_box_cm_set_tls_for_outbound "$CONFIG" "vless-reality-out" "example.com" false null "chrome" \
# "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0" "0123456789abcdef"
# )
#######################################
sing_box_cm_set_tls_for_outbound() {
local config="$1"
local tag="$2"
local server_name="$3"
local insecure="$4"
local alpn="$5"
local utls_fingerprint="$6"
local reality_public_key="$7"
local reality_short_id="$8"
echo "$config" | jq \
--arg tag "$tag" \
--arg server_name "$server_name" \
--arg insecure "$insecure" \
--argjson alpn "$alpn" \
--arg utls_fingerprint "$utls_fingerprint" \
--arg reality_public_key "$reality_public_key" \
--arg reality_short_id "$reality_short_id" \
'.outbounds |= map(
if .tag == $tag then
. + {
tls: (
{ enabled: true }
+ (if $server_name != "" then {server_name: $server_name} else {} end)
+ (if $insecure == "true" then {insecure: true} else {} end)
+ (if $alpn != null then {alpn: $alpn} else {} end)
+ (if $utls_fingerprint != "" then {
utls: {
enabled: true,
fingerprint: $utls_fingerprint
}
} else {} end)
+ (if $reality_public_key != "" then {
reality: {
enabled: true,
public_key: $reality_public_key,
short_id: $reality_short_id
}
} else {} end)
)
}
else
.
end
)'
}
#######################################
# Add a Direct outbound with a specific network interface to a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the outbound
# interface: string, network interface to bind the outbound
# domain_resolver: string, tag of the domain resolver to be used for this outbound (optional)
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_interface_outbound "$CONFIG" "warp-out" "awg0")
#######################################
sing_box_cm_add_interface_outbound() {
local config="$1"
local tag="$2"
local interface="$3"
local domain_resolver="$4"
echo "$config" | jq \
--arg tag "$tag" \
--arg interface "$interface" \
--arg domain_resolver "$domain_resolver" \
'.outbounds += [
{
type: "direct",
tag: $tag,
bind_interface: $interface
}
+ (if $domain_resolver != "" then {domain_resolver: $domain_resolver} else {} end)
]'
}
#######################################
# Add a raw outbound JSON object to the outbounds section of a sing-box configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the outbound
# raw_outbound: JSON object, the raw outbound configuration to add
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_raw_outbound "$CONFIG" "raw-out" '{"type":"trojan","tag":"trojan-out","server":"127.0.0.1","server_port":1080,"password":"8JCsPssfgS8tiRwiMlhARg==","network":"tcp"}')
#######################################
sing_box_cm_add_raw_outbound() {
local config="$1"
local tag="$2"
local raw_outbound="$3"
echo "$config" | jq \
--arg tag "$tag" \
--argjson raw_outbound "$raw_outbound" \
'.outbounds += [(
$raw_outbound + {tag: $tag}
)]'
}
#######################################
# Add a URLTest outbound to the outbounds section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration
# tag: string, identifier for the URLTest outbound
# outbounds: JSON array of outbound tags to test
# url: URL to probe (optional)
# interval: test interval (e.g., "10s") (optional)
# tolerance: max latency difference tolerated (optional)
# idle_timeout: idle timeout duration (optional)
# interrupt_exist_connections: flag to interrupt existing connections ("true"/"false") (optional)
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_urltest_outbound "$CONFIG" "auto-select" '["proxy1","proxy2"]')
#######################################
sing_box_cm_add_urltest_outbound() {
local config="$1"
local tag="$2"
local outbounds="$3"
local url="$4"
local interval="$5"
local tolerance="$6"
local idle_timeout="$7"
local interrupt_exist_connections="$8"
echo "$config" | jq \
--arg tag "$tag" \
--argjson outbounds "$outbounds" \
--arg url "$url" \
--arg interval "$interval" \
--arg tolerance "$tolerance" \
--arg idle_timeout "$idle_timeout" \
--arg interrupt_exist_connections "$interrupt_exist_connections" \
'.outbounds += [
{
type: "urltest",
tag: $tag,
outbounds: $outbounds
}
+ (if $url != "" then {url: $url} else {} end)
+ (if $interval != "" then {interval: $interval} else {} end)
+ (if $tolerance != "" then {tolerance: ($tolerance | tonumber)} else {} end)
+ (if $idle_timeout != "" then {idle_timeout: $idle_timeout} else {} end)
+ (if $interrupt_exist_connections == "true" then {interrupt_exist_connections: true} else {} end)
]'
}
#######################################
# Add a Selector outbound to the outbounds section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration
# tag: string, identifier for the Selector outbound
# outbounds: JSON array of outbound tags to choose from
# default: default outbound tag if none selected (optional)
# interrupt_exist_connections: flag to interrupt existing connections ("true"/"false") (optional)
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_selector_outbound "$CONFIG" "select-proxy" '["proxy1","proxy2"]')
#######################################
sing_box_cm_add_selector_outbound() {
local config="$1"
local tag="$2"
local outbounds="$3"
local default="$4"
local interrupt_exist_connections="$5"
echo "$config" | jq \
--arg tag "$tag" \
--argjson outbounds "$outbounds" \
--arg default "$default" \
--arg interrupt_exist_connections "$interrupt_exist_connections" \
'.outbounds += [
{
type: "selector",
tag: $tag,
outbounds: $outbounds,
default: $default
}
+ (if $interrupt_exist_connections == "true" then {interrupt_exist_connections: true} else {} end)
]'
}
#######################################
# Configure the route section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# final: string, final outbound tag for unmatched traffic
# auto_detect_interface: boolean, enable or disable automatic interface detection
# default_domain_resolver: string, default DNS resolver for domain-based routing
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_configure_route "$CONFIG" "direct-out" true "udp-server")
#######################################
sing_box_cm_configure_route() {
local config="$1"
local final="$2"
local auto_detect_interface="$3"
local default_domain_resolver="$4"
echo "$config" | jq \
--arg final "$final" \
--argjson auto_detect_interface "$auto_detect_interface" \
--arg default_domain_resolver "$default_domain_resolver" \
'.route = {
rules: (.route.rules // []),
rule_set: (.route.rule_set // []),
final: $final,
auto_detect_interface: $auto_detect_interface,
default_domain_resolver: $default_domain_resolver
}'
}
#######################################
# Add a routing rule to the route section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the route rule
# inbound: string, inbound tag to match
# outbound: string, outbound tag to route matched traffic to
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_route_rule "$CONFIG" "main-route-rule" "tproxy-in" "main")
#######################################
sing_box_cm_add_route_rule() {
local config="$1"
local tag="$2"
local inbound="$3"
local outbound="$4"
echo "$config" | jq \
--arg service_tag "$SERVICE_TAG" \
--arg tag "$tag" \
--arg inbound "$inbound" \
--arg outbound "$outbound" \
'.route.rules += [{
action: "route",
inbound: $inbound,
outbound: $outbound,
$service_tag: $tag
}]'
}
#######################################
# Patch a routing rule in the route section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier of the route rule to patch
# key: string, the key in the rule to update or add
# value: JSON value to assign to the key
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_patch_route_rule "$CONFIG" "main-route-rule" "rule_set" "inline-ruleset")
#######################################
sing_box_cm_patch_route_rule() {
local config="$1"
local tag="$2"
local key="$3"
local value="$4"
value=$(_normalize_arg "$value")
echo "$config" | jq \
--arg service_tag "$SERVICE_TAG" \
--arg tag "$tag" \
--arg key "$key" \
--argjson value "$value" \
'import "helpers" as h {"search": "/usr/lib/podkop"};
.route.rules |= map(
if .[$service_tag] == $tag then
if has($key) then
.[$key] |= h::extend_key_value(.; $value)
else
. + { ($key): $value }
end
else
.
end
)'
}
#######################################
# Add a reject rule to the route section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# key: string, the key to set for the reject rule
# value: JSON value to assign to the key
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_reject_route_rule "$CONFIG" "reject-rule-tag")
#######################################
sing_box_cm_add_reject_route_rule() {
local config="$1"
local tag="$2"
local inbound="$3"
echo "$config" | jq \
--arg service_tag "$SERVICE_TAG" \
--arg tag "$tag" \
--arg inbound "$inbound" \
'.route.rules += [{
action: "reject",
inbound: $inbound,
$service_tag: $tag
}]'
}
#######################################
# Add a hijack-dns rule to the route section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# key: string, the key to set for the hijack-dns rule
# value: JSON value to assign to the key
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_hijack_dns_route_rule "$CONFIG" "protocol" "dns")
#######################################
sing_box_cm_add_hijack_dns_route_rule() {
local config="$1"
local key="$2"
local value="$3"
value=$(_normalize_arg "$value")
echo "$config" | jq \
--arg key "$key" \
--argjson value "$value" \
'.route.rules += [{
action: "hijack-dns",
($key): $value
}]'
}
#######################################
# Add a route-options rule to the route section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the route-options rule
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_options_route_rule "$CONFIG" "override-fakeip-port")
#######################################
sing_box_cm_add_options_route_rule() {
local config="$1"
local tag="$2"
echo "$config" | jq \
--arg service_tag "$SERVICE_TAG" \
--arg tag "$tag" \
'.route.rules += [{
action: "route-options",
$service_tag: $tag
}]'
}
#######################################
# Add a sniff rule to the route section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# key: string, the key to set for the sniff rule
# value: JSON value to assign to the key
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_sniff_route_rule "$CONFIG" "inbound" "tproxy-in")
# CONFIG=$(sing_box_cm_sniff_route_rule "$CONFIG" "inbound" '["tproxy-in","dns-in"]')
#######################################
sing_box_cm_sniff_route_rule() {
local config="$1"
local key="$2"
local value="$3"
value=$(_normalize_arg "$value")
echo "$config" | jq \
--arg key "$key" \
--argjson value "$value" \
'.route.rules += [{
action: "sniff",
($key): $value
}]'
}
#######################################
# Add an inline ruleset to the route.rule_set section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the inline ruleset
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_inline_ruleset "$CONFIG" "inline-ruleset")
#######################################
sing_box_cm_add_inline_ruleset() {
local config="$1"
local tag="$2"
echo "$config" | jq \
--arg tag "$tag" \
'.route.rule_set += [{
type: "inline",
tag: $tag
}]'
}
#######################################
# Add or update a rule in an inline ruleset within the route.rule_set section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier of the inline ruleset
# key: string, the key in the ruleset to update or add
# value: JSON value to assign to the key
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_inline_ruleset_rule "$CONFIG" "inline-ruleset" "domain_suffix" "telegram.org")
# CONFIG=$(sing_box_cm_add_inline_ruleset_rule "$CONFIG" "inline-ruleset" "domain_suffix" "discord.com")
# CONFIG=$(sing_box_cm_add_inline_ruleset_rule "$CONFIG" "inline-ruleset" "ip_cidr" "111.111.111.111/32")
#######################################
sing_box_cm_add_inline_ruleset_rule() {
local config="$1"
local tag="$2"
local key="$3"
local value="$4"
value=$(_normalize_arg "$value")
echo "$config" | jq -L /usr/lib/podkop \
--arg tag "$tag" \
--arg key "$key" \
--argjson value "$value" \
'import "helpers" as h {"search": "/usr/lib/podkop"};
.route.rule_set |= map(
if .tag == $tag then
if has($key) then
.[$key] |= h::extend_key_value(.; $value)
else
. + { ($key): $value }
end
else
.
end
)'
}
#######################################
# Add a local ruleset to the route.rule_set section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the local ruleset
# format: string, format of the local ruleset ("source" or "binary")
# path: string, file path to the local ruleset
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_local_ruleset "$CONFIG" "local-source-ruleset" "source" "/tmp/local-ruleset.json")
# CONFIG=$(sing_box_cm_add_local_ruleset "$CONFIG" "local-binary-ruleset" "binary" "/tmp/local-ruleset.srs")
#######################################
sing_box_cm_add_local_ruleset() {
local config="$1"
local tag="$2"
local format="$3"
local path="$4"
echo "$config" | jq \
--arg tag "$tag" \
--arg format "$format" \
--arg path "$path" \
'.route.rule_set += [{
type: "local",
tag: $tag,
format: $format,
path: $path
}]'
}
#######################################
# Add a remote ruleset to the route.rule_set section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# tag: string, identifier for the remote ruleset
# format: string, format of the remote ruleset ("source" or "binary")
# url: string, URL to download the ruleset from
# download_detour: string, optional detour tag for downloading
# update_interval: string, optional update interval for the ruleset
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_add_remote_ruleset "$CONFIG" "remote-source-ruleset" "source" "https://example.com/telegram.json")
#######################################
sing_box_cm_add_remote_ruleset() {
local config="$1"
local tag="$2"
local format="$3"
local url="$4"
local download_detour="$5"
local update_interval="$6"
echo "$config" | jq \
--arg tag "$tag" \
--arg format "$format" \
--arg url "$url" \
--arg download_detour "$download_detour" \
--arg update_interval "$update_interval" \
'.route.rule_set += [(
{
type: "remote",
tag: $tag,
format: $format,
url: $url
}
+ (if $download_detour != "" then { download_detour: $download_detour } else {} end)
+ (if $update_interval != "" then { update_interval: $update_interval } else {} end)
)]'
}
#######################################
# Configure the experimental cache_file section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# enabled: boolean, enable or disable file caching
# path: string, file path for cache storage
# store_fakeip: boolean, whether to store fake IPs in the cache
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_configure_cache_file "$CONFIG" true "/tmp/cache.db" true)
#######################################
sing_box_cm_configure_cache_file() {
local config="$1"
local enabled="$2"
local path="$3"
local store_fakeip="$4"
echo "$config" | jq \
--argjson enabled "$enabled" \
--arg path "$path" \
--argjson store_fakeip "$store_fakeip" \
'.experimental.cache_file = {
enabled: $enabled,
path: $path,
store_fakeip: $store_fakeip
}'
}
#######################################
# Configure the experimental clash_api section of a sing-box JSON configuration.
# Arguments:
# config: JSON configuration (string)
# external_controller: string, URL or path for the external controller
# external_ui: string, URL or path for the external UI
# Outputs:
# Writes updated JSON configuration to stdout
# Example:
# CONFIG=$(sing_box_cm_configure_clash_api "$CONFIG" "192.168.1.1:9090" "ui")
#######################################
sing_box_cm_configure_clash_api() {
local config="$1"
local external_controller="$2"
local external_ui="$3"
echo "$config" | jq \
--arg external_controller "$external_controller" \
--arg external_ui "$external_ui" \
'.experimental.clash_api = {
external_controller: $external_controller,
external_ui: $external_ui
}'
}
#######################################
# Create a local source ruleset JSON file for sing-box.
# Arguments:
# filepath: path to the JSON file to create
# Example:
# sing_box_cm_create_local_source_ruleset "/tmp/sing-box/ruleset.json"
#######################################
sing_box_cm_create_local_source_ruleset() {
local filepath="$1"
jq -n '{version: 3, rules: []}' > "$filepath"
}
#######################################
# Patch a local source ruleset JSON file for sing-box by adding unique! values to a given key.
# Arguments:
# filepath: path to the JSON file to patch
# key: the ruleset key to update (e.g., "ip_cidr")
# value: a JSON array of values to add to the key
# Example:
# sing_box_cm_patch_local_source_ruleset_rules "/tmp/sing-box/ruleset.json" "ip_cidr" '["1.1.1.1","2.2.2.2"]'
#######################################
sing_box_cm_patch_local_source_ruleset_rules() {
local filepath="$1"
local key="$2"
local value="$3"
value=$(_normalize_arg "$value")
local content
content="$(cat "$filepath")"
echo "$content" | jq \
--arg key "$key" \
--argjson value "$value" '
([.rules[]?[$key][]] | unique) as $existing
| ($value - $existing) as $value
| if ($value | length) > 0 then
.rules += [{($key): $value}]
else
.
end
' > "$filepath"
}
#######################################
# Save a sing-box JSON configuration to a file, removing service-specific tags.
# Arguments:
# config: JSON configuration (string)
# file_path: string, path to save the configuration file
# Outputs:
# Writes the cleaned JSON configuration to the specified file
# Example:
# sing_box_cm_save_config_to_file "$CONFIG" "/tmp/sing-box-config.json"
#######################################
sing_box_cm_save_config_to_file() {
local config="$1"
local filepath="$2"
echo "$config" | jq \
--arg tag "$SERVICE_TAG" \
'walk(if type == "object" then del(.[$tag]) else . end)' \
> "$filepath"
}
_normalize_arg() {
local value="$1"
if echo "$value" | jq -e . > /dev/null 2>&1; then
printf '%s' "$value"
else
printf '%s' "$value" | jq -R .
fi
}