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