nixos/privoxy: add https and settings options
This is a major rewrite of the Privoxy module: - As per RFC0042, remove privoxy.extraConfig and replace it with a privoxy.settings option, which maps a NixOS freeform submodule to the Privoxy configuration format. - Move all top-level options that mirrored a setting to the real ones in privoxy.settings. This still keeps the type-checking, default values and examples in places. - Add two convenience options: userActions and userFilters, which simplify the operation of creating a file with pkgs.writeText, converting it to a string and adding it to the actionsfile/ filterfile list. - Add a privoxy.inspectHttps option to automagically setup TLS decryption support. I don't know how long have been waiting for this feature: can't believe it has just happened. - Also add a privoxy.certsLifetime to control the periodical cleanup of the temporary certificates generate by Privoxy.
This commit is contained in:
parent
7fd9d4bda7
commit
3673ded392
|
@ -4,26 +4,46 @@ with lib;
|
|||
|
||||
let
|
||||
|
||||
inherit (pkgs) privoxy;
|
||||
|
||||
cfg = config.services.privoxy;
|
||||
|
||||
confFile = pkgs.writeText "privoxy.conf" (''
|
||||
user-manual ${privoxy}/share/doc/privoxy/user-manual
|
||||
confdir ${privoxy}/etc/
|
||||
listen-address ${cfg.listenAddress}
|
||||
enable-edit-actions ${if (cfg.enableEditActions == true) then "1" else "0"}
|
||||
${concatMapStrings (f: "actionsfile ${f}\n") cfg.actionsFiles}
|
||||
${concatMapStrings (f: "filterfile ${f}\n") cfg.filterFiles}
|
||||
'' + optionalString cfg.enableTor ''
|
||||
forward-socks5t / 127.0.0.1:9063 .
|
||||
toggle 1
|
||||
enable-remote-toggle 0
|
||||
enable-edit-actions 0
|
||||
enable-remote-http-toggle 0
|
||||
'' + ''
|
||||
${cfg.extraConfig}
|
||||
'');
|
||||
serialise = name: val:
|
||||
if isList val then concatMapStrings (serialise name) val
|
||||
else if isBool val then serialise name (if val then "1" else "0")
|
||||
else "${name} ${toString val}\n";
|
||||
|
||||
configType = with types;
|
||||
let atom = oneOf [ int bool string path ];
|
||||
in attrsOf (either atom (listOf atom))
|
||||
// { description = ''
|
||||
privoxy configuration type. The format consists of an attribute
|
||||
set of settings. Each setting can be either a value (integer, string,
|
||||
boolean or path) or a list of such values.
|
||||
'';
|
||||
};
|
||||
|
||||
ageType = types.str // {
|
||||
check = x:
|
||||
isString x &&
|
||||
(builtins.match "([0-9]+([smhdw]|min|ms|us)*)+" x != null);
|
||||
description = "tmpfiles.d(5) age format";
|
||||
};
|
||||
|
||||
configFile = pkgs.writeText "privoxy.conf"
|
||||
(concatStrings (
|
||||
# Relative paths in some options are relative to confdir. Privoxy seems
|
||||
# to parse the options in order of appearance, so this must come first.
|
||||
# Nix however doesn't preserve the order in attrsets, so we have to
|
||||
# hardcode confdir here.
|
||||
[ "confdir ${pkgs.privoxy}/etc\n" ]
|
||||
++ mapAttrsToList serialise cfg.settings
|
||||
));
|
||||
|
||||
inspectAction = pkgs.writeText "inspect-all-https.action"
|
||||
''
|
||||
# Enable HTTPS inspection for all requests
|
||||
{+https-inspection}
|
||||
/
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
|
@ -31,70 +51,130 @@ in
|
|||
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
options.services.privoxy = {
|
||||
|
||||
services.privoxy = {
|
||||
enable = mkEnableOption "Privoxy, non-caching filtering proxy";
|
||||
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable the Privoxy non-caching filtering proxy.
|
||||
'';
|
||||
};
|
||||
|
||||
listenAddress = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1:8118";
|
||||
description = ''
|
||||
Address the proxy server is listening to.
|
||||
'';
|
||||
};
|
||||
|
||||
actionsFiles = mkOption {
|
||||
type = types.listOf types.str;
|
||||
example = [ "match-all.action" "default.action" "/etc/privoxy/user.action" ];
|
||||
default = [ "match-all.action" "default.action" ];
|
||||
description = ''
|
||||
List of paths to Privoxy action files.
|
||||
These paths may either be absolute or relative to the privoxy configuration directory.
|
||||
'';
|
||||
};
|
||||
|
||||
filterFiles = mkOption {
|
||||
type = types.listOf types.str;
|
||||
example = [ "default.filter" "/etc/privoxy/user.filter" ];
|
||||
default = [ "default.filter" ];
|
||||
description = ''
|
||||
List of paths to Privoxy filter files.
|
||||
These paths may either be absolute or relative to the privoxy configuration directory.
|
||||
'';
|
||||
};
|
||||
|
||||
enableEditActions = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether or not the web-based actions file editor may be used.
|
||||
'';
|
||||
};
|
||||
|
||||
enableTor = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to configure Privoxy to use Tor's faster SOCKS port,
|
||||
suitable for HTTP.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "" ;
|
||||
description = ''
|
||||
Extra configuration. Contents will be added verbatim to the configuration file.
|
||||
'';
|
||||
enableTor = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to configure Privoxy to use Tor's faster SOCKS port,
|
||||
suitable for HTTP.
|
||||
'';
|
||||
};
|
||||
|
||||
inspectHttps = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to configure Privoxy to inspect HTTPS requests, meaning all
|
||||
encrypted traffic will be filtered as well. This works by decrypting
|
||||
and re-encrypting the requests using a per-domain generated certificate.
|
||||
|
||||
To issue per-domain certificates, Privoxy must be provided with a CA
|
||||
certificate, using the <literal>ca-cert-file</literal>,
|
||||
<literal>ca-key-file</literal> settings.
|
||||
|
||||
<warning><para>
|
||||
The CA certificate must also be added to the system trust roots,
|
||||
otherwise browsers will reject all Privoxy certificates as invalid.
|
||||
You can do so by using the option
|
||||
<option>security.pki.certificateFiles</option>.
|
||||
</para></warning>
|
||||
'';
|
||||
};
|
||||
|
||||
certsLifetime = mkOption {
|
||||
type = ageType;
|
||||
default = "10d";
|
||||
example = "12h";
|
||||
description = ''
|
||||
If <literal>inspectHttps</literal> is enabled, the time generated HTTPS
|
||||
certificates will be stored in a temporary directory for reuse. Once
|
||||
the lifetime has expired the directory will cleared and the certificate
|
||||
will have to be generated again, on-demand.
|
||||
|
||||
Depending on the traffic, you may want to reduce the lifetime to limit
|
||||
the disk usage, since Privoxy itself never deletes the certificates.
|
||||
|
||||
<note><para>The format is that of the <literal>tmpfiles.d(5)</literal>
|
||||
Age parameter.</para></note>
|
||||
'';
|
||||
};
|
||||
|
||||
userActions = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Actions to be included in a <literal>user.action</literal> file. This
|
||||
will have a higher priority and can be used to override all other
|
||||
actions.
|
||||
'';
|
||||
};
|
||||
|
||||
userFilters = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Filters to be included in a <literal>user.filter</literal> file. This
|
||||
will have a higher priority and can be used to override all other
|
||||
filters definitions.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = configType;
|
||||
|
||||
options.listen-address = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1:8118";
|
||||
description = "Pair of address:port the proxy server is listening to.";
|
||||
};
|
||||
|
||||
options.enable-edit-actions = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether the web-based actions file editor may be used.";
|
||||
};
|
||||
|
||||
options.actionsfile = mkOption {
|
||||
type = types.listOf types.str;
|
||||
# This must come after all other entries, in order to override the
|
||||
# other actions/filters installed by Privoxy or the user.
|
||||
apply = x: x ++ optional (cfg.userActions != "")
|
||||
(toString (pkgs.writeText "user.actions" cfg.userActions));
|
||||
default = [ "match-all.action" "default.action" ];
|
||||
description = ''
|
||||
List of paths to Privoxy action files. These paths may either be
|
||||
absolute or relative to the privoxy configuration directory.
|
||||
'';
|
||||
};
|
||||
|
||||
options.filterfile = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ "default.filter" ];
|
||||
apply = x: x ++ optional (cfg.userFilters != "")
|
||||
(toString (pkgs.writeText "user.filter" cfg.userFilters));
|
||||
description = ''
|
||||
List of paths to Privoxy filter files. These paths may either be
|
||||
absolute or relative to the privoxy configuration directory.
|
||||
'';
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
example = literalExample ''
|
||||
{ listen-address = "[::]:8118"; # listen on IPv6 only
|
||||
forward-socks5 = ".onion localhost:9050 ."; # forward .onion requests to Tor
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
This option is mapped to the main Privoxy configuration file.
|
||||
Check out the Privoxy user manual at
|
||||
<link xlink:href="file://${pkgs.privoxy}/share/doc/privoxy/user-manual/config.html"/>
|
||||
for available settings and documentation.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -104,23 +184,34 @@ in
|
|||
config = mkIf cfg.enable {
|
||||
|
||||
users.users.privoxy = {
|
||||
description = "Privoxy daemon user";
|
||||
isSystemUser = true;
|
||||
home = "/var/empty";
|
||||
group = "privoxy";
|
||||
};
|
||||
|
||||
users.groups.privoxy = {};
|
||||
|
||||
systemd.tmpfiles.rules = with cfg.settings; [
|
||||
"d ${certificate-directory} 0770 privoxy privoxy ${cfg.certsLifetime}"
|
||||
];
|
||||
|
||||
systemd.services.privoxy = {
|
||||
description = "Filtering web proxy";
|
||||
after = [ "network.target" "nss-lookup.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig.ExecStart = "${privoxy}/bin/privoxy --no-daemon --user privoxy ${confFile}";
|
||||
|
||||
serviceConfig.PrivateDevices = true;
|
||||
serviceConfig.PrivateTmp = true;
|
||||
serviceConfig.ProtectHome = true;
|
||||
serviceConfig.ProtectSystem = "full";
|
||||
serviceConfig = {
|
||||
User = "privoxy";
|
||||
Group = "privoxy";
|
||||
ExecStart = "${pkgs.privoxy}/bin/privoxy --no-daemon ${configFile}";
|
||||
PrivateDevices = true;
|
||||
PrivateTmp = true;
|
||||
ProtectHome = true;
|
||||
ProtectSystem = "full";
|
||||
};
|
||||
unitConfig = mkIf cfg.inspectHttps {
|
||||
ConditionPathExists = with cfg.settings;
|
||||
[ ca-cert-file ca-key-file ];
|
||||
};
|
||||
};
|
||||
|
||||
services.tor.settings.SOCKSPort = mkIf cfg.enableTor [
|
||||
|
@ -128,8 +219,46 @@ in
|
|||
{ addr = "127.0.0.1"; port = 9063; IsolateDestAddr = false; }
|
||||
];
|
||||
|
||||
services.privoxy.settings = {
|
||||
user-manual = "${pkgs.privoxy}/share/doc/privoxy/user-manual";
|
||||
filterfile = [ "default.filter" ];
|
||||
actionsfile =
|
||||
[ "match-all.action"
|
||||
"default.action"
|
||||
] ++ optional cfg.inspectHttps (toString inspectAction);
|
||||
} // (optionalAttrs cfg.enableTor {
|
||||
forward-socks5 = "127.0.0.1:9063 .";
|
||||
toggle = true;
|
||||
enable-remote-toggle = false;
|
||||
enable-edit-actions = false;
|
||||
enable-remote-http-toggle = false;
|
||||
}) // (optionalAttrs cfg.inspectHttps {
|
||||
# This allows setting absolute key/crt paths
|
||||
ca-directory = "/var/empty";
|
||||
certificate-directory = "/run/privoxy/certs";
|
||||
trusted-cas-file = "/etc/ssl/certs/ca-certificates.crt";
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
imports =
|
||||
let
|
||||
top = x: [ "services" "privoxy" x ];
|
||||
setting = x: [ "services" "privoxy" "settings" x ];
|
||||
in
|
||||
[ (mkRenamedOptionModule (top "enableEditActions") (setting "enable-edit-actions"))
|
||||
(mkRenamedOptionModule (top "listenAddress") (setting "listen-address"))
|
||||
(mkRenamedOptionModule (top "actionsFiles") (setting "actionsfile"))
|
||||
(mkRenamedOptionModule (top "filterFiles") (setting "filterfile"))
|
||||
(mkRemovedOptionModule (top "extraConfig")
|
||||
''
|
||||
Use services.privoxy.settings instead.
|
||||
This is part of the general move to use structured settings instead of raw
|
||||
text for config as introduced by RFC0042:
|
||||
https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md
|
||||
'')
|
||||
];
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ rnhmjoj ];
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue