{ config, pkgs, lib, ... }: with lib; let cfgPrivoxy = config.services.privoxy; cfg = cfgPrivoxy.tls-wrapper; package = ./.; action = pkgs.writeText "privoxy-tls.action" '' { +client-header-tagger{privoxy-tls-tagger} } / { +forward-override{forward localhost:${toString cfg.rearPort}} } TAG:.*?privoxy-tls ''; filter = pkgs.writeText "privoxy-tls.filter" '' CLIENT-HEADER-TAGGER: privoxy-tls-tagger s@Tagged: privoxy-tls front@$0@i ''; dataDir = "/var/lib/privoxy"; # make attributes only a default mkDefaultAttrs = mapAttrs (n: v: mkDefault v); # INI format with sections that may also contains a list toSpecialINI = with lib; { mkSectionName ? (name: escape [ "[" "]" ] name), mkKeyValue ? generators.mkKeyValueDefault {} "=" }: attrsOfAttrs: let # map function to string for each key val mapAttrsToStringsSep = sep: mapFn: attrs: concatStringsSep sep (mapAttrsToList mapFn attrs); stripPriority = val: if val ? priority then val.content else val; mkSectionVal = val: if isList val then concatMapStringsSep "\n" toString val else generators.toKeyValue { inherit mkKeyValue; } val; # handle both list and attributes mkSection = sectName: sectValues: '' [${mkSectionName sectName}] ${mkSectionVal (stripPriority sectValues)} ''; in # map input to ini sections mapAttrsToStringsSep "\n" mkSection attrsOfAttrs; configFile = pkgs.writeText "config.ini" (toSpecialINI {} cfg.settings); python = pkgs.python3.withPackages (p: [ p.urllib3 ]); in { ###### interface options = { services.privoxy.tls-wrapper = { enable = mkEnableOption '' TLS proxy wrapper to allow privoxy to inspect and modify HTTPS traffic. ''; caCert = mkOption { type = types.path; description = '' The CA certificate, in PEM format, that will be used to reencrypt the traffic. This will be installed in the local trust store. ''; }; caKey = mkOption { type = types.path; description = '' The CA key, in PEM format, associated to the certificate. This will be copied to the privoxy-owned state directory but not in the nix-store. ''; }; frontPort = mkOption { type = types.port; default = 8117; description = '' The port the privoxy-tls front proxy will bind to. This will replace the standard privoxy HTTP proxy in your settings. ''; }; rearPort = mkOption { type = types.port; default = 8119; description = '' The port the privoxy-tls rear proxy will bind to. This will be the proxy privoxy will forward TLS-stripped HTTP traffic for reencryption. ''; }; passthru = mkOption { type = types.listOf types.str; default = []; example = [ "*.local" ]; description = '' List of URLs configured for passthrough, meaning the proxy will directly connect skipping privoxy. ''; }; noVerify = mkOption { type = types.listOf types.str; default = []; example = [ "localhost" ]; description = '' List of URLs configured that the TLS verification will ignore. For example use it for self-signed certs. ''; }; logLevel = mkOption { type = types.enum [ "DEBUG" "INFO" "WARNING" "ERROR" "CRITICAL" ]; default = "WARNING"; example = "The level of logging of privoxy-tls"; }; settings = mkOption { type = types.attrs; default = { }; example = literalExample '' { bypassURL = [ "example.com" ]; } ''; description = '' Privoxy-TLS settings. Use this option to configure not exposed in a NixOS option or to bypass one. See the documentation at for the available options. ''; }; }; }; ###### implementation config = mkIf cfg.enable { # install custom CA security.pki.certificateFiles = [ cfg.caCert ]; # tag traffic for the rear proxy server services.privoxy.actionsFiles = [ (toString action) ]; services.privoxy.filterFiles = [ (toString filter) ]; users.users.privoxy-tls = { group = "privoxy"; home = dataDir; }; # default configuration services.privoxy.tls-wrapper.settings = mkDefaultAttrs { general = { proxAddr = "http://${cfgPrivoxy.listenAddress}"; frontPort = cfg.frontPort; rearPort = cfg.rearPort; caCert = "${dataDir}/ca.crt"; certdir = "/tmp"; logLevel = cfg.logLevel; }; noVerify = cfg.noVerify; passthru = cfg.passthru; }; systemd.services.privoxy-tls = { description = "Privoxy TLS proxy wrapper."; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; serviceConfig = { User = "privoxy-tls"; PrivateTmp = true; PermissionsStartOnly = true; ExecStart = "${python}/bin/python ${package}/src/main.py -c ${configFile}"; }; preStart = '' if ! test -f ${dataDir}/ca.crt; then cat ${toString cfg.caCert} ${toString cfg.caKey} > ${dataDir}/ca.crt fi ''; }; }; }