|
8 | 8 | cfg = config.services.zapret; |
9 | 9 |
|
10 | 10 | whitelist = lib.optionalString ( |
11 | | - cfg.whitelist != null |
| 11 | + (builtins.length cfg.whitelist) != 0 |
12 | 12 | ) "--hostlist ${pkgs.writeText "zapret-whitelist" (lib.concatStringsSep "\n" cfg.whitelist)}"; |
13 | 13 |
|
14 | 14 | blacklist = |
15 | | - lib.optionalString (cfg.blacklist != null) |
| 15 | + lib.optionalString ((builtins.length cfg.blacklist) != 0) |
16 | 16 | "--hostlist-exclude ${pkgs.writeText "zapret-blacklist" (lib.concatStringsSep "\n" cfg.blacklist)}"; |
17 | 17 |
|
18 | | - ports = if cfg.httpSupport then "80,443" else "443"; |
| 18 | + params = lib.concatStringsSep " " cfg.params; |
| 19 | + |
| 20 | + qnum = toString cfg.qnum; |
19 | 21 | in |
20 | 22 | { |
21 | 23 | options.services.zapret = { |
|
29 | 31 | "--dpi-desync=fake,disorder2" |
30 | 32 | "--dpi-desync-ttl=1" |
31 | 33 | "--dpi-desync-autottl=2" |
32 | | - ]; |
| 34 | + ] |
33 | 35 | ''; |
34 | 36 | description = '' |
35 | 37 | Specify the bypass parameters for Zapret binary. |
|
40 | 42 | ''; |
41 | 43 | }; |
42 | 44 | whitelist = lib.mkOption { |
43 | | - default = null; |
44 | | - type = with lib.types; nullOr (listOf str); |
| 45 | + default = [ ]; |
| 46 | + type = with lib.types; listOf str; |
45 | 47 | example = '' |
46 | 48 | [ |
47 | 49 | "youtube.com" |
|
59 | 61 | ''; |
60 | 62 | }; |
61 | 63 | blacklist = lib.mkOption { |
62 | | - default = null; |
63 | | - type = with lib.types; nullOr (listOf str); |
| 64 | + default = [ ]; |
| 65 | + type = with lib.types; listOf str; |
64 | 66 | example = '' |
65 | 67 | [ |
66 | 68 | "example.com" |
|
96 | 98 | Http bypass rarely works and you might want to disable it if you don't utilise http connections. |
97 | 99 | ''; |
98 | 100 | }; |
| 101 | + httpMode = lib.mkOption { |
| 102 | + default = "first"; |
| 103 | + type = lib.types.enum [ |
| 104 | + "first" |
| 105 | + "full" |
| 106 | + ]; |
| 107 | + example = "full"; |
| 108 | + description = '' |
| 109 | + By default this service only changes the first packet sent, which is enough in most cases. |
| 110 | + But there are DPIs that monitor the whole traffic within a session. |
| 111 | + That requires full processing of every packet, which increases the CPU usage. |
| 112 | +
|
| 113 | + Set the mode to `full` if http doesn't work. |
| 114 | + ''; |
| 115 | + }; |
| 116 | + udpSupport = lib.mkOption { |
| 117 | + default = false; |
| 118 | + type = lib.types.bool; |
| 119 | + description = '' |
| 120 | + Enable UDP routing. |
| 121 | + This requires you to specify `udpPorts` and `--dpi-desync-any-protocol` parameter. |
| 122 | + ''; |
| 123 | + }; |
| 124 | + udpPorts = lib.mkOption { |
| 125 | + default = [ ]; |
| 126 | + type = with lib.types; listOf str; |
| 127 | + example = '' |
| 128 | + [ |
| 129 | + "50000:50099" |
| 130 | + "1234" |
| 131 | + ] |
| 132 | + ''; |
| 133 | + description = '' |
| 134 | + List of UDP ports to route. |
| 135 | + Port ranges are delimited with a colon like this "50000:50099". |
| 136 | + ''; |
| 137 | + }; |
99 | 138 | }; |
100 | 139 |
|
101 | 140 | config = lib.mkIf cfg.enable ( |
102 | 141 | lib.mkMerge [ |
103 | 142 | { |
104 | 143 | assertions = [ |
105 | 144 | { |
106 | | - assertion = (cfg.whitelist == null) || (cfg.blacklist == null); |
| 145 | + assertion = (builtins.length cfg.whitelist) == 0 || (builtins.length cfg.blacklist) == 0; |
107 | 146 | message = "Can't specify both whitelist and blacklist."; |
108 | 147 | } |
109 | 148 | { |
110 | 149 | assertion = (builtins.length cfg.params) != 0; |
111 | 150 | message = "You have to specify zapret parameters. See the params option's description."; |
112 | 151 | } |
| 152 | + { |
| 153 | + assertion = cfg.udpSupport -> (builtins.length cfg.udpPorts) != 0; |
| 154 | + message = "You have to specify UDP ports or disable UDP support."; |
| 155 | + } |
| 156 | + { |
| 157 | + assertion = !cfg.configureFirewall || !config.networking.nftables.enable; |
| 158 | + message = "You need to manually configure you firewall for Zapret service when using nftables."; |
| 159 | + } |
113 | 160 | ]; |
114 | 161 |
|
115 | 162 | systemd.services.zapret = { |
116 | 163 | description = "DPI bypass service"; |
117 | 164 | wantedBy = [ "multi-user.target" ]; |
118 | 165 | after = [ "network.target" ]; |
119 | 166 | serviceConfig = { |
120 | | - ExecStart = "${cfg.package}/bin/nfqws --pidfile=/run/nfqws.pid ${lib.concatStringsSep " " cfg.params} ${whitelist} ${blacklist} --qnum=${toString cfg.qnum}"; |
| 167 | + ExecStart = "${cfg.package}/bin/nfqws --pidfile=/run/nfqws.pid ${params} ${whitelist} ${blacklist} --qnum=${qnum}"; |
121 | 168 | Type = "simple"; |
122 | 169 | PIDFile = "/run/nfqws.pid"; |
123 | 170 | Restart = "always"; |
124 | | - RuntimeMaxSec = "1h"; # This service loves to crash silently or cause network slowdowns. It also restarts instantly. In my experience restarting it hourly provided the best experience. |
| 171 | + RuntimeMaxSec = "1h"; # This service loves to crash silently or cause network slowdowns. It also restarts instantly. Restarting it at least hourly provided the best experience. |
125 | 172 |
|
126 | | - # hardening |
| 173 | + # Hardening. |
127 | 174 | DevicePolicy = "closed"; |
128 | 175 | KeyringMode = "private"; |
129 | 176 | PrivateTmp = true; |
|
145 | 192 |
|
146 | 193 | # Route system traffic via service for specified ports. |
147 | 194 | (lib.mkIf cfg.configureFirewall { |
148 | | - networking.firewall.extraCommands = '' |
149 | | - iptables -t mangle -I POSTROUTING -p tcp -m multiport --dports ${ports} -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num ${toString cfg.qnum} --queue-bypass |
150 | | - ''; |
| 195 | + networking.firewall.extraCommands = |
| 196 | + let |
| 197 | + httpParams = lib.optionalString ( |
| 198 | + cfg.httpMode == "first" |
| 199 | + ) "-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6"; |
| 200 | + |
| 201 | + udpPorts = lib.concatStringsSep "," cfg.udpPorts; |
| 202 | + in |
| 203 | + '' |
| 204 | + ip46tables -t mangle -I POSTROUTING -p tcp --dport 443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num ${qnum} --queue-bypass |
| 205 | + '' |
| 206 | + + lib.optionalString (cfg.httpSupport) '' |
| 207 | + ip46tables -t mangle -I POSTROUTING -p tcp --dport 80 ${httpParams} -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num ${qnum} --queue-bypass |
| 208 | + '' |
| 209 | + + lib.optionalString (cfg.udpSupport) '' |
| 210 | + ip46tables -t mangle -A POSTROUTING -p udp -m multiport --dports ${udpPorts} -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num ${qnum} --queue-bypass |
| 211 | + ''; |
151 | 212 | }) |
152 | 213 | ] |
153 | 214 | ); |
|
0 commit comments