at 21.11-pre 9.2 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 "Privoxy, non-caching filtering proxy"; 57 58 enableTor = mkOption { 59 type = types.bool; 60 default = false; 61 description = '' 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 = '' 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 <literal>ca-cert-file</literal>, 77 <literal>ca-key-file</literal> settings. 78 79 <warning><para> 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</option>. 84 </para></warning> 85 ''; 86 }; 87 88 certsLifetime = mkOption { 89 type = ageType; 90 default = "10d"; 91 example = "12h"; 92 description = '' 93 If <literal>inspectHttps</literal> 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><para>The format is that of the <literal>tmpfiles.d(5)</literal> 102 Age parameter.</para></note> 103 ''; 104 }; 105 106 userActions = mkOption { 107 type = types.lines; 108 default = ""; 109 description = '' 110 Actions to be included in a <literal>user.action</literal> file. This 111 will have a higher priority and can be used to override all other 112 actions. 113 ''; 114 }; 115 116 userFilters = mkOption { 117 type = types.lines; 118 default = ""; 119 description = '' 120 Filters to be included in a <literal>user.filter</literal> file. This 121 will have a higher priority and can be used to override all other 122 filters definitions. 123 ''; 124 }; 125 126 settings = mkOption { 127 type = types.submodule { 128 freeformType = configType; 129 130 options.listen-address = mkOption { 131 type = types.str; 132 default = "127.0.0.1:8118"; 133 description = "Pair of address:port the proxy server is listening to."; 134 }; 135 136 options.enable-edit-actions = mkOption { 137 type = types.bool; 138 default = false; 139 description = "Whether the web-based actions file editor may be used."; 140 }; 141 142 options.actionsfile = mkOption { 143 type = types.listOf types.str; 144 # This must come after all other entries, in order to override the 145 # other actions/filters installed by Privoxy or the user. 146 apply = x: x ++ optional (cfg.userActions != "") 147 (toString (pkgs.writeText "user.actions" cfg.userActions)); 148 default = [ "match-all.action" "default.action" ]; 149 description = '' 150 List of paths to Privoxy action files. These paths may either be 151 absolute or relative to the privoxy configuration directory. 152 ''; 153 }; 154 155 options.filterfile = mkOption { 156 type = types.listOf types.str; 157 default = [ "default.filter" ]; 158 apply = x: x ++ optional (cfg.userFilters != "") 159 (toString (pkgs.writeText "user.filter" cfg.userFilters)); 160 description = '' 161 List of paths to Privoxy filter files. These paths may either be 162 absolute or relative to the privoxy configuration directory. 163 ''; 164 }; 165 }; 166 default = {}; 167 example = literalExample '' 168 { # Listen on IPv6 only 169 listen-address = "[::]:8118"; 170 171 # Forward .onion requests to Tor 172 forward-socks5 = ".onion localhost:9050 ."; 173 174 # Log redirects and filters 175 debug = [ 128 64 ]; 176 # This is equivalent to writing these lines 177 # in the Privoxy configuration file: 178 # debug 128 179 # debug 64 180 } 181 ''; 182 description = '' 183 This option is mapped to the main Privoxy configuration file. 184 Check out the Privoxy user manual at 185 <link xlink:href="https://www.privoxy.org/user-manual/config.html"/> 186 for available settings and documentation. 187 188 <note><para> 189 Repeated settings can be represented by using a list. 190 </para></note> 191 ''; 192 }; 193 194 }; 195 196 ###### implementation 197 198 config = mkIf cfg.enable { 199 200 users.users.privoxy = { 201 description = "Privoxy daemon user"; 202 isSystemUser = true; 203 group = "privoxy"; 204 }; 205 206 users.groups.privoxy = {}; 207 208 systemd.tmpfiles.rules = optional cfg.inspectHttps 209 "d ${cfg.settings.certificate-directory} 0770 privoxy privoxy ${cfg.certsLifetime}"; 210 211 systemd.services.privoxy = { 212 description = "Filtering web proxy"; 213 after = [ "network.target" "nss-lookup.target" ]; 214 wantedBy = [ "multi-user.target" ]; 215 serviceConfig = { 216 User = "privoxy"; 217 Group = "privoxy"; 218 ExecStart = "${pkgs.privoxy}/bin/privoxy --no-daemon ${configFile}"; 219 PrivateDevices = true; 220 PrivateTmp = true; 221 ProtectHome = true; 222 ProtectSystem = "full"; 223 }; 224 unitConfig = mkIf cfg.inspectHttps { 225 ConditionPathExists = with cfg.settings; 226 [ ca-cert-file ca-key-file ]; 227 }; 228 }; 229 230 services.tor.settings.SOCKSPort = mkIf cfg.enableTor [ 231 # Route HTTP traffic over a faster port (without IsolateDestAddr). 232 { addr = "127.0.0.1"; port = 9063; IsolateDestAddr = false; } 233 ]; 234 235 services.privoxy.settings = { 236 user-manual = "${pkgs.privoxy}/share/doc/privoxy/user-manual"; 237 # This is needed for external filters 238 temporary-directory = "/tmp"; 239 filterfile = [ "default.filter" ]; 240 actionsfile = 241 [ "match-all.action" 242 "default.action" 243 ] ++ optional cfg.inspectHttps (toString inspectAction); 244 } // (optionalAttrs cfg.enableTor { 245 forward-socks5 = "/ 127.0.0.1:9063 ."; 246 toggle = true; 247 enable-remote-toggle = false; 248 enable-edit-actions = false; 249 enable-remote-http-toggle = false; 250 }) // (optionalAttrs cfg.inspectHttps { 251 # This allows setting absolute key/crt paths 252 ca-directory = "/var/empty"; 253 certificate-directory = "/run/privoxy/certs"; 254 trusted-cas-file = "/etc/ssl/certs/ca-certificates.crt"; 255 }); 256 257 }; 258 259 imports = 260 let 261 top = x: [ "services" "privoxy" x ]; 262 setting = x: [ "services" "privoxy" "settings" x ]; 263 in 264 [ (mkRenamedOptionModule (top "enableEditActions") (setting "enable-edit-actions")) 265 (mkRenamedOptionModule (top "listenAddress") (setting "listen-address")) 266 (mkRenamedOptionModule (top "actionsFiles") (setting "actionsfile")) 267 (mkRenamedOptionModule (top "filterFiles") (setting "filterfile")) 268 (mkRemovedOptionModule (top "extraConfig") 269 '' 270 Use services.privoxy.settings instead. 271 This is part of the general move to use structured settings instead of raw 272 text for config as introduced by RFC0042: 273 https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md 274 '') 275 ]; 276 277 meta.maintainers = with lib.maintainers; [ rnhmjoj ]; 278 279}