8 cfg = config.services.zapret;
10 whitelist = lib.optionalString (
11 (builtins.length cfg.whitelist) != 0
12 ) "--hostlist ${pkgs.writeText "zapret-whitelist" (lib.concatStringsSep "\n" cfg.whitelist)}";
15 lib.optionalString ((builtins.length cfg.blacklist) != 0)
16 "--hostlist-exclude ${pkgs.writeText "zapret-blacklist" (lib.concatStringsSep "\n" cfg.blacklist)}";
18 params = lib.concatStringsSep " " cfg.params;
20 qnum = toString cfg.qnum;
23 options.services.zapret = {
24 enable = lib.mkEnableOption "the Zapret DPI bypass service.";
25 package = lib.mkPackageOption pkgs "zapret" { };
26 params = lib.mkOption {
28 type = with lib.types; listOf str;
31 "--dpi-desync=fake,disorder2"
33 "--dpi-desync-autottl=2"
37 Specify the bypass parameters for Zapret binary.
38 There are no universal parameters as they vary between different networks, so you'll have to find them yourself.
40 This can be done by running the `blockcheck` binary from zapret package, i.e. `nix-shell -p zapret --command blockcheck`.
41 It'll try different params and then tell you which params are working for your network.
44 whitelist = lib.mkOption {
46 type = with lib.types; listOf str;
56 Specify a list of domains to bypass. All other domains will be ignored.
57 You can specify either whitelist or blacklist, but not both.
58 If neither are specified, then bypass all domains.
60 It is recommended to specify the whitelist. This will make sure that other resources won't be affected by this service.
63 blacklist = lib.mkOption {
65 type = with lib.types; listOf str;
72 Specify a list of domains NOT to bypass. All other domains will be bypassed.
73 You can specify either whitelist or blacklist, but not both.
74 If neither are specified, then bypass all domains.
82 Only change this if you already use the default queue number somewhere else.
85 configureFirewall = lib.mkOption {
87 type = lib.types.bool;
89 Whether to setup firewall routing so that system http(s) traffic is forwarded via this service.
90 Disable if you want to set it up manually.
93 httpSupport = lib.mkOption {
95 type = lib.types.bool;
97 Whether to route http traffic on port 80.
98 Http bypass rarely works and you might want to disable it if you don't utilise http connections.
101 httpMode = lib.mkOption {
103 type = lib.types.enum [
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.
113 Set the mode to `full` if http doesn't work.
116 udpSupport = lib.mkOption {
118 type = lib.types.bool;
121 This requires you to specify `udpPorts` and `--dpi-desync-any-protocol` parameter.
124 udpPorts = lib.mkOption {
126 type = with lib.types; listOf str;
134 List of UDP ports to route.
135 Port ranges are delimited with a colon like this "50000:50099".
140 config = lib.mkIf cfg.enable (
145 assertion = (builtins.length cfg.whitelist) == 0 || (builtins.length cfg.blacklist) == 0;
146 message = "Can't specify both whitelist and blacklist.";
149 assertion = (builtins.length cfg.params) != 0;
150 message = "You have to specify zapret parameters. See the params option's description.";
153 assertion = cfg.udpSupport -> (builtins.length cfg.udpPorts) != 0;
154 message = "You have to specify UDP ports or disable UDP support.";
157 assertion = !cfg.configureFirewall || !config.networking.nftables.enable;
158 message = "You need to manually configure you firewall for Zapret service when using nftables.";
162 systemd.services.zapret = {
163 description = "DPI bypass service";
164 wantedBy = [ "multi-user.target" ];
165 after = [ "network.target" ];
167 ExecStart = "${cfg.package}/bin/nfqws --pidfile=/run/nfqws.pid ${params} ${whitelist} ${blacklist} --qnum=${qnum}";
169 PIDFile = "/run/nfqws.pid";
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.
174 DevicePolicy = "closed";
175 KeyringMode = "private";
177 PrivateMounts = true;
179 ProtectHostname = true;
180 ProtectKernelModules = true;
181 ProtectKernelTunables = true;
182 ProtectSystem = "strict";
183 ProtectProc = "invisible";
185 RestrictNamespaces = true;
186 RestrictRealtime = true;
187 RestrictSUIDSGID = true;
188 SystemCallArchitectures = "native";
193 # Route system traffic via service for specified ports.
194 (lib.mkIf cfg.configureFirewall {
195 networking.firewall.extraCommands =
197 httpParams = lib.optionalString (
198 cfg.httpMode == "first"
199 ) "-m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6";
201 udpPorts = lib.concatStringsSep "," cfg.udpPorts;
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
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
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
216 meta.maintainers = with lib.maintainers; [