feat: add DoH blocking, subscription keyword filtering, bulk proxy text inputs, and insecure TLS flag
This commit is contained in:
@@ -258,6 +258,11 @@ assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tTachyonTable\ttachyon_inte
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\telement\tinet\tTachyonTable\ttachyon_interfaces\t{ tun0 }' "runtime base from UCI tun0 interface"
|
||||
assert_contains "$NFT_LOG" $'nft\tinsert\trule\tinet\tTachyonTable\tmangle\tudp\tdport\t123\treturn' "runtime base from UCI ntp exclusion"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-create-runtime-base TachyonTable localv4 tachyon_subnets tachyon_ports tachyon_ip_ports tachyon_interfaces "br-lan" 0x00100000 0x00200000 198.18.0.0/15 1602 0 "" "" "" "" "" 1
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tTachyonTable\tmangle\tiifname\t@tachyon_interfaces\tip\tdaddr\t1.1.1.1/32\tmeta\tmark\tset\t0x00100000\tcounter' "runtime doh block marking rule"
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tTachyonTable\tmangle\tiifname\t@tachyon_interfaces\tip6\tdaddr\t2606:4700:4700::1111/128\tmeta\tmark\tset\t0x00100000\tcounter' "runtime doh6 block marking rule"
|
||||
|
||||
: > "$NFT_LOG"
|
||||
nft_ucode nft-create-runtime-output-rules TachyonTable localv4 tachyon_subnets tachyon_ports tachyon_ip_ports 0x00100000 198.18.0.0/15
|
||||
assert_contains "$NFT_LOG" $'nft\tadd\trule\tinet\tTachyonTable\tmangle_output\tip\tdaddr\t@tachyon_subnets\tmeta\tl4proto\ttcp\tmeta\tmark\tset\t0x00100000\tcounter' "runtime output common tcp"
|
||||
@@ -672,6 +677,8 @@ cat >"$WORK_DIR/signature-expected.txt" <<'EOF_EXPECTED'
|
||||
br-lan tun0
|
||||
[settings.exclude_ntp]
|
||||
1
|
||||
[settings.block_doh]
|
||||
0
|
||||
[settings.game_console_optimizer]
|
||||
0
|
||||
[settings.game_console_ips]
|
||||
@@ -766,6 +773,8 @@ cat >"$WORK_DIR/signature-uci-expected.txt" <<'EOF_EXPECTED'
|
||||
br-lan tun0
|
||||
[settings.exclude_ntp]
|
||||
1
|
||||
[settings.block_doh]
|
||||
0
|
||||
[settings.game_console_optimizer]
|
||||
0
|
||||
[settings.game_console_ips]
|
||||
@@ -816,6 +825,8 @@ cat >"$WORK_DIR/signature-defaults-expected.txt" <<'EOF_EXPECTED'
|
||||
br-lan
|
||||
[settings.exclude_ntp]
|
||||
0
|
||||
[settings.block_doh]
|
||||
0
|
||||
[settings.game_console_optimizer]
|
||||
0
|
||||
[settings.game_console_ips]
|
||||
|
||||
@@ -563,6 +563,8 @@ sequential
|
||||
wan
|
||||
[settings.disable_quic]
|
||||
1
|
||||
[settings.block_doh]
|
||||
0
|
||||
[settings.update_interval]
|
||||
2h
|
||||
[settings.cache_path]
|
||||
|
||||
@@ -1043,6 +1043,81 @@ JSON
|
||||
printf '%s' 'https://example.com/only-xhttp.json' >"$WORK_DIR/subscriptions/only_xhttp-subscription-1.url"
|
||||
printf '%s' 'Happ' >"$WORK_DIR/subscriptions/only_xhttp-subscription-1.user_agent"
|
||||
|
||||
cat >"$WORK_DIR/block-doh-fixture.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
"config_path": "/tmp/sing-box/config.json",
|
||||
"dns_server": "1.1.1.1",
|
||||
"service_listen_address": "127.0.0.1",
|
||||
"block_doh": "1"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
".name": "doh_test",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "proxy",
|
||||
"selector_proxy_links": [ "vless://00000000-0000-0000-0000-000000000001@127.0.0.1:443?encryption=none#dummy" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
cat >"$WORK_DIR/bulk-proxy-links-fixture.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
"config_path": "/tmp/sing-box/config.json",
|
||||
"dns_server": "1.1.1.1",
|
||||
"service_listen_address": "127.0.0.1"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
".name": "sec_test",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "proxy",
|
||||
"selector_proxy_links_text": "vless://11111111-1111-4111-8111-111111111111@127.0.0.1:443?encryption=none#BulkOne\n# comment\nvless://22222222-2222-4222-8222-222222222222@127.0.0.1:444?encryption=none#BulkTwo",
|
||||
"urltest_proxy_links_text": "vless://33333333-3333-4333-8333-333333333333@127.0.0.1:445?encryption=none#BulkUT"
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
cat >"$WORK_DIR/subscription-filter-fixture.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
"config_path": "/tmp/sing-box/config.json",
|
||||
"dns_server": "1.1.1.1",
|
||||
"service_listen_address": "127.0.0.1"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
".name": "sub_test",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "proxy",
|
||||
"subscription_urls": [ "https://example.com/filter-sub.json" ],
|
||||
"subscription_filter_include_keywords": [ "нидерланды", "германия" ],
|
||||
"subscription_filter_exclude_keywords": [ "ads" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
cat >"$WORK_DIR/subscriptions/sub_test-subscription-1.json" <<'JSON'
|
||||
{
|
||||
"outbounds": [
|
||||
{ "type": "socks", "tag": "nl-fast", "remark": "Нидерланды Fast", "server": "127.0.0.1", "server_port": 1080 },
|
||||
{ "type": "socks", "tag": "nl-ads", "remark": "Нидерланды Ads Server", "server": "127.0.0.2", "server_port": 1080 },
|
||||
{ "type": "socks", "tag": "de-highspeed", "remark": "ГЕРМАНИЯ Premium", "server": "127.0.0.3", "server_port": 1080 },
|
||||
{ "type": "socks", "tag": "us-proxy", "remark": "США Server", "server": "127.0.0.4", "server_port": 1080 }
|
||||
]
|
||||
}
|
||||
JSON
|
||||
printf '%s' 'https://example.com/filter-sub.json' >"$WORK_DIR/subscriptions/sub_test-subscription-1.url"
|
||||
|
||||
generate_config "$WORK_DIR/block-doh-fixture.json" "$WORK_DIR/block-doh.json"
|
||||
generate_config "$WORK_DIR/bulk-proxy-links-fixture.json" "$WORK_DIR/bulk-proxy-links.json"
|
||||
generate_config_with_subscription_cache "$WORK_DIR/subscription-filter-fixture.json" "$WORK_DIR/subscription-filter.json"
|
||||
generate_config "$WORK_DIR/disabled-updates-fixture.json" "$WORK_DIR/disabled.json"
|
||||
generate_config "$WORK_DIR/default-updates-fixture.json" "$WORK_DIR/default.json"
|
||||
generate_config "$WORK_DIR/server-inbound-fixture.json" "$WORK_DIR/server.json"
|
||||
@@ -1428,6 +1503,22 @@ assert(proxy_metadata[0].sourceSection == "proxy-subscription-1" && proxy_metada
|
||||
assert(proxy_metadata[1].sourceSection == "proxy-subscription-2" && proxy_metadata[1].title == "WolfPN", "proxy source 2 metadata marker");
|
||||
assert(length(test_metadata) == 1, "same subscription URL metadata kept for second section");
|
||||
assert(test_metadata[0].sourceSection == "test-subscription-1" && test_metadata[0].title == "WolfPN", "test source metadata marker");
|
||||
|
||||
let doh_cfg = cfg("block-doh");
|
||||
assert(route_rule(doh_cfg, r => r.action == "reject" && contains(r.inbound, "tproxy-in") && contains(r.ip_cidr, "1.1.1.1/32")) != null, "block_doh IPv4 reject rule present");
|
||||
assert(route_rule(doh_cfg, r => r.action == "reject" && contains(r.inbound, "tproxy-in") && contains(r.ip_cidr, "2606:4700:4700::1111/128")) != null, "block_doh IPv6 reject rule present");
|
||||
|
||||
let bulk_cfg = cfg("bulk-proxy-links");
|
||||
assert(outbound(bulk_cfg, "sec_test-1-out") != null, "bulk selector proxy link 1 created");
|
||||
assert(outbound(bulk_cfg, "sec_test-2-out") != null, "bulk selector proxy link 2 created");
|
||||
assert(outbound(bulk_cfg, "sec_test-3-out") != null, "bulk urltest proxy link created");
|
||||
assert(outbound(bulk_cfg, "sec_test-urltest-out") != null, "bulk urltest group created");
|
||||
|
||||
let filter_cfg = cfg("subscription-filter");
|
||||
assert(outbound(filter_cfg, "nl-fast") != null, "include keyword match preserves node");
|
||||
assert(outbound(filter_cfg, "de-highspeed") != null, "case-insensitive Cyrillic include keyword match preserves node");
|
||||
assert(outbound(filter_cfg, "nl-ads") == null, "exclude keyword drops node even if included");
|
||||
assert(outbound(filter_cfg, "us-proxy") == null, "unmatched include keyword drops node");
|
||||
' "$WORK_DIR"
|
||||
|
||||
printf 'sing-box runtime checks passed\n'
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CACHE_UC="$ROOT_DIR/tachyon/files/usr/lib/subscription/cache.uc"
|
||||
CONNECTIONS_UC="$ROOT_DIR/tachyon/files/usr/lib/config/connections.uc"
|
||||
TACHYON_LIB="$ROOT_DIR/tachyon/files/usr/lib"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$WORK_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
local file="$1"
|
||||
local expected="$2"
|
||||
local label="$3"
|
||||
|
||||
grep -Fq -- "$expected" "$file" || fail "$label: expected '$expected' in '$file'"
|
||||
}
|
||||
|
||||
assert_not_contains() {
|
||||
local file="$1"
|
||||
local expected="$2"
|
||||
local label="$3"
|
||||
|
||||
if grep -Fq -- "$expected" "$file"; then
|
||||
fail "$label: unexpected '$expected' in '$file'"
|
||||
fi
|
||||
}
|
||||
|
||||
mkdir -p "$WORK_DIR/bin"
|
||||
cat >"$WORK_DIR/bin/curl" <<SH
|
||||
#!/bin/sh
|
||||
printf '%s\n' "\$*" >>"$WORK_DIR/curl.log"
|
||||
target=""
|
||||
while [ \$# -gt 0 ]; do
|
||||
case "\$1" in
|
||||
-o)
|
||||
target="\$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [ -n "\$target" ]; then
|
||||
printf '%s\n' "dummy subscription data" > "\$target"
|
||||
fi
|
||||
exit 0
|
||||
SH
|
||||
chmod +x "$WORK_DIR/bin/curl"
|
||||
|
||||
export PATH="$WORK_DIR/bin:$PATH"
|
||||
|
||||
cache_ucode() {
|
||||
ucode -L "$TACHYON_LIB" "$CACHE_UC" "$@"
|
||||
}
|
||||
|
||||
# Test 1: cache.download_subscription with allow_insecure = 1
|
||||
: >"$WORK_DIR/curl.log"
|
||||
cache_ucode download-subscription "https://example.com/sub" "$WORK_DIR/sub.txt" "" "" "" "" 1
|
||||
assert_contains "$WORK_DIR/curl.log" "-k" "download_subscription with allow_insecure=1 must pass -k to curl"
|
||||
|
||||
# Test 2: cache.download_subscription with allow_insecure = 0
|
||||
: >"$WORK_DIR/curl.log"
|
||||
cache_ucode download-subscription "https://example.com/sub" "$WORK_DIR/sub.txt" "" "" "" "" 0
|
||||
assert_not_contains "$WORK_DIR/curl.log" "-k" "download_subscription with allow_insecure=0 must not pass -k to curl"
|
||||
|
||||
# Test 3: connections.uc subscription_insecure helper contract
|
||||
ucode -L "$TACHYON_LIB" -e '
|
||||
let conn = require("config.connections");
|
||||
let sec1 = { "subscription_insecure": "1" };
|
||||
let sec0 = { "subscription_insecure": "0" };
|
||||
let sec_unset = {};
|
||||
if (conn.subscription_insecure(sec1) !== "1") exit(1);
|
||||
if (conn.subscription_insecure(sec0) !== "0") exit(2);
|
||||
if (conn.subscription_insecure(sec_unset) !== "0") exit(3);
|
||||
' || fail "connections.subscription_insecure getter contract"
|
||||
|
||||
printf 'subscription insecure checks passed\n'
|
||||
Reference in New Issue
Block a user