diff options
Diffstat (limited to 'nixos/modules/services/networking')
45 files changed, 1640 insertions, 561 deletions
diff --git a/nixos/modules/services/networking/adguardhome.nix b/nixos/modules/services/networking/adguardhome.nix index 03f9b9f9bad45..98ddf0716087a 100644 --- a/nixos/modules/services/networking/adguardhome.nix +++ b/nixos/modules/services/networking/adguardhome.nix @@ -10,12 +10,20 @@ let "--pidfile /run/AdGuardHome/AdGuardHome.pid" "--work-dir /var/lib/AdGuardHome/" "--config /var/lib/AdGuardHome/AdGuardHome.yaml" - "--host ${cfg.host}" - "--port ${toString cfg.port}" ] ++ cfg.extraArgs); -in -{ + baseConfig = { + bind_host = cfg.host; + bind_port = cfg.port; + }; + + configFile = pkgs.writeTextFile { + name = "AdGuardHome.yaml"; + text = builtins.toJSON (recursiveUpdate cfg.settings baseConfig); + checkPhase = "${pkgs.adguardhome}/bin/adguardhome -c $out --check-config"; + }; + +in { options.services.adguardhome = with types; { enable = mkEnableOption "AdGuard Home network-wide ad blocker"; @@ -44,6 +52,31 @@ in ''; }; + mutableSettings = mkOption { + default = true; + type = bool; + description = '' + Allow changes made on the AdGuard Home web interface to persist between + service restarts. + ''; + }; + + settings = mkOption { + type = (pkgs.formats.yaml { }).type; + default = { }; + description = '' + AdGuard Home configuration. Refer to + <link xlink:href="https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration#configuration-file"/> + for details on supported values. + + <note><para> + On start and if <option>mutableSettings</option> is <literal>true</literal>, + these options are merged into the configuration file on start, taking + precedence over configuration changes made on the web interface. + </para></note> + ''; + }; + extraArgs = mkOption { default = [ ]; type = listOf str; @@ -54,6 +87,22 @@ in }; config = mkIf cfg.enable { + assertions = [ + { + assertion = cfg.settings != { } + -> (hasAttrByPath [ "dns" "bind_host" ] cfg.settings) + || (hasAttrByPath [ "dns" "bind_hosts" ] cfg.settings); + message = + "AdGuard setting dns.bind_host or dns.bind_hosts needs to be configured for a minimal working configuration"; + } + { + assertion = cfg.settings != { } + -> hasAttrByPath [ "dns" "bootstrap_dns" ] cfg.settings; + message = + "AdGuard setting dns.bootstrap_dns needs to be configured for a minimal working configuration"; + } + ]; + systemd.services.adguardhome = { description = "AdGuard Home: Network-level blocker"; after = [ "network.target" ]; @@ -62,6 +111,19 @@ in StartLimitIntervalSec = 5; StartLimitBurst = 10; }; + + preStart = optionalString (cfg.settings != { }) '' + if [ -e "$STATE_DIRECTORY/AdGuardHome.yaml" ] \ + && [ "${toString cfg.mutableSettings}" = "1" ]; then + # Writing directly to AdGuardHome.yaml results in empty file + ${pkgs.yaml-merge}/bin/yaml-merge "$STATE_DIRECTORY/AdGuardHome.yaml" "${configFile}" > "$STATE_DIRECTORY/AdGuardHome.yaml.tmp" + mv "$STATE_DIRECTORY/AdGuardHome.yaml.tmp" "$STATE_DIRECTORY/AdGuardHome.yaml" + else + cp --force "${configFile}" "$STATE_DIRECTORY/AdGuardHome.yaml" + chmod 600 "$STATE_DIRECTORY/AdGuardHome.yaml" + fi + ''; + serviceConfig = { DynamicUser = true; ExecStart = "${pkgs.adguardhome}/bin/adguardhome ${args}"; diff --git a/nixos/modules/services/networking/bind.nix b/nixos/modules/services/networking/bind.nix index e44f8d4cf3026..2045612ec0549 100644 --- a/nixos/modules/services/networking/bind.nix +++ b/nixos/modules/services/networking/bind.nix @@ -59,7 +59,7 @@ let listen-on-v6 { ${concatMapStrings (entry: " ${entry}; ") cfg.listenOnIpv6} }; allow-query { cachenetworks; }; blackhole { badnetworks; }; - forward first; + forward ${cfg.forward}; forwarders { ${concatMapStrings (entry: " ${entry}; ") cfg.forwarders} }; directory "${cfg.directory}"; pid-file "/run/named/named.pid"; @@ -151,6 +151,14 @@ in "; }; + forward = mkOption { + default = "first"; + type = types.enum ["first" "only"]; + description = " + Whether to forward 'first' (try forwarding but lookup directly if forwarding fails) or 'only'. + "; + }; + listenOn = mkOption { default = [ "any" ]; type = types.listOf types.str; diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix index c14adbda3c5ac..fc06cdaa6e58c 100644 --- a/nixos/modules/services/networking/bird.nix +++ b/nixos/modules/services/networking/bird.nix @@ -31,7 +31,23 @@ let default = true; description = '' Whether the config should be checked at build time. - Disabling this might become necessary if the config includes files not present during build time. + When the config can't be checked during build time, for example when it includes + other files, either disable this option or use <code>preCheckConfig</code> to create + the included files before checking. + ''; + }; + preCheckConfig = mkOption { + type = types.lines; + default = ""; + example = '' + echo "cost 100;" > include.conf + ''; + description = '' + Commands to execute before the config file check. The file to be checked will be + available as <code>${variant}.conf</code> in the current directory. + + Files created with this option will not be available at service runtime, only during + build time checking. ''; }; }; @@ -45,7 +61,9 @@ let name = "${variant}.conf"; text = cfg.config; checkPhase = optionalString cfg.checkConfig '' - ${pkg}/bin/${birdBin} -d -p -c $out + ln -s $out ${variant}.conf + ${cfg.preCheckConfig} + ${pkg}/bin/${birdBin} -d -p -c ${variant}.conf ''; }; diff --git a/nixos/modules/services/networking/blocky.nix b/nixos/modules/services/networking/blocky.nix new file mode 100644 index 0000000000000..7488e05fc0331 --- /dev/null +++ b/nixos/modules/services/networking/blocky.nix @@ -0,0 +1,40 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.blocky; + + format = pkgs.formats.yaml { }; + configFile = format.generate "config.yaml" cfg.settings; +in +{ + options.services.blocky = { + enable = mkEnableOption "Fast and lightweight DNS proxy as ad-blocker for local network with many features"; + + settings = mkOption { + type = format.type; + default = { }; + description = '' + Blocky configuration. Refer to + <link xlink:href="https://0xerr0r.github.io/blocky/configuration/"/> + for details on supported values. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.blocky = { + description = "A DNS proxy and ad-blocker for the local network"; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + DynamicUser = true; + ExecStart = "${pkgs.blocky}/bin/blocky --config ${configFile}"; + + AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; + CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/connman.nix b/nixos/modules/services/networking/connman.nix index 8886e7a30f1fd..9945dc83a279e 100644 --- a/nixos/modules/services/networking/connman.nix +++ b/nixos/modules/services/networking/connman.nix @@ -127,7 +127,7 @@ in { description = "ConnMan VPN service"; wantedBy = [ "multi-user.target" ]; after = [ "syslog.target" ]; - before = [ "connman" ]; + before = [ "connman.service" ]; serviceConfig = { Type = "dbus"; BusName = "net.connman.vpn"; @@ -140,7 +140,7 @@ in { description = "D-BUS Service"; serviceConfig = { Name = "net.connman.vpn"; - before = [ "connman" ]; + before = [ "connman.service" ]; ExecStart = "${cfg.package}/sbin/connman-vpnd -n"; User = "root"; SystemdService = "connman-vpn.service"; diff --git a/nixos/modules/services/networking/croc.nix b/nixos/modules/services/networking/croc.nix index 9466adf71d8c2..d044979e10dfa 100644 --- a/nixos/modules/services/networking/croc.nix +++ b/nixos/modules/services/networking/croc.nix @@ -51,7 +51,7 @@ in ProtectKernelLogs = true; ProtectKernelModules = true; ProtectKernelTunables = true; - ProtectProc = "noaccess"; + ProtectProc = "invisible"; ProtectSystem = "strict"; RemoveIPC = true; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix index 8a2c0fc7080cf..d025c8f8177a8 100644 --- a/nixos/modules/services/networking/ddclient.nix +++ b/nixos/modules/services/networking/ddclient.nix @@ -13,7 +13,7 @@ let foreground=YES use=${cfg.use} login=${cfg.username} - password= + password=${lib.optionalString (cfg.protocol == "nsupdate") "/run/${RuntimeDirectory}/ddclient.key"} protocol=${cfg.protocol} ${lib.optionalString (cfg.script != "") "script=${cfg.script}"} ${lib.optionalString (cfg.server != "") "server=${cfg.server}"} @@ -30,7 +30,9 @@ let preStart = '' install ${configFile} /run/${RuntimeDirectory}/ddclient.conf - ${lib.optionalString (cfg.configFile == null) (if (cfg.passwordFile != null) then '' + ${lib.optionalString (cfg.configFile == null) (if (cfg.protocol == "nsupdate") then '' + install ${cfg.passwordFile} /run/${RuntimeDirectory}/ddclient.key + '' else if (cfg.passwordFile != null) then '' password=$(printf "%q" "$(head -n 1 "${cfg.passwordFile}")") sed -i "s|^password=$|password=$password|" /run/${RuntimeDirectory}/ddclient.conf '' else '' @@ -85,7 +87,9 @@ with lib; }; username = mkOption { - default = ""; + # For `nsupdate` username contains the path to the nsupdate executable + default = lib.optionalString (config.services.ddclient.protocol == "nsupdate") "${pkgs.bind.dnsutils}/bin/nsupdate"; + defaultText = ""; type = str; description = '' User name. @@ -96,7 +100,7 @@ with lib; default = null; type = nullOr str; description = '' - A file containing the password. + A file containing the password or a TSIG key in named format when using the nsupdate protocol. ''; }; diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix index 2c339350acd34..3eb7ca99eafd4 100644 --- a/nixos/modules/services/networking/dhcpcd.nix +++ b/nixos/modules/services/networking/dhcpcd.nix @@ -183,6 +183,20 @@ in config = mkIf enableDHCP { + assertions = [ { + # dhcpcd doesn't start properly with malloc ∉ [ libc scudo ] + # see https://github.com/NixOS/nixpkgs/issues/151696 + assertion = + dhcpcd.enablePrivSep + -> elem config.environment.memoryAllocator.provider [ "libc" "scudo" ]; + message = '' + dhcpcd with privilege separation is incompatible with chosen system malloc. + Currently only the `libc` and `scudo` allocators are known to work. + To disable dhcpcd's privilege separation, overlay Nixpkgs and override dhcpcd + to set `enablePrivSep = false`. + ''; + } ]; + systemd.services.dhcpcd = let cfgN = config.networking; hasDefaultGatewaySet = (cfgN.defaultGateway != null && cfgN.defaultGateway.address != "") diff --git a/nixos/modules/services/networking/dhcpd.nix b/nixos/modules/services/networking/dhcpd.nix index 54e4f90028598..3c4c0069dfd00 100644 --- a/nixos/modules/services/networking/dhcpd.nix +++ b/nixos/modules/services/networking/dhcpd.nix @@ -28,38 +28,45 @@ let } ''; - dhcpdService = postfix: cfg: optionalAttrs cfg.enable { - "dhcpd${postfix}" = { - description = "DHCPv${postfix} server"; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - - preStart = '' - mkdir -m 755 -p ${cfg.stateDir} - chown dhcpd:nogroup ${cfg.stateDir} - touch ${cfg.stateDir}/dhcpd.leases - ''; - - serviceConfig = - let - configFile = if cfg.configFile != null then cfg.configFile else writeConfig cfg; - args = [ "@${pkgs.dhcp}/sbin/dhcpd" "dhcpd${postfix}" "-${postfix}" - "-pf" "/run/dhcpd${postfix}/dhcpd.pid" - "-cf" "${configFile}" - "-lf" "${cfg.stateDir}/dhcpd.leases" - "-user" "dhcpd" "-group" "nogroup" - ] ++ cfg.extraFlags - ++ cfg.interfaces; - - in { - ExecStart = concatMapStringsSep " " escapeShellArg args; - Type = "forking"; - Restart = "always"; - RuntimeDirectory = [ "dhcpd${postfix}" ]; - PIDFile = "/run/dhcpd${postfix}/dhcpd.pid"; + dhcpdService = postfix: cfg: + let + configFile = + if cfg.configFile != null + then cfg.configFile + else writeConfig cfg; + leaseFile = "/var/lib/dhcpd${postfix}/dhcpd.leases"; + args = [ + "@${pkgs.dhcp}/sbin/dhcpd" "dhcpd${postfix}" "-${postfix}" + "-pf" "/run/dhcpd${postfix}/dhcpd.pid" + "-cf" configFile + "-lf" leaseFile + ] ++ cfg.extraFlags + ++ cfg.interfaces; + in + optionalAttrs cfg.enable { + "dhcpd${postfix}" = { + description = "DHCPv${postfix} server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + preStart = "touch ${leaseFile}"; + serviceConfig = { + ExecStart = concatMapStringsSep " " escapeShellArg args; + Type = "forking"; + Restart = "always"; + DynamicUser = true; + User = "dhcpd"; + Group = "dhcpd"; + AmbientCapabilities = [ + "CAP_NET_RAW" # to send ICMP messages + "CAP_NET_BIND_SERVICE" # to bind on DHCP port (67) + ]; + StateDirectory = "dhcpd${postfix}"; + RuntimeDirectory = "dhcpd${postfix}"; + PIDFile = "/run/dhcpd${postfix}/dhcpd.pid"; + }; }; - }; - }; + }; machineOpts = { ... }: { @@ -102,15 +109,6 @@ let ''; }; - stateDir = mkOption { - type = types.path; - # We use /var/lib/dhcp for DHCPv4 to save backwards compatibility. - default = "/var/lib/dhcp${if postfix == "4" then "" else postfix}"; - description = '' - State directory for the DHCP server. - ''; - }; - extraConfig = mkOption { type = types.lines; default = ""; @@ -194,7 +192,13 @@ in imports = [ (mkRenamedOptionModule [ "services" "dhcpd" ] [ "services" "dhcpd4" ]) - ]; + ] ++ flip map [ "4" "6" ] (postfix: + mkRemovedOptionModule [ "services" "dhcpd${postfix}" "stateDir" ] '' + The DHCP server state directory is now managed with the systemd's DynamicUser mechanism. + This means the directory is named after the service (dhcpd${postfix}), created under + /var/lib/private/ and symlinked to /var/lib/. + '' + ); ###### interface @@ -210,15 +214,6 @@ in config = mkIf (cfg4.enable || cfg6.enable) { - users = { - users.dhcpd = { - isSystemUser = true; - group = "dhcpd"; - description = "DHCP daemon user"; - }; - groups.dhcpd = {}; - }; - systemd.services = dhcpdService "4" cfg4 // dhcpdService "6" cfg6; }; diff --git a/nixos/modules/services/networking/dnscrypt-proxy2.nix b/nixos/modules/services/networking/dnscrypt-proxy2.nix index dc6a019e9b776..316e6e37f9da8 100644 --- a/nixos/modules/services/networking/dnscrypt-proxy2.nix +++ b/nixos/modules/services/networking/dnscrypt-proxy2.nix @@ -118,4 +118,7 @@ in }; }; }; + + # uses attributes of the linked package + meta.buildDocsInSandbox = false; } diff --git a/nixos/modules/services/networking/ergochat.nix b/nixos/modules/services/networking/ergochat.nix new file mode 100644 index 0000000000000..cfaf69fc61391 --- /dev/null +++ b/nixos/modules/services/networking/ergochat.nix @@ -0,0 +1,155 @@ +{ config, lib, options, pkgs, ... }: let + cfg = config.services.ergochat; +in { + options = { + services.ergochat = { + + enable = lib.mkEnableOption "Ergo IRC daemon"; + + openFilesLimit = lib.mkOption { + type = lib.types.int; + default = 1024; + description = '' + Maximum number of open files. Limits the clients and server connections. + ''; + }; + + configFile = lib.mkOption { + type = lib.types.path; + default = (pkgs.formats.yaml {}).generate "ergo.conf" cfg.settings; + defaultText = "generated config file from <literal>.settings</literal>"; + description = '' + Path to configuration file. + Setting this will skip any configuration done via <literal>.settings</literal> + ''; + }; + + settings = lib.mkOption { + type = (pkgs.formats.yaml {}).type; + description = '' + Ergo IRC daemon configuration file. + https://raw.githubusercontent.com/ergochat/ergo/master/default.yaml + ''; + default = { + network = { + name = "testnetwork"; + }; + server = { + name = "example.com"; + listeners = { + ":6667" = {}; + }; + casemapping = "permissive"; + enforce-utf = true; + lookup-hostnames = false; + ip-cloaking = { + enabled = false; + }; + forward-confirm-hostnames = false; + check-ident = false; + relaymsg = { + enabled = false; + }; + max-sendq = "1M"; + ip-limits = { + count = false; + throttle = false; + }; + }; + datastore = { + autoupgrade = true; + # this points to the StateDirectory of the systemd service + path = "/var/lib/ergo/ircd.db"; + }; + accounts = { + authentication-enabled = true; + registration = { + enabled = true; + allow-before-connect = true; + throttling = { + enabled = true; + duration = "10m"; + max-attempts = 30; + }; + bcrypt-cost = 4; + email-verification.enabled = false; + }; + multiclient = { + enabled = true; + allowed-by-default = true; + always-on = "opt-out"; + auto-away = "opt-out"; + }; + }; + channels = { + default-modes = "+ntC"; + registration = { + enabled = true; + }; + }; + limits = { + nicklen = 32; + identlen = 20; + channellen = 64; + awaylen = 390; + kicklen = 390; + topiclen = 390; + }; + history = { + enabled = true; + channel-length = 2048; + client-length = 256; + autoresize-window = "3d"; + autoreplay-on-join = 0; + chathistory-maxmessages = 100; + znc-maxmessages = 2048; + restrictions = { + expire-time = "1w"; + query-cutoff = "none"; + grace-period = "1h"; + }; + retention = { + allow-individual-delete = false; + enable-account-indexing = false; + }; + tagmsg-storage = { + default = false; + whitelist = [ + "+draft/react" + "+react" + ]; + }; + }; + }; + }; + + }; + }; + config = lib.mkIf cfg.enable { + + environment.etc."ergo.yaml".source = cfg.configFile; + + # merge configured values with default values + services.ergochat.settings = + lib.mapAttrsRecursive (_: lib.mkDefault) options.services.ergochat.settings.default; + + systemd.services.ergochat = { + description = "Ergo IRC daemon"; + wantedBy = [ "multi-user.target" ]; + # reload is not applying the changed config. further investigation is needed + # at some point this should be enabled, since we don't want to restart for + # every config change + # reloadIfChanged = true; + restartTriggers = [ cfg.configFile ]; + serviceConfig = { + ExecStart = "${pkgs.ergochat}/bin/ergo run --conf /etc/ergo.yaml"; + ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID"; + DynamicUser = true; + StateDirectory = "ergo"; + LimitNOFILE = toString cfg.openFilesLimit; + }; + }; + + }; + meta.maintainers = with lib.maintainers; [ lassulus tv ]; +} diff --git a/nixos/modules/services/networking/firefox/sync-server.nix b/nixos/modules/services/networking/firefox/sync-server.nix deleted file mode 100644 index 1ad573abfca3c..0000000000000 --- a/nixos/modules/services/networking/firefox/sync-server.nix +++ /dev/null @@ -1,183 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - cfg = config.services.firefox.syncserver; - - defaultDbLocation = "/var/db/firefox-sync-server/firefox-sync-server.db"; - defaultSqlUri = "sqlite:///${defaultDbLocation}"; - - syncServerIni = pkgs.writeText "syncserver.ini" '' - [DEFAULT] - overrides = ${cfg.privateConfig} - - [server:main] - use = egg:gunicorn - host = ${cfg.listen.address} - port = ${toString cfg.listen.port} - - [app:main] - use = egg:syncserver - - [syncserver] - public_url = ${cfg.publicUrl} - ${optionalString (cfg.sqlUri != "") "sqluri = ${cfg.sqlUri}"} - allow_new_users = ${boolToString cfg.allowNewUsers} - - [browserid] - backend = tokenserver.verifiers.LocalVerifier - audiences = ${removeSuffix "/" cfg.publicUrl} - ''; - - user = "syncserver"; - group = "syncserver"; -in - -{ - meta.maintainers = with lib.maintainers; [ nadrieril ]; - - options = { - services.firefox.syncserver = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - Whether to enable a Firefox Sync Server, this give the opportunity to - Firefox users to store all synchronized data on their own server. To use this - server, Firefox users should visit the <option>about:config</option>, and - replicate the following change - - <screen> - services.sync.tokenServerURI: http://localhost:5000/token/1.0/sync/1.5 - </screen> - - where <option>http://localhost:5000/</option> corresponds to the - public url of the server. - ''; - }; - - listen.address = mkOption { - type = types.str; - default = "127.0.0.1"; - example = "0.0.0.0"; - description = '' - Address on which the sync server listen to. - ''; - }; - - listen.port = mkOption { - type = types.port; - default = 5000; - description = '' - Port on which the sync server listen to. - ''; - }; - - publicUrl = mkOption { - type = types.str; - default = "http://localhost:5000/"; - example = "http://sync.example.com/"; - description = '' - Public URL with which firefox users can use to access the sync server. - ''; - }; - - allowNewUsers = mkOption { - type = types.bool; - default = true; - description = '' - Whether to allow new-user signups on the server. Only request by - existing accounts will be honored. - ''; - }; - - sqlUri = mkOption { - type = types.str; - default = defaultSqlUri; - example = "postgresql://scott:tiger@localhost/test"; - description = '' - The location of the database. This URL is composed of - <option>dialect[+driver]://user:password@host/dbname[?key=value..]</option>, - where <option>dialect</option> is a database name such as - <option>mysql</option>, <option>oracle</option>, <option>postgresql</option>, - etc., and <option>driver</option> the name of a DBAPI, such as - <option>psycopg2</option>, <option>pyodbc</option>, <option>cx_oracle</option>, - etc. The <link - xlink:href="http://docs.sqlalchemy.org/en/rel_0_9/core/engines.html#database-urls"> - SQLAlchemy documentation</link> provides more examples and describe the syntax of - the expected URL. - ''; - }; - - privateConfig = mkOption { - type = types.str; - default = "/etc/firefox/syncserver-secret.ini"; - description = '' - The private config file is used to extend the generated config with confidential - information, such as the <option>syncserver.sqlUri</option> setting if it contains a - password, and the <option>syncserver.secret</option> setting is used by the server to - generate cryptographically-signed authentication tokens. - - If this file does not exist, then it is created with a generated - <option>syncserver.secret</option> settings. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - - systemd.services.syncserver = { - after = [ "network.target" ]; - description = "Firefox Sync Server"; - wantedBy = [ "multi-user.target" ]; - path = [ - pkgs.coreutils - (pkgs.python.withPackages (ps: [ pkgs.syncserver ps.gunicorn ])) - ]; - - serviceConfig = { - User = user; - Group = group; - PermissionsStartOnly = true; - }; - - preStart = '' - if ! test -e ${cfg.privateConfig}; then - mkdir -p $(dirname ${cfg.privateConfig}) - echo > ${cfg.privateConfig} '[syncserver]' - chmod 600 ${cfg.privateConfig} - echo >> ${cfg.privateConfig} "secret = $(head -c 20 /dev/urandom | sha1sum | tr -d ' -')" - fi - chmod 600 ${cfg.privateConfig} - chmod 755 $(dirname ${cfg.privateConfig}) - chown ${user}:${group} ${cfg.privateConfig} - - '' + optionalString (cfg.sqlUri == defaultSqlUri) '' - if ! test -e $(dirname ${defaultDbLocation}); then - mkdir -m 700 -p $(dirname ${defaultDbLocation}) - chown ${user}:${group} $(dirname ${defaultDbLocation}) - fi - - # Move previous database file if it exists - oldDb="/var/db/firefox-sync-server.db" - if test -f $oldDb; then - mv $oldDb ${defaultDbLocation} - chown ${user}:${group} ${defaultDbLocation} - fi - ''; - - script = '' - gunicorn --paste ${syncServerIni} - ''; - }; - - users.users.${user} = { - inherit group; - isSystemUser = true; - }; - - users.groups.${group} = {}; - }; -} diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix index ff023a888f268..7482e29a3fda9 100644 --- a/nixos/modules/services/networking/firewall.nix +++ b/nixos/modules/services/networking/firewall.nix @@ -179,10 +179,6 @@ let ) cfg.allowedUDPPortRanges ) allInterfaces)} - # Accept IPv4 multicast. Not a big security risk since - # probably nobody is listening anyway. - #iptables -A nixos-fw -d 224.0.0.0/4 -j nixos-fw-accept - # Optionally respond to ICMPv4 pings. ${optionalString cfg.allowPing '' iptables -w -A nixos-fw -p icmp --icmp-type echo-request ${optionalString (cfg.pingLimit != null) @@ -326,7 +322,7 @@ in type = types.package; default = pkgs.iptables; defaultText = literalExpression "pkgs.iptables"; - example = literalExpression "pkgs.iptables-nftables-compat"; + example = literalExpression "pkgs.iptables-legacy"; description = '' The iptables package to use for running the firewall service." diff --git a/nixos/modules/services/networking/frr.nix b/nixos/modules/services/networking/frr.nix new file mode 100644 index 0000000000000..45a82b9450a44 --- /dev/null +++ b/nixos/modules/services/networking/frr.nix @@ -0,0 +1,211 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.frr; + + services = [ + "static" + "bgp" + "ospf" + "ospf6" + "rip" + "ripng" + "isis" + "pim" + "ldp" + "nhrp" + "eigrp" + "babel" + "sharp" + "pbr" + "bfd" + "fabric" + ]; + + allServices = services ++ [ "zebra" ]; + + isEnabled = service: cfg.${service}.enable; + + daemonName = service: if service == "zebra" then service else "${service}d"; + + configFile = service: + let + scfg = cfg.${service}; + in + if scfg.configFile != null then scfg.configFile + else pkgs.writeText "${daemonName service}.conf" + '' + ! FRR ${daemonName service} configuration + ! + hostname ${config.networking.hostName} + log syslog + service password-encryption + ! + ${scfg.config} + ! + end + ''; + + serviceOptions = service: + { + enable = mkEnableOption "the FRR ${toUpper service} routing protocol"; + + configFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/etc/frr/${daemonName service}.conf"; + description = '' + Configuration file to use for FRR ${daemonName service}. + By default the NixOS generated files are used. + ''; + }; + + config = mkOption { + type = types.lines; + default = ""; + example = + let + examples = { + rip = '' + router rip + network 10.0.0.0/8 + ''; + + ospf = '' + router ospf + network 10.0.0.0/8 area 0 + ''; + + bgp = '' + router bgp 65001 + neighbor 10.0.0.1 remote-as 65001 + ''; + }; + in + examples.${service} or ""; + description = '' + ${daemonName service} configuration statements. + ''; + }; + + vtyListenAddress = mkOption { + type = types.str; + default = "localhost"; + description = '' + Address to bind to for the VTY interface. + ''; + }; + + vtyListenPort = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + TCP Port to bind to for the VTY interface. + ''; + }; + }; + +in + +{ + + ###### interface + imports = [ + { + options.services.frr = { + zebra = (serviceOptions "zebra") // { + enable = mkOption { + type = types.bool; + default = any isEnabled services; + description = '' + Whether to enable the Zebra routing manager. + + The Zebra routing manager is automatically enabled + if any routing protocols are configured. + ''; + }; + }; + }; + } + { options.services.frr = (genAttrs services serviceOptions); } + ]; + + ###### implementation + + config = mkIf (any isEnabled allServices) { + + environment.systemPackages = [ + pkgs.frr # for the vtysh tool + ]; + + users.users.frr = { + description = "FRR daemon user"; + isSystemUser = true; + group = "frr"; + }; + + users.groups = { + frr = {}; + # Members of the frrvty group can use vtysh to inspect the FRR daemons + frrvty = { members = [ "frr" ]; }; + }; + + environment.etc = let + mkEtcLink = service: { + name = "frr/${service}.conf"; + value.source = configFile service; + }; + in + (builtins.listToAttrs + (map mkEtcLink (filter isEnabled allServices))) // { + "frr/vtysh.conf".text = ""; + }; + + systemd.tmpfiles.rules = [ + "d /run/frr 0750 frr frr -" + ]; + + systemd.services = + let + frrService = service: + let + scfg = cfg.${service}; + daemon = daemonName service; + in + nameValuePair daemon ({ + wantedBy = [ "multi-user.target" ]; + after = [ "network-pre.target" "systemd-sysctl.service" ] ++ lib.optionals (service != "zebra") [ "zebra.service" ]; + bindsTo = lib.optionals (service != "zebra") [ "zebra.service" ]; + wants = [ "network.target" ]; + + description = if service == "zebra" then "FRR Zebra routing manager" + else "FRR ${toUpper service} routing daemon"; + + unitConfig.Documentation = if service == "zebra" then "man:zebra(8)" + else "man:${daemon}(8) man:zebra(8)"; + + restartTriggers = [ + (configFile service) + ]; + reloadIfChanged = true; + + serviceConfig = { + PIDFile = "frr/${daemon}.pid"; + ExecStart = "${pkgs.frr}/libexec/frr/${daemon} -f /etc/frr/${service}.conf" + + optionalString (scfg.vtyListenAddress != "") " -A ${scfg.vtyListenAddress}" + + optionalString (scfg.vtyListenPort != null) " -P ${toString scfg.vtyListenPort}"; + ExecReload = "${pkgs.python3.interpreter} ${pkgs.frr}/libexec/frr/frr-reload.py --reload --daemon ${daemonName service} --bindir ${pkgs.frr}/bin --rundir /run/frr /etc/frr/${service}.conf"; + Restart = "on-abnormal"; + }; + }); + in + listToAttrs (map frrService (filter isEnabled allServices)); + + }; + + meta.maintainers = with lib.maintainers; [ woffs ]; + +} diff --git a/nixos/modules/services/networking/gogoclient.nix b/nixos/modules/services/networking/gogoclient.nix deleted file mode 100644 index 1205321818b97..0000000000000 --- a/nixos/modules/services/networking/gogoclient.nix +++ /dev/null @@ -1,87 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let cfg = config.services.gogoclient; -in - -{ - - ###### interface - - options = { - services.gogoclient = { - enable = mkOption { - default = false; - type = types.bool; - description = '' - Enable the gogoCLIENT IPv6 tunnel. - ''; - }; - autorun = mkOption { - type = types.bool; - default = true; - description = '' - Whether to automatically start the tunnel. - ''; - }; - - username = mkOption { - default = ""; - type = types.str; - description = '' - Your Gateway6 login name, if any. - ''; - }; - - password = mkOption { - default = ""; - type = types.str; - description = '' - Path to a file (as a string), containing your gogoNET password, if any. - ''; - }; - - server = mkOption { - type = types.str; - default = "anonymous.freenet6.net"; - example = "broker.freenet6.net"; - description = "The Gateway6 server to be used."; - }; - }; - }; - - ###### implementation - - config = mkIf cfg.enable { - boot.kernelModules = [ "tun" ]; - - networking.enableIPv6 = true; - - systemd.services.gogoclient = { - description = "ipv6 tunnel"; - - after = [ "network.target" ]; - requires = [ "network.target" ]; - - unitConfig.RequiresMountsFor = "/var/lib/gogoc"; - - script = let authMethod = if cfg.password == "" then "anonymous" else "any"; in '' - mkdir -p -m 700 /var/lib/gogoc - cat ${pkgs.gogoclient}/share/${pkgs.gogoclient.name}/gogoc.conf.sample | \ - ${pkgs.gnused}/bin/sed \ - -e "s|^userid=|&${cfg.username}|" \ - -e "s|^passwd=|&${optionalString (cfg.password != "") "$(cat ${cfg.password})"}|" \ - -e "s|^server=.*|server=${cfg.server}|" \ - -e "s|^auth_method=.*|auth_method=${authMethod}|" \ - -e "s|^#log_file=|log_file=1|" > /var/lib/gogoc/gogoc.conf - cd /var/lib/gogoc - exec ${pkgs.gogoclient}/bin/gogoc -y -f /var/lib/gogoc/gogoc.conf - ''; - } // optionalAttrs cfg.autorun { - wantedBy = [ "multi-user.target" ]; - }; - - }; - -} diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix new file mode 100644 index 0000000000000..091d2a938cd4e --- /dev/null +++ b/nixos/modules/services/networking/headscale.nix @@ -0,0 +1,490 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.headscale; + + dataDir = "/var/lib/headscale"; + runDir = "/run/headscale"; + + settingsFormat = pkgs.formats.yaml { }; + configFile = settingsFormat.generate "headscale.yaml" cfg.settings; +in +{ + options = { + services.headscale = { + enable = mkEnableOption "headscale, Open Source coordination server for Tailscale"; + + package = mkOption { + type = types.package; + default = pkgs.headscale; + defaultText = literalExpression "pkgs.headscale"; + description = '' + Which headscale package to use for the running server. + ''; + }; + + user = mkOption { + default = "headscale"; + type = types.str; + description = '' + User account under which headscale runs. + <note><para> + If left as the default value this user will automatically be created + on system activation, otherwise you are responsible for + ensuring the user exists before the headscale service starts. + </para></note> + ''; + }; + + group = mkOption { + default = "headscale"; + type = types.str; + description = '' + Group under which headscale runs. + <note><para> + If left as the default value this group will automatically be created + on system activation, otherwise you are responsible for + ensuring the user exists before the headscale service starts. + </para></note> + ''; + }; + + serverUrl = mkOption { + type = types.str; + default = "http://127.0.0.1:8080"; + description = '' + The url clients will connect to. + ''; + example = "https://myheadscale.example.com:443"; + }; + + address = mkOption { + type = types.str; + default = "127.0.0.1"; + description = '' + Listening address of headscale. + ''; + example = "0.0.0.0"; + }; + + port = mkOption { + type = types.port; + default = 8080; + description = '' + Listening port of headscale. + ''; + example = 443; + }; + + privateKeyFile = mkOption { + type = types.path; + default = "${dataDir}/private.key"; + description = '' + Path to private key file, generated automatically if it does not exist. + ''; + }; + + derp = { + urls = mkOption { + type = types.listOf types.str; + default = [ "https://controlplane.tailscale.com/derpmap/default" ]; + description = '' + List of urls containing DERP maps. + See <link xlink:href="https://tailscale.com/blog/how-tailscale-works/">How Tailscale works</link> for more information on DERP maps. + ''; + }; + + paths = mkOption { + type = types.listOf types.path; + default = [ ]; + description = '' + List of file paths containing DERP maps. + See <link xlink:href="https://tailscale.com/blog/how-tailscale-works/">How Tailscale works</link> for more information on DERP maps. + ''; + }; + + + autoUpdate = mkOption { + type = types.bool; + default = true; + description = '' + Whether to automatically update DERP maps on a set frequency. + ''; + example = false; + }; + + updateFrequency = mkOption { + type = types.str; + default = "24h"; + description = '' + Frequency to update DERP maps. + ''; + example = "5m"; + }; + + }; + + ephemeralNodeInactivityTimeout = mkOption { + type = types.str; + default = "30m"; + description = '' + Time before an inactive ephemeral node is deleted. + ''; + example = "5m"; + }; + + database = { + type = mkOption { + type = types.enum [ "sqlite3" "postgres" ]; + example = "postgres"; + default = "sqlite3"; + description = "Database engine to use."; + }; + + host = mkOption { + type = types.nullOr types.str; + default = null; + example = "127.0.0.1"; + description = "Database host address."; + }; + + port = mkOption { + type = types.nullOr types.port; + default = null; + example = 3306; + description = "Database host port."; + }; + + name = mkOption { + type = types.nullOr types.str; + default = null; + example = "headscale"; + description = "Database name."; + }; + + user = mkOption { + type = types.nullOr types.str; + default = null; + example = "headscale"; + description = "Database user."; + }; + + passwordFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/keys/headscale-dbpassword"; + description = '' + A file containing the password corresponding to + <option>database.user</option>. + ''; + }; + + path = mkOption { + type = types.nullOr types.str; + default = "${dataDir}/db.sqlite"; + description = "Path to the sqlite3 database file."; + }; + }; + + logLevel = mkOption { + type = types.str; + default = "info"; + description = '' + headscale log level. + ''; + example = "debug"; + }; + + dns = { + nameservers = mkOption { + type = types.listOf types.str; + default = [ "1.1.1.1" ]; + description = '' + List of nameservers to pass to Tailscale clients. + ''; + }; + + domains = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + Search domains to inject to Tailscale clients. + ''; + example = [ "mydomain.internal" ]; + }; + + magicDns = mkOption { + type = types.bool; + default = true; + description = '' + Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/). + Only works if there is at least a nameserver defined. + ''; + example = false; + }; + + baseDomain = mkOption { + type = types.str; + default = ""; + description = '' + Defines the base domain to create the hostnames for MagicDNS. + <option>baseDomain</option> must be a FQDNs, without the trailing dot. + The FQDN of the hosts will be + <literal>hostname.namespace.base_domain</literal> (e.g. + <literal>myhost.mynamespace.example.com</literal>). + ''; + }; + }; + + openIdConnect = { + issuer = mkOption { + type = types.str; + default = ""; + description = '' + URL to OpenID issuer. + ''; + example = "https://openid.example.com"; + }; + + clientId = mkOption { + type = types.str; + default = ""; + description = '' + OpenID Connect client ID. + ''; + }; + + clientSecretFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Path to OpenID Connect client secret file. + ''; + }; + + domainMap = mkOption { + type = types.attrsOf types.str; + default = { }; + description = '' + Domain map is used to map incomming users (by their email) to + a namespace. The key can be a string, or regex. + ''; + example = { + ".*" = "default-namespace"; + }; + }; + + }; + + tls = { + letsencrypt = { + hostname = mkOption { + type = types.nullOr types.str; + default = ""; + description = '' + Domain name to request a TLS certificate for. + ''; + }; + challengeType = mkOption { + type = types.enum [ "TLS_ALPN-01" "HTTP-01" ]; + default = "HTTP-01"; + description = '' + Type of ACME challenge to use, currently supported types: + <literal>HTTP-01</literal> or <literal>TLS_ALPN-01</literal>. + ''; + }; + httpListen = mkOption { + type = types.nullOr types.str; + default = ":http"; + description = '' + When HTTP-01 challenge is chosen, letsencrypt must set up a + verification endpoint, and it will be listening on: + <literal>:http = port 80</literal>. + ''; + }; + }; + + certFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Path to already created certificate. + ''; + }; + keyFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Path to key for already created certificate. + ''; + }; + }; + + aclPolicyFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Path to a file containg ACL policies. + ''; + }; + + settings = mkOption { + type = settingsFormat.type; + default = { }; + description = '' + Overrides to <filename>config.yaml</filename> as a Nix attribute set. + This option is ideal for overriding settings not exposed as Nix options. + Check the <link xlink:href="https://github.com/juanfont/headscale/blob/main/config-example.yaml">example config</link> + for possible options. + ''; + }; + + + }; + + }; + config = mkIf cfg.enable { + + services.headscale.settings = { + server_url = mkDefault cfg.serverUrl; + listen_addr = mkDefault "${cfg.address}:${toString cfg.port}"; + + private_key_path = mkDefault cfg.privateKeyFile; + + derp = { + urls = mkDefault cfg.derp.urls; + paths = mkDefault cfg.derp.paths; + auto_update_enable = mkDefault cfg.derp.autoUpdate; + update_frequency = mkDefault cfg.derp.updateFrequency; + }; + + # Turn off update checks since the origin of our package + # is nixpkgs and not Github. + disable_check_updates = true; + + ephemeral_node_inactivity_timeout = mkDefault cfg.ephemeralNodeInactivityTimeout; + + db_type = mkDefault cfg.database.type; + db_path = mkDefault cfg.database.path; + + log_level = mkDefault cfg.logLevel; + + dns_config = { + nameservers = mkDefault cfg.dns.nameservers; + domains = mkDefault cfg.dns.domains; + magic_dns = mkDefault cfg.dns.magicDns; + base_domain = mkDefault cfg.dns.baseDomain; + }; + + unix_socket = "${runDir}/headscale.sock"; + + # OpenID Connect + oidc = { + issuer = mkDefault cfg.openIdConnect.issuer; + client_id = mkDefault cfg.openIdConnect.clientId; + domain_map = mkDefault cfg.openIdConnect.domainMap; + }; + + tls_letsencrypt_cache_dir = "${dataDir}/.cache"; + + } // optionalAttrs (cfg.database.host != null) { + db_host = mkDefault cfg.database.host; + } // optionalAttrs (cfg.database.port != null) { + db_port = mkDefault cfg.database.port; + } // optionalAttrs (cfg.database.name != null) { + db_name = mkDefault cfg.database.name; + } // optionalAttrs (cfg.database.user != null) { + db_user = mkDefault cfg.database.user; + } // optionalAttrs (cfg.tls.letsencrypt.hostname != null) { + tls_letsencrypt_hostname = mkDefault cfg.tls.letsencrypt.hostname; + } // optionalAttrs (cfg.tls.letsencrypt.challengeType != null) { + tls_letsencrypt_challenge_type = mkDefault cfg.tls.letsencrypt.challengeType; + } // optionalAttrs (cfg.tls.letsencrypt.httpListen != null) { + tls_letsencrypt_listen = mkDefault cfg.tls.letsencrypt.httpListen; + } // optionalAttrs (cfg.tls.certFile != null) { + tls_cert_path = mkDefault cfg.tls.certFile; + } // optionalAttrs (cfg.tls.keyFile != null) { + tls_key_path = mkDefault cfg.tls.keyFile; + } // optionalAttrs (cfg.aclPolicyFile != null) { + acl_policy_path = mkDefault cfg.aclPolicyFile; + }; + + # Setup the headscale configuration in a known path in /etc to + # allow both the Server and the Client use it to find the socket + # for communication. + environment.etc."headscale/config.yaml".source = configFile; + + users.groups.headscale = mkIf (cfg.group == "headscale") { }; + + users.users.headscale = mkIf (cfg.user == "headscale") { + description = "headscale user"; + home = dataDir; + group = cfg.group; + isSystemUser = true; + }; + + systemd.services.headscale = { + description = "headscale coordination server for Tailscale"; + after = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + restartTriggers = [ configFile ]; + + script = '' + ${optionalString (cfg.database.passwordFile != null) '' + export HEADSCALE_DB_PASS="$(head -n1 ${escapeShellArg cfg.database.passwordFile})" + ''} + + export HEADSCALE_OIDC_CLIENT_SECRET="$(head -n1 ${escapeShellArg cfg.openIdConnect.clientSecretFile})" + exec ${cfg.package}/bin/headscale serve + ''; + + serviceConfig = + let + capabilityBoundingSet = [ "CAP_CHOWN" ] ++ optional (cfg.port < 1024) "CAP_NET_BIND_SERVICE"; + in + { + Restart = "always"; + Type = "simple"; + User = cfg.user; + Group = cfg.group; + + # Hardening options + RuntimeDirectory = "headscale"; + # Allow headscale group access so users can be added and use the CLI. + RuntimeDirectoryMode = "0750"; + + StateDirectory = "headscale"; + StateDirectoryMode = "0750"; + + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + RestrictSUIDSGID = true; + PrivateMounts = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectHostname = true; + ProtectClock = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + RestrictNamespaces = true; + RemoveIPC = true; + UMask = "0077"; + + CapabilityBoundingSet = capabilityBoundingSet; + AmbientCapabilities = capabilityBoundingSet; + NoNewPrivileges = true; + LockPersonality = true; + RestrictRealtime = true; + SystemCallFilter = [ "@system-service" "~@priviledged" "@chown" ]; + SystemCallArchitectures = "native"; + RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; + }; + }; + }; + + meta.maintainers = with maintainers; [ kradalby ]; +} diff --git a/nixos/modules/services/networking/hylafax/options.nix b/nixos/modules/services/networking/hylafax/options.nix index 8e59c68054d2a..8f621b61002fc 100644 --- a/nixos/modules/services/networking/hylafax/options.nix +++ b/nixos/modules/services/networking/hylafax/options.nix @@ -3,7 +3,7 @@ let inherit (lib.options) literalExpression mkEnableOption mkOption; - inherit (lib.types) bool enum ints lines attrsOf nullOr path str submodule; + inherit (lib.types) bool enum ints lines attrsOf nonEmptyStr nullOr path str submodule; inherit (lib.modules) mkDefault mkIf mkMerge; commonDescr = '' @@ -17,8 +17,6 @@ let configuration to yield an operational system. ''; - str1 = lib.types.addCheck str (s: s!=""); # non-empty string - configAttrType = # Options in HylaFAX configuration files can be # booleans, strings, integers, or list thereof @@ -37,7 +35,7 @@ let modemConfigOptions = { name, config, ... }: { options = { name = mkOption { - type = str1; + type = nonEmptyStr; example = "ttyS1"; description = '' Name of modem device, @@ -45,7 +43,7 @@ let ''; }; type = mkOption { - type = str1; + type = nonEmptyStr; example = "cirrus"; description = '' Name of modem configuration file, @@ -135,14 +133,14 @@ in }; countryCode = mkOption { - type = nullOr str1; + type = nullOr nonEmptyStr; default = null; example = "49"; description = "Country code for server and all modems."; }; areaCode = mkOption { - type = nullOr str1; + type = nullOr nonEmptyStr; default = null; example = "30"; description = "Area code for server and all modems."; @@ -279,7 +277,7 @@ in each time the spooling area is initialized. ''; faxcron.enable.frequency = mkOption { - type = nullOr str1; + type = nullOr nonEmptyStr; default = null; example = "daily"; description = '' @@ -319,7 +317,7 @@ in each time the spooling area is initialized. ''; faxqclean.enable.frequency = mkOption { - type = nullOr str1; + type = nullOr nonEmptyStr; default = null; example = "daily"; description = '' diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix index 17828ca44ff21..34fda57b23d23 100644 --- a/nixos/modules/services/networking/i2pd.nix +++ b/nixos/modules/services/networking/i2pd.nix @@ -222,14 +222,12 @@ let in concatStringsSep "\n" inTunOpts))]; in pkgs.writeText "i2pd-tunnels.conf" opts; - i2pdSh = pkgs.writeScriptBin "i2pd" '' - #!/bin/sh - exec ${pkgs.i2pd}/bin/i2pd \ - ${if cfg.address == null then "" else "--host="+cfg.address} \ - --service \ - --conf=${i2pdConf} \ - --tunconf=${tunnelConf} - ''; + i2pdFlags = concatStringsSep " " ( + optional (cfg.address != null) ("--host=" + cfg.address) ++ [ + "--service" + ("--conf=" + i2pdConf) + ("--tunconf=" + tunnelConf) + ]); in @@ -253,6 +251,15 @@ in ''; }; + package = mkOption { + type = types.package; + default = pkgs.i2pd; + defaultText = literalExpression "pkgs.i2pd"; + description = '' + i2pd package to use. + ''; + }; + logLevel = mkOption { type = types.enum ["debug" "info" "warn" "error"]; default = "error"; @@ -677,7 +684,7 @@ in User = "i2pd"; WorkingDirectory = homeDir; Restart = "on-abort"; - ExecStart = "${i2pdSh}/bin/i2pd"; + ExecStart = "${cfg.package}/bin/i2pd ${i2pdFlags}"; }; }; }; diff --git a/nixos/modules/services/networking/kea.nix b/nixos/modules/services/networking/kea.nix index 4da47f575f79f..17b4eb2e283be 100644 --- a/nixos/modules/services/networking/kea.nix +++ b/nixos/modules/services/networking/kea.nix @@ -378,4 +378,6 @@ in ]); meta.maintainers = with maintainers; [ hexa ]; + # uses attributes of the linked package + meta.buildDocsInSandbox = false; } diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix index 3a36ac7e6670e..28b8be7a9a0de 100644 --- a/nixos/modules/services/networking/kresd.nix +++ b/nixos/modules/services/networking/kresd.nix @@ -7,15 +7,16 @@ let # Convert systemd-style address specification to kresd config line(s). # On Nix level we don't attempt to precisely validate the address specifications. + # The optional IPv6 scope spec comes *after* port, perhaps surprisingly. mkListen = kind: addr: let - al_v4 = builtins.match "([0-9.]+):([0-9]+)" addr; - al_v6 = builtins.match "\\[(.+)]:([0-9]+)" addr; + al_v4 = builtins.match "([0-9.]+):([0-9]+)($)" addr; + al_v6 = builtins.match "\\[(.+)]:([0-9]+)(%.*|$)" addr; al_portOnly = builtins.match "([0-9]+)" addr; al = findFirst (a: a != null) (throw "services.kresd.*: incorrect address specification '${addr}'") [ al_v4 al_v6 al_portOnly ]; - port = last al; - addrSpec = if al_portOnly == null then "'${head al}'" else "{'::', '0.0.0.0'}"; + port = elemAt al 1; + addrSpec = if al_portOnly == null then "'${head al}${elemAt al 2}'" else "{'::', '0.0.0.0'}"; in # freebind is set for compatibility with earlier kresd services; # it could be configurable, for example. '' diff --git a/nixos/modules/services/networking/mailpile.nix b/nixos/modules/services/networking/mailpile.nix deleted file mode 100644 index 4673a2580b602..0000000000000 --- a/nixos/modules/services/networking/mailpile.nix +++ /dev/null @@ -1,74 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - - cfg = config.services.mailpile; - - hostname = cfg.hostname; - port = cfg.port; - -in - -{ - - ###### interface - - options = { - - services.mailpile = { - enable = mkEnableOption "Mailpile the mail client"; - - hostname = mkOption { - type = types.str; - default = "localhost"; - description = "Listen to this hostname or ip."; - }; - port = mkOption { - type = types.port; - default = 33411; - description = "Listen on this port."; - }; - }; - - }; - - - ###### implementation - - config = mkIf config.services.mailpile.enable { - - users.users.mailpile = - { uid = config.ids.uids.mailpile; - description = "Mailpile user"; - createHome = true; - home = "/var/lib/mailpile"; - }; - - users.groups.mailpile = - { gid = config.ids.gids.mailpile; - }; - - systemd.services.mailpile = - { - description = "Mailpile server."; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - User = "mailpile"; - ExecStart = "${pkgs.mailpile}/bin/mailpile --www ${hostname}:${port} --wait"; - # mixed - first send SIGINT to main process, - # then after 2min send SIGKILL to whole group if neccessary - KillMode = "mixed"; - KillSignal = "SIGINT"; # like Ctrl+C - safe mailpile shutdown - TimeoutSec = 120; # wait 2min untill SIGKILL - }; - environment.MAILPILE_HOME = "/var/lib/mailpile/.local/share/Mailpile"; - }; - - environment.systemPackages = [ pkgs.mailpile ]; - - }; - -} diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix index 2d498d4dbbcf5..b41a2fd27be2f 100644 --- a/nixos/modules/services/networking/mosquitto.nix +++ b/nixos/modules/services/networking/mosquitto.nix @@ -136,7 +136,7 @@ let + concatStringsSep "\n" (plainLines ++ optional (plainLines != []) '' - ${pkgs.mosquitto}/bin/mosquitto_passwd -U "$file" + ${cfg.package}/bin/mosquitto_passwd -U "$file" '' ++ hashedLines)); @@ -444,6 +444,15 @@ let globalOptions = with types; { enable = mkEnableOption "the MQTT Mosquitto broker"; + package = mkOption { + type = package; + default = pkgs.mosquitto; + defaultText = literalExpression "pkgs.mosquitto"; + description = '' + Mosquitto package to use. + ''; + }; + bridges = mkOption { type = attrsOf bridgeOptions; default = {}; @@ -556,7 +565,7 @@ in systemd.services.mosquitto = { description = "Mosquitto MQTT Broker Daemon"; wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; + after = [ "network-online.target" ]; serviceConfig = { Type = "notify"; NotifyAccess = "main"; @@ -565,7 +574,7 @@ in RuntimeDirectory = "mosquitto"; WorkingDirectory = cfg.dataDir; Restart = "on-failure"; - ExecStart = "${pkgs.mosquitto}/bin/mosquitto -c ${configFile}"; + ExecStart = "${cfg.package}/bin/mosquitto -c ${configFile}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; # Hardening diff --git a/nixos/modules/services/networking/mtr-exporter.nix b/nixos/modules/services/networking/mtr-exporter.nix new file mode 100644 index 0000000000000..ca261074ebde7 --- /dev/null +++ b/nixos/modules/services/networking/mtr-exporter.nix @@ -0,0 +1,87 @@ +{ config, lib, pkgs, ... }: + +let + inherit (lib) + maintainers types mkEnableOption mkOption mkIf + literalExpression escapeShellArg escapeShellArgs; + cfg = config.services.mtr-exporter; +in { + options = { + services = { + mtr-exporter = { + enable = mkEnableOption "a Prometheus exporter for MTR"; + + target = mkOption { + type = types.str; + example = "example.org"; + description = "Target to check using MTR."; + }; + + interval = mkOption { + type = types.int; + default = 60; + description = "Interval between MTR checks in seconds."; + }; + + port = mkOption { + type = types.port; + default = 8080; + description = "Listen port for MTR exporter."; + }; + + address = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "Listen address for MTR exporter."; + }; + + mtrFlags = mkOption { + type = with types; listOf str; + default = []; + example = ["-G1"]; + description = "Additional flags to pass to MTR."; + }; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.mtr-exporter = { + script = '' + exec ${pkgs.mtr-exporter}/bin/mtr-exporter \ + -mtr ${pkgs.mtr}/bin/mtr \ + -schedule '@every ${toString cfg.interval}s' \ + -bind ${escapeShellArg cfg.address}:${toString cfg.port} \ + -- \ + ${escapeShellArgs (cfg.mtrFlags ++ [ cfg.target ])} + ''; + wantedBy = [ "multi-user.target" ]; + requires = [ "network.target" ]; + after = [ "network.target" ]; + serviceConfig = { + Restart = "on-failure"; + # Hardening + CapabilityBoundingSet = [ "" ]; + DynamicUser = true; + LockPersonality = true; + ProcSubset = "pid"; + PrivateDevices = true; + PrivateUsers = true; + PrivateTmp = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RestrictNamespaces = true; + RestrictRealtime = true; + }; + }; + }; + + meta.maintainers = with maintainers; [ jakubgs ]; +} diff --git a/nixos/modules/services/networking/murmur.nix b/nixos/modules/services/networking/murmur.nix index bbbe1e181bba1..992678c43ebe2 100644 --- a/nixos/modules/services/networking/murmur.nix +++ b/nixos/modules/services/networking/murmur.nix @@ -294,7 +294,7 @@ in systemd.services.murmur = { description = "Murmur Chat Service"; wantedBy = [ "multi-user.target" ]; - after = [ "network-online.target "]; + after = [ "network-online.target" ]; preStart = '' ${pkgs.envsubst}/bin/envsubst \ -o /run/murmur/murmurd.ini \ diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index 73e63e2ee99b5..a9801036b00cd 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -384,6 +384,17 @@ in { so you don't need to to that yourself. ''; }; + + enableFccUnlock = mkOption { + type = types.bool; + default = false; + description = '' + Enable FCC unlock procedures. Since release 1.18.4, the ModemManager daemon no longer + automatically performs the FCC unlock procedure by default. See + <link xlink:href="https://modemmanager.org/docs/modemmanager/fcc-unlock/">the docs</link> + for more details. + ''; + }; }; }; @@ -438,7 +449,13 @@ in { "NetworkManager/VPN/nm-sstp-service.name".source = "${networkmanager-sstp}/lib/NetworkManager/VPN/nm-sstp-service.name"; + } + // optionalAttrs cfg.enableFccUnlock + { + "ModemManager/fcc-unlock.d".source = + "${pkgs.modemmanager}/share/ModemManager/fcc-unlock.available.d/*"; + } // optionalAttrs (cfg.appendNameservers != [] || cfg.insertNameservers != []) { "NetworkManager/dispatcher.d/02overridedns".source = overrideNameserversScript; diff --git a/nixos/modules/services/networking/nftables.nix b/nixos/modules/services/networking/nftables.nix index eb74d373b0af2..b911f97491eb1 100644 --- a/nixos/modules/services/networking/nftables.nix +++ b/nixos/modules/services/networking/nftables.nix @@ -25,9 +25,10 @@ in for more information. There are other programs that use iptables internally too, such as - libvirt. + libvirt. For information on how the two firewalls interact, see [2]. [1]: https://github.com/NixOS/nixpkgs/issues/24318#issuecomment-289216273 + [2]: https://wiki.nftables.org/wiki-nftables/index.php/Troubleshooting#Question_4._How_do_nftables_and_iptables_interact_when_used_on_the_same_system.3F ''; }; networking.nftables.ruleset = mkOption { @@ -118,20 +119,11 @@ in flush ruleset include "${cfg.rulesetFile}" ''; - checkScript = pkgs.writeScript "nftables-check" '' - #! ${pkgs.runtimeShell} -e - if $(${pkgs.kmod}/bin/lsmod | grep -q ip_tables); then - echo "Unload ip_tables before using nftables!" 1>&2 - exit 1 - else - ${rulesScript} - fi - ''; in { Type = "oneshot"; RemainAfterExit = true; - ExecStart = checkScript; - ExecReload = checkScript; + ExecStart = rulesScript; + ExecReload = rulesScript; ExecStop = "${pkgs.nftables}/bin/nft flush ruleset"; }; }; diff --git a/nixos/modules/services/networking/nix-serve.nix b/nixos/modules/services/networking/nix-serve.nix index 390f0ddaee83c..432938d59d90c 100644 --- a/nixos/modules/services/networking/nix-serve.nix +++ b/nixos/modules/services/networking/nix-serve.nix @@ -26,6 +26,12 @@ in ''; }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = "Open ports in the firewall for nix-serve."; + }; + secretKeyFile = mkOption { type = types.nullOr types.str; default = null; @@ -77,5 +83,9 @@ in "NIX_SECRET_KEY_FILE:${cfg.secretKeyFile}"; }; }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ]; + }; }; } diff --git a/nixos/modules/services/networking/ntopng.nix b/nixos/modules/services/networking/ntopng.nix index 77a004e8ab3a5..022fc923edaa3 100644 --- a/nixos/modules/services/networking/ntopng.nix +++ b/nixos/modules/services/networking/ntopng.nix @@ -6,7 +6,13 @@ let cfg = config.services.ntopng; opt = options.services.ntopng; - redisCfg = config.services.redis; + + createRedis = cfg.redis.createInstance != null; + redisService = + if cfg.redis.createInstance == "" then + "redis.service" + else + "redis-${cfg.redis.createInstance}.service"; configFile = if cfg.configText != "" then pkgs.writeText "ntopng.conf" '' @@ -15,8 +21,10 @@ let else pkgs.writeText "ntopng.conf" '' ${concatStringsSep " " (map (e: "--interface=" + e) cfg.interfaces)} - --http-port=${toString cfg.http-port} - --redis=localhost:${toString redisCfg.port} + --http-port=${toString cfg.httpPort} + --redis=${cfg.redis.address} + --data-dir=/var/lib/ntopng + --user=ntopng ${cfg.extraConfig} ''; @@ -24,6 +32,10 @@ in { + imports = [ + (mkRenamedOptionModule [ "services" "ntopng" "http-port" ] [ "services" "ntopng" "httpPort" ]) + ]; + options = { services.ntopng = { @@ -56,7 +68,7 @@ in ''; }; - http-port = mkOption { + httpPort = mkOption { default = 3000; type = types.int; description = '' @@ -64,6 +76,24 @@ in ''; }; + redis.address = mkOption { + type = types.str; + example = literalExpression "config.services.redis.ntopng.unixSocket"; + description = '' + Redis address - may be a Unix socket or a network host and port. + ''; + }; + + redis.createInstance = mkOption { + type = types.nullOr types.str; + default = if versionAtLeast config.system.stateVersion "22.05" then "ntopng" else ""; + description = '' + Local Redis instance name. Set to <literal>null</literal> to disable + local Redis instance. Defaults to <literal>""</literal> for + <literal>system.stateVersion</literal> older than 22.05. + ''; + }; + configText = mkOption { default = ""; example = '' @@ -95,23 +125,36 @@ in config = mkIf cfg.enable { # ntopng uses redis for data storage - services.redis.enable = true; + services.ntopng.redis.address = + mkIf createRedis config.services.redis.servers.${cfg.redis.createInstance}.unixSocket; + + services.redis.servers = mkIf createRedis { + ${cfg.redis.createInstance} = { + enable = true; + user = mkIf (cfg.redis.createInstance == "ntopng") "ntopng"; + }; + }; # nice to have manual page and ntopng command in PATH environment.systemPackages = [ pkgs.ntopng ]; + systemd.tmpfiles.rules = [ "d /var/lib/ntopng 0700 ntopng ntopng -" ]; + systemd.services.ntopng = { description = "Ntopng Network Monitor"; - requires = [ "redis.service" ]; - after = [ "network.target" "redis.service" ]; + requires = optional createRedis redisService; + after = [ "network.target" ] ++ optional createRedis redisService; wantedBy = [ "multi-user.target" ]; - preStart = "mkdir -p /var/lib/ntopng/"; serviceConfig.ExecStart = "${pkgs.ntopng}/bin/ntopng ${configFile}"; unitConfig.Documentation = "man:ntopng(8)"; }; - # ntopng drops priveleges to user "nobody" and that user is already defined - # in users-groups.nix. + users.extraUsers.ntopng = { + group = "ntopng"; + isSystemUser = true; + }; + + users.extraGroups.ntopng = { }; }; } diff --git a/nixos/modules/services/networking/racoon.nix b/nixos/modules/services/networking/racoon.nix deleted file mode 100644 index 328f4cb1497fd..0000000000000 --- a/nixos/modules/services/networking/racoon.nix +++ /dev/null @@ -1,45 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - cfg = config.services.racoon; -in { - options.services.racoon = { - enable = mkEnableOption "racoon"; - - config = mkOption { - description = "Contents of racoon configuration file."; - default = ""; - type = types.str; - }; - - configPath = mkOption { - description = "Location of racoon config if config is not provided."; - default = "/etc/racoon/racoon.conf"; - type = types.path; - }; - }; - - config = mkIf cfg.enable { - systemd.services.racoon = { - description = "Racoon Daemon"; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - serviceConfig = { - ExecStart = "${pkgs.ipsecTools}/bin/racoon -f ${ - if (cfg.config != "") then pkgs.writeText "racoon.conf" cfg.config - else cfg.configPath - }"; - ExecReload = "${pkgs.ipsecTools}/bin/racoonctl reload-config"; - PIDFile = "/run/racoon.pid"; - Type = "forking"; - Restart = "always"; - }; - preStart = '' - rm /run/racoon.pid || true - mkdir -p /var/racoon - ''; - }; - }; -} diff --git a/nixos/modules/services/networking/seafile.nix b/nixos/modules/services/networking/seafile.nix index d7fb22edebed7..2839ffb60a1fd 100644 --- a/nixos/modules/services/networking/seafile.nix +++ b/nixos/modules/services/networking/seafile.nix @@ -1,7 +1,6 @@ { config, lib, pkgs, ... }: with lib; let - python = pkgs.python3Packages.python; cfg = config.services.seafile; settingsFormat = pkgs.formats.ini { }; @@ -221,9 +220,7 @@ in { ''; }; - seahub = let - penv = (pkgs.python3.withPackages (ps: with ps; [ gunicorn seahub ])); - in { + seahub = { description = "Seafile Server Web Frontend"; wantedBy = [ "seafile.target" ]; partOf = [ "seafile.target" ]; @@ -231,8 +228,7 @@ in { requires = [ "seaf-server.service" ]; restartTriggers = [ seahubSettings ]; environment = { - PYTHONPATH = - "${pkgs.python3Packages.seahub}/thirdpart:${pkgs.python3Packages.seahub}:${penv}/${python.sitePackages}"; + PYTHONPATH = "${pkgs.seahub.pythonPath}:${pkgs.seahub}/thirdpart:${pkgs.seahub}"; DJANGO_SETTINGS_MODULE = "seahub.settings"; CCNET_CONF_DIR = ccnetDir; SEAFILE_CONF_DIR = dataDir; @@ -249,7 +245,7 @@ in { LogsDirectory = "seafile"; ConfigurationDirectory = "seafile"; ExecStart = '' - ${penv}/bin/gunicorn seahub.wsgi:application \ + ${pkgs.seahub.python.pkgs.gunicorn}/bin/gunicorn seahub.wsgi:application \ --name seahub \ --workers ${toString cfg.workers} \ --log-level=info \ @@ -262,27 +258,27 @@ in { preStart = '' mkdir -p ${seahubDir}/media # Link all media except avatars - for m in `find ${pkgs.python3Packages.seahub}/media/ -maxdepth 1 -not -name "avatars"`; do + for m in `find ${pkgs.seahub}/media/ -maxdepth 1 -not -name "avatars"`; do ln -sf $m ${seahubDir}/media/ done if [ ! -e "${seafRoot}/.seahubSecret" ]; then - ${penv}/bin/python ${pkgs.python3Packages.seahub}/tools/secret_key_generator.py > ${seafRoot}/.seahubSecret + ${pkgs.seahub.python}/bin/python ${pkgs.seahub}/tools/secret_key_generator.py > ${seafRoot}/.seahubSecret chmod 400 ${seafRoot}/.seahubSecret fi if [ ! -f "${seafRoot}/seahub-setup" ]; then # avatars directory should be writable - install -D -t ${seahubDir}/media/avatars/ ${pkgs.python3Packages.seahub}/media/avatars/default.png - install -D -t ${seahubDir}/media/avatars/groups ${pkgs.python3Packages.seahub}/media/avatars/groups/default.png + install -D -t ${seahubDir}/media/avatars/ ${pkgs.seahub}/media/avatars/default.png + install -D -t ${seahubDir}/media/avatars/groups ${pkgs.seahub}/media/avatars/groups/default.png # init database - ${pkgs.python3Packages.seahub}/manage.py migrate + ${pkgs.seahub}/manage.py migrate # create admin account - ${pkgs.expect}/bin/expect -c 'spawn ${pkgs.python3Packages.seahub}/manage.py createsuperuser --email=${cfg.adminEmail}; expect "Password: "; send "${cfg.initialAdminPassword}\r"; expect "Password (again): "; send "${cfg.initialAdminPassword}\r"; expect "Superuser created successfully."' - echo "${pkgs.python3Packages.seahub.version}-sqlite" > "${seafRoot}/seahub-setup" + ${pkgs.expect}/bin/expect -c 'spawn ${pkgs.seahub}/manage.py createsuperuser --email=${cfg.adminEmail}; expect "Password: "; send "${cfg.initialAdminPassword}\r"; expect "Password (again): "; send "${cfg.initialAdminPassword}\r"; expect "Superuser created successfully."' + echo "${pkgs.seahub.version}-sqlite" > "${seafRoot}/seahub-setup" fi - if [ $(cat "${seafRoot}/seahub-setup" | cut -d"-" -f1) != "${pkgs.python3Packages.seahub.version}" ]; then + if [ $(cat "${seafRoot}/seahub-setup" | cut -d"-" -f1) != "${pkgs.seahub.version}" ]; then # update database - ${pkgs.python3Packages.seahub}/manage.py migrate - echo "${pkgs.python3Packages.seahub.version}-sqlite" > "${seafRoot}/seahub-setup" + ${pkgs.seahub}/manage.py migrate + echo "${pkgs.seahub.version}-sqlite" > "${seafRoot}/seahub-setup" fi ''; }; diff --git a/nixos/modules/services/networking/searx.nix b/nixos/modules/services/networking/searx.nix index 9fb06af7442e4..b73f255eb9dd2 100644 --- a/nixos/modules/services/networking/searx.nix +++ b/nixos/modules/services/networking/searx.nix @@ -228,5 +228,4 @@ in }; meta.maintainers = with maintainers; [ rnhmjoj ]; - } diff --git a/nixos/modules/services/networking/sniproxy.nix b/nixos/modules/services/networking/sniproxy.nix index 28c201f0565e9..adca5398e4abf 100644 --- a/nixos/modules/services/networking/sniproxy.nix +++ b/nixos/modules/services/networking/sniproxy.nix @@ -14,6 +14,8 @@ let in { + imports = [ (mkRemovedOptionModule [ "services" "sniproxy" "logDir" ] "Now done by LogsDirectory=. Set to a custom path if you log to a different folder in your config.") ]; + options = { services.sniproxy = { enable = mkEnableOption "sniproxy server"; @@ -50,13 +52,6 @@ in } ''; }; - - logDir = mkOption { - type = types.str; - default = "/var/log/sniproxy/"; - description = "Location of the log directory for sniproxy."; - }; - }; }; @@ -66,18 +61,12 @@ in description = "sniproxy server"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - preStart = '' - test -d ${cfg.logDir} || { - echo "Creating initial log directory for sniproxy in ${cfg.logDir}" - mkdir -p ${cfg.logDir} - chmod 640 ${cfg.logDir} - } - chown -R ${cfg.user}:${cfg.group} ${cfg.logDir} - ''; serviceConfig = { Type = "forking"; ExecStart = "${pkgs.sniproxy}/bin/sniproxy -c ${configFile}"; + LogsDirectory = "sniproxy"; + LogsDirectoryMode = "0640"; Restart = "always"; }; }; diff --git a/nixos/modules/services/networking/squid.nix b/nixos/modules/services/networking/squid.nix index 9d063b92aa1e7..4f3881af8bbf8 100644 --- a/nixos/modules/services/networking/squid.nix +++ b/nixos/modules/services/networking/squid.nix @@ -81,7 +81,9 @@ let http_access deny all # Squid normally listens to port 3128 - http_port ${toString cfg.proxyPort} + http_port ${ + optionalString (cfg.proxyAddress != null) "${cfg.proxyAddress}:" + }${toString cfg.proxyPort} # Leave coredumps in the first cache dir coredump_dir /var/cache/squid @@ -109,6 +111,12 @@ in description = "Whether to run squid web proxy."; }; + proxyAddress = mkOption { + type = types.nullOr types.str; + default = null; + description = "IP address on which squid will listen."; + }; + proxyPort = mkOption { type = types.int; default = 3128; diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index 004b4f99670f8..230ab673a9761 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -30,7 +30,7 @@ let options.openssh.authorizedKeys = { keys = mkOption { - type = types.listOf types.str; + type = types.listOf types.singleLineStr; default = []; description = '' A list of verbatim OpenSSH public keys that should be added to the @@ -81,6 +81,7 @@ in imports = [ (mkAliasOptionModule [ "services" "sshd" "enable" ] [ "services" "openssh" "enable" ]) (mkAliasOptionModule [ "services" "openssh" "knownHosts" ] [ "programs" "ssh" "knownHosts" ]) + (mkRenamedOptionModule [ "services" "openssh" "challengeResponseAuthentication" ] [ "services" "openssh" "kbdInteractiveAuthentication" ]) ]; ###### interface @@ -218,11 +219,11 @@ in ''; }; - challengeResponseAuthentication = mkOption { + kbdInteractiveAuthentication = mkOption { type = types.bool; default = true; description = '' - Specifies whether challenge/response authentication is allowed. + Specifies whether keyboard-interactive authentication is allowed. ''; }; @@ -480,6 +481,8 @@ in else cfg.ports; socketConfig.Accept = true; + # Prevent brute-force attacks from shutting down socket + socketConfig.TriggerLimitIntervalSec = 0; }; services."sshd@" = service; @@ -532,7 +535,7 @@ in PermitRootLogin ${cfg.permitRootLogin} GatewayPorts ${cfg.gatewayPorts} PasswordAuthentication ${if cfg.passwordAuthentication then "yes" else "no"} - ChallengeResponseAuthentication ${if cfg.challengeResponseAuthentication then "yes" else "no"} + KbdInteractiveAuthentication ${if cfg.kbdInteractiveAuthentication then "yes" else "no"} PrintMotd no # handled by pam_motd diff --git a/nixos/modules/services/networking/stunnel.nix b/nixos/modules/services/networking/stunnel.nix index 70d0a7d3c12ea..df4908a0fff9e 100644 --- a/nixos/modules/services/networking/stunnel.nix +++ b/nixos/modules/services/networking/stunnel.nix @@ -25,8 +25,8 @@ let }; connect = mkOption { - type = types.int; - description = "To which port the decrypted connection should be forwarded."; + type = types.either types.str types.int; + description = "Port or IP:Port to which the decrypted connection should be forwarded."; }; cert = mkOption { diff --git a/nixos/modules/services/networking/syncplay.nix b/nixos/modules/services/networking/syncplay.nix index 27a16fb2e29f8..b6faf2d3f7727 100644 --- a/nixos/modules/services/networking/syncplay.nix +++ b/nixos/modules/services/networking/syncplay.nix @@ -68,7 +68,7 @@ in systemd.services.syncplay = { description = "Syncplay Service"; wantedBy = [ "multi-user.target" ]; - after = [ "network-online.target "]; + after = [ "network-online.target" ]; serviceConfig = { ExecStart = "${pkgs.syncplay}/bin/syncplay-server ${escapeShellArgs cmdArgs}"; diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix index e37e324019e81..3a3d4c80ecff4 100644 --- a/nixos/modules/services/networking/syncthing.nix +++ b/nixos/modules/services/networking/syncthing.nix @@ -468,7 +468,7 @@ in { default = false; example = true; description = '' - Whether to open the default ports in the firewall: TCP 22000 for transfers + Whether to open the default ports in the firewall: TCP/UDP 22000 for transfers and UDP 21027 for discovery. If multiple users are running Syncthing on this machine, you will need @@ -504,7 +504,7 @@ in { networking.firewall = mkIf cfg.openDefaultPorts { allowedTCPPorts = [ 22000 ]; - allowedUDPPorts = [ 21027 ]; + allowedUDPPorts = [ 21027 22000 ]; }; systemd.packages = [ pkgs.syncthing ]; diff --git a/nixos/modules/services/networking/teleport.nix b/nixos/modules/services/networking/teleport.nix new file mode 100644 index 0000000000000..454791621800a --- /dev/null +++ b/nixos/modules/services/networking/teleport.nix @@ -0,0 +1,99 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.teleport; + settingsYaml = pkgs.formats.yaml { }; +in +{ + options = { + services.teleport = with lib.types; { + enable = mkEnableOption "the Teleport service"; + + settings = mkOption { + type = settingsYaml.type; + default = { }; + example = literalExpression '' + { + teleport = { + nodename = "client"; + advertise_ip = "192.168.1.2"; + auth_token = "60bdc117-8ff4-478d-95e4-9914597847eb"; + auth_servers = [ "192.168.1.1:3025" ]; + log.severity = "DEBUG"; + }; + ssh_service = { + enabled = true; + labels = { + role = "client"; + }; + }; + proxy_service.enabled = false; + auth_service.enabled = false; + } + ''; + description = '' + Contents of the <literal>teleport.yaml</literal> config file. + The <literal>--config</literal> arguments will only be passed if this set is not empty. + + See <link xlink:href="https://goteleport.com/docs/setup/reference/config/"/>. + ''; + }; + + insecure.enable = mkEnableOption '' + starting teleport in insecure mode. + + This is dangerous! + Sensitive information will be logged to console and certificates will not be verified. + Proceed with caution! + + Teleport starts with disabled certificate validation on Proxy Service, validation still occurs on Auth Service + ''; + + diag = { + enable = mkEnableOption '' + endpoints for monitoring purposes. + + See <link xlink:href="https://goteleport.com/docs/setup/admin/troubleshooting/#troubleshooting/"/> + ''; + + addr = mkOption { + type = str; + default = "127.0.0.1"; + description = "Metrics and diagnostics address."; + }; + + port = mkOption { + type = int; + default = 3000; + description = "Metrics and diagnostics port."; + }; + }; + }; + }; + + config = mkIf config.services.teleport.enable { + environment.systemPackages = [ pkgs.teleport ]; + + systemd.services.teleport = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + ExecStart = '' + ${pkgs.teleport}/bin/teleport start \ + ${optionalString cfg.insecure.enable "--insecure"} \ + ${optionalString cfg.diag.enable "--diag-addr=${cfg.diag.addr}:${toString cfg.diag.port}"} \ + ${optionalString (cfg.settings != { }) "--config=${settingsYaml.generate "teleport.yaml" cfg.settings}"} + ''; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + LimitNOFILE = 65536; + Restart = "always"; + RestartSec = "5s"; + RuntimeDirectory = "teleport"; + Type = "simple"; + }; + }; + }; +} + diff --git a/nixos/modules/services/networking/tetrd.nix b/nixos/modules/services/networking/tetrd.nix new file mode 100644 index 0000000000000..ead73c497764d --- /dev/null +++ b/nixos/modules/services/networking/tetrd.nix @@ -0,0 +1,96 @@ +{ config, lib, pkgs, ... }: + +{ + options.services.tetrd.enable = lib.mkEnableOption pkgs.tetrd.meta.description; + + config = lib.mkIf config.services.tetrd.enable { + environment = { + systemPackages = [ pkgs.tetrd ]; + etc."resolv.conf".source = "/etc/tetrd/resolv.conf"; + }; + + systemd = { + tmpfiles.rules = [ "f /etc/tetrd/resolv.conf - - -" ]; + + services.tetrd = { + description = pkgs.tetrd.meta.description; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + ExecStart = "${pkgs.tetrd}/opt/Tetrd/bin/tetrd"; + Restart = "always"; + RuntimeDirectory = "tetrd"; + RootDirectory = "/run/tetrd"; + DynamicUser = true; + UMask = "006"; + DeviceAllow = "usb_device"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateMounts = true; + PrivateNetwork = lib.mkDefault false; + PrivateTmp = true; + PrivateUsers = lib.mkDefault false; + ProtectClock = lib.mkDefault false; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + + SystemCallFilter = [ + "@system-service" + "~@aio" + "~@chown" + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@keyring" + "~@memlock" + "~@module" + "~@mount" + "~@obsolete" + "~@pkey" + "~@raw-io" + "~@reboot" + "~@swap" + "~@sync" + ]; + + BindReadOnlyPaths = [ + builtins.storeDir + "/etc/ssl" + "/etc/static/ssl" + "${pkgs.nettools}/bin/route:/usr/bin/route" + "${pkgs.nettools}/bin/ifconfig:/usr/bin/ifconfig" + ]; + + BindPaths = [ + "/etc/tetrd/resolv.conf:/etc/resolv.conf" + "/run" + "/var/log" + ]; + + CapabilityBoundingSet = [ + "CAP_DAC_OVERRIDE" + "CAP_NET_ADMIN" + ]; + + AmbientCapabilities = [ + "CAP_DAC_OVERRIDE" + "CAP_NET_ADMIN" + ]; + }; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/thelounge.nix b/nixos/modules/services/networking/thelounge.nix index b944916391631..a5118fd8b3398 100644 --- a/nixos/modules/services/networking/thelounge.nix +++ b/nixos/modules/services/networking/thelounge.nix @@ -6,17 +6,31 @@ let cfg = config.services.thelounge; dataDir = "/var/lib/thelounge"; configJsData = "module.exports = " + builtins.toJSON ( - { private = cfg.private; port = cfg.port; } // cfg.extraConfig + { inherit (cfg) public port; } // cfg.extraConfig ); -in { + pluginManifest = { + dependencies = builtins.listToAttrs (builtins.map (pkg: { name = getName pkg; value = getVersion pkg; }) cfg.plugins); + }; + plugins = pkgs.runCommandLocal "thelounge-plugins" { } '' + mkdir -p $out/node_modules + echo ${escapeShellArg (builtins.toJSON pluginManifest)} >> $out/package.json + ${concatMapStringsSep "\n" (pkg: '' + ln -s ${pkg}/lib/node_modules/${getName pkg} $out/node_modules/${getName pkg} + '') cfg.plugins} + ''; +in +{ + imports = [ (mkRemovedOptionModule [ "services" "thelounge" "private" ] "The option was renamed to `services.thelounge.public` to follow upstream changes.") ]; + options.services.thelounge = { enable = mkEnableOption "The Lounge web IRC client"; - private = mkOption { + public = mkOption { type = types.bool; default = false; description = '' - Make your The Lounge instance private. You will need to configure user + Make your The Lounge instance public. + Setting this to <literal>false</literal> will require you to configure user accounts by using the (<command>thelounge</command>) command or by adding entries in <filename>${dataDir}/users</filename>. You might need to restart The Lounge after making changes to the state directory. @@ -30,7 +44,7 @@ in { }; extraConfig = mkOption { - default = {}; + default = { }; type = types.attrs; example = literalExpression ''{ reverseProxy = true; @@ -50,19 +64,32 @@ in { Documentation: <link xlink:href="https://thelounge.chat/docs/server/configuration" /> ''; }; + + plugins = mkOption { + default = [ ]; + type = types.listOf types.package; + example = literalExpression "[ pkgs.theLoungePlugins.themes.solarized ]"; + description = '' + The Lounge plugins to install. Plugins can be found in + <literal>pkgs.theLoungePlugins.plugins</literal> and <literal>pkgs.theLoungePlugins.themes</literal>. + ''; + }; }; config = mkIf cfg.enable { users.users.thelounge = { - description = "thelounge service user"; + description = "The Lounge service user"; group = "thelounge"; isSystemUser = true; }; - users.groups.thelounge = {}; + + users.groups.thelounge = { }; + systemd.services.thelounge = { description = "The Lounge web IRC client"; wantedBy = [ "multi-user.target" ]; preStart = "ln -sf ${pkgs.writeText "config.js" configJsData} ${dataDir}/config.js"; + environment.THELOUNGE_PACKAGES = mkIf (cfg.plugins != [ ]) "${plugins}"; serviceConfig = { User = "thelounge"; StateDirectory = baseNameOf dataDir; @@ -72,4 +99,8 @@ in { environment.systemPackages = [ pkgs.thelounge ]; }; + + meta = { + maintainers = with lib.maintainers; [ winter ]; + }; } diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix index 9db433fa0735c..31731b60d484d 100644 --- a/nixos/modules/services/networking/tinc.nix +++ b/nixos/modules/services/networking/tinc.nix @@ -435,5 +435,5 @@ in ); }; - meta.maintainers = with maintainers; [ minijackson ]; + meta.maintainers = with maintainers; [ minijackson mic92 ]; } diff --git a/nixos/modules/services/networking/wg-netmanager.nix b/nixos/modules/services/networking/wg-netmanager.nix new file mode 100644 index 0000000000000..493ff7ceba9f1 --- /dev/null +++ b/nixos/modules/services/networking/wg-netmanager.nix @@ -0,0 +1,42 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.wg-netmanager; +in +{ + + options = { + services.wg-netmanager = { + enable = mkEnableOption "Wireguard network manager"; + }; + }; + + ###### implementation + config = mkIf cfg.enable { + # NOTE: wg-netmanager runs as root + systemd.services.wg-netmanager = { + description = "Wireguard network manager"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + path = with pkgs; [ wireguard-tools iproute2 wireguard-go ]; + serviceConfig = { + Type = "simple"; + Restart = "on-failure"; + ExecStart = "${pkgs.wg-netmanager}/bin/wg_netmanager"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + ExecStop = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + + ReadWritePaths = [ + "/tmp" # wg-netmanager creates files in /tmp before deleting them after use + ]; + }; + unitConfig = { + ConditionPathExists = ["/etc/wg_netmanager/network.yaml" "/etc/wg_netmanager/peer.yaml"]; + }; + }; + }; + + meta.maintainers = with maintainers; [ gin66 ]; +} diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix index 07dec8ea71815..c2e1d37e28bf4 100644 --- a/nixos/modules/services/networking/wpa_supplicant.nix +++ b/nixos/modules/services/networking/wpa_supplicant.nix @@ -10,14 +10,45 @@ let cfg = config.networking.wireless; opt = options.networking.wireless; + wpa3Protocols = [ "SAE" "FT-SAE" ]; + hasMixedWPA = opts: + let + hasWPA3 = !mutuallyExclusive opts.authProtocols wpa3Protocols; + others = subtractLists wpa3Protocols opts.authProtocols; + in hasWPA3 && others != []; + + # Gives a WPA3 network higher priority + increaseWPA3Priority = opts: + opts // optionalAttrs (hasMixedWPA opts) + { priority = if opts.priority == null + then 1 + else opts.priority + 1; + }; + + # Creates a WPA2 fallback network + mkWPA2Fallback = opts: + opts // { authProtocols = subtractLists wpa3Protocols opts.authProtocols; }; + + # Networks attrset as a list + networkList = mapAttrsToList (ssid: opts: opts // { inherit ssid; }) + cfg.networks; + + # List of all networks (normal + generated fallbacks) + allNetworks = + if cfg.fallbackToWPA2 + then map increaseWPA3Priority networkList + ++ map mkWPA2Fallback (filter hasMixedWPA networkList) + else networkList; + # Content of wpa_supplicant.conf generatedConfig = concatStringsSep "\n" ( - (mapAttrsToList mkNetwork cfg.networks) + (map mkNetwork allNetworks) ++ optional cfg.userControlled.enable (concatStringsSep "\n" [ "ctrl_interface=/run/wpa_supplicant" "ctrl_interface_group=${cfg.userControlled.group}" "update_config=1" ]) + ++ [ "pmf=1" ] ++ optional cfg.scanOnLowSignal ''bgscan="simple:30:-70:3600"'' ++ optional (cfg.extraConfig != "") cfg.extraConfig); @@ -33,7 +64,7 @@ let finalConfig = ''"$RUNTIME_DIRECTORY"/wpa_supplicant.conf''; # Creates a network block for wpa_supplicant.conf - mkNetwork = ssid: opts: + mkNetwork = opts: let quote = x: ''"${x}"''; indent = x: " " + x; @@ -43,7 +74,7 @@ let else opts.pskRaw; options = [ - "ssid=${quote ssid}" + "ssid=${quote opts.ssid}" (if pskString != null || opts.auth != null then "key_mgmt=${concatStringsSep " " opts.authProtocols}" else "key_mgmt=NONE") @@ -175,6 +206,18 @@ in { ''; }; + fallbackToWPA2 = mkOption { + type = types.bool; + default = true; + description = '' + Whether to fall back to WPA2 authentication protocols if WPA3 failed. + This allows old wireless cards (that lack recent features required by + WPA3) to connect to mixed WPA2/WPA3 access points. + + To avoid possible downgrade attacks, disable this options. + ''; + }; + environmentFile = mkOption { type = types.nullOr types.path; default = null; diff --git a/nixos/modules/services/networking/xrdp.nix b/nixos/modules/services/networking/xrdp.nix index e9f123a181aec..747fb7a1f9c4e 100644 --- a/nixos/modules/services/networking/xrdp.nix +++ b/nixos/modules/services/networking/xrdp.nix @@ -100,6 +100,7 @@ in confDir = mkOption { type = types.path; default = confDir; + defaultText = literalDocBook "generated from configuration"; description = "The location of the config files for xrdp."; }; }; diff --git a/nixos/modules/services/networking/yggdrasil.xml b/nixos/modules/services/networking/yggdrasil.xml index c012cd4a92949..a341d5d8153b7 100644 --- a/nixos/modules/services/networking/yggdrasil.xml +++ b/nixos/modules/services/networking/yggdrasil.xml @@ -84,7 +84,6 @@ in { interface eth0 { AdvSendAdvert on; - AdvDefaultLifetime 0; prefix ${prefix}::/64 { AdvOnLink on; AdvAutonomous on; |