at 25.11-pre 9.3 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8with lib; 9 10let 11 12 cfg = config.services.privoxy; 13 14 serialise = 15 name: val: 16 if isList val then 17 concatMapStrings (serialise name) val 18 else if isBool val then 19 serialise name (if val then "1" else "0") 20 else 21 "${name} ${toString val}\n"; 22 23 configType = 24 with types; 25 let 26 atom = oneOf [ 27 int 28 bool 29 str 30 path 31 ]; 32 in 33 attrsOf (either atom (listOf atom)) 34 // { 35 description = '' 36 privoxy configuration type. The format consists of an attribute 37 set of settings. Each setting can be either a value (integer, string, 38 boolean or path) or a list of such values. 39 ''; 40 }; 41 42 ageType = types.str // { 43 check = x: isString x && (builtins.match "([0-9]+([smhdw]|min|ms|us)*)+" x != null); 44 description = "tmpfiles.d(5) age format"; 45 }; 46 47 configFile = pkgs.writeText "privoxy.conf" ( 48 concatStrings ( 49 # Relative paths in some options are relative to confdir. Privoxy seems 50 # to parse the options in order of appearance, so this must come first. 51 # Nix however doesn't preserve the order in attrsets, so we have to 52 # hardcode confdir here. 53 [ "confdir ${pkgs.privoxy}/etc\n" ] ++ mapAttrsToList serialise cfg.settings 54 ) 55 ); 56 57 inspectAction = pkgs.writeText "inspect-all-https.action" '' 58 # Enable HTTPS inspection for all requests 59 {+https-inspection} 60 / 61 ''; 62 63in 64 65{ 66 67 ###### interface 68 69 options.services.privoxy = { 70 71 enable = mkEnableOption "Privoxy, non-caching filtering proxy"; 72 73 enableTor = mkOption { 74 type = types.bool; 75 default = false; 76 description = '' 77 Whether to configure Privoxy to use Tor's faster SOCKS port, 78 suitable for HTTP. 79 ''; 80 }; 81 82 inspectHttps = mkOption { 83 type = types.bool; 84 default = false; 85 description = '' 86 Whether to configure Privoxy to inspect HTTPS requests, meaning all 87 encrypted traffic will be filtered as well. This works by decrypting 88 and re-encrypting the requests using a per-domain generated certificate. 89 90 To issue per-domain certificates, Privoxy must be provided with a CA 91 certificate, using the `ca-cert-file`, 92 `ca-key-file` settings. 93 94 ::: {.warning} 95 The CA certificate must also be added to the system trust roots, 96 otherwise browsers will reject all Privoxy certificates as invalid. 97 You can do so by using the option 98 {option}`security.pki.certificateFiles`. 99 ::: 100 ''; 101 }; 102 103 certsLifetime = mkOption { 104 type = ageType; 105 default = "10d"; 106 example = "12h"; 107 description = '' 108 If `inspectHttps` is enabled, the time generated HTTPS 109 certificates will be stored in a temporary directory for reuse. Once 110 the lifetime has expired the directory will cleared and the certificate 111 will have to be generated again, on-demand. 112 113 Depending on the traffic, you may want to reduce the lifetime to limit 114 the disk usage, since Privoxy itself never deletes the certificates. 115 116 ::: {.note} 117 The format is that of the {manpage}`tmpfiles.d(5)` 118 Age parameter. 119 ::: 120 ''; 121 }; 122 123 userActions = mkOption { 124 type = types.lines; 125 default = ""; 126 description = '' 127 Actions to be included in a `user.action` file. This 128 will have a higher priority and can be used to override all other 129 actions. 130 ''; 131 }; 132 133 userFilters = mkOption { 134 type = types.lines; 135 default = ""; 136 description = '' 137 Filters to be included in a `user.filter` file. This 138 will have a higher priority and can be used to override all other 139 filters definitions. 140 ''; 141 }; 142 143 settings = mkOption { 144 type = types.submodule { 145 freeformType = configType; 146 147 options.listen-address = mkOption { 148 type = types.str; 149 default = "127.0.0.1:8118"; 150 description = "Pair of address:port the proxy server is listening to."; 151 }; 152 153 options.enable-edit-actions = mkOption { 154 type = types.bool; 155 default = false; 156 description = "Whether the web-based actions file editor may be used."; 157 }; 158 159 options.actionsfile = mkOption { 160 type = types.listOf types.str; 161 # This must come after all other entries, in order to override the 162 # other actions/filters installed by Privoxy or the user. 163 apply = 164 x: x ++ optional (cfg.userActions != "") (toString (pkgs.writeText "user.actions" cfg.userActions)); 165 default = [ 166 "match-all.action" 167 "default.action" 168 ]; 169 description = '' 170 List of paths to Privoxy action files. These paths may either be 171 absolute or relative to the privoxy configuration directory. 172 ''; 173 }; 174 175 options.filterfile = mkOption { 176 type = types.listOf types.str; 177 default = [ "default.filter" ]; 178 apply = 179 x: x ++ optional (cfg.userFilters != "") (toString (pkgs.writeText "user.filter" cfg.userFilters)); 180 description = '' 181 List of paths to Privoxy filter files. These paths may either be 182 absolute or relative to the privoxy configuration directory. 183 ''; 184 }; 185 }; 186 default = { }; 187 example = literalExpression '' 188 { # Listen on IPv6 only 189 listen-address = "[::]:8118"; 190 191 # Forward .onion requests to Tor 192 forward-socks5 = ".onion localhost:9050 ."; 193 194 # Log redirects and filters 195 debug = [ 128 64 ]; 196 # This is equivalent to writing these lines 197 # in the Privoxy configuration file: 198 # debug 128 199 # debug 64 200 } 201 ''; 202 description = '' 203 This option is mapped to the main Privoxy configuration file. 204 Check out the Privoxy user manual at 205 <https://www.privoxy.org/user-manual/config.html> 206 for available settings and documentation. 207 208 ::: {.note} 209 Repeated settings can be represented by using a list. 210 ::: 211 ''; 212 }; 213 214 }; 215 216 ###### implementation 217 218 config = mkIf cfg.enable { 219 220 users.users.privoxy = { 221 description = "Privoxy daemon user"; 222 isSystemUser = true; 223 group = "privoxy"; 224 }; 225 226 users.groups.privoxy = { }; 227 228 systemd.tmpfiles.rules = optional cfg.inspectHttps "d ${cfg.settings.certificate-directory} 0770 privoxy privoxy ${cfg.certsLifetime}"; 229 230 systemd.services.privoxy = { 231 description = "Filtering web proxy"; 232 documentation = [ "man:privoxy(8)" ]; 233 after = [ 234 "network.target" 235 "nss-lookup.target" 236 ]; 237 wantedBy = [ "multi-user.target" ]; 238 serviceConfig = { 239 User = "privoxy"; 240 Group = "privoxy"; 241 ExecStart = "${pkgs.privoxy}/bin/privoxy --no-daemon ${configFile}"; 242 PrivateDevices = true; 243 PrivateTmp = true; 244 ProtectHome = true; 245 ProtectSystem = "full"; 246 }; 247 unitConfig = mkIf cfg.inspectHttps { 248 ConditionPathExists = with cfg.settings; [ 249 ca-cert-file 250 ca-key-file 251 ]; 252 }; 253 }; 254 255 services.tor.settings.SOCKSPort = mkIf cfg.enableTor [ 256 # Route HTTP traffic over a faster port (without IsolateDestAddr). 257 { 258 addr = "127.0.0.1"; 259 port = 9063; 260 IsolateDestAddr = false; 261 } 262 ]; 263 264 services.privoxy.settings = 265 { 266 user-manual = "${pkgs.privoxy}/share/doc/privoxy/user-manual"; 267 # This is needed for external filters 268 temporary-directory = "/tmp"; 269 filterfile = [ "default.filter" ]; 270 actionsfile = [ 271 "match-all.action" 272 "default.action" 273 ] ++ optional cfg.inspectHttps (toString inspectAction); 274 } 275 // (optionalAttrs cfg.enableTor { 276 forward-socks5 = "/ 127.0.0.1:9063 ."; 277 toggle = true; 278 enable-remote-toggle = false; 279 enable-edit-actions = false; 280 enable-remote-http-toggle = false; 281 }) 282 // (optionalAttrs cfg.inspectHttps { 283 # This allows setting absolute key/crt paths 284 ca-directory = "/var/empty"; 285 certificate-directory = "/run/privoxy/certs"; 286 trusted-cas-file = config.security.pki.caBundle; 287 }); 288 }; 289 290 imports = 291 let 292 top = x: [ 293 "services" 294 "privoxy" 295 x 296 ]; 297 setting = x: [ 298 "services" 299 "privoxy" 300 "settings" 301 x 302 ]; 303 in 304 [ 305 (mkRenamedOptionModule (top "enableEditActions") (setting "enable-edit-actions")) 306 (mkRenamedOptionModule (top "listenAddress") (setting "listen-address")) 307 (mkRenamedOptionModule (top "actionsFiles") (setting "actionsfile")) 308 (mkRenamedOptionModule (top "filterFiles") (setting "filterfile")) 309 (mkRemovedOptionModule (top "extraConfig") '' 310 Use services.privoxy.settings instead. 311 This is part of the general move to use structured settings instead of raw 312 text for config as introduced by RFC0042: 313 https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md 314 '') 315 ]; 316 317 meta.maintainers = with lib.maintainers; [ rnhmjoj ]; 318 319}