|
| 1 | +{ |
| 2 | + lib, |
| 3 | + config, |
| 4 | + pkgs, |
| 5 | + ... |
| 6 | +}: |
| 7 | +let |
| 8 | + cfg = config.services.zapret; |
| 9 | + |
| 10 | + whitelist = lib.optionalString ( |
| 11 | + cfg.whitelist != null |
| 12 | + ) "--hostlist ${pkgs.writeText "zapret-whitelist" (lib.concatStringsSep "\n" cfg.whitelist)}"; |
| 13 | + |
| 14 | + blacklist = |
| 15 | + lib.optionalString (cfg.blacklist != null) |
| 16 | + "--hostlist-exclude ${pkgs.writeText "zapret-blacklist" (lib.concatStringsSep "\n" cfg.blacklist)}"; |
| 17 | + |
| 18 | + ports = if cfg.httpSupport then "80,443" else "443"; |
| 19 | +in |
| 20 | +{ |
| 21 | + options.services.zapret = { |
| 22 | + enable = lib.mkEnableOption "the Zapret DPI bypass service."; |
| 23 | + package = lib.mkPackageOption pkgs "zapret" { }; |
| 24 | + params = lib.mkOption { |
| 25 | + default = [ ]; |
| 26 | + type = with lib.types; listOf str; |
| 27 | + example = '' |
| 28 | + [ |
| 29 | + "--dpi-desync=fake,disorder2" |
| 30 | + "--dpi-desync-ttl=1" |
| 31 | + "--dpi-desync-autottl=2" |
| 32 | + ]; |
| 33 | + ''; |
| 34 | + description = '' |
| 35 | + Specify the bypass parameters for Zapret binary. |
| 36 | + There are no universal parameters as they vary between different networks, so you'll have to find them yourself. |
| 37 | +
|
| 38 | + This can be done by running the `blockcheck` binary from zapret package, i.e. `nix-shell -p zapret --command blockcheck`. |
| 39 | + It'll try different params and then tell you which params are working for your network. |
| 40 | + ''; |
| 41 | + }; |
| 42 | + whitelist = lib.mkOption { |
| 43 | + default = null; |
| 44 | + type = with lib.types; nullOr (listOf str); |
| 45 | + example = '' |
| 46 | + [ |
| 47 | + "youtube.com" |
| 48 | + "googlevideo.com" |
| 49 | + "ytimg.com" |
| 50 | + "youtu.be" |
| 51 | + ] |
| 52 | + ''; |
| 53 | + description = '' |
| 54 | + Specify a list of domains to bypass. All other domains will be ignored. |
| 55 | + You can specify either whitelist or blacklist, but not both. |
| 56 | + If neither are specified, then bypass all domains. |
| 57 | +
|
| 58 | + It is recommended to specify the whitelist. This will make sure that other resources won't be affected by this service. |
| 59 | + ''; |
| 60 | + }; |
| 61 | + blacklist = lib.mkOption { |
| 62 | + default = null; |
| 63 | + type = with lib.types; nullOr (listOf str); |
| 64 | + example = '' |
| 65 | + [ |
| 66 | + "example.com" |
| 67 | + ] |
| 68 | + ''; |
| 69 | + description = '' |
| 70 | + Specify a list of domains NOT to bypass. All other domains will be bypassed. |
| 71 | + You can specify either whitelist or blacklist, but not both. |
| 72 | + If neither are specified, then bypass all domains. |
| 73 | + ''; |
| 74 | + }; |
| 75 | + qnum = lib.mkOption { |
| 76 | + default = 200; |
| 77 | + type = lib.types.int; |
| 78 | + description = '' |
| 79 | + Routing queue number. |
| 80 | + Only change this if you already use the default queue number somewhere else. |
| 81 | + ''; |
| 82 | + }; |
| 83 | + configureFirewall = lib.mkOption { |
| 84 | + default = true; |
| 85 | + type = lib.types.bool; |
| 86 | + description = '' |
| 87 | + Whether to setup firewall routing so that system http(s) traffic is forwarded via this service. |
| 88 | + Disable if you want to set it up manually. |
| 89 | + ''; |
| 90 | + }; |
| 91 | + httpSupport = lib.mkOption { |
| 92 | + default = true; |
| 93 | + type = lib.types.bool; |
| 94 | + description = '' |
| 95 | + Whether to route http traffic on port 80. |
| 96 | + Http bypass rarely works and you might want to disable it if you don't utilise http connections. |
| 97 | + ''; |
| 98 | + }; |
| 99 | + }; |
| 100 | + |
| 101 | + config = lib.mkIf cfg.enable ( |
| 102 | + lib.mkMerge [ |
| 103 | + { |
| 104 | + assertions = [ |
| 105 | + { |
| 106 | + assertion = (cfg.whitelist == null) || (cfg.blacklist == null); |
| 107 | + message = "Can't specify both whitelist and blacklist."; |
| 108 | + } |
| 109 | + { |
| 110 | + assertion = (builtins.length cfg.params) != 0; |
| 111 | + message = "You have to specify zapret parameters. See the params option's description."; |
| 112 | + } |
| 113 | + ]; |
| 114 | + |
| 115 | + systemd.services.zapret = { |
| 116 | + description = "DPI bypass service"; |
| 117 | + wantedBy = [ "multi-user.target" ]; |
| 118 | + after = [ "network.target" ]; |
| 119 | + serviceConfig = { |
| 120 | + ExecStart = "${cfg.package}/bin/nfqws --pidfile=/run/nfqws.pid ${lib.concatStringsSep " " cfg.params} ${whitelist} ${blacklist} --qnum=${toString cfg.qnum}"; |
| 121 | + Type = "simple"; |
| 122 | + PIDFile = "/run/nfqws.pid"; |
| 123 | + 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. |
| 125 | + |
| 126 | + # hardening |
| 127 | + DevicePolicy = "closed"; |
| 128 | + KeyringMode = "private"; |
| 129 | + PrivateTmp = true; |
| 130 | + PrivateMounts = true; |
| 131 | + ProtectHome = true; |
| 132 | + ProtectHostname = true; |
| 133 | + ProtectKernelModules = true; |
| 134 | + ProtectKernelTunables = true; |
| 135 | + ProtectSystem = "strict"; |
| 136 | + ProtectProc = "invisible"; |
| 137 | + RemoveIPC = true; |
| 138 | + RestrictNamespaces = true; |
| 139 | + RestrictRealtime = true; |
| 140 | + RestrictSUIDSGID = true; |
| 141 | + SystemCallArchitectures = "native"; |
| 142 | + }; |
| 143 | + }; |
| 144 | + } |
| 145 | + |
| 146 | + # Route system traffic via service for specified ports. |
| 147 | + (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 | + ''; |
| 151 | + }) |
| 152 | + ] |
| 153 | + ); |
| 154 | + |
| 155 | + meta.maintainers = with lib.maintainers; [ |
| 156 | + voronind |
| 157 | + nishimara |
| 158 | + ]; |
| 159 | +} |
0 commit comments