diff options
author | Uli Baum <xeji@cat3.de> | 2018-09-13 10:08:53 +0200 |
---|---|---|
committer | Uli Baum <xeji@cat3.de> | 2018-09-13 10:08:53 +0200 |
commit | 1df2560dde3df459dd0e71883c5eac014100b0b0 (patch) | |
tree | b0151bf1948574b52b6d29fd8a656f576cfbf7f7 /nixos/modules/services/networking | |
parent | 1428d00aa4a58095ba510145eb25233591ac6051 (diff) | |
parent | 0f2b10d1ac41c2f573fddbdf110a1ae435ffdd8a (diff) | |
download | nixlib-1df2560dde3df459dd0e71883c5eac014100b0b0.tar nixlib-1df2560dde3df459dd0e71883c5eac014100b0b0.tar.gz nixlib-1df2560dde3df459dd0e71883c5eac014100b0b0.tar.bz2 nixlib-1df2560dde3df459dd0e71883c5eac014100b0b0.tar.lz nixlib-1df2560dde3df459dd0e71883c5eac014100b0b0.tar.xz nixlib-1df2560dde3df459dd0e71883c5eac014100b0b0.tar.zst nixlib-1df2560dde3df459dd0e71883c5eac014100b0b0.zip |
Merge branch 'master' into staging-next
Diffstat (limited to 'nixos/modules/services/networking')
-rw-r--r-- | nixos/modules/services/networking/hylafax/default.nix | 29 | ||||
-rw-r--r-- | nixos/modules/services/networking/hylafax/faxq-default.nix | 12 | ||||
-rwxr-xr-x | nixos/modules/services/networking/hylafax/faxq-wait.sh | 29 | ||||
-rw-r--r-- | nixos/modules/services/networking/hylafax/hfaxd-default.nix | 10 | ||||
-rw-r--r-- | nixos/modules/services/networking/hylafax/modem-default.nix | 22 | ||||
-rw-r--r-- | nixos/modules/services/networking/hylafax/options.nix | 375 | ||||
-rwxr-xr-x | nixos/modules/services/networking/hylafax/spool.sh | 111 | ||||
-rw-r--r-- | nixos/modules/services/networking/hylafax/systemd.nix | 249 | ||||
-rw-r--r-- | nixos/modules/services/networking/i2pd.nix | 604 | ||||
-rw-r--r-- | nixos/modules/services/networking/iperf3.nix | 87 | ||||
-rw-r--r-- | nixos/modules/services/networking/networkmanager.nix | 14 | ||||
-rw-r--r-- | nixos/modules/services/networking/zeronet.nix | 20 |
12 files changed, 1348 insertions, 214 deletions
diff --git a/nixos/modules/services/networking/hylafax/default.nix b/nixos/modules/services/networking/hylafax/default.nix new file mode 100644 index 000000000000..4c63b822d165 --- /dev/null +++ b/nixos/modules/services/networking/hylafax/default.nix @@ -0,0 +1,29 @@ +{ config, lib, pkgs, ... }: + +{ + + imports = [ + ./options.nix + ./systemd.nix + ]; + + config = lib.modules.mkIf config.services.hylafax.enable { + environment.systemPackages = [ pkgs.hylafaxplus ]; + users.users.uucp = { + uid = config.ids.uids.uucp; + group = "uucp"; + description = "Unix-to-Unix CoPy system"; + isSystemUser = true; + inherit (config.users.users.nobody) home; + }; + assertions = [{ + assertion = config.services.hylafax.modems != {}; + message = '' + HylaFAX cannot be used without modems. + Please define at least one modem with + <option>config.services.hylafax.modems</option>. + ''; + }]; + }; + +} diff --git a/nixos/modules/services/networking/hylafax/faxq-default.nix b/nixos/modules/services/networking/hylafax/faxq-default.nix new file mode 100644 index 000000000000..a2630ce66b71 --- /dev/null +++ b/nixos/modules/services/networking/hylafax/faxq-default.nix @@ -0,0 +1,12 @@ +{ ... }: + +# see man:hylafax-config(5) + +{ + + ModemGroup = [ ''"any:.*"'' ]; + ServerTracing = "0x78701"; + SessionTracing = "0x78701"; + UUCPLockDir = "/var/lock"; + +} diff --git a/nixos/modules/services/networking/hylafax/faxq-wait.sh b/nixos/modules/services/networking/hylafax/faxq-wait.sh new file mode 100755 index 000000000000..8c39e9d20c18 --- /dev/null +++ b/nixos/modules/services/networking/hylafax/faxq-wait.sh @@ -0,0 +1,29 @@ +#! @shell@ -e + +# skip this if there are no modems at all +if ! stat -t "@spoolAreaPath@"/etc/config.* >/dev/null 2>&1 +then + exit 0 +fi + +echo "faxq started, waiting for modem(s) to initialize..." + +for i in `seq @timeoutSec@0 -1 0` # gracefully timeout +do + sleep 0.1 + # done if status files exist, but don't mention initialization + if \ + stat -t "@spoolAreaPath@"/status/* >/dev/null 2>&1 \ + && \ + ! grep --silent --ignore-case 'initializing server' \ + "@spoolAreaPath@"/status/* + then + echo "modem(s) apparently ready" + exit 0 + fi + # if i reached 0, modems probably failed to initialize + if test $i -eq 0 + then + echo "warning: modem initialization timed out" + fi +done diff --git a/nixos/modules/services/networking/hylafax/hfaxd-default.nix b/nixos/modules/services/networking/hylafax/hfaxd-default.nix new file mode 100644 index 000000000000..8999dae57f41 --- /dev/null +++ b/nixos/modules/services/networking/hylafax/hfaxd-default.nix @@ -0,0 +1,10 @@ +{ ... }: + +# see man:hfaxd(8) + +{ + + ServerTracing = "0x91"; + XferLogFile = "/clientlog"; + +} diff --git a/nixos/modules/services/networking/hylafax/modem-default.nix b/nixos/modules/services/networking/hylafax/modem-default.nix new file mode 100644 index 000000000000..7529b5b0aafd --- /dev/null +++ b/nixos/modules/services/networking/hylafax/modem-default.nix @@ -0,0 +1,22 @@ +{ pkgs, ... }: + +# see man:hylafax-config(5) + +{ + + TagLineFont = "etc/LiberationSans-25.pcf"; + TagLineLocale = ''en_US.UTF-8''; + + AdminGroup = "root"; # groups that can change server config + AnswerRotary = "fax"; # don't accept anything else but faxes + LogFileMode = "0640"; + PriorityScheduling = true; + RecvFileMode = "0640"; + ServerTracing = "0x78701"; + SessionTracing = "0x78701"; + UUCPLockDir = "/var/lock"; + + SendPageCmd = ''${pkgs.coreutils}/bin/false''; # prevent pager transmit + SendUUCPCmd = ''${pkgs.coreutils}/bin/false''; # prevent UUCP transmit + +} diff --git a/nixos/modules/services/networking/hylafax/options.nix b/nixos/modules/services/networking/hylafax/options.nix new file mode 100644 index 000000000000..4ac6d3fa8432 --- /dev/null +++ b/nixos/modules/services/networking/hylafax/options.nix @@ -0,0 +1,375 @@ +{ config, lib, pkgs, ... }: + +let + + inherit (lib.options) literalExample mkEnableOption mkOption; + inherit (lib.types) bool enum int lines loaOf nullOr path str submodule; + inherit (lib.modules) mkDefault mkIf mkMerge; + + commonDescr = '' + Values can be either strings or integers + (which will be added to the config file verbatimly) + or lists thereof + (which will be translated to multiple + lines with the same configuration key). + Boolean values are translated to "Yes" or "No". + The default contains some reasonable + configuration to yield an operational system. + ''; + + str1 = lib.types.addCheck str (s: s!=""); # non-empty string + int1 = lib.types.addCheck int (i: i>0); # positive integer + + configAttrType = + # Options in HylaFAX configuration files can be + # booleans, strings, integers, or list thereof + # representing multiple config directives with the same key. + # This type definition resolves all + # those types into a list of strings. + let + inherit (lib.types) attrsOf coercedTo listOf; + innerType = coercedTo bool (x: if x then "Yes" else "No") + (coercedTo int (toString) str); + in + attrsOf (coercedTo innerType lib.singleton (listOf innerType)); + + cfg = config.services.hylafax; + + modemConfigOptions = { name, config, ... }: { + options = { + name = mkOption { + type = str1; + example = "ttyS1"; + description = '' + Name of modem device, + will be searched for in <filename>/dev</filename>. + ''; + }; + type = mkOption { + type = str1; + example = "cirrus"; + description = '' + Name of modem configuration file, + will be searched for in <filename>config</filename> + in the spooling area directory. + ''; + }; + config = mkOption { + type = configAttrType; + example = { + AreaCode = "49"; + LocalCode = "30"; + FAXNumber = "123456"; + LocalIdentifier = "LostInBerlin"; + }; + description = '' + Attribute set of values for the given modem. + ${commonDescr} + Options defined here override options in + <option>commonModemConfig</option> for this modem. + ''; + }; + }; + config.name = mkDefault name; + config.config.Include = [ "config/${config.type}" ]; + }; + + defaultConfig = + let + inherit (config.security) wrapperDir; + inherit (config.services.mail.sendmailSetuidWrapper) program; + mkIfDefault = cond: value: mkIf cond (mkDefault value); + noWrapper = config.services.mail.sendmailSetuidWrapper==null; + # If a sendmail setuid wrapper exists, + # we add the path to the default configuration file. + # Otherwise, we use `false` to provoke + # an error if hylafax tries to use it. + c.sendmailPath = mkMerge [ + (mkIfDefault noWrapper ''${pkgs.coreutils}/bin/false'') + (mkIfDefault (!noWrapper) ''${wrapperDir}/${program}'') + ]; + importDefaultConfig = file: + lib.attrsets.mapAttrs + (lib.trivial.const mkDefault) + (import file { inherit pkgs; }); + c.commonModemConfig = importDefaultConfig ./modem-default.nix; + c.faxqConfig = importDefaultConfig ./faxq-default.nix; + c.hfaxdConfig = importDefaultConfig ./hfaxd-default.nix; + in + c; + + localConfig = + let + c.hfaxdConfig.UserAccessFile = cfg.userAccessFile; + c.faxqConfig = lib.attrsets.mapAttrs + (lib.trivial.const (v: mkIf (v!=null) v)) + { + AreaCode = cfg.areaCode; + CountryCode = cfg.countryCode; + LongDistancePrefix = cfg.longDistancePrefix; + InternationalPrefix = cfg.internationalPrefix; + }; + c.commonModemConfig = c.faxqConfig; + in + c; + +in + + +{ + + + options.services.hylafax = { + + enable = mkEnableOption ''HylaFAX server''; + + autostart = mkOption { + type = bool; + default = true; + example = false; + description = '' + Autostart the HylaFAX queue manager at system start. + If this is <literal>false</literal>, the queue manager + will still be started if there are pending + jobs or if a user tries to connect to it. + ''; + }; + + countryCode = mkOption { + type = nullOr str1; + default = null; + example = "49"; + description = ''Country code for server and all modems.''; + }; + + areaCode = mkOption { + type = nullOr str1; + default = null; + example = "30"; + description = ''Area code for server and all modems.''; + }; + + longDistancePrefix = mkOption { + type = nullOr str; + default = null; + example = "0"; + description = ''Long distance prefix for server and all modems.''; + }; + + internationalPrefix = mkOption { + type = nullOr str; + default = null; + example = "00"; + description = ''International prefix for server and all modems.''; + }; + + spoolAreaPath = mkOption { + type = path; + default = "/var/spool/fax"; + description = '' + The spooling area will be created/maintained + at the location given here. + ''; + }; + + userAccessFile = mkOption { + type = path; + default = "/etc/hosts.hfaxd"; + description = '' + The <filename>hosts.hfaxd</filename> + file entry in the spooling area + will be symlinked to the location given here. + This file must exist and be + readable only by the <literal>uucp</literal> user. + See hosts.hfaxd(5) for details. + This configuration permits access for all users: + <literal> + environment.etc."hosts.hfaxd" = { + mode = "0600"; + user = "uucp"; + text = ".*"; + }; + </literal> + Note that host-based access can be controlled with + <option>config.systemd.sockets.hylafax-hfaxd.listenStreams</option>; + by default, only 127.0.0.1 is permitted to connect. + ''; + }; + + sendmailPath = mkOption { + type = path; + example = literalExample "''${pkgs.postfix}/bin/sendmail"; + # '' ; # fix vim + description = '' + Path to <filename>sendmail</filename> program. + The default uses the local sendmail wrapper + (see <option>config.services.mail.sendmailSetuidWrapper</option>), + otherwise the <filename>false</filename> + binary to cause an error if used. + ''; + }; + + hfaxdConfig = mkOption { + type = configAttrType; + example.RecvqProtection = "0400"; + description = '' + Attribute set of lines for the global + hfaxd config file <filename>etc/hfaxd.conf</filename>. + ${commonDescr} + ''; + }; + + faxqConfig = mkOption { + type = configAttrType; + example = { + InternationalPrefix = "00"; + LongDistancePrefix = "0"; + }; + description = '' + Attribute set of lines for the global + faxq config file <filename>etc/config</filename>. + ${commonDescr} + ''; + }; + + commonModemConfig = mkOption { + type = configAttrType; + example = { + InternationalPrefix = "00"; + LongDistancePrefix = "0"; + }; + description = '' + Attribute set of default values for + modem config files <filename>etc/config.*</filename>. + ${commonDescr} + Think twice before changing + paths of fax-processing scripts. + ''; + }; + + modems = mkOption { + type = loaOf (submodule [ modemConfigOptions ]); + default = {}; + example.ttyS1 = { + type = "cirrus"; + config = { + FAXNumber = "123456"; + LocalIdentifier = "Smith"; + }; + }; + description = '' + Description of installed modems. + At least on modem must be defined + to enable the HylaFAX server. + ''; + }; + + spoolExtraInit = mkOption { + type = lines; + default = ""; + example = ''chmod 0755 . # everyone may read my faxes''; + description = '' + Additional shell code that is executed within the + spooling area directory right after its setup. + ''; + }; + + faxcron.enable.spoolInit = mkEnableOption '' + Purge old files from the spooling area with + <filename>faxcron</filename> + each time the spooling area is initialized. + ''; + faxcron.enable.frequency = mkOption { + type = nullOr str1; + default = null; + example = "daily"; + description = '' + Purge old files from the spooling area with + <filename>faxcron</filename> with the given frequency + (see systemd.time(7)). + ''; + }; + faxcron.infoDays = mkOption { + type = int1; + default = 30; + description = '' + Set the expiration time for data in the + remote machine information directory in days. + ''; + }; + faxcron.logDays = mkOption { + type = int1; + default = 30; + description = '' + Set the expiration time for + session trace log files in days. + ''; + }; + faxcron.rcvDays = mkOption { + type = int1; + default = 7; + description = '' + Set the expiration time for files in + the received facsimile queue in days. + ''; + }; + + faxqclean.enable.spoolInit = mkEnableOption '' + Purge old files from the spooling area with + <filename>faxqclean</filename> + each time the spooling area is initialized. + ''; + faxqclean.enable.frequency = mkOption { + type = nullOr str1; + default = null; + example = "daily"; + description = '' + Purge old files from the spooling area with + <filename>faxcron</filename> with the given frequency + (see systemd.time(7)). + ''; + }; + faxqclean.archiving = mkOption { + type = enum [ "never" "as-flagged" "always" ]; + default = "as-flagged"; + example = "always"; + description = '' + Enable or suppress job archiving: + <literal>never</literal> disables job archiving, + <literal>as-flagged</literal> archives jobs that + have been flagged for archiving by sendfax, + <literal>always</literal> forces archiving of all jobs. + See also sendfax(1) and faxqclean(8). + ''; + }; + faxqclean.doneqMinutes = mkOption { + type = int1; + default = 15; + example = literalExample ''24*60''; + description = '' + Set the job + age threshold (in minutes) that controls how long + jobs may reside in the doneq directory. + ''; + }; + faxqclean.docqMinutes = mkOption { + type = int1; + default = 60; + example = literalExample ''24*60''; + description = '' + Set the document + age threshold (in minutes) that controls how long + unreferenced files may reside in the docq directory. + ''; + }; + + }; + + + config.services.hylafax = + mkIf + (config.services.hylafax.enable) + (mkMerge [ defaultConfig localConfig ]) + ; + +} diff --git a/nixos/modules/services/networking/hylafax/spool.sh b/nixos/modules/services/networking/hylafax/spool.sh new file mode 100755 index 000000000000..31e930e8c597 --- /dev/null +++ b/nixos/modules/services/networking/hylafax/spool.sh @@ -0,0 +1,111 @@ +#! @shell@ -e + +# The following lines create/update the HylaFAX spool directory: +# Subdirectories/files with persistent data are kept, +# other directories/files are removed/recreated, +# mostly from the template spool +# directory in the HylaFAX package. + +# This block explains how the spool area is +# derived from the spool template in the HylaFAX package: +# +# + capital letter: directory; file otherwise +# + P/p: persistent directory +# + F/f: directory with symlinks per entry +# + T/t: temporary data +# + S/s: single symlink into package +# | +# | + u: change ownership to uucp:uucp +# | + U: ..also change access mode to user-only +# | | +# archive P U +# bin S +# client T u (client connection info) +# config S +# COPYRIGHT s +# dev T u (maybe some FIFOs) +# docq P U +# doneq P U +# etc F contains customized config files! +# etc/hosts.hfaxd f +# etc/xferfaxlog f +# info P u (database of called devices) +# log P u (communication logs) +# pollq P U +# recvq P u +# sendq P U +# status T u (modem status info files) +# tmp T U + + +shopt -s dotglob # if bash sees "*", it also includes dot files +lnsym () { ln --symbol "$@" ; } +lnsymfrc () { ln --symbolic --force "$@" ; } +cprd () { cp --remove-destination "$@" ; } +update () { install --owner=@faxuser@ --group=@faxgroup@ "$@" ; } + + +## create/update spooling area + +update --mode=0750 -d "@spoolAreaPath@" +cd "@spoolAreaPath@" + +persist=(archive docq doneq info log pollq recvq sendq) + +# remove entries that don't belong here +touch dummy # ensure "*" resolves to something +for k in * +do + keep=0 + for j in "${persist[@]}" xferfaxlog clientlog faxcron.lastrun + do + if test "$k" == "$j" + then + keep=1 + break + fi + done + if test "$keep" == "0" + then + rm --recursive "$k" + fi +done + +# create persistent data directories (unless they exist already) +update --mode=0700 -d "${persist[@]}" +chmod 0755 info log recvq + +# create ``xferfaxlog``, ``faxcron.lastrun``, ``clientlog`` +touch clientlog faxcron.lastrun xferfaxlog +chown @faxuser@:@faxgroup@ clientlog faxcron.lastrun xferfaxlog + +# create symlinks for frozen directories/files +lnsym --target-directory=. "@hylafax@"/spool/{COPYRIGHT,bin,config} + +# create empty temporary directories +update --mode=0700 -d client dev status +update -d tmp + + +## create and fill etc + +install -d "@spoolAreaPath@/etc" +cd "@spoolAreaPath@/etc" + +# create symlinks to all files in template's etc +lnsym --target-directory=. "@hylafax@/spool/etc"/* + +# set LOCKDIR in setup.cache +sed --regexp-extended 's|^(UUCP_LOCKDIR=).*$|\1'"'@lockPath@'|g" --in-place setup.cache + +# etc/{xferfaxlog,lastrun} are stored in the spool root +lnsymfrc --target-directory=. ../xferfaxlog +lnsymfrc --no-target-directory ../faxcron.lastrun lastrun + +# etc/hosts.hfaxd is provided by the NixOS configuration +lnsymfrc --no-target-directory "@userAccessFile@" hosts.hfaxd + +# etc/config and etc/config.${DEVID} must be copied: +# hfaxd reads these file after locking itself up in a chroot +cprd --no-target-directory "@globalConfigPath@" config +cprd --target-directory=. "@modemConfigPath@"/* diff --git a/nixos/modules/services/networking/hylafax/systemd.nix b/nixos/modules/services/networking/hylafax/systemd.nix new file mode 100644 index 000000000000..91d9c1a37da6 --- /dev/null +++ b/nixos/modules/services/networking/hylafax/systemd.nix @@ -0,0 +1,249 @@ +{ config, lib, pkgs, ... }: + + +let + + inherit (lib) mkIf mkMerge; + inherit (lib) concatStringsSep optionalString; + + cfg = config.services.hylafax; + mapModems = lib.flip map (lib.attrValues cfg.modems); + + mkConfigFile = name: conf: + # creates hylafax config file, + # makes sure "Include" is listed *first* + let + mkLines = conf: + (lib.concatLists + (lib.flip lib.mapAttrsToList conf + (k: map (v: ''${k}: ${v}'') + ))); + include = mkLines { Include = conf.Include or []; }; + other = mkLines ( conf // { Include = []; } ); + in + pkgs.writeText ''hylafax-config${name}'' + (concatStringsSep "\n" (include ++ other)); + + globalConfigPath = mkConfigFile "" cfg.faxqConfig; + + modemConfigPath = + let + mkModemConfigFile = { config, name, ... }: + mkConfigFile ''.${name}'' + (cfg.commonModemConfig // config); + mkLine = { name, type, ... }@modem: '' + # check if modem config file exists: + test -f "${pkgs.hylafaxplus}/spool/config/${type}" + ln \ + --symbolic \ + --no-target-directory \ + "${mkModemConfigFile modem}" \ + "$out/config.${name}" + ''; + in + pkgs.runCommand "hylafax-config-modems" {} + ''mkdir --parents "$out/" ${concatStringsSep "\n" (mapModems mkLine)}''; + + setupSpoolScript = pkgs.substituteAll { + name = "hylafax-setup-spool.sh"; + src = ./spool.sh; + isExecutable = true; + inherit (pkgs.stdenv) shell; + hylafax = pkgs.hylafaxplus; + faxuser = "uucp"; + faxgroup = "uucp"; + lockPath = "/var/lock"; + inherit globalConfigPath modemConfigPath; + inherit (cfg) sendmailPath spoolAreaPath userAccessFile; + }; + + waitFaxqScript = pkgs.substituteAll { + # This script checks the modems status files + # and waits until all modems report readiness. + name = "hylafax-faxq-wait-start.sh"; + src = ./faxq-wait.sh; + isExecutable = true; + timeoutSec = toString 10; + inherit (pkgs.stdenv) shell; + inherit (cfg) spoolAreaPath; + }; + + sockets."hylafax-hfaxd" = { + description = "HylaFAX server socket"; + documentation = [ "man:hfaxd(8)" ]; + wantedBy = [ "multi-user.target" ]; + listenStreams = [ "127.0.0.1:4559" ]; + socketConfig.FreeBind = true; + socketConfig.Accept = true; + }; + + paths."hylafax-faxq" = { + description = "HylaFAX queue manager sendq watch"; + documentation = [ "man:faxq(8)" "man:sendq(5)" ]; + wantedBy = [ "multi-user.target" ]; + pathConfig.PathExistsGlob = [ ''${cfg.spoolAreaPath}/sendq/q*'' ]; + }; + + timers = mkMerge [ + ( + mkIf (cfg.faxcron.enable.frequency!=null) + { "hylafax-faxcron".timerConfig.Persistent = true; } + ) + ( + mkIf (cfg.faxqclean.enable.frequency!=null) + { "hylafax-faxqclean".timerConfig.Persistent = true; } + ) + ]; + + hardenService = + # Add some common systemd service hardening settings, + # but allow each service (here) to override + # settings by explicitely setting those to `null`. + # More hardening would be nice but makes + # customizing hylafax setups very difficult. + # If at all, it should only be added along + # with some options to customize it. + let + hardening = { + PrivateDevices = true; # breaks /dev/tty... + PrivateNetwork = true; + PrivateTmp = true; + ProtectControlGroups = true; + #ProtectHome = true; # breaks custom spool dirs + ProtectKernelModules = true; + ProtectKernelTunables = true; + #ProtectSystem = "strict"; # breaks custom spool dirs + RestrictNamespaces = true; + RestrictRealtime = true; + }; + filter = key: value: (value != null) || ! (lib.hasAttr key hardening); + apply = service: lib.filterAttrs filter (hardening // (service.serviceConfig or {})); + in + service: service // { serviceConfig = apply service; }; + + services."hylafax-spool" = { + description = "HylaFAX spool area preparation"; + documentation = [ "man:hylafax-server(4)" ]; + script = '' + ${setupSpoolScript} + cd "${cfg.spoolAreaPath}" + ${cfg.spoolExtraInit} + if ! test -f "${cfg.spoolAreaPath}/etc/hosts.hfaxd" + then + echo hosts.hfaxd is missing + exit 1 + fi + ''; + serviceConfig.ExecStop = ''${setupSpoolScript}''; + serviceConfig.RemainAfterExit = true; + serviceConfig.Type = "oneshot"; + unitConfig.RequiresMountsFor = [ cfg.spoolAreaPath ]; + }; + + services."hylafax-faxq" = { + description = "HylaFAX queue manager"; + documentation = [ "man:faxq(8)" ]; + requires = [ "hylafax-spool.service" ]; + after = [ "hylafax-spool.service" ]; + wants = mapModems ( { name, ... }: ''hylafax-faxgetty@${name}.service'' ); + wantedBy = mkIf cfg.autostart [ "multi-user.target" ]; + serviceConfig.Type = "forking"; + serviceConfig.ExecStart = ''${pkgs.hylafaxplus}/spool/bin/faxq -q "${cfg.spoolAreaPath}"''; + # This delays the "readiness" of this service until + # all modems are initialized (or a timeout is reached). + # Otherwise, sending a fax with the fax service + # stopped will always yield a failed send attempt: + # The fax service is started when the job is created with + # `sendfax`, but modems need some time to initialize. + serviceConfig.ExecStartPost = [ ''${waitFaxqScript}'' ]; + # faxquit fails if the pipe is already gone + # (e.g. the service is already stopping) + serviceConfig.ExecStop = ''-${pkgs.hylafaxplus}/spool/bin/faxquit -q "${cfg.spoolAreaPath}"''; + # disable some systemd hardening settings + serviceConfig.PrivateDevices = null; + serviceConfig.RestrictRealtime = null; + }; + + services."hylafax-hfaxd@" = { + description = "HylaFAX server"; + documentation = [ "man:hfaxd(8)" ]; + after = [ "hylafax-faxq.service" ]; + requires = [ "hylafax-faxq.service" ]; + serviceConfig.StandardInput = "socket"; + serviceConfig.StandardOutput = "socket"; + serviceConfig.ExecStart = ''${pkgs.hylafaxplus}/spool/bin/hfaxd -q "${cfg.spoolAreaPath}" -d -I''; + unitConfig.RequiresMountsFor = [ cfg.userAccessFile ]; + # disable some systemd hardening settings + serviceConfig.PrivateDevices = null; + serviceConfig.PrivateNetwork = null; + }; + + services."hylafax-faxcron" = rec { + description = "HylaFAX spool area maintenance"; + documentation = [ "man:faxcron(8)" ]; + after = [ "hylafax-spool.service" ]; + requires = [ "hylafax-spool.service" ]; + wantedBy = mkIf cfg.faxcron.enable.spoolInit requires; + startAt = mkIf (cfg.faxcron.enable.frequency!=null) cfg.faxcron.enable.frequency; + serviceConfig.ExecStart = concatStringsSep " " [ + ''${pkgs.hylafaxplus}/spool/bin/faxcron'' + ''-q "${cfg.spoolAreaPath}"'' + ''-info ${toString cfg.faxcron.infoDays}'' + ''-log ${toString cfg.faxcron.logDays}'' + ''-rcv ${toString cfg.faxcron.rcvDays}'' + ]; + }; + + services."hylafax-faxqclean" = rec { + description = "HylaFAX spool area queue cleaner"; + documentation = [ "man:faxqclean(8)" ]; + after = [ "hylafax-spool.service" ]; + requires = [ "hylafax-spool.service" ]; + wantedBy = mkIf cfg.faxqclean.enable.spoolInit requires; + startAt = mkIf (cfg.faxqclean.enable.frequency!=null) cfg.faxqclean.enable.frequency; + serviceConfig.ExecStart = concatStringsSep " " [ + ''${pkgs.hylafaxplus}/spool/bin/faxqclean'' + ''-q "${cfg.spoolAreaPath}"'' + ''-v'' + (optionalString (cfg.faxqclean.archiving!="never") ''-a'') + (optionalString (cfg.faxqclean.archiving=="always") ''-A'') + ''-j ${toString (cfg.faxqclean.doneqMinutes*60)}'' + ''-d ${toString (cfg.faxqclean.docqMinutes*60)}'' + ]; + }; + + mkFaxgettyService = { name, ... }: + lib.nameValuePair ''hylafax-faxgetty@${name}'' rec { + description = "HylaFAX faxgetty for %I"; + documentation = [ "man:faxgetty(8)" ]; + bindsTo = [ "dev-%i.device" ]; + requires = [ "hylafax-spool.service" ]; + after = bindsTo ++ requires; + before = [ "hylafax-faxq.service" "getty.target" ]; + unitConfig.StopWhenUnneeded = true; + unitConfig.AssertFileNotEmpty = ''${cfg.spoolAreaPath}/etc/config.%I''; + serviceConfig.UtmpIdentifier = "%I"; + serviceConfig.TTYPath = "/dev/%I"; + serviceConfig.Restart = "always"; + serviceConfig.KillMode = "process"; + serviceConfig.IgnoreSIGPIPE = false; + serviceConfig.ExecStart = ''-${pkgs.hylafaxplus}/spool/bin/faxgetty -q "${cfg.spoolAreaPath}" /dev/%I''; + # faxquit fails if the pipe is already gone + # (e.g. the service is already stopping) + serviceConfig.ExecStop = ''-${pkgs.hylafaxplus}/spool/bin/faxquit -q "${cfg.spoolAreaPath}" %I''; + # disable some systemd hardening settings + serviceConfig.PrivateDevices = null; + serviceConfig.RestrictRealtime = null; + }; + + modemServices = + lib.listToAttrs (mapModems mkFaxgettyService); + +in + +{ + config.systemd = mkIf cfg.enable { + inherit sockets timers paths; + services = lib.mapAttrs (lib.const hardenService) (services // modemServices); + }; +} diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix index 3afafaf3fed5..0e9b354cfcaf 100644 --- a/nixos/modules/services/networking/i2pd.nix +++ b/nixos/modules/services/networking/i2pd.nix @@ -8,6 +8,17 @@ let homeDir = "/var/lib/i2pd"; + strOpt = k: v: k + " = " + v; + boolOpt = k: v: k + " = " + boolToString v; + intOpt = k: v: k + " = " + toString v; + lstOpt = k: xs: k + " = " + concatStringsSep "," xs; + optionalNullString = o: s: optional (! isNull s) (strOpt o s); + optionalNullBool = o: b: optional (! isNull b) (boolOpt o b); + optionalNullInt = o: i: optional (! isNull i) (intOpt o i); + optionalEmptyList = o: l: optional ([] != l) (lstOpt o l); + + mkEnableTrueOption = name: mkEnableOption name // { default = true; }; + mkEndpointOpt = name: addr: port: { enable = mkEnableOption name; name = mkOption { @@ -18,42 +29,54 @@ let address = mkOption { type = types.str; default = addr; - description = "Bind address for ${name} endpoint. Default: " + addr; + description = "Bind address for ${name} endpoint."; }; port = mkOption { type = types.int; default = port; - description = "Bind port for ${name} endoint. Default: " + toString port; + description = "Bind port for ${name} endoint."; }; }; - mkKeyedEndpointOpt = name: addr: port: keyFile: + i2cpOpts = name: { + length = mkOption { + type = types.int; + description = "Guaranteed minimum hops for ${name} tunnels."; + default = 3; + }; + quantity = mkOption { + type = types.int; + description = "Number of simultaneous ${name} tunnels."; + default = 5; + }; + }; + + mkKeyedEndpointOpt = name: addr: port: keyloc: (mkEndpointOpt name addr port) // { keys = mkOption { - type = types.str; - default = ""; + type = with types; nullOr str; + default = keyloc; description = '' File to persist ${lib.toUpper name} keys. ''; }; - }; - - commonTunOpts = let - i2cpOpts = { - length = mkOption { - type = types.int; - description = "Guaranteed minimum hops."; - default = 3; + inbound = i2cpOpts name; + outbound = i2cpOpts name; + latency.min = mkOption { + type = with types; nullOr int; + description = "Min latency for tunnels."; + default = null; }; - quantity = mkOption { - type = types.int; - description = "Number of simultaneous tunnels."; - default = 5; + latency.max = mkOption { + type = with types; nullOr int; + description = "Max latency for tunnels."; + default = null; }; }; - in name: { - outbound = i2cpOpts; - inbound = i2cpOpts; + + commonTunOpts = name: { + outbound = i2cpOpts name; + inbound = i2cpOpts name; crypto.tagsToSend = mkOption { type = types.int; description = "Number of ElGamal/AES tags to send."; @@ -70,94 +93,142 @@ let }; } // mkEndpointOpt name "127.0.0.1" 0; - i2pdConf = pkgs.writeText "i2pd.conf" '' - # DO NOT EDIT -- this file has been generated automatically. - loglevel = ${cfg.logLevel} - - ipv4 = ${boolToString cfg.enableIPv4} - ipv6 = ${boolToString cfg.enableIPv6} - notransit = ${boolToString cfg.notransit} - floodfill = ${boolToString cfg.floodfill} - netid = ${toString cfg.netid} - ${if isNull cfg.bandwidth then "" else "bandwidth = ${toString cfg.bandwidth}" } - ${if isNull cfg.port then "" else "port = ${toString cfg.port}"} - - [limits] - transittunnels = ${toString cfg.limits.transittunnels} - - [upnp] - enabled = ${boolToString cfg.upnp.enable} - name = ${cfg.upnp.name} - - [precomputation] - elgamal = ${boolToString cfg.precomputation.elgamal} - - [reseed] - verify = ${boolToString cfg.reseed.verify} - file = ${cfg.reseed.file} - urls = ${builtins.concatStringsSep "," cfg.reseed.urls} - - [addressbook] - defaulturl = ${cfg.addressbook.defaulturl} - subscriptions = ${builtins.concatStringsSep "," cfg.addressbook.subscriptions} - - ${flip concatMapStrings + sec = name: "\n[" + name + "]"; + notice = "# DO NOT EDIT -- this file has been generated automatically."; + i2pdConf = let + opts = [ + notice + (strOpt "loglevel" cfg.logLevel) + (boolOpt "logclftime" cfg.logCLFTime) + (boolOpt "ipv4" cfg.enableIPv4) + (boolOpt "ipv6" cfg.enableIPv6) + (boolOpt "notransit" cfg.notransit) + (boolOpt "floodfill" cfg.floodfill) + (intOpt "netid" cfg.netid) + ] ++ (optionalNullInt "bandwidth" cfg.bandwidth) + ++ (optionalNullInt "port" cfg.port) + ++ (optionalNullString "family" cfg.family) + ++ (optionalNullString "datadir" cfg.dataDir) + ++ (optionalNullInt "share" cfg.share) + ++ (optionalNullBool "ssu" cfg.ssu) + ++ (optionalNullBool "ntcp" cfg.ntcp) + ++ (optionalNullString "ntcpproxy" cfg.ntcpProxy) + ++ (optionalNullString "ifname" cfg.ifname) + ++ (optionalNullString "ifname4" cfg.ifname4) + ++ (optionalNullString "ifname6" cfg.ifname6) + ++ [ + (sec "limits") + (intOpt "transittunnels" cfg.limits.transittunnels) + (intOpt "coresize" cfg.limits.coreSize) + (intOpt "openfiles" cfg.limits.openFiles) + (intOpt "ntcphard" cfg.limits.ntcpHard) + (intOpt "ntcpsoft" cfg.limits.ntcpSoft) + (intOpt "ntcpthreads" cfg.limits.ntcpThreads) + (sec "upnp") + (boolOpt "enabled" cfg.upnp.enable) + (sec "precomputation") + (boolOpt "elgamal" cfg.precomputation.elgamal) + (sec "reseed") + (boolOpt "verify" cfg.reseed.verify) + ] ++ (optionalNullString "file" cfg.reseed.file) + ++ (optionalEmptyList "urls" cfg.reseed.urls) + ++ (optionalNullString "floodfill" cfg.reseed.floodfill) + ++ (optionalNullString "zipfile" cfg.reseed.zipfile) + ++ (optionalNullString "proxy" cfg.reseed.proxy) + ++ [ + (sec "trust") + (boolOpt "enabled" cfg.trust.enable) + (boolOpt "hidden" cfg.trust.hidden) + ] ++ (optionalEmptyList "routers" cfg.trust.routers) + ++ (optionalNullString "family" cfg.trust.family) + ++ [ + (sec "websockets") + (boolOpt "enabled" cfg.websocket.enable) + (strOpt "address" cfg.websocket.address) + (intOpt "port" cfg.websocket.port) + (sec "exploratory") + (intOpt "inbound.length" cfg.exploratory.inbound.length) + (intOpt "inbound.quantity" cfg.exploratory.inbound.quantity) + (intOpt "outbound.length" cfg.exploratory.outbound.length) + (intOpt "outbound.quantity" cfg.exploratory.outbound.quantity) + (sec "ntcp2") + (boolOpt "enabled" cfg.ntcp2.enable) + (boolOpt "published" cfg.ntcp2.published) + (intOpt "port" cfg.ntcp2.port) + (sec "addressbook") + (strOpt "defaulturl" cfg.addressbook.defaulturl) + ] ++ (optionalEmptyList "subscriptions" cfg.addressbook.subscriptions) + ++ (flip map (collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto) - (proto: '' - [${proto.name}] - enabled = ${boolToString proto.enable} - address = ${proto.address} - port = ${toString proto.port} - ${if proto ? keys then "keys = ${proto.keys}" else ""} - ${if proto ? auth then "auth = ${boolToString proto.auth}" else ""} - ${if proto ? user then "user = ${proto.user}" else ""} - ${if proto ? pass then "pass = ${proto.pass}" else ""} - ${if proto ? outproxy then "outproxy = ${proto.outproxy}" else ""} - ${if proto ? outproxyPort then "outproxyport = ${toString proto.outproxyPort}" else ""} - '') - } - ''; - - i2pdTunnelConf = pkgs.writeText "i2pd-tunnels.conf" '' - # DO NOT EDIT -- this file has been generated automatically. - ${flip concatMapStrings + (proto: let protoOpts = [ + (sec proto.name) + (boolOpt "enabled" proto.enable) + (strOpt "address" proto.address) + (intOpt "port" proto.port) + ] ++ (if proto ? keys then optionalNullString "keys" proto.keys else []) + ++ (if proto ? auth then optionalNullBool "auth" proto.auth else []) + ++ (if proto ? user then optionalNullString "user" proto.user else []) + ++ (if proto ? pass then optionalNullString "pass" proto.pass else []) + ++ (if proto ? strictHeaders then optionalNullBool "strictheaders" proto.strictHeaders else []) + ++ (if proto ? hostname then optionalNullString "hostname" proto.hostname else []) + ++ (if proto ? outproxy then optionalNullString "outproxy" proto.outproxy else []) + ++ (if proto ? outproxyPort then optionalNullInt "outproxyport" proto.outproxyPort else []) + ++ (if proto ? outproxyEnable then optionalNullBool "outproxy.enabled" proto.outproxyEnable else []); + in (concatStringsSep "\n" protoOpts) + )); + in + pkgs.writeText "i2pd.conf" (concatStringsSep "\n" opts); + + tunnelConf = let opts = [ + notice + (flip map (collect (tun: tun ? port && tun ? destination) cfg.outTunnels) - (tun: '' - [${tun.name}] - type = client - destination = ${tun.destination} - destinationport = ${toString tun.destinationPort} - keys = ${tun.keys} - address = ${tun.address} - port = ${toString tun.port} - inbound.length = ${toString tun.inbound.length} - outbound.length = ${toString tun.outbound.length} - inbound.quantity = ${toString tun.inbound.quantity} - outbound.quantity = ${toString tun.outbound.quantity} - crypto.tagsToSend = ${toString tun.crypto.tagsToSend} - '') - } - ${flip concatMapStrings + (tun: let outTunOpts = [ + (sec tun.name) + "type = client" + (intOpt "port" tun.port) + (strOpt "destination" tun.destination) + ] ++ (if tun ? destinationPort then optionalNullInt "destinationport" tun.destinationPort else []) + ++ (if tun ? keys then + optionalNullString "keys" tun.keys else []) + ++ (if tun ? address then + optionalNullString "address" tun.address else []) + ++ (if tun ? inbound.length then + optionalNullInt "inbound.length" tun.inbound.length else []) + ++ (if tun ? inbound.quantity then + optionalNullInt "inbound.quantity" tun.inbound.quantity else []) + ++ (if tun ? outbound.length then + optionalNullInt "outbound.length" tun.outbound.length else []) + ++ (if tun ? outbound.quantity then + optionalNullInt "outbound.quantity" tun.outbound.quantity else []) + ++ (if tun ? crypto.tagsToSend then + optionalNullInt "crypto.tagstosend" tun.crypto.tagsToSend else []); + in concatStringsSep "\n" outTunOpts)) + (flip map (collect (tun: tun ? port && tun ? address) cfg.inTunnels) - (tun: '' - [${tun.name}] - type = server - destination = ${tun.destination} - keys = ${tun.keys} - host = ${tun.address} - port = ${toString tun.port} - inport = ${toString tun.inPort} - accesslist = ${builtins.concatStringsSep "," tun.accessList} - '') - } - ''; + (tun: let inTunOpts = [ + (sec tun.name) + "type = server" + (intOpt "port" tun.port) + (strOpt "host" tun.address) + ] ++ (if tun ? destination then + optionalNullString "destination" tun.destination else []) + ++ (if tun ? keys then + optionalNullString "keys" tun.keys else []) + ++ (if tun ? inPort then + optionalNullInt "inport" tun.inPort else []) + ++ (if tun ? accessList then + optionalEmptyList "accesslist" tun.accessList else []); + in concatStringsSep "\n" inTunOpts))]; + in pkgs.writeText "i2pd-tunnels.conf" opts; i2pdSh = pkgs.writeScriptBin "i2pd" '' #!/bin/sh exec ${pkgs.i2pd}/bin/i2pd \ ${if isNull cfg.address then "" else "--host="+cfg.address} \ + --service \ --conf=${i2pdConf} \ - --tunconf=${i2pdTunnelConf} + --tunconf=${tunnelConf} ''; in @@ -170,9 +241,7 @@ in services.i2pd = { - enable = mkOption { - type = types.bool; - default = false; + enable = mkEnableOption "I2Pd daemon" // { description = '' Enables I2Pd as a running service upon activation. Please read http://i2pd.readthedocs.io/en/latest/ for further @@ -192,6 +261,8 @@ in ''; }; + logCLFTime = mkEnableOption "Full CLF-formatted date and time to log"; + address = mkOption { type = with types; nullOr str; default = null; @@ -200,17 +271,72 @@ in ''; }; - notransit = mkOption { - type = types.bool; - default = false; + family = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Specify a family the router belongs to. + ''; + }; + + dataDir = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Alternative path to storage of i2pd data (RI, keys, peer profiles, ...) + ''; + }; + + share = mkOption { + type = types.int; + default = 100; + description = '' + Limit of transit traffic from max bandwidth in percents. + ''; + }; + + ifname = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Network interface to bind to. + ''; + }; + + ifname4 = mkOption { + type = with types; nullOr str; + default = null; + description = '' + IPv4 interface to bind to. + ''; + }; + + ifname6 = mkOption { + type = with types; nullOr str; + default = null; + description = '' + IPv6 interface to bind to. + ''; + }; + + ntcpProxy = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Proxy URL for NTCP transport. + ''; + }; + + ntcp = mkEnableTrueOption "ntcp"; + ssu = mkEnableTrueOption "ssu"; + + notransit = mkEnableOption "notransit" // { description = '' Tells the router to not accept transit tunnels during startup. ''; }; - floodfill = mkOption { - type = types.bool; - default = false; + floodfill = mkEnableOption "floodfill" // { description = '' If the router is declared to be unreachable and needs introduction nodes. ''; @@ -241,131 +367,178 @@ in ''; }; - enableIPv4 = mkOption { - type = types.bool; - default = true; + enableIPv4 = mkEnableTrueOption "IPv4 connectivity"; + enableIPv6 = mkEnableOption "IPv6 connectivity"; + nat = mkEnableTrueOption "NAT bypass"; + + upnp.enable = mkEnableOption "UPnP service discovery"; + upnp.name = mkOption { + type = types.str; + default = "I2Pd"; description = '' - Enables IPv4 connectivity. Enabled by default. + Name i2pd appears in UPnP forwardings list. ''; }; - enableIPv6 = mkOption { - type = types.bool; - default = false; + precomputation.elgamal = mkEnableTrueOption "Precomputed ElGamal tables" // { description = '' - Enables IPv6 connectivity. Disabled by default. + Whenever to use precomputated tables for ElGamal. + <command>i2pd</command> defaults to <literal>false</literal> + to save 64M of memory (and looses some performance). + + We default to <literal>true</literal> as that is what most + users want anyway. ''; }; - nat = mkOption { - type = types.bool; - default = true; + reseed.verify = mkEnableOption "SU3 signature verification"; + + reseed.file = mkOption { + type = with types; nullOr str; + default = null; description = '' - Assume router is NATed. Enabled by default. + Full path to SU3 file to reseed from. ''; }; - upnp = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - Enables UPnP. - ''; - }; + reseed.urls = mkOption { + type = with types; listOf str; + default = []; + description = '' + Reseed URLs. + ''; + }; - name = mkOption { - type = types.str; - default = "I2Pd"; - description = '' - Name i2pd appears in UPnP forwardings list. - ''; - }; + reseed.floodfill = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Path to router info of floodfill to reseed from. + ''; }; - precomputation.elgamal = mkOption { - type = types.bool; - default = true; + reseed.zipfile = mkOption { + type = with types; nullOr str; + default = null; description = '' - Whenever to use precomputated tables for ElGamal. - <command>i2pd</command> defaults to <literal>false</literal> - to save 64M of memory (and looses some performance). + Path to local .zip file to reseed from. + ''; + }; - We default to <literal>true</literal> as that is what most - users want anyway. + reseed.proxy = mkOption { + type = with types; nullOr str; + default = null; + description = '' + URL for reseed proxy, supports http/socks. ''; }; - reseed = { - verify = mkOption { - type = types.bool; - default = false; - description = '' - Request SU3 signature verification - ''; - }; + addressbook.defaulturl = mkOption { + type = types.str; + default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt"; + description = '' + AddressBook subscription URL for initial setup + ''; + }; + addressbook.subscriptions = mkOption { + type = with types; listOf str; + default = [ + "http://inr.i2p/export/alive-hosts.txt" + "http://i2p-projekt.i2p/hosts.txt" + "http://stats.i2p/cgi-bin/newhosts.txt" + ]; + description = '' + AddressBook subscription URLs + ''; + }; - file = mkOption { - type = types.str; - default = ""; - description = '' - Full path to SU3 file to reseed from - ''; - }; + trust.enable = mkEnableOption "Explicit trust options"; - urls = mkOption { - type = with types; listOf str; - default = [ - "https://reseed.i2p-project.de/" - "https://i2p.mooo.com/netDb/" - "https://netdb.i2p2.no/" - "https://us.reseed.i2p2.no:444/" - "https://uk.reseed.i2p2.no:444/" - "https://i2p.manas.ca:8443/" - ]; - description = '' - Reseed URLs - ''; - }; + trust.family = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Router Familiy to trust for first hops. + ''; }; - addressbook = { - defaulturl = mkOption { - type = types.str; - default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt"; - description = '' - AddressBook subscription URL for initial setup - ''; - }; - subscriptions = mkOption { - type = with types; listOf str; - default = [ - "http://inr.i2p/export/alive-hosts.txt" - "http://i2p-projekt.i2p/hosts.txt" - "http://stats.i2p/cgi-bin/newhosts.txt" - ]; - description = '' - AddressBook subscription URLs - ''; - }; + trust.routers = mkOption { + type = with types; listOf str; + default = []; + description = '' + Only connect to the listed routers. + ''; + }; + + trust.hidden = mkEnableOption "Router concealment."; + + websocket = mkEndpointOpt "websockets" "127.0.0.1" 7666; + + exploratory.inbound = i2cpOpts "exploratory"; + exploratory.outbound = i2cpOpts "exploratory"; + + ntcp2.enable = mkEnableTrueOption "NTCP2."; + ntcp2.published = mkEnableOption "NTCP2 publication."; + ntcp2.port = mkOption { + type = types.int; + default = 0; + description = '' + Port to listen for incoming NTCP2 connections (0=auto). + ''; }; limits.transittunnels = mkOption { type = types.int; default = 2500; description = '' - Maximum number of active transit sessions + Maximum number of active transit sessions. + ''; + }; + + limits.coreSize = mkOption { + type = types.int; + default = 0; + description = '' + Maximum size of corefile in Kb (0 - use system limit). + ''; + }; + + limits.openFiles = mkOption { + type = types.int; + default = 0; + description = '' + Maximum number of open files (0 - use system default). + ''; + }; + + limits.ntcpHard = mkOption { + type = types.int; + default = 0; + description = '' + Maximum number of active transit sessions. + ''; + }; + + limits.ntcpSoft = mkOption { + type = types.int; + default = 0; + description = '' + Threshold to start probabalistic backoff with ntcp sessions (default: use system limit). + ''; + }; + + limits.ntcpThreads = mkOption { + type = types.int; + default = 1; + description = '' + Maximum number of threads used by NTCP DH worker. ''; }; proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // { - auth = mkOption { - type = types.bool; - default = false; - description = '' - Enable authentication for webconsole. - ''; - }; + + auth = mkEnableOption "Webconsole authentication"; + user = mkOption { type = types.str; default = "i2pd"; @@ -373,6 +546,7 @@ in Username for webconsole access ''; }; + pass = mkOption { type = types.str; default = "i2pd"; @@ -380,11 +554,35 @@ in Password for webconsole access. ''; }; + + strictHeaders = mkOption { + type = with types; nullOr bool; + default = null; + description = '' + Enable strict host checking on WebUI. + ''; + }; + + hostname = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Expected hostname for WebUI. + ''; + }; }; - proto.httpProxy = mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4444 ""; - proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "") + proto.httpProxy = (mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4444 "httpproxy-keys.dat") + // { + outproxy = mkOption { + type = with types; nullOr str; + default = null; + description = "Upstream outproxy bind address."; + }; + }; + proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "socksproxy-keys.dat") // { + outproxyEnable = mkEnableOption "SOCKS outproxy"; outproxy = mkOption { type = types.str; default = "127.0.0.1"; @@ -408,8 +606,8 @@ in { name, ... }: { options = { destinationPort = mkOption { - type = types.int; - default = 0; + type = with types; nullOr int; + default = null; description = "Connect to particular port at destination."; }; } // commonTunOpts name; diff --git a/nixos/modules/services/networking/iperf3.nix b/nixos/modules/services/networking/iperf3.nix new file mode 100644 index 000000000000..742404a5692f --- /dev/null +++ b/nixos/modules/services/networking/iperf3.nix @@ -0,0 +1,87 @@ +{ config, lib, pkgs, ... }: with lib; +let + cfg = config.services.iperf3; + + api = { + enable = mkEnableOption "iperf3 network throughput testing server"; + port = mkOption { + type = types.ints.u16; + default = 5201; + description = "Server port to listen on for iperf3 client requsts."; + }; + affinity = mkOption { + type = types.nullOr types.ints.unsigned; + default = null; + description = "CPU affinity for the process."; + }; + bind = mkOption { + type = types.nullOr types.str; + default = null; + description = "Bind to the specific interface associated with the given address."; + }; + verbose = mkOption { + type = types.bool; + default = false; + description = "Give more detailed output."; + }; + forceFlush = mkOption { + type = types.bool; + default = false; + description = "Force flushing output at every interval."; + }; + debug = mkOption { + type = types.bool; + default = false; + description = "Emit debugging output."; + }; + rsaPrivateKey = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to the RSA private key (not password-protected) used to decrypt authentication credentials from the client."; + }; + authorizedUsersFile = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to the configuration file containing authorized users credentials to run iperf tests."; + }; + extraFlags = mkOption { + type = types.listOf types.str; + default = [ ]; + description = "Extra flags to pass to iperf3(1)."; + }; + }; + + imp = { + systemd.services.iperf3 = { + description = "iperf3 daemon"; + unitConfig.Documentation = "man:iperf3(1) https://iperf.fr/iperf-doc.php"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + Restart = "on-failure"; + RestartSec = 2; + DynamicUser = true; + PrivateDevices = true; + CapabilityBoundingSet = ""; + NoNewPrivileges = true; + ExecStart = '' + ${pkgs.iperf3}/bin/iperf \ + --server \ + --port ${toString cfg.port} \ + ${optionalString (cfg.affinity != null) "--affinity ${toString cfg.affinity}"} \ + ${optionalString (cfg.bind != null) "--bind ${cfg.bind}"} \ + ${optionalString (cfg.rsaPrivateKey != null) "--rsa-private-key-path ${cfg.rsaPrivateKey}"} \ + ${optionalString (cfg.authorizedUsersFile != null) "--authorized-users-path ${cfg.authorizedUsersFile}"} \ + ${optionalString cfg.verbose "--verbose"} \ + ${optionalString cfg.debug "--debug"} \ + ${optionalString cfg.forceFlush "--forceflush"} \ + ${escapeShellArgs cfg.extraFlags} + ''; + }; + }; + }; +in { + options.services.iperf3 = api; + config = mkIf cfg.enable imp; +} diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index d5af4648e8f9..2d76e0676b24 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -406,25 +406,25 @@ in { { source = configFile; target = "NetworkManager/NetworkManager.conf"; } - { source = "${networkmanager-openvpn}/etc/NetworkManager/VPN/nm-openvpn-service.name"; + { source = "${networkmanager-openvpn}/lib/NetworkManager/VPN/nm-openvpn-service.name"; target = "NetworkManager/VPN/nm-openvpn-service.name"; } - { source = "${networkmanager-vpnc}/etc/NetworkManager/VPN/nm-vpnc-service.name"; + { source = "${networkmanager-vpnc}/lib/NetworkManager/VPN/nm-vpnc-service.name"; target = "NetworkManager/VPN/nm-vpnc-service.name"; } - { source = "${networkmanager-openconnect}/etc/NetworkManager/VPN/nm-openconnect-service.name"; + { source = "${networkmanager-openconnect}/lib/NetworkManager/VPN/nm-openconnect-service.name"; target = "NetworkManager/VPN/nm-openconnect-service.name"; } - { source = "${networkmanager-fortisslvpn}/etc/NetworkManager/VPN/nm-fortisslvpn-service.name"; + { source = "${networkmanager-fortisslvpn}/lib/NetworkManager/VPN/nm-fortisslvpn-service.name"; target = "NetworkManager/VPN/nm-fortisslvpn-service.name"; } - { source = "${networkmanager-l2tp}/etc/NetworkManager/VPN/nm-l2tp-service.name"; + { source = "${networkmanager-l2tp}/lib/NetworkManager/VPN/nm-l2tp-service.name"; target = "NetworkManager/VPN/nm-l2tp-service.name"; } - { source = "${networkmanager_strongswan}/etc/NetworkManager/VPN/nm-strongswan-service.name"; + { source = "${networkmanager_strongswan}/lib/NetworkManager/VPN/nm-strongswan-service.name"; target = "NetworkManager/VPN/nm-strongswan-service.name"; } - { source = "${networkmanager-iodine}/etc/NetworkManager/VPN/nm-iodine-service.name"; + { source = "${networkmanager-iodine}/lib/NetworkManager/VPN/nm-iodine-service.name"; target = "NetworkManager/VPN/nm-iodine-service.name"; } ] ++ optional (cfg.appendNameservers == [] || cfg.insertNameservers == []) diff --git a/nixos/modules/services/networking/zeronet.nix b/nixos/modules/services/networking/zeronet.nix index 2377cb2c8f11..8b60799891ca 100644 --- a/nixos/modules/services/networking/zeronet.nix +++ b/nixos/modules/services/networking/zeronet.nix @@ -12,6 +12,8 @@ let log_dir = ${cfg.logDir} '' + lib.optionalString (cfg.port != null) '' ui_port = ${toString cfg.port} + '' + lib.optionalString (cfg.torAlways) '' + tor = always '' + cfg.extraConfig; }; in with lib; { @@ -35,13 +37,19 @@ in with lib; { port = mkOption { type = types.nullOr types.int; default = null; - example = 15441; - description = "Optional zeronet port."; + example = 43110; + description = "Optional zeronet web UI port."; }; tor = mkOption { type = types.bool; default = false; + description = "Use TOR for zeronet traffic where possible."; + }; + + torAlways = mkOption { + type = types.bool; + default = false; description = "Use TOR for all zeronet traffic."; }; @@ -60,9 +68,13 @@ in with lib; { services.tor = mkIf cfg.tor { enable = true; controlPort = 9051; - extraConfig = "CookieAuthentication 1"; + extraConfig = '' + CacheDirectoryGroupReadable 1 + CookieAuthentication 1 + CookieAuthFileGroupReadable 1 + ''; }; - + systemd.services.zeronet = { description = "zeronet"; after = [ "network.target" (optionalString cfg.tor "tor.service") ]; |