1 import ./make-test-python.nix (
4 wg-keys = import ./wireguard/snakeoil-keys.nix;
6 target_host = "acme.test";
7 server_host = "sing-box.test";
10 "${target_host}" = "1.1.1.1";
11 "${server_host}" = "1.1.1.2";
13 hostsEntries = lib.mapAttrs' (k: v: {
15 value = lib.singleton k;
19 vmessUUID = "bf000d23-0752-40b4-affe-68f7707a9661";
22 tag = "inbound:vmess";
24 listen_port = vmessPort;
35 tag = "outbound:vmess";
37 server_port = vmessPort;
46 interface_name = "tun0";
52 iproute2_table_index = 2024;
53 iproute2_rule_index = 9001;
55 "${hosts."${target_host}"}/32"
57 route_exclude_address = [
58 "${hosts."${server_host}"}/32"
62 sniff_override_destination = false;
66 tproxyPost = pkgs.writeShellApplication {
68 runtimeInputs = with pkgs; [
73 ip route add local default dev lo table 100
74 ip rule add fwmark 1 table 100
76 iptables -t mangle -N SING_BOX
77 iptables -t mangle -A SING_BOX -d 100.64.0.0/10 -j RETURN
78 iptables -t mangle -A SING_BOX -d 127.0.0.0/8 -j RETURN
79 iptables -t mangle -A SING_BOX -d 169.254.0.0/16 -j RETURN
80 iptables -t mangle -A SING_BOX -d 172.16.0.0/12 -j RETURN
81 iptables -t mangle -A SING_BOX -d 192.0.0.0/24 -j RETURN
82 iptables -t mangle -A SING_BOX -d 224.0.0.0/4 -j RETURN
83 iptables -t mangle -A SING_BOX -d 240.0.0.0/4 -j RETURN
84 iptables -t mangle -A SING_BOX -d 255.255.255.255/32 -j RETURN
86 iptables -t mangle -A SING_BOX -d ${hosts."${server_host}"}/32 -p tcp -j RETURN
87 iptables -t mangle -A SING_BOX -d ${hosts."${server_host}"}/32 -p udp -j RETURN
89 iptables -t mangle -A SING_BOX -d ${hosts."${target_host}"}/32 -p tcp -j TPROXY --on-port ${toString tproxyPort} --tproxy-mark 1
90 iptables -t mangle -A SING_BOX -d ${hosts."${target_host}"}/32 -p udp -j TPROXY --on-port ${toString tproxyPort} --tproxy-mark 1
91 iptables -t mangle -A PREROUTING -j SING_BOX
93 iptables -t mangle -N SING_BOX_SELF
94 iptables -t mangle -A SING_BOX_SELF -d 100.64.0.0/10 -j RETURN
95 iptables -t mangle -A SING_BOX_SELF -d 127.0.0.0/8 -j RETURN
96 iptables -t mangle -A SING_BOX_SELF -d 169.254.0.0/16 -j RETURN
97 iptables -t mangle -A SING_BOX_SELF -d 172.16.0.0/12 -j RETURN
98 iptables -t mangle -A SING_BOX_SELF -d 192.0.0.0/24 -j RETURN
99 iptables -t mangle -A SING_BOX_SELF -d 224.0.0.0/4 -j RETURN
100 iptables -t mangle -A SING_BOX_SELF -d 240.0.0.0/4 -j RETURN
101 iptables -t mangle -A SING_BOX_SELF -d 255.255.255.255/32 -j RETURN
102 iptables -t mangle -A SING_BOX_SELF -j RETURN -m mark --mark 1234
104 iptables -t mangle -A SING_BOX_SELF -d ${hosts."${server_host}"}/32 -p tcp -j RETURN
105 iptables -t mangle -A SING_BOX_SELF -d ${hosts."${server_host}"}/32 -p udp -j RETURN
106 iptables -t mangle -A SING_BOX_SELF -p tcp -j MARK --set-mark 1
107 iptables -t mangle -A SING_BOX_SELF -p udp -j MARK --set-mark 1
108 iptables -t mangle -A OUTPUT -j SING_BOX_SELF
117 maintainers = with lib.maintainers; [ nickcao ];
125 firewall.enable = false;
126 hosts = hostsEntries;
131 address = hosts."${target_host}";
138 services.dnsmasq.enable = true;
142 package = pkgs.nginxQuic;
144 virtualHosts."${target_host}" = {
146 sslCertificate = ./common/acme/server/acme.test.cert.pem;
147 sslCertificateKey = ./common/acme/server/acme.test.key.pem;
155 default_type text/plain;
156 return 200 "$server_protocol $remote_addr";
157 allow ${hosts."${server_host}"}/32;
168 boot.kernel.sysctl = {
169 "net.ipv4.conf.all.forwarding" = 1;
173 firewall.enable = false;
174 hosts = hostsEntries;
179 address = hosts."${server_host}";
186 systemd.network.wait-online.ignoredInterfaces = [ "wg0" ];
188 networking.wg-quick.interfaces.wg0 = {
195 inherit (wg-keys.peer0) privateKey;
197 peers = lib.singleton {
202 inherit (wg-keys.peer1) publicKey;
206 ${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -j ACCEPT
207 ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.23.42.0/24 -o eth1 -j MASQUERADE
211 services.sing-box = {
220 tag = "outbound:direct";
231 firewall.enable = false;
232 hosts = hostsEntries;
244 security.pki.certificates = [
245 (builtins.readFile ./common/acme/server/ca.cert.pem)
248 environment.systemPackages = [
253 services.sing-box = {
262 tag = "outbound:block";
266 tag = "outbound:direct";
271 final = "outbound:block";
277 outbound = "outbound:vmess";
289 firewall.enable = false;
290 hosts = hostsEntries;
302 security.pki.certificates = [
303 (builtins.readFile ./common/acme/server/ca.cert.pem)
306 environment.systemPackages = [
311 services.sing-box = {
317 tag = "outbound:block";
321 tag = "outbound:direct";
324 detour = "outbound:direct";
326 tag = "outbound:wireguard";
327 interface_name = "wg0";
328 local_address = [ "10.23.42.2/32" ];
330 private_key = wg-keys.peer1.privateKey;
331 peer_public_key = wg-keys.peer0.publicKey;
332 server = server_host;
334 system_interface = true;
338 final = "outbound:block";
348 firewall.enable = false;
349 hosts = hostsEntries;
361 security.pki.certificates = [
362 (builtins.readFile ./common/acme/server/ca.cert.pem)
365 environment.systemPackages = [ pkgs.curlHTTP3 ];
367 systemd.services.sing-box.serviceConfig.ExecStartPost = [
368 "+${tproxyPost}/bin/exe"
371 services.sing-box = {
376 tag = "inbound:tproxy";
379 listen_port = tproxyPort;
382 sniff_override_destination = false;
388 tag = "outbound:block";
392 tag = "outbound:direct";
397 final = "outbound:block";
403 outbound = "outbound:vmess";
415 firewall.enable = false;
416 hosts = hostsEntries;
428 environment.systemPackages = [ pkgs.dnsutils ];
430 services.sing-box = {
434 final = "dns:default";
435 independent_cache = true;
438 "inet4_range" = "198.18.0.0/16";
442 detour = "outbound:direct";
444 address = hosts."${target_host}";
453 outbound = [ "any" ];
454 server = "dns:default";
461 server = "dns:fakeip";
472 tag = "outbound:block";
476 tag = "outbound:direct";
480 tag = "outbound:dns";
484 final = "outbound:direct";
488 outbound = "outbound:dns";
498 target.wait_for_unit("nginx.service")
499 target.wait_for_open_port(443)
500 target.wait_for_unit("dnsmasq.service")
501 target.wait_for_open_port(53)
503 server.wait_for_unit("sing-box.service")
504 server.wait_for_open_port(1080)
505 server.wait_for_unit("wg-quick-wg0.service")
506 server.wait_for_file("/sys/class/net/wg0")
508 def test_curl(machine, extra_args=""):
510 machine.succeed(f"curl --fail --max-time 10 --http2 https://${target_host} {extra_args}")
511 == "HTTP/2.0 ${hosts.${server_host}}"
514 machine.succeed(f"curl --fail --max-time 10 --http3-only https://${target_host} {extra_args}")
515 == "HTTP/3.0 ${hosts.${server_host}}"
519 tun.wait_for_unit("sing-box.service")
520 tun.wait_for_unit("sys-devices-virtual-net-${tunInbound.interface_name}.device")
521 tun.wait_until_succeeds("ip route get ${hosts."${target_host}"} | grep 'dev ${tunInbound.interface_name}'")
522 tun.succeed("ip addr show ${tunInbound.interface_name}")
523 tun.succeed("ip route show table ${toString tunInbound.iproute2_table_index} | grep ${tunInbound.interface_name}")
525 tun.succeed("ip rule list table ${toString tunInbound.iproute2_table_index} | sort | head -1 | awk -F: '{print $1}' | tr -d '\n'")
526 == "${toString tunInbound.iproute2_rule_index}"
530 with subtest("wireguard"):
531 wireguard.wait_for_unit("sing-box.service")
532 wireguard.wait_for_unit("sys-devices-virtual-net-wg0.device")
533 wireguard.succeed("ip addr show wg0")
534 test_curl(wireguard, "--interface wg0")
536 with subtest("tproxy"):
537 tproxy.wait_for_unit("sing-box.service")
540 with subtest("fakeip"):
541 fakeip.wait_for_unit("sing-box.service")
542 fakeip.wait_for_unit("sys-devices-virtual-net-${tunInbound.interface_name}.device")
543 fakeip.wait_until_succeeds("ip route get ${hosts."${target_host}"} | grep 'dev ${tunInbound.interface_name}'")
544 fakeip.succeed("dig +short A ${target_host} @${target_host} | grep '^198.18.'")