diff options
Diffstat (limited to 'nixos/modules/services/networking')
153 files changed, 2800 insertions, 1469 deletions
diff --git a/nixos/modules/services/networking/acme-dns.nix b/nixos/modules/services/networking/acme-dns.nix index 5c53fa2cc4f1..08fde65e4ca4 100644 --- a/nixos/modules/services/networking/acme-dns.nix +++ b/nixos/modules/services/networking/acme-dns.nix @@ -12,7 +12,7 @@ let mdDoc mkEnableOption mkOption - mkPackageOptionMD + mkPackageOption types ; domain = "acme-dns.example.com"; @@ -21,7 +21,7 @@ in options.services.acme-dns = { enable = mkEnableOption (mdDoc "acme-dns"); - package = mkPackageOptionMD pkgs "acme-dns" { }; + package = mkPackageOption pkgs "acme-dns" { }; settings = mkOption { description = mdDoc '' diff --git a/nixos/modules/services/networking/alice-lg.nix b/nixos/modules/services/networking/alice-lg.nix index 06b9ac89f12f..fbf127d9410f 100644 --- a/nixos/modules/services/networking/alice-lg.nix +++ b/nixos/modules/services/networking/alice-lg.nix @@ -11,7 +11,7 @@ in services.alice-lg = { enable = mkEnableOption (lib.mdDoc "Alice Looking Glass"); - package = mkPackageOptionMD pkgs "alice-lg" { }; + package = mkPackageOption pkgs "alice-lg" { }; settings = mkOption { type = settingsFormat.type; diff --git a/nixos/modules/services/networking/aria2.nix b/nixos/modules/services/networking/aria2.nix index e848869cc0ac..1fb55b836798 100644 --- a/nixos/modules/services/networking/aria2.nix +++ b/nixos/modules/services/networking/aria2.nix @@ -18,11 +18,14 @@ let dir=${cfg.downloadDir} listen-port=${concatStringsSep "," (rangesToStringList cfg.listenPortRange)} rpc-listen-port=${toString cfg.rpcListenPort} - rpc-secret=${cfg.rpcSecret} ''; in { + imports = [ + (mkRemovedOptionModule [ "services" "aria2" "rpcSecret" ] "Use services.aria2.rpcSecretFile instead") + ]; + options = { services.aria2 = { enable = mkOption { @@ -65,11 +68,11 @@ in default = 6800; description = lib.mdDoc "Specify a port number for JSON-RPC/XML-RPC server to listen to. Possible Values: 1024-65535"; }; - rpcSecret = mkOption { - type = types.str; - default = "aria2rpc"; + rpcSecretFile = mkOption { + type = types.path; + example = "/run/secrets/aria2-rpc-token.txt"; description = lib.mdDoc '' - Set RPC secret authorization token. + A file containing the RPC secret authorization token. Read https://aria2.github.io/manual/en/html/aria2c.html#rpc-auth to know how this option value is used. ''; }; @@ -117,6 +120,7 @@ in touch "${sessionFile}" fi cp -f "${settingsFile}" "${settingsDir}/aria2.conf" + echo "rpc-secret=$(cat "$CREDENTIALS_DIRECTORY/rpcSecretFile")" >> "${settingsDir}/aria2.conf" ''; serviceConfig = { @@ -125,6 +129,7 @@ in ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; User = "aria2"; Group = "aria2"; + LoadCredential="rpcSecretFile:${cfg.rpcSecretFile}"; }; }; }; diff --git a/nixos/modules/services/networking/asterisk.nix b/nixos/modules/services/networking/asterisk.nix index 5a1d03f07211..78a69efc86af 100644 --- a/nixos/modules/services/networking/asterisk.nix +++ b/nixos/modules/services/networking/asterisk.nix @@ -139,7 +139,7 @@ in path. See - <http://www.asterisk.org/community/documentation> + <https://www.asterisk.org/community/documentation/> for more examples of what is possible here. ''; }; @@ -163,12 +163,7 @@ in Additional command line arguments to pass to Asterisk. ''; }; - package = mkOption { - type = types.package; - default = pkgs.asterisk; - defaultText = literalExpression "pkgs.asterisk"; - description = lib.mdDoc "The Asterisk package to use."; - }; + package = mkPackageOption pkgs "asterisk" { }; }; }; diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix index bdbf9aad9acc..782681018116 100644 --- a/nixos/modules/services/networking/avahi-daemon.nix +++ b/nixos/modules/services/networking/avahi-daemon.nix @@ -42,6 +42,7 @@ in { imports = [ (lib.mkRenamedOptionModule [ "services" "avahi" "interfaces" ] [ "services" "avahi" "allowInterfaces" ]) + (lib.mkRenamedOptionModule [ "services" "avahi" "nssmdns" ] [ "services" "avahi" "nssmdns4" ]) ]; options.services.avahi = { @@ -56,14 +57,7 @@ in ''; }; - package = mkOption { - type = types.package; - default = pkgs.avahi; - defaultText = literalExpression "pkgs.avahi"; - description = lib.mdDoc '' - The avahi package to use for running the daemon. - ''; - }; + package = mkPackageOption pkgs "avahi" { }; hostName = mkOption { type = types.str; @@ -100,8 +94,7 @@ in ipv6 = mkOption { type = types.bool; - default = config.networking.enableIPv6; - defaultText = literalExpression "config.networking.enableIPv6"; + default = false; description = lib.mdDoc "Whether to use IPv6."; }; @@ -225,16 +218,31 @@ in }; }; - nssmdns = mkOption { + nssmdns4 = mkOption { type = types.bool; default = false; description = lib.mdDoc '' - Whether to enable the mDNS NSS (Name Service Switch) plug-in. + Whether to enable the mDNS NSS (Name Service Switch) plug-in for IPv4. Enabling it allows applications to resolve names in the `.local` domain by transparently querying the Avahi daemon. ''; }; + nssmdns6 = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether to enable the mDNS NSS (Name Service Switch) plug-in for IPv6. + Enabling it allows applications to resolve names in the `.local` + domain by transparently querying the Avahi daemon. + + ::: {.note} + Due to the fact that most mDNS responders only register local IPv4 addresses, + most user want to leave this option disabled to avoid long timeouts when applications first resolve the none existing IPv6 address. + ::: + ''; + }; + cacheEntriesMax = mkOption { type = types.nullOr types.int; default = null; @@ -263,10 +271,19 @@ in users.groups.avahi = { }; - system.nssModules = optional cfg.nssmdns pkgs.nssmdns; - system.nssDatabases.hosts = optionals cfg.nssmdns (mkMerge [ - (mkBefore [ "mdns_minimal [NOTFOUND=return]" ]) # before resolve - (mkAfter [ "mdns" ]) # after dns + system.nssModules = optional (cfg.nssmdns4 || cfg.nssmdns6) pkgs.nssmdns; + system.nssDatabases.hosts = let + mdns = if (cfg.nssmdns4 && cfg.nssmdns6) then + "mdns" + else if (!cfg.nssmdns4 && cfg.nssmdns6) then + "mdns6" + else if (cfg.nssmdns4 && !cfg.nssmdns6) then + "mdns4" + else + ""; + in optionals (cfg.nssmdns4 || cfg.nssmdns6) (mkMerge [ + (mkBefore [ "${mdns}_minimal [NOTFOUND=return]" ]) # before resolve + (mkAfter [ "${mdns}" ]) # after dns ]); environment.systemPackages = [ cfg.package ]; diff --git a/nixos/modules/services/networking/bee-clef.nix b/nixos/modules/services/networking/bee-clef.nix deleted file mode 100644 index 75e76f019a71..000000000000 --- a/nixos/modules/services/networking/bee-clef.nix +++ /dev/null @@ -1,107 +0,0 @@ -{ config, lib, pkgs, ... }: - -# NOTE for now nothing is installed into /etc/bee-clef/. the config files are used as read-only from the nix store. - -with lib; -let - cfg = config.services.bee-clef; -in { - meta = { - maintainers = with maintainers; [ attila-lendvai ]; - }; - - ### interface - - options = { - services.bee-clef = { - enable = mkEnableOption (lib.mdDoc "clef external signer instance for Ethereum Swarm Bee"); - - dataDir = mkOption { - type = types.nullOr types.str; - default = "/var/lib/bee-clef"; - description = lib.mdDoc '' - Data dir for bee-clef. Beware that some helper scripts may not work when changed! - The service itself should work fine, though. - ''; - }; - - passwordFile = mkOption { - type = types.nullOr types.str; - default = "/var/lib/bee-clef/password"; - description = lib.mdDoc "Password file for bee-clef."; - }; - - user = mkOption { - type = types.str; - default = "bee-clef"; - description = lib.mdDoc '' - User the bee-clef daemon should execute under. - ''; - }; - - group = mkOption { - type = types.str; - default = "bee-clef"; - description = lib.mdDoc '' - Group the bee-clef daemon should execute under. - ''; - }; - }; - }; - - ### implementation - - config = mkIf cfg.enable { - # if we ever want to have rules.js under /etc/bee-clef/ - # environment.etc."bee-clef/rules.js".source = ${pkgs.bee-clef}/rules.js - - systemd.packages = [ pkgs.bee-clef ]; # include the upstream bee-clef.service file - - systemd.tmpfiles.rules = [ - "d '${cfg.dataDir}/' 0750 ${cfg.user} ${cfg.group}" - "d '${cfg.dataDir}/keystore' 0700 ${cfg.user} ${cfg.group}" - ]; - - systemd.services.bee-clef = { - path = [ - # these are needed for the ensure-clef-account script - pkgs.coreutils - pkgs.gnused - pkgs.gawk - ]; - - wantedBy = [ "bee.service" "multi-user.target" ]; - - serviceConfig = { - User = cfg.user; - Group = cfg.group; - ExecStartPre = ''${pkgs.bee-clef}/share/bee-clef/ensure-clef-account "${cfg.dataDir}" "${pkgs.bee-clef}/share/bee-clef/"''; - ExecStart = [ - "" # this hides/overrides what's in the original entry - "${pkgs.bee-clef}/share/bee-clef/bee-clef-service start" - ]; - ExecStop = [ - "" # this hides/overrides what's in the original entry - "${pkgs.bee-clef}/share/bee-clef/bee-clef-service stop" - ]; - Environment = [ - "CONFIGDIR=${cfg.dataDir}" - "PASSWORD_FILE=${cfg.passwordFile}" - ]; - }; - }; - - users.users = optionalAttrs (cfg.user == "bee-clef") { - bee-clef = { - group = cfg.group; - home = cfg.dataDir; - isSystemUser = true; - description = "Daemon user for the bee-clef service"; - }; - }; - - users.groups = optionalAttrs (cfg.group == "bee-clef") { - bee-clef = {}; - }; - }; -} diff --git a/nixos/modules/services/networking/bee.nix b/nixos/modules/services/networking/bee.nix index add9861ebfcd..a4d20494bf6b 100644 --- a/nixos/modules/services/networking/bee.nix +++ b/nixos/modules/services/networking/bee.nix @@ -8,7 +8,7 @@ let in { meta = { # doc = ./bee.xml; - maintainers = with maintainers; [ attila-lendvai ]; + maintainers = with maintainers; [ ]; }; ### interface @@ -17,12 +17,8 @@ in { services.bee = { enable = mkEnableOption (lib.mdDoc "Ethereum Swarm Bee"); - package = mkOption { - type = types.package; - default = pkgs.bee; - defaultText = literalExpression "pkgs.bee"; - example = literalExpression "pkgs.bee-unstable"; - description = lib.mdDoc "The package providing the bee binary for the service."; + package = mkPackageOption pkgs "bee" { + example = "bee-unstable"; }; settings = mkOption { @@ -77,13 +73,10 @@ in { } ]; - warnings = optional (! config.services.bee-clef.enable) "The bee service requires an external signer. Consider setting `config.services.bee-clef.enable` = true"; - services.bee.settings = { data-dir = lib.mkDefault "/var/lib/bee"; password-file = lib.mkDefault "/var/lib/bee/password"; clef-signer-enable = lib.mkDefault true; - clef-signer-endpoint = lib.mkDefault "/var/lib/bee-clef/clef.ipc"; swap-endpoint = lib.mkDefault "https://rpc.slock.it/goerli"; }; @@ -94,9 +87,6 @@ in { ]; systemd.services.bee = { - requires = optional config.services.bee-clef.enable - "bee-clef.service"; - wantedBy = [ "multi-user.target" ]; serviceConfig = { @@ -124,7 +114,6 @@ Bee has SWAP enabled by default and it needs ethereum endpoint to operate. It is recommended to use external signer with bee. Check documentation for more info: - SWAP https://docs.ethswarm.org/docs/installation/manual#swap-bandwidth-incentives -- External signer https://docs.ethswarm.org/docs/installation/bee-clef After you finish configuration run 'sudo bee-get-addr'." fi @@ -137,8 +126,6 @@ After you finish configuration run 'sudo bee-get-addr'." home = cfg.settings.data-dir; isSystemUser = true; description = "Daemon user for Ethereum Swarm Bee"; - extraGroups = optional config.services.bee-clef.enable - config.services.bee-clef.group; }; }; diff --git a/nixos/modules/services/networking/bind.nix b/nixos/modules/services/networking/bind.nix index f1829747bb1e..da8633d5066f 100644 --- a/nixos/modules/services/networking/bind.nix +++ b/nixos/modules/services/networking/bind.nix @@ -118,12 +118,7 @@ in enable = mkEnableOption (lib.mdDoc "BIND domain name server"); - package = mkOption { - type = types.package; - default = pkgs.bind; - defaultText = literalExpression "pkgs.bind"; - description = lib.mdDoc "The BIND package to use."; - }; + package = mkPackageOption pkgs "bind" { }; cacheNetworks = mkOption { default = [ "127.0.0.0/24" ]; diff --git a/nixos/modules/services/networking/bird-lg.nix b/nixos/modules/services/networking/bird-lg.nix index dc861dbfd11b..be9f4101e6ab 100644 --- a/nixos/modules/services/networking/bird-lg.nix +++ b/nixos/modules/services/networking/bird-lg.nix @@ -51,12 +51,7 @@ in { options = { services.bird-lg = { - package = mkOption { - type = types.package; - default = pkgs.bird-lg; - defaultText = literalExpression "pkgs.bird-lg"; - description = lib.mdDoc "The Bird Looking Glass package to use."; - }; + package = mkPackageOption pkgs "bird-lg" { }; user = mkOption { type = types.str; diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix index 9deeb7694d2a..e25f5c7b0379 100644 --- a/nixos/modules/services/networking/bird.nix +++ b/nixos/modules/services/networking/bird.nix @@ -18,6 +18,13 @@ in <http://bird.network.cz/> ''; }; + autoReload = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether bird2 should be automatically reloaded when the configuration changes. + ''; + }; checkConfig = mkOption { type = types.bool; default = true; @@ -68,7 +75,7 @@ in systemd.services.bird2 = { description = "BIRD Internet Routing Daemon"; wantedBy = [ "multi-user.target" ]; - reloadTriggers = [ config.environment.etc."bird/bird2.conf".source ]; + reloadTriggers = lib.optional cfg.autoReload config.environment.etc."bird/bird2.conf".source; serviceConfig = { Type = "forking"; Restart = "on-failure"; diff --git a/nixos/modules/services/networking/birdwatcher.nix b/nixos/modules/services/networking/birdwatcher.nix index a129b7a2b4cf..c8ebb2269764 100644 --- a/nixos/modules/services/networking/birdwatcher.nix +++ b/nixos/modules/services/networking/birdwatcher.nix @@ -8,12 +8,7 @@ in { options = { services.birdwatcher = { - package = mkOption { - type = types.package; - default = pkgs.birdwatcher; - defaultText = literalExpression "pkgs.birdwatcher"; - description = lib.mdDoc "The Birdwatcher package to use."; - }; + package = mkPackageOption pkgs "birdwatcher" { }; enable = mkEnableOption (lib.mdDoc "Birdwatcher"); flags = mkOption { default = [ ]; diff --git a/nixos/modules/services/networking/bitcoind.nix b/nixos/modules/services/networking/bitcoind.nix index a86d52b7202d..59722e31c62a 100644 --- a/nixos/modules/services/networking/bitcoind.nix +++ b/nixos/modules/services/networking/bitcoind.nix @@ -3,8 +3,7 @@ with lib; let - - eachBitcoind = config.services.bitcoind; + eachBitcoind = filterAttrs (bitcoindName: cfg: cfg.enable) config.services.bitcoind; rpcUserOpts = { name, ... }: { options = { @@ -37,12 +36,7 @@ let enable = mkEnableOption (lib.mdDoc "Bitcoin daemon"); - package = mkOption { - type = types.package; - default = pkgs.bitcoind; - defaultText = literalExpression "pkgs.bitcoind"; - description = lib.mdDoc "The package providing bitcoin binaries."; - }; + package = mkPackageOption pkgs "bitcoind" { }; configFile = mkOption { type = types.nullOr types.path; @@ -204,6 +198,7 @@ in ''; in { description = "Bitcoin daemon"; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { diff --git a/nixos/modules/services/networking/blockbook-frontend.nix b/nixos/modules/services/networking/blockbook-frontend.nix index 46b26195d211..bf476d814140 100644 --- a/nixos/modules/services/networking/blockbook-frontend.nix +++ b/nixos/modules/services/networking/blockbook-frontend.nix @@ -12,12 +12,7 @@ let enable = mkEnableOption (lib.mdDoc "blockbook-frontend application"); - package = mkOption { - type = types.package; - default = pkgs.blockbook; - defaultText = literalExpression "pkgs.blockbook"; - description = lib.mdDoc "Which blockbook package to use."; - }; + package = mkPackageOption pkgs "blockbook" { }; user = mkOption { type = types.str; diff --git a/nixos/modules/services/networking/centrifugo.nix b/nixos/modules/services/networking/centrifugo.nix new file mode 100644 index 000000000000..7c6c9a362fd2 --- /dev/null +++ b/nixos/modules/services/networking/centrifugo.nix @@ -0,0 +1,123 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.centrifugo; + + settingsFormat = pkgs.formats.json { }; + + configFile = settingsFormat.generate "centrifugo.json" cfg.settings; +in +{ + options.services.centrifugo = { + enable = lib.mkEnableOption (lib.mdDoc "Centrifugo messaging server"); + + package = lib.mkPackageOption pkgs "centrifugo" { }; + + settings = lib.mkOption { + type = settingsFormat.type; + default = { }; + description = lib.mdDoc '' + Declarative Centrifugo configuration. See the [Centrifugo + documentation] for a list of options. + + [Centrifugo documentation]: https://centrifugal.dev/docs/server/configuration + ''; + }; + + credentials = lib.mkOption { + type = lib.types.attrsOf lib.types.path; + default = { }; + example = { + CENTRIFUGO_UNI_GRPC_TLS_KEY = "/run/keys/centrifugo-uni-grpc-tls.key"; + }; + description = lib.mdDoc '' + Environment variables with absolute paths to credentials files to load + on service startup. + ''; + }; + + environmentFiles = lib.mkOption { + type = lib.types.listOf lib.types.path; + default = [ ]; + description = lib.mdDoc '' + Files to load environment variables from. Options set via environment + variables take precedence over {option}`settings`. + + See the [Centrifugo documentation] for the environment variable name + format. + + [Centrifugo documentation]: https://centrifugal.dev/docs/server/configuration#os-environment-variables + ''; + }; + + extraGroups = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + example = [ "redis-centrifugo" ]; + description = lib.mdDoc '' + Additional groups for the systemd service. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.centrifugo = { + description = "Centrifugo messaging server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + Type = "exec"; + + ExecStartPre = "${lib.getExe cfg.package} checkconfig --config ${configFile}"; + ExecStart = "${lib.getExe cfg.package} --config ${configFile}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + + Restart = "always"; + RestartSec = "1s"; + + # Copy files to the credentials directory with file name being the + # environment variable name. Note that "%d" specifier expands to the + # path of the credentials directory. + LoadCredential = lib.mapAttrsToList (name: value: "${name}:${value}") cfg.credentials; + Environment = lib.mapAttrsToList (name: _: "${name}=%d/${name}") cfg.credentials; + + EnvironmentFile = cfg.environmentFiles; + + SupplementaryGroups = cfg.extraGroups; + + DynamicUser = true; + UMask = "0077"; + + ProtectHome = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + ProtectClock = true; + ProtectHostname = true; + ProtectControlGroups = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + PrivateUsers = true; + PrivateDevices = true; + RestrictRealtime = true; + RestrictNamespaces = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + DeviceAllow = [ "" ]; + DevicePolicy = "closed"; + CapabilityBoundingSet = [ "" ]; + MemoryDenyWriteExecute = true; + LockPersonality = true; + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + ]; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/cgit.nix b/nixos/modules/services/networking/cgit.nix index 7d1f12fa9146..3de2eb192ed1 100644 --- a/nixos/modules/services/networking/cgit.nix +++ b/nixos/modules/services/networking/cgit.nix @@ -102,7 +102,7 @@ in options = { enable = mkEnableOption (mdDoc "cgit"); - package = mkPackageOptionMD pkgs "cgit" {}; + package = mkPackageOption pkgs "cgit" {}; nginx.virtualHost = mkOption { description = mdDoc "VirtualHost to serve cgit on, defaults to the attribute name."; diff --git a/nixos/modules/services/networking/charybdis.nix b/nixos/modules/services/networking/charybdis.nix index 168da243dba1..6eacdde7bb93 100644 --- a/nixos/modules/services/networking/charybdis.nix +++ b/nixos/modules/services/networking/charybdis.nix @@ -81,9 +81,9 @@ in gid = config.ids.gids.ircd; }; - systemd.tmpfiles.rules = [ - "d ${cfg.statedir} - ${cfg.user} ${cfg.group} - -" - ]; + systemd.tmpfiles.settings."10-charybdis".${cfg.statedir}.d = { + inherit (cfg) user group; + }; environment.etc."charybdis/ircd.conf".source = configFile; diff --git a/nixos/modules/services/networking/cloudflared.nix b/nixos/modules/services/networking/cloudflared.nix index b3f0e37d8e9e..b9556bfa60d0 100644 --- a/nixos/modules/services/networking/cloudflared.nix +++ b/nixos/modules/services/networking/cloudflared.nix @@ -152,12 +152,7 @@ in description = lib.mdDoc "Group under which cloudflared runs."; }; - package = mkOption { - type = types.package; - default = pkgs.cloudflared; - defaultText = "pkgs.cloudflared"; - description = lib.mdDoc "The package to use for Cloudflared."; - }; + package = mkPackageOption pkgs "cloudflared" { }; tunnels = mkOption { description = lib.mdDoc '' @@ -281,9 +276,11 @@ in ingressesSet = filterIngressSet tunnel.ingress; ingressesStr = filterIngressStr tunnel.ingress; - fullConfig = { + fullConfig = filterConfig { tunnel = name; "credentials-file" = tunnel.credentialsFile; + warp-routing = filterConfig tunnel.warp-routing; + originRequest = filterConfig tunnel.originRequest; ingress = (map (key: { @@ -299,6 +296,7 @@ in (attrNames ingressesStr)) ++ [{ service = tunnel.default; }]; }; + mkConfigFile = pkgs.writeText "cloudflared.yml" (builtins.toJSON fullConfig); in nameValuePair "cloudflared-tunnel-${name}" ({ @@ -327,5 +325,5 @@ in }; }; - meta.maintainers = with maintainers; [ bbigras ]; + meta.maintainers = with maintainers; [ bbigras anpin ]; } diff --git a/nixos/modules/services/networking/consul.nix b/nixos/modules/services/networking/consul.nix index 955463b9031e..1a0910fc9344 100644 --- a/nixos/modules/services/networking/consul.nix +++ b/nixos/modules/services/networking/consul.nix @@ -33,15 +33,7 @@ in ''; }; - package = mkOption { - type = types.package; - default = pkgs.consul; - defaultText = literalExpression "pkgs.consul"; - description = lib.mdDoc '' - The package used for the Consul agent and CLI. - ''; - }; - + package = mkPackageOption pkgs "consul" { }; webUi = mkOption { type = types.bool; @@ -128,12 +120,7 @@ in alerts = { enable = mkEnableOption (lib.mdDoc "consul-alerts"); - package = mkOption { - description = lib.mdDoc "Package to use for consul-alerts."; - default = pkgs.consul-alerts; - defaultText = literalExpression "pkgs.consul-alerts"; - type = types.package; - }; + package = mkPackageOption pkgs "consul-alerts" { }; listenAddr = mkOption { description = lib.mdDoc "Api listening address."; diff --git a/nixos/modules/services/networking/coredns.nix b/nixos/modules/services/networking/coredns.nix index f1fe7b2f1241..f6eec2f962dd 100644 --- a/nixos/modules/services/networking/coredns.nix +++ b/nixos/modules/services/networking/coredns.nix @@ -23,12 +23,7 @@ in { ''; }; - package = mkOption { - default = pkgs.coredns; - defaultText = literalExpression "pkgs.coredns"; - type = types.package; - description = lib.mdDoc "Coredns package to use."; - }; + package = mkPackageOption pkgs "coredns" { }; extraArgs = mkOption { default = []; diff --git a/nixos/modules/services/networking/corerad.nix b/nixos/modules/services/networking/corerad.nix index 0c6fb7a17cab..33ea2862174e 100644 --- a/nixos/modules/services/networking/corerad.nix +++ b/nixos/modules/services/networking/corerad.nix @@ -48,12 +48,7 @@ in { description = lib.mdDoc "Path to CoreRAD TOML configuration file."; }; - package = mkOption { - default = pkgs.corerad; - defaultText = literalExpression "pkgs.corerad"; - type = types.package; - description = lib.mdDoc "CoreRAD package to use."; - }; + package = mkPackageOption pkgs "corerad" { }; }; config = mkIf cfg.enable { diff --git a/nixos/modules/services/networking/dae.nix b/nixos/modules/services/networking/dae.nix index cf3fead19be5..404ce59741f8 100644 --- a/nixos/modules/services/networking/dae.nix +++ b/nixos/modules/services/networking/dae.nix @@ -16,7 +16,7 @@ in enable = mkEnableOption (mdDoc "dae, a Linux high-performance transparent proxy solution based on eBPF"); - package = mkPackageOptionMD pkgs "dae" { }; + package = mkPackageOption pkgs "dae" { }; assets = mkOption { diff --git a/nixos/modules/services/networking/dante.nix b/nixos/modules/services/networking/dante.nix index 605f2d74f827..f0d1d6305c54 100644 --- a/nixos/modules/services/networking/dante.nix +++ b/nixos/modules/services/networking/dante.nix @@ -47,6 +47,7 @@ in systemd.services.dante = { description = "Dante SOCKS v4 and v5 compatible proxy server"; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix index 8f4fb0bc78d4..18f205b8d99e 100644 --- a/nixos/modules/services/networking/ddclient.nix +++ b/nixos/modules/services/networking/ddclient.nix @@ -126,7 +126,7 @@ with lib; default = "dyndns2"; type = str; description = lib.mdDoc '' - Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols). + Protocol to use with dynamic DNS provider (see https://ddclient.net/protocols.html ). ''; }; @@ -217,7 +217,7 @@ with lib; inherit RuntimeDirectory; inherit StateDirectory; Type = "oneshot"; - ExecStartPre = "!${pkgs.writeShellScript "ddclient-prestart" preStart}"; + ExecStartPre = [ "!${pkgs.writeShellScript "ddclient-prestart" preStart}" ]; ExecStart = "${lib.getExe cfg.package} -file /run/${RuntimeDirectory}/ddclient.conf"; }; }; diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix index 8b6d3fc55f3e..266a7ea1435e 100644 --- a/nixos/modules/services/networking/dhcpcd.nix +++ b/nixos/modules/services/networking/dhcpcd.nix @@ -98,7 +98,7 @@ let # anything ever again ("couldn't resolve ..., giving up on # it"), so we silently lose time synchronisation. This also # applies to openntpd. - /run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd.service openntpd.service chronyd.service || true + /run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd.service openntpd.service chronyd.service ntpd-rs.service || true fi ${cfg.runHook} @@ -219,6 +219,8 @@ in ''; } ]; + environment.etc."dhcpcd.conf".source = dhcpcdConf; + systemd.services.dhcpcd = let cfgN = config.networking; hasDefaultGatewaySet = (cfgN.defaultGateway != null && cfgN.defaultGateway.address != "") diff --git a/nixos/modules/services/networking/dnsdist.nix b/nixos/modules/services/networking/dnsdist.nix index 483300111df9..792185c9fbea 100644 --- a/nixos/modules/services/networking/dnsdist.nix +++ b/nixos/modules/services/networking/dnsdist.nix @@ -4,10 +4,79 @@ with lib; let cfg = config.services.dnsdist; + + toLua = lib.generators.toLua {}; + + mkBind = cfg: toLua "${cfg.listenAddress}:${toString cfg.listenPort}"; + configFile = pkgs.writeText "dnsdist.conf" '' - setLocal('${cfg.listenAddress}:${toString cfg.listenPort}') + setLocal(${mkBind cfg}) + ${lib.optionalString cfg.dnscrypt.enable dnscryptSetup} ${cfg.extraConfig} ''; + + dnscryptSetup = '' + last_rotation = 0 + cert_serial = 0 + provider_key = ${toLua cfg.dnscrypt.providerKey} + cert_lifetime = ${toLua cfg.dnscrypt.certLifetime} * 60 + + function file_exists(name) + local f = io.open(name, "r") + return f ~= nil and io.close(f) + end + + function dnscrypt_setup() + -- generate provider keys on first run + if provider_key == nil then + provider_key = "/var/lib/dnsdist/private.key" + if not file_exists(provider_key) then + generateDNSCryptProviderKeys("/var/lib/dnsdist/public.key", + "/var/lib/dnsdist/private.key") + print("DNSCrypt: generated provider keypair") + end + end + + -- generate resolver certificate + local now = os.time() + generateDNSCryptCertificate( + provider_key, "/run/dnsdist/resolver.cert", "/run/dnsdist/resolver.key", + cert_serial, now - 60, now + cert_lifetime) + addDNSCryptBind( + ${mkBind cfg.dnscrypt}, ${toLua cfg.dnscrypt.providerName}, + "/run/dnsdist/resolver.cert", "/run/dnsdist/resolver.key") + end + + function maintenance() + -- certificate rotation + local now = os.time() + local dnscrypt = getDNSCryptBind(0) + + if ((now - last_rotation) > 0.9 * cert_lifetime) then + -- generate and start using a new certificate + dnscrypt:generateAndLoadInMemoryCertificate( + provider_key, cert_serial + 1, + now - 60, now + cert_lifetime) + + -- stop advertising the last certificate + dnscrypt:markInactive(cert_serial) + + -- remove the second to last certificate + if (cert_serial > 1) then + dnscrypt:removeInactiveCertificate(cert_serial - 1) + end + + print("DNSCrypt: rotated certificate") + + -- increment serial number + cert_serial = cert_serial + 1 + last_rotation = now + end + end + + dnscrypt_setup() + ''; + in { options = { services.dnsdist = { @@ -15,15 +84,69 @@ in { listenAddress = mkOption { type = types.str; - description = lib.mdDoc "Listen IP Address"; + description = lib.mdDoc "Listen IP address"; default = "0.0.0.0"; }; listenPort = mkOption { - type = types.int; + type = types.port; description = lib.mdDoc "Listen port"; default = 53; }; + dnscrypt = { + enable = mkEnableOption (lib.mdDoc "a DNSCrypt endpoint to dnsdist"); + + listenAddress = mkOption { + type = types.str; + description = lib.mdDoc "Listen IP address of the endpoint"; + default = "0.0.0.0"; + }; + + listenPort = mkOption { + type = types.port; + description = lib.mdDoc "Listen port of the endpoint"; + default = 443; + }; + + providerName = mkOption { + type = types.str; + default = "2.dnscrypt-cert.${config.networking.hostName}"; + defaultText = literalExpression "2.dnscrypt-cert.\${config.networking.hostName}"; + example = "2.dnscrypt-cert.myresolver"; + description = lib.mdDoc '' + The name that will be given to this DNSCrypt resolver. + + ::: {.note} + The provider name must start with `2.dnscrypt-cert.`. + ::: + ''; + }; + + providerKey = mkOption { + type = types.nullOr types.path; + default = null; + description = lib.mdDoc '' + The filepath to the provider secret key. + If not given a new provider key pair will be generated in + /var/lib/dnsdist on the first run. + + ::: {.note} + The file must be readable by the dnsdist user/group. + ::: + ''; + }; + + certLifetime = mkOption { + type = types.ints.positive; + default = 15; + description = lib.mdDoc '' + The lifetime (in minutes) of the resolver certificate. + This will be automatically rotated before expiration. + ''; + }; + + }; + extraConfig = mkOption { type = types.lines; default = ""; @@ -35,6 +158,14 @@ in { }; config = mkIf cfg.enable { + users.users.dnsdist = { + description = "dnsdist daemons user"; + isSystemUser = true; + group = "dnsdist"; + }; + + users.groups.dnsdist = {}; + systemd.packages = [ pkgs.dnsdist ]; systemd.services.dnsdist = { @@ -42,8 +173,10 @@ in { startLimitIntervalSec = 0; serviceConfig = { - DynamicUser = true; - + User = "dnsdist"; + Group = "dnsdist"; + RuntimeDirectory = "dnsdist"; + StateDirectory = "dnsdist"; # upstream overrides for better nixos compatibility ExecStartPre = [ "" "${pkgs.dnsdist}/bin/dnsdist --check-config --config ${configFile}" ]; ExecStart = [ "" "${pkgs.dnsdist}/bin/dnsdist --supervised --disable-syslog --config ${configFile}" ]; diff --git a/nixos/modules/services/networking/dnsmasq.md b/nixos/modules/services/networking/dnsmasq.md new file mode 100644 index 000000000000..6fc9178b1c0d --- /dev/null +++ b/nixos/modules/services/networking/dnsmasq.md @@ -0,0 +1,68 @@ +# Dnsmasq {#module-services-networking-dnsmasq} + +Dnsmasq is an integrated DNS, DHCP and TFTP server for small networks. + +## Configuration {#module-services-networking-dnsmasq-configuration} + +### An authoritative DHCP and DNS server on a home network {#module-services-networking-dnsmasq-configuration-home} + +On a home network, you can use Dnsmasq as a DHCP and DNS server. New devices on +your network will be configured by Dnsmasq, and instructed to use it as the DNS +server by default. This allows you to rely on your own server to perform DNS +queries and caching, with DNSSEC enabled. + +The following example assumes that + +- you have disabled your router's integrated DHCP server, if it has one +- your router's address is set in [](#opt-networking.defaultGateway.address) +- your system's Ethernet interface is `eth0` +- you have configured the address(es) to forward DNS queries in [](#opt-networking.nameservers) + +```nix +{ + services.dnsmasq = { + enable = true; + settings = { + interface = "eth0"; + bind-interfaces = true; # Only bind to the specified interface + dhcp-authoritative = true; # Should be set when dnsmasq is definitely the only DHCP server on a network + + server = config.networking.nameservers; # Upstream dns servers to which requests should be forwarded + + dhcp-host = [ + # Give the current system a fixed address of 192.168.0.254 + "dc:a6:32:0b:ea:b9,192.168.0.254,${config.networking.hostName},infinite" + ]; + + dhcp-option = [ + # Address of the gateway, i.e. your router + "option:router,${config.networking.defaultGateway.address}" + ]; + + dhcp-range = [ + # Range of IPv4 addresses to give out + # <range start>,<range end>,<lease time> + "192.168.0.10,192.168.0.253,24h" + # Enable stateless IPv6 allocation + "::f,::ff,constructor:eth0,ra-stateless" + ]; + + dhcp-rapid-commit = true; # Faster DHCP negotiation for IPv6 + local-service = true; # Accept DNS queries only from hosts whose address is on a local subnet + log-queries = true; # Log results of all DNS queries + bogus-priv = true; # Don't forward requests for the local address ranges (192.168.x.x etc) to upstream nameservers + domain-needed = true; # Don't forward requests without dots or domain parts to upstream nameservers + + dnssec = true; # Enable DNSSEC + # DNSSEC trust anchor. Source: https://data.iana.org/root-anchors/root-anchors.xml + trust-anchor = ".,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D"; + }; + }; +} +``` + +## References {#module-services-networking-dnsmasq-references} + +- Upstream website: <https://dnsmasq.org> +- Manpage: <https://dnsmasq.org/docs/dnsmasq-man.html> +- FAQ: <https://dnsmasq.org/docs/FAQ> diff --git a/nixos/modules/services/networking/dnsmasq.nix b/nixos/modules/services/networking/dnsmasq.nix index 14bbe334e50d..d01a1b6707a5 100644 --- a/nixos/modules/services/networking/dnsmasq.nix +++ b/nixos/modules/services/networking/dnsmasq.nix @@ -53,7 +53,7 @@ in ''; }; - package = mkPackageOptionMD pkgs "dnsmasq" {}; + package = mkPackageOption pkgs "dnsmasq" {}; resolveLocalQueries = mkOption { type = types.bool; @@ -181,4 +181,6 @@ in restartTriggers = [ config.environment.etc.hosts.source ]; }; }; + + meta.doc = ./dnsmasq.md; } diff --git a/nixos/modules/services/networking/ejabberd.nix b/nixos/modules/services/networking/ejabberd.nix index 3feafc3bb3bd..78af256f9c81 100644 --- a/nixos/modules/services/networking/ejabberd.nix +++ b/nixos/modules/services/networking/ejabberd.nix @@ -29,12 +29,7 @@ in { description = lib.mdDoc "Whether to enable ejabberd server"; }; - package = mkOption { - type = types.package; - default = pkgs.ejabberd; - defaultText = literalExpression "pkgs.ejabberd"; - description = lib.mdDoc "ejabberd server package to use"; - }; + package = mkPackageOption pkgs "ejabberd" { }; user = mkOption { type = types.str; @@ -125,6 +120,12 @@ in { if [ -z "$(ls -A '${cfg.spoolDir}')" ]; then touch "${cfg.spoolDir}/.firstRun" fi + + if ! test -e ${cfg.spoolDir}/.erlang.cookie; then + touch ${cfg.spoolDir}/.erlang.cookie + chmod 600 ${cfg.spoolDir}/.erlang.cookie + dd if=/dev/random bs=16 count=1 | base64 > ${cfg.spoolDir}/.erlang.cookie + fi ''; postStart = '' diff --git a/nixos/modules/services/networking/envoy.nix b/nixos/modules/services/networking/envoy.nix index c68ceab9619c..779c77ff6c81 100644 --- a/nixos/modules/services/networking/envoy.nix +++ b/nixos/modules/services/networking/envoy.nix @@ -17,7 +17,7 @@ in options.services.envoy = { enable = mkEnableOption (lib.mdDoc "Envoy reverse proxy"); - package = mkPackageOptionMD pkgs "envoy" { }; + package = mkPackageOption pkgs "envoy" { }; requireValidConfig = mkOption { type = types.bool; diff --git a/nixos/modules/services/networking/epmd.nix b/nixos/modules/services/networking/epmd.nix index 0bc8c71f4eaa..318e325944b5 100644 --- a/nixos/modules/services/networking/epmd.nix +++ b/nixos/modules/services/networking/epmd.nix @@ -17,15 +17,7 @@ in Erlang computations. ''; }; - package = mkOption { - type = types.package; - default = pkgs.erlang; - defaultText = literalExpression "pkgs.erlang"; - description = lib.mdDoc '' - The Erlang package to use to get epmd binary. That way you can re-use - an Erlang runtime that is already installed for other purposes. - ''; - }; + package = mkPackageOption pkgs "erlang" { }; listenStream = mkOption { type = types.str; diff --git a/nixos/modules/services/networking/ergo.nix b/nixos/modules/services/networking/ergo.nix index 033d4d9caf8a..1bee0f43f988 100644 --- a/nixos/modules/services/networking/ergo.nix +++ b/nixos/modules/services/networking/ergo.nix @@ -114,6 +114,7 @@ in { systemd.services.ergo = { description = "ergo server"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { User = cfg.user; diff --git a/nixos/modules/services/networking/expressvpn.nix b/nixos/modules/services/networking/expressvpn.nix index 30de6987d31f..05c24d8bccff 100644 --- a/nixos/modules/services/networking/expressvpn.nix +++ b/nixos/modules/services/networking/expressvpn.nix @@ -21,6 +21,7 @@ with lib; RestartSec = 5; }; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network.target" "network-online.target" ]; }; }; diff --git a/nixos/modules/services/networking/ferm.nix b/nixos/modules/services/networking/ferm.nix index 09151eb0b544..5ebf7aacb4db 100644 --- a/nixos/modules/services/networking/ferm.nix +++ b/nixos/modules/services/networking/ferm.nix @@ -33,12 +33,7 @@ in { defaultText = literalMD "empty firewall, allows any traffic"; type = types.lines; }; - package = mkOption { - description = lib.mdDoc "The ferm package."; - type = types.package; - default = pkgs.ferm; - defaultText = literalExpression "pkgs.ferm"; - }; + package = mkPackageOption pkgs "ferm" { }; }; }; diff --git a/nixos/modules/services/networking/firewall-iptables.nix b/nixos/modules/services/networking/firewall-iptables.nix index 63e952194d67..2d1151770008 100644 --- a/nixos/modules/services/networking/firewall-iptables.nix +++ b/nixos/modules/services/networking/firewall-iptables.nix @@ -301,14 +301,16 @@ in } ]; + environment.systemPackages = [ pkgs.nixos-firewall-tool ]; networking.firewall.checkReversePath = mkIf (!kernelHasRPFilter) (mkDefault false); systemd.services.firewall = { description = "Firewall"; wantedBy = [ "sysinit.target" ]; wants = [ "network-pre.target" ]; - before = [ "network-pre.target" ]; after = [ "systemd-modules-load.service" ]; + before = [ "network-pre.target" "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; path = [ cfg.package ] ++ cfg.extraPackages; diff --git a/nixos/modules/services/networking/flannel.nix b/nixos/modules/services/networking/flannel.nix index 6ed4f78ddc92..2c2b6dc58cce 100644 --- a/nixos/modules/services/networking/flannel.nix +++ b/nixos/modules/services/networking/flannel.nix @@ -16,12 +16,7 @@ in { options.services.flannel = { enable = mkEnableOption (lib.mdDoc "flannel"); - package = mkOption { - description = lib.mdDoc "Package to use for flannel"; - type = types.package; - default = pkgs.flannel; - defaultText = literalExpression "pkgs.flannel"; - }; + package = mkPackageOption pkgs "flannel" { }; publicIp = mkOption { description = lib.mdDoc '' diff --git a/nixos/modules/services/networking/frp.nix b/nixos/modules/services/networking/frp.nix index e4f9a220b5e8..eb022308bc29 100644 --- a/nixos/modules/services/networking/frp.nix +++ b/nixos/modules/services/networking/frp.nix @@ -4,8 +4,8 @@ with lib; let cfg = config.services.frp; - settingsFormat = pkgs.formats.ini { }; - configFile = settingsFormat.generate "frp.ini" cfg.settings; + settingsFormat = pkgs.formats.toml { }; + configFile = settingsFormat.generate "frp.toml" cfg.settings; isClient = (cfg.role == "client"); isServer = (cfg.role == "server"); in @@ -14,7 +14,7 @@ in services.frp = { enable = mkEnableOption (mdDoc "frp"); - package = mkPackageOptionMD pkgs "frp" { }; + package = mkPackageOption pkgs "frp" { }; role = mkOption { type = types.enum [ "server" "client" ]; @@ -31,17 +31,13 @@ in default = { }; description = mdDoc '' Frp configuration, for configuration options - see the example of [client](https://github.com/fatedier/frp/blob/dev/conf/frpc_legacy_full.ini) - or [server](https://github.com/fatedier/frp/blob/dev/conf/frps_legacy_full.ini) on github. - ''; - example = literalExpression '' - { - common = { - server_addr = "x.x.x.x"; - server_port = 7000; - }; - } + see the example of [client](https://github.com/fatedier/frp/blob/dev/conf/frpc_full_example.toml) + or [server](https://github.com/fatedier/frp/blob/dev/conf/frps_full_example.toml) on github. ''; + example = { + serverAddr = "x.x.x.x"; + serverPort = 7000; + }; }; }; }; @@ -62,7 +58,7 @@ in Type = "simple"; Restart = "on-failure"; RestartSec = 15; - ExecStart = "${cfg.package}/bin/${executableFile} -c ${configFile}"; + ExecStart = "${cfg.package}/bin/${executableFile} --strict_config -c ${configFile}"; StateDirectoryMode = optionalString isServer "0700"; DynamicUser = true; # Hardening diff --git a/nixos/modules/services/networking/ghostunnel.nix b/nixos/modules/services/networking/ghostunnel.nix index 4902367e2a6a..d5e2ff19ce50 100644 --- a/nixos/modules/services/networking/ghostunnel.nix +++ b/nixos/modules/services/networking/ghostunnel.nix @@ -9,6 +9,7 @@ let mapAttrs' mkDefault mkEnableOption + mkPackageOption mkIf mkOption nameValuePair @@ -215,12 +216,7 @@ in options = { services.ghostunnel.enable = mkEnableOption (lib.mdDoc "ghostunnel"); - services.ghostunnel.package = mkOption { - description = lib.mdDoc "The ghostunnel package to use."; - type = types.package; - default = pkgs.ghostunnel; - defaultText = literalExpression "pkgs.ghostunnel"; - }; + services.ghostunnel.package = mkPackageOption pkgs "ghostunnel" { }; services.ghostunnel.servers = mkOption { description = lib.mdDoc '' diff --git a/nixos/modules/services/networking/gns3-server.md b/nixos/modules/services/networking/gns3-server.md new file mode 100644 index 000000000000..9320d914fbd3 --- /dev/null +++ b/nixos/modules/services/networking/gns3-server.md @@ -0,0 +1,31 @@ +# GNS3 Server {#module-services-gns3-server} + +[GNS3](https://www.gns3.com/), a network software emulator. + +## Basic Usage {#module-services-gns3-server-basic-usage} + +A minimal configuration looks like this: + +```nix +{ + services.gns3-server = { + enable = true; + + auth = { + enable = true; + user = "gns3"; + passwordFile = "/var/lib/secrets/gns3_password"; + }; + + ssl = { + enable = true; + certFile = "/var/lib/gns3/ssl/cert.pem"; + keyFile = "/var/lib/gns3/ssl/key.pem"; + }; + + dynamips.enable = true; + ubridge.enable = true; + vpcs.enable = true; + }; +} +``` diff --git a/nixos/modules/services/networking/gns3-server.nix b/nixos/modules/services/networking/gns3-server.nix new file mode 100644 index 000000000000..25583765de67 --- /dev/null +++ b/nixos/modules/services/networking/gns3-server.nix @@ -0,0 +1,263 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.gns3-server; + + settingsFormat = pkgs.formats.ini { }; + configFile = settingsFormat.generate "gns3-server.conf" cfg.settings; + +in { + meta = { + doc = ./gns3-server.md; + maintainers = [ lib.maintainers.anthonyroussel ]; + }; + + options = { + services.gns3-server = { + enable = lib.mkEnableOption (lib.mdDoc "GNS3 Server daemon"); + + package = lib.mkPackageOptionMD pkgs "gns3-server" { }; + + auth = { + enable = lib.mkEnableOption (lib.mdDoc "password based HTTP authentication to access the GNS3 Server"); + + user = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "gns3"; + description = lib.mdDoc ''Username used to access the GNS3 Server.''; + }; + + passwordFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/run/secrets/gns3-server-password"; + description = lib.mdDoc '' + A file containing the password to access the GNS3 Server. + + ::: {.warning} + This should be a string, not a nix path, since nix paths + are copied into the world-readable nix store. + ::: + ''; + }; + }; + + settings = lib.mkOption { + type = lib.types.submodule { freeformType = settingsFormat.type; }; + default = {}; + example = { host = "127.0.0.1"; port = 3080; }; + description = lib.mdDoc '' + The global options in `config` file in ini format. + + Refer to <https://docs.gns3.com/docs/using-gns3/administration/gns3-server-configuration-file/> + for all available options. + ''; + }; + + log = { + file = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = "/var/log/gns3/server.log"; + description = lib.mdDoc ''Path of the file GNS3 Server should log to.''; + }; + + debug = lib.mkEnableOption (lib.mdDoc "debug logging"); + }; + + ssl = { + enable = lib.mkEnableOption (lib.mdDoc "SSL encryption"); + + certFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/var/lib/gns3/ssl/server.pem"; + description = lib.mdDoc '' + Path to the SSL certificate file. This certificate will + be offered to, and may be verified by, clients. + ''; + }; + + keyFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/var/lib/gns3/ssl/server.key"; + description = lib.mdDoc "Private key file for the certificate."; + }; + }; + + dynamips = { + enable = lib.mkEnableOption (lib.mdDoc ''Whether to enable Dynamips support.''); + package = lib.mkPackageOptionMD pkgs "dynamips" { }; + }; + + ubridge = { + enable = lib.mkEnableOption (lib.mdDoc ''Whether to enable uBridge support.''); + package = lib.mkPackageOptionMD pkgs "ubridge" { }; + }; + + vpcs = { + enable = lib.mkEnableOption (lib.mdDoc ''Whether to enable VPCS support.''); + package = lib.mkPackageOptionMD pkgs "vpcs" { }; + }; + }; + }; + + config = let + flags = { + enableDocker = config.virtualisation.docker.enable; + enableLibvirtd = config.virtualisation.libvirtd.enable; + }; + + in lib.mkIf cfg.enable { + assertions = [ + { + assertion = cfg.ssl.enable -> cfg.ssl.certFile != null; + message = "Please provide a certificate to use for SSL encryption."; + } + { + assertion = cfg.ssl.enable -> cfg.ssl.keyFile != null; + message = "Please provide a private key to use for SSL encryption."; + } + { + assertion = cfg.auth.enable -> cfg.auth.user != null; + message = "Please provide a username to use for HTTP authentication."; + } + { + assertion = cfg.auth.enable -> cfg.auth.passwordFile != null; + message = "Please provide a password file to use for HTTP authentication."; + } + ]; + + users.groups.ubridge = lib.mkIf cfg.ubridge.enable { }; + + security.wrappers.ubridge = lib.mkIf cfg.ubridge.enable { + capabilities = "cap_net_raw,cap_net_admin=eip"; + group = "ubridge"; + owner = "root"; + permissions = "u=rwx,g=rx,o=r"; + source = lib.getExe cfg.ubridge.package; + }; + + services.gns3-server.settings = lib.mkMerge [ + { + Server = { + appliances_path = lib.mkDefault "/var/lib/gns3/appliances"; + configs_path = lib.mkDefault "/var/lib/gns3/configs"; + images_path = lib.mkDefault "/var/lib/gns3/images"; + projects_path = lib.mkDefault "/var/lib/gns3/projects"; + symbols_path = lib.mkDefault "/var/lib/gns3/symbols"; + }; + } + (lib.mkIf (cfg.ubridge.enable) { + Server.ubridge_path = lib.mkDefault (lib.getExe cfg.ubridge.package); + }) + (lib.mkIf (cfg.auth.enable) { + Server = { + auth = lib.mkDefault (lib.boolToString cfg.auth.enable); + user = lib.mkDefault cfg.auth.user; + password = lib.mkDefault "@AUTH_PASSWORD@"; + }; + }) + (lib.mkIf (cfg.vpcs.enable) { + VPCS.vpcs_path = lib.mkDefault (lib.getExe cfg.vpcs.package); + }) + (lib.mkIf (cfg.dynamips.enable) { + Dynamips.dynamips_path = lib.mkDefault (lib.getExe cfg.dynamips.package); + }) + ]; + + systemd.services.gns3-server = let + commandArgs = lib.cli.toGNUCommandLineShell { } { + config = "/etc/gns3/gns3_server.conf"; + pid = "/run/gns3/server.pid"; + log = cfg.log.file; + ssl = cfg.ssl.enable; + # These are implicitly not set if `null` + certfile = cfg.ssl.certFile; + certkey = cfg.ssl.keyFile; + }; + in + { + description = "GNS3 Server"; + + after = [ "network.target" "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; + + # configFile cannot be stored in RuntimeDirectory, because GNS3 + # uses the `--config` base path to stores supplementary configuration files at runtime. + # + preStart = '' + install -m660 ${configFile} /etc/gns3/gns3_server.conf + + ${lib.optionalString cfg.auth.enable '' + ${pkgs.replace-secret}/bin/replace-secret \ + '@AUTH_PASSWORD@' \ + "''${CREDENTIALS_DIRECTORY}/AUTH_PASSWORD" \ + /etc/gns3/gns3_server.conf + ''} + ''; + + path = lib.optional flags.enableLibvirtd pkgs.qemu; + + reloadTriggers = [ configFile ]; + + serviceConfig = { + ConfigurationDirectory = "gns3"; + ConfigurationDirectoryMode = "0750"; + DynamicUser = true; + Environment = "HOME=%S/gns3"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + ExecStart = "${lib.getExe cfg.package} ${commandArgs}"; + Group = "gns3"; + LimitNOFILE = 16384; + LoadCredential = lib.mkIf cfg.auth.enable [ "AUTH_PASSWORD:${cfg.auth.passwordFile}" ]; + LogsDirectory = "gns3"; + LogsDirectoryMode = "0750"; + PIDFile = "/run/gns3/server.pid"; + Restart = "on-failure"; + RestartSec = 5; + RuntimeDirectory = "gns3"; + StateDirectory = "gns3"; + StateDirectoryMode = "0750"; + SupplementaryGroups = lib.optional flags.enableDocker "docker" + ++ lib.optional flags.enableLibvirtd "libvirtd" + ++ lib.optional cfg.ubridge.enable "ubridge"; + User = "gns3"; + WorkingDirectory = "%S/gns3"; + + # Hardening + DeviceAllow = lib.optional flags.enableLibvirtd "/dev/kvm"; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateTmp = true; + PrivateUsers = true; + # Don't restrict ProcSubset because python3Packages.psutil requires read access to /proc/stat + # ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_NETLINK" + "AF_UNIX" + "AF_PACKET" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + UMask = "0077"; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/gnunet.nix b/nixos/modules/services/networking/gnunet.nix index fdb353fd3443..a235f1605e54 100644 --- a/nixos/modules/services/networking/gnunet.nix +++ b/nixos/modules/services/networking/gnunet.nix @@ -112,12 +112,8 @@ in }; }; - package = mkOption { - type = types.package; - default = pkgs.gnunet; - defaultText = literalExpression "pkgs.gnunet"; - description = lib.mdDoc "Overridable attribute of the gnunet package to use."; - example = literalExpression "pkgs.gnunet_git"; + package = mkPackageOption pkgs "gnunet" { + example = "gnunet_git"; }; extraOptions = mkOption { diff --git a/nixos/modules/services/networking/go-camo.nix b/nixos/modules/services/networking/go-camo.nix new file mode 100644 index 000000000000..cb3b6eade464 --- /dev/null +++ b/nixos/modules/services/networking/go-camo.nix @@ -0,0 +1,73 @@ +{ lib, pkgs, config, ... }: + +let + cfg = config.services.go-camo; + inherit (lib) mkOption mkEnableOption mkIf mkMerge types optionalString; +in +{ + options.services.go-camo = { + enable = mkEnableOption "go-camo service"; + listen = mkOption { + type = types.nullOr types.str; + default = null; + description = "Address:Port to bind to for HTTP (default: 0.0.0.0:8080)."; + apply = v: optionalString (v != null) "--listen=${v}"; + }; + sslListen = mkOption { + type = types.nullOr types.str; + default = null; + description = "Address:Port to bind to for HTTPS."; + apply = v: optionalString (v != null) "--ssl-listen=${v}"; + }; + sslKey = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to TLS private key."; + apply = v: optionalString (v != null) "--ssl-key=${v}"; + }; + sslCert = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to TLS certificate."; + apply = v: optionalString (v != null) "--ssl-cert=${v}"; + }; + keyFile = mkOption { + type = types.path; + default = null; + description = '' + A file containing the HMAC key to use for signing URLs. + The file can contain any string. Can be generated using "openssl rand -base64 18 > the_file". + ''; + }; + extraOptions = mkOption { + type = with types; listOf str; + default = []; + description = "Extra options passed to the go-camo command."; + }; + }; + + config = mkIf cfg.enable { + systemd.services.go-camo = { + description = "go-camo service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + environment = { + GOCAMO_HMAC_FILE = "%d/hmac"; + }; + script = '' + export GOCAMO_HMAC=$(cat "$GOCAMO_HMAC_FILE") + exec ${lib.escapeShellArgs(lib.lists.remove "" ([ "${pkgs.go-camo}/bin/go-camo" cfg.listen cfg.sslListen cfg.sslKey cfg.sslCert ] ++ cfg.extraOptions))} + ''; + serviceConfig = { + NoNewPrivileges = true; + ProtectSystem = "strict"; + DynamicUser = true; + User = "gocamo"; + Group = "gocamo"; + LoadCredential = [ + "hmac:${cfg.keyFile}" + ]; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/gvpe.nix b/nixos/modules/services/networking/gvpe.nix index 2279ceee2f58..558f499022c8 100644 --- a/nixos/modules/services/networking/gvpe.nix +++ b/nixos/modules/services/networking/gvpe.nix @@ -29,7 +29,7 @@ let export PATH=$PATH:${pkgs.iproute2}/sbin - ip link set $IFNAME up + ip link set dev $IFNAME up ip address add ${cfg.ipAddress} dev $IFNAME ip route add ${cfg.subnet} dev $IFNAME diff --git a/nixos/modules/services/networking/haproxy.nix b/nixos/modules/services/networking/haproxy.nix index 208eb356d629..a2f3be6c49ce 100644 --- a/nixos/modules/services/networking/haproxy.nix +++ b/nixos/modules/services/networking/haproxy.nix @@ -19,7 +19,7 @@ with lib; enable = mkEnableOption (lib.mdDoc "HAProxy, the reliable, high performance TCP/HTTP load balancer."); - package = mkPackageOptionMD pkgs "haproxy" { }; + package = mkPackageOption pkgs "haproxy" { }; user = mkOption { type = types.str; diff --git a/nixos/modules/services/networking/harmonia.nix b/nixos/modules/services/networking/harmonia.nix index 144fa6c708e2..b384ac926137 100644 --- a/nixos/modules/services/networking/harmonia.nix +++ b/nixos/modules/services/networking/harmonia.nix @@ -14,7 +14,7 @@ in description = lib.mdDoc "Path to the signing key that will be used for signing the cache"; }; - package = lib.mkPackageOptionMD pkgs "harmonia" { }; + package = lib.mkPackageOption pkgs "harmonia" { }; settings = lib.mkOption { inherit (format) type; @@ -28,6 +28,13 @@ in }; config = lib.mkIf cfg.enable { + nix.settings.extra-allowed-users = [ "harmonia" ]; + users.users.harmonia = { + isSystemUser = true; + group = "harmonia"; + }; + users.groups.harmonia = { }; + systemd.services.harmonia = { description = "harmonia binary cache service"; @@ -48,7 +55,7 @@ in ExecStart = lib.getExe cfg.package; User = "harmonia"; Group = "harmonia"; - DynamicUser = true; + Restart = "on-failure"; PrivateUsers = true; DeviceAllow = [ "" ]; UMask = "0066"; diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix index 03e6f86af53f..0159da37de87 100644 --- a/nixos/modules/services/networking/headscale.nix +++ b/nixos/modules/services/networking/headscale.nix @@ -17,14 +17,7 @@ in { services.headscale = { enable = mkEnableOption (lib.mdDoc "headscale, Open Source coordination server for Tailscale"); - package = mkOption { - type = types.package; - default = pkgs.headscale; - defaultText = literalExpression "pkgs.headscale"; - description = lib.mdDoc '' - Which headscale package to use for the running server. - ''; - }; + package = mkPackageOption pkgs "headscale" { }; user = mkOption { default = "headscale"; @@ -451,10 +444,14 @@ in { tls_letsencrypt_cache_dir = "${dataDir}/.cache"; }; - # 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; + environment = { + # 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. + etc."headscale/config.yaml".source = configFile; + + systemPackages = [ cfg.package ]; + }; users.groups.headscale = mkIf (cfg.group == "headscale") {}; @@ -467,6 +464,7 @@ in { systemd.services.headscale = { description = "headscale coordination server for Tailscale"; + wants = [ "network-online.target" ]; after = ["network-online.target"]; wantedBy = ["multi-user.target"]; restartTriggers = [configFile]; diff --git a/nixos/modules/services/networking/hostapd.nix b/nixos/modules/services/networking/hostapd.nix index ffb154463053..40542155ed63 100644 --- a/nixos/modules/services/networking/hostapd.nix +++ b/nixos/modules/services/networking/hostapd.nix @@ -899,25 +899,6 @@ in { ''; }; }; - - managementFrameProtection = mkOption { - default = "required"; - type = types.enum ["disabled" "optional" "required"]; - apply = x: - getAttr x { - "disabled" = 0; - "optional" = 1; - "required" = 2; - }; - description = mdDoc '' - Management frame protection (MFP) authenticates management frames - to prevent deauthentication (or related) attacks. - - - {var}`"disabled"`: No management frame protection - - {var}`"optional"`: Use MFP if a connection allows it - - {var}`"required"`: Force MFP for all clients - ''; - }; }; config = let @@ -928,7 +909,7 @@ in { in { settings = { ssid = bssCfg.ssid; - utf8_ssid = bssCfg.ssid; + utf8_ssid = bssCfg.utf8Ssid; logger_syslog = mkDefault (-1); logger_syslog_level = bssCfg.logLevel; @@ -943,7 +924,8 @@ in { # IEEE 802.11i (authentication) related configuration # Encrypt management frames to protect against deauthentication and similar attacks - ieee80211w = bssCfg.managementFrameProtection; + ieee80211w = mkDefault 1; + sae_require_mfp = mkDefault 1; # Only allow WPA by default and disable insecure WEP auth_algs = mkDefault 1; @@ -1185,14 +1167,6 @@ in { message = ''hostapd radio ${radio} bss ${bss}: bssid must be specified manually (for now) since this radio uses multiple BSS.''; } { - assertion = auth.mode == "wpa3-sae" -> bssCfg.managementFrameProtection == 2; - message = ''hostapd radio ${radio} bss ${bss}: uses WPA3-SAE which requires managementFrameProtection="required"''; - } - { - assertion = auth.mode == "wpa3-sae-transition" -> bssCfg.managementFrameProtection != 0; - message = ''hostapd radio ${radio} bss ${bss}: uses WPA3-SAE in transition mode with WPA2-SHA256, which requires managementFrameProtection="optional" or ="required"''; - } - { assertion = countWpaPasswordDefinitions <= 1; message = ''hostapd radio ${radio} bss ${bss}: must use at most one WPA password option (wpaPassword, wpaPasswordFile, wpaPskFile)''; } @@ -1223,8 +1197,6 @@ in { environment.systemPackages = [cfg.package]; - services.udev.packages = with pkgs; [crda]; - systemd.services.hostapd = { description = "IEEE 802.11 Host Access-Point Daemon"; diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix index f872daf05b8f..8d9eff61488c 100644 --- a/nixos/modules/services/networking/i2pd.nix +++ b/nixos/modules/services/networking/i2pd.nix @@ -239,19 +239,12 @@ in enable = mkEnableOption (lib.mdDoc "I2Pd daemon") // { description = lib.mdDoc '' Enables I2Pd as a running service upon activation. - Please read http://i2pd.readthedocs.io/en/latest/ for further + Please read <https://i2pd.readthedocs.io/en/latest/> for further configuration help. ''; }; - package = mkOption { - type = types.package; - default = pkgs.i2pd; - defaultText = literalExpression "pkgs.i2pd"; - description = lib.mdDoc '' - i2pd package to use. - ''; - }; + package = mkPackageOption pkgs "i2pd" { }; logLevel = mkOption { type = types.enum ["debug" "info" "warn" "error"]; diff --git a/nixos/modules/services/networking/icecream/daemon.nix b/nixos/modules/services/networking/icecream/daemon.nix index fdd7a139c2fa..48363cc22c36 100644 --- a/nixos/modules/services/networking/icecream/daemon.nix +++ b/nixos/modules/services/networking/icecream/daemon.nix @@ -99,12 +99,7 @@ in { ''; }; - package = mkOption { - default = pkgs.icecream; - defaultText = literalExpression "pkgs.icecream"; - type = types.package; - description = lib.mdDoc "Icecream package to use."; - }; + package = mkPackageOption pkgs "icecream" { }; extraArgs = mkOption { type = types.listOf types.str; diff --git a/nixos/modules/services/networking/icecream/scheduler.nix b/nixos/modules/services/networking/icecream/scheduler.nix index 33aee1bb19cc..2d53282ba88f 100644 --- a/nixos/modules/services/networking/icecream/scheduler.nix +++ b/nixos/modules/services/networking/icecream/scheduler.nix @@ -54,12 +54,7 @@ in { ''; }; - package = mkOption { - default = pkgs.icecream; - defaultText = literalExpression "pkgs.icecream"; - type = types.package; - description = lib.mdDoc "Icecream package to use."; - }; + package = mkPackageOption pkgs "icecream" { }; extraArgs = mkOption { type = types.listOf types.str; diff --git a/nixos/modules/services/networking/ircd-hybrid/builder.sh b/nixos/modules/services/networking/ircd-hybrid/builder.sh index d9d2e4264dfd..07a3788abf7d 100644 --- a/nixos/modules/services/networking/ircd-hybrid/builder.sh +++ b/nixos/modules/services/networking/ircd-hybrid/builder.sh @@ -1,4 +1,4 @@ -if [ -e .attrs.sh ]; then source .attrs.sh; fi +if [ -e "$NIX_ATTRS_SH_FILE" ]; then . "$NIX_ATTRS_SH_FILE"; elif [ -f .attrs.sh ]; then . .attrs.sh; fi source $stdenv/setup doSub() { diff --git a/nixos/modules/services/networking/ircd-hybrid/default.nix b/nixos/modules/services/networking/ircd-hybrid/default.nix index 554b0f7bb8b4..64a34cc52d25 100644 --- a/nixos/modules/services/networking/ircd-hybrid/default.nix +++ b/nixos/modules/services/networking/ircd-hybrid/default.nix @@ -125,7 +125,8 @@ in systemd.services.ircd-hybrid = { description = "IRCD Hybrid server"; - after = [ "started networking" ]; + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; script = "${ircdService}/bin/control start"; }; diff --git a/nixos/modules/services/networking/iscsi/initiator.nix b/nixos/modules/services/networking/iscsi/initiator.nix index 9c71a988f29c..2d802d8cfc70 100644 --- a/nixos/modules/services/networking/iscsi/initiator.nix +++ b/nixos/modules/services/networking/iscsi/initiator.nix @@ -19,12 +19,7 @@ in description = lib.mdDoc "Name of this iscsi initiator"; example = "iqn.2020-08.org.linux-iscsi.initiatorhost:example"; }; - package = mkOption { - type = package; - description = lib.mdDoc "openiscsi package to use"; - default = pkgs.openiscsi; - defaultText = literalExpression "pkgs.openiscsi"; - }; + package = mkPackageOption pkgs "openiscsi" { }; extraConfig = mkOption { type = str; @@ -52,25 +47,27 @@ in ''; environment.etc."iscsi/initiatorname.iscsi".text = "InitiatorName=${cfg.name}"; - system.activationScripts.iscsid = let - extraCfgDumper = optionalString (cfg.extraConfigFile != null) '' - if [ -f "${cfg.extraConfigFile}" ]; then - printf "\n# The following is from ${cfg.extraConfigFile}:\n" - cat "${cfg.extraConfigFile}" - else - echo "Warning: services.openiscsi.extraConfigFile ${cfg.extraConfigFile} does not exist!" >&2 - fi - ''; - in '' - ( - cat ${config.environment.etc."iscsi/iscsid.conf.fragment".source} - ${extraCfgDumper} - ) > /etc/iscsi/iscsid.conf - ''; - systemd.packages = [ cfg.package ]; - systemd.services."iscsid".wantedBy = [ "multi-user.target" ]; + systemd.services."iscsid" = { + wantedBy = [ "multi-user.target" ]; + preStart = + let + extraCfgDumper = optionalString (cfg.extraConfigFile != null) '' + if [ -f "${cfg.extraConfigFile}" ]; then + printf "\n# The following is from ${cfg.extraConfigFile}:\n" + cat "${cfg.extraConfigFile}" + else + echo "Warning: services.openiscsi.extraConfigFile ${cfg.extraConfigFile} does not exist!" >&2 + fi + ''; + in '' + ( + cat ${config.environment.etc."iscsi/iscsid.conf.fragment".source} + ${extraCfgDumper} + ) > /etc/iscsi/iscsid.conf + ''; + }; systemd.sockets."iscsid".wantedBy = [ "sockets.target" ]; systemd.services."iscsi" = mkIf cfg.enableAutoLoginOut { diff --git a/nixos/modules/services/networking/ivpn.nix b/nixos/modules/services/networking/ivpn.nix index 6df630c1f194..6c9ae599e670 100644 --- a/nixos/modules/services/networking/ivpn.nix +++ b/nixos/modules/services/networking/ivpn.nix @@ -27,7 +27,7 @@ with lib; systemd.services.ivpn-service = { description = "iVPN daemon"; wantedBy = [ "multi-user.target" ]; - wants = [ "network.target" ]; + wants = [ "network.target" "network-online.target" ]; after = [ "network-online.target" "NetworkManager.service" diff --git a/nixos/modules/services/networking/iwd.nix b/nixos/modules/services/networking/iwd.nix index 993a603c1ed5..d46c1a69a619 100644 --- a/nixos/modules/services/networking/iwd.nix +++ b/nixos/modules/services/networking/iwd.nix @@ -2,7 +2,7 @@ let inherit (lib) - mkEnableOption mkIf mkOption types + mkEnableOption mkPackageOption mkIf mkOption types recursiveUpdate; cfg = config.networking.wireless.iwd; @@ -19,14 +19,7 @@ in options.networking.wireless.iwd = { enable = mkEnableOption (lib.mdDoc "iwd"); - package = mkOption { - type = types.package; - default = pkgs.iwd; - defaultText = lib.literalExpression "pkgs.iwd"; - description = lib.mdDoc '' - The iwd package to use. - ''; - }; + package = mkPackageOption pkgs "iwd" { }; settings = mkOption { type = ini.type; @@ -71,8 +64,10 @@ in }; systemd.services.iwd = { + path = [ config.networking.resolvconf.package ]; wantedBy = [ "multi-user.target" ]; restartTriggers = [ configFile ]; + serviceConfig.ReadWritePaths = "-/etc/resolv.conf"; }; }; diff --git a/nixos/modules/services/networking/jibri/default.nix b/nixos/modules/services/networking/jibri/default.nix index a931831fc281..dfba38896a91 100644 --- a/nixos/modules/services/networking/jibri/default.nix +++ b/nixos/modules/services/networking/jibri/default.nix @@ -5,12 +5,7 @@ with lib; let cfg = config.services.jibri; - # Copied from the jitsi-videobridge.nix file. - toHOCON = x: - if isAttrs x && x ? __hocon_envvar then ("\${" + x.__hocon_envvar + "}") - else if isAttrs x then "{${ concatStringsSep "," (mapAttrsToList (k: v: ''"${k}":${toHOCON v}'') x) }}" - else if isList x then "[${ concatMapStringsSep "," toHOCON x }]" - else builtins.toJSON x; + format = pkgs.formats.hocon { }; # We're passing passwords in environment variables that have names generated # from an attribute name, which may not be a valid bash identifier. @@ -38,13 +33,13 @@ let control-login = { domain = env.control.login.domain; username = env.control.login.username; - password.__hocon_envvar = toVarName "${name}_control"; + password = format.lib.mkSubstitution (toVarName "${name}_control"); }; call-login = { domain = env.call.login.domain; username = env.call.login.username; - password.__hocon_envvar = toVarName "${name}_call"; + password = format.lib.mkSubstitution (toVarName "${name}_call"); }; strip-from-room-domain = env.stripFromRoomDomain; @@ -85,13 +80,13 @@ let }; # Allow overriding leaves of the default config despite types.attrs not doing any merging. jibriConfig = recursiveUpdate defaultJibriConfig cfg.config; - configFile = pkgs.writeText "jibri.conf" (toHOCON { jibri = jibriConfig; }); + configFile = format.generate "jibri.conf" { jibri = jibriConfig; }; in { options.services.jibri = with types; { enable = mkEnableOption (lib.mdDoc "Jitsi BRoadcasting Infrastructure. Currently Jibri must be run on a host that is also running {option}`services.jitsi-meet.enable`, so for most use cases it will be simpler to run {option}`services.jitsi-meet.jibri.enable`"); config = mkOption { - type = attrs; + type = format.type; default = { }; description = lib.mdDoc '' Jibri configuration. @@ -395,11 +390,11 @@ in }; }; - systemd.tmpfiles.rules = [ - "d /var/log/jitsi/jibri 755 jibri jibri" - ]; - - + systemd.tmpfiles.settings."10-jibri"."/var/log/jitsi/jibri".d = { + user = "jibri"; + group = "jibri"; + mode = "755"; + }; # Configure Chromium to not show the "Chrome is being controlled by automatic test software" message. environment.etc."chromium/policies/managed/managed_policies.json".text = builtins.toJSON { CommandLineFlagSecurityWarningsEnabled = false; }; diff --git a/nixos/modules/services/networking/jicofo.nix b/nixos/modules/services/networking/jicofo.nix index 0886bbe004c4..380344c8eaa1 100644 --- a/nixos/modules/services/networking/jicofo.nix +++ b/nixos/modules/services/networking/jicofo.nix @@ -5,14 +5,9 @@ with lib; let cfg = config.services.jicofo; - # HOCON is a JSON superset that some jitsi-meet components use for configuration - toHOCON = x: if isAttrs x && x ? __hocon_envvar then ("\${" + x.__hocon_envvar + "}") - else if isAttrs x && x ? __hocon_unquoted_string then x.__hocon_unquoted_string - else if isAttrs x then "{${ concatStringsSep "," (mapAttrsToList (k: v: ''"${k}":${toHOCON v}'') x) }}" - else if isList x then "[${ concatMapStringsSep "," toHOCON x }]" - else builtins.toJSON x; - - configFile = pkgs.writeText "jicofo.conf" (toHOCON cfg.config); + format = pkgs.formats.hocon { }; + + configFile = format.generate "jicofo.conf" cfg.config; in { options.services.jicofo = with types; { @@ -77,7 +72,7 @@ in }; config = mkOption { - type = (pkgs.formats.json {}).type; + type = format.type; default = { }; example = literalExpression '' { @@ -99,7 +94,7 @@ in hostname = cfg.xmppHost; username = cfg.userName; domain = cfg.userDomain; - password = { __hocon_envvar = "JICOFO_AUTH_PASS"; }; + password = format.lib.mkSubstitution "JICOFO_AUTH_PASS"; xmpp-domain = if cfg.xmppDomain == null then cfg.xmppHost else cfg.xmppDomain; }; service = client; diff --git a/nixos/modules/services/networking/jigasi.nix b/nixos/modules/services/networking/jigasi.nix new file mode 100644 index 000000000000..e701689031b1 --- /dev/null +++ b/nixos/modules/services/networking/jigasi.nix @@ -0,0 +1,237 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.jigasi; + homeDirName = "jigasi-home"; + stateDir = "/tmp"; + sipCommunicatorPropertiesFile = "${stateDir}/${homeDirName}/sip-communicator.properties"; + sipCommunicatorPropertiesFileUnsubstituted = "${pkgs.jigasi}/etc/jitsi/jigasi/sip-communicator.properties"; +in +{ + options.services.jigasi = with types; { + enable = mkEnableOption "Jitsi Gateway to SIP - component of Jitsi Meet"; + + xmppHost = mkOption { + type = str; + example = "localhost"; + description = '' + Hostname of the XMPP server to connect to. + ''; + }; + + xmppDomain = mkOption { + type = nullOr str; + example = "meet.example.org"; + description = '' + Domain name of the XMMP server to which to connect as a component. + + If null, <option>xmppHost</option> is used. + ''; + }; + + componentPasswordFile = mkOption { + type = str; + example = "/run/keys/jigasi-component"; + description = '' + Path to file containing component secret. + ''; + }; + + userName = mkOption { + type = str; + default = "callcontrol"; + description = '' + User part of the JID for XMPP user connection. + ''; + }; + + userDomain = mkOption { + type = str; + example = "internal.meet.example.org"; + description = '' + Domain part of the JID for XMPP user connection. + ''; + }; + + userPasswordFile = mkOption { + type = str; + example = "/run/keys/jigasi-user"; + description = '' + Path to file containing password for XMPP user connection. + ''; + }; + + bridgeMuc = mkOption { + type = str; + example = "jigasibrewery@internal.meet.example.org"; + description = '' + JID of the internal MUC used to communicate with Videobridges. + ''; + }; + + defaultJvbRoomName = mkOption { + type = str; + default = ""; + example = "siptest"; + description = '' + Name of the default JVB room that will be joined if no special header is included in SIP invite. + ''; + }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + File containing environment variables to be passed to the jigasi service, + in which secret tokens can be specified securely by defining values for + <literal>JIGASI_SIPUSER</literal>, + <literal>JIGASI_SIPPWD</literal>, + <literal>JIGASI_SIPSERVER</literal> and + <literal>JIGASI_SIPPORT</literal>. + ''; + }; + + config = mkOption { + type = attrsOf str; + default = { }; + example = literalExpression '' + { + "org.jitsi.jigasi.auth.URL" = "XMPP:jitsi-meet.example.com"; + } + ''; + description = '' + Contents of the <filename>sip-communicator.properties</filename> configuration file for jigasi. + ''; + }; + }; + + config = mkIf cfg.enable { + services.jicofo.config = { + "org.jitsi.jicofo.jigasi.BREWERY" = "${cfg.bridgeMuc}"; + }; + + services.jigasi.config = mapAttrs (_: v: mkDefault v) { + "org.jitsi.jigasi.BRIDGE_MUC" = cfg.bridgeMuc; + }; + + users.groups.jitsi-meet = {}; + + systemd.services.jigasi = let + jigasiProps = { + "-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION" = "${stateDir}"; + "-Dnet.java.sip.communicator.SC_HOME_DIR_NAME" = "${homeDirName}"; + "-Djava.util.logging.config.file" = "${pkgs.jigasi}/etc/jitsi/jigasi/logging.properties"; + }; + in + { + description = "Jitsi Gateway to SIP"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + preStart = '' + [ -f "${sipCommunicatorPropertiesFile}" ] && rm -f "${sipCommunicatorPropertiesFile}" + mkdir -p "$(dirname ${sipCommunicatorPropertiesFile})" + temp="${sipCommunicatorPropertiesFile}.unsubstituted" + + export DOMAIN_BASE="${cfg.xmppDomain}" + export JIGASI_XMPP_PASSWORD=$(cat "${cfg.userPasswordFile}") + export JIGASI_DEFAULT_JVB_ROOM_NAME="${cfg.defaultJvbRoomName}" + + # encode the credentials to base64 + export JIGASI_SIPPWD=$(echo -n "$JIGASI_SIPPWD" | base64 -w 0) + export JIGASI_XMPP_PASSWORD_BASE64=$(cat "${cfg.userPasswordFile}" | base64 -w 0) + + cp "${sipCommunicatorPropertiesFileUnsubstituted}" "$temp" + chmod 644 "$temp" + cat <<EOF >>"$temp" + net.java.sip.communicator.impl.protocol.sip.acc1403273890647.SERVER_PORT=$JIGASI_SIPPORT + net.java.sip.communicator.impl.protocol.sip.acc1403273890647.PREFERRED_TRANSPORT=udp + EOF + chmod 444 "$temp" + + # Replace <<$VAR_NAME>> from example config to $VAR_NAME for environment substitution + sed -i -E \ + 's/<<([^>]+)>>/\$\1/g' \ + "$temp" + + sed -i \ + 's|\(net\.java\.sip\.communicator\.impl\.protocol\.jabber\.acc-xmpp-1\.PASSWORD=\).*|\1\$JIGASI_XMPP_PASSWORD_BASE64|g' \ + "$temp" + + sed -i \ + 's|\(#\)\(org.jitsi.jigasi.DEFAULT_JVB_ROOM_NAME=\).*|\2\$JIGASI_DEFAULT_JVB_ROOM_NAME|g' \ + "$temp" + + ${pkgs.envsubst}/bin/envsubst \ + -o "${sipCommunicatorPropertiesFile}" \ + -i "$temp" + + # Set the brewery room name + sed -i \ + 's|\(net\.java\.sip\.communicator\.impl\.protocol\.jabber\.acc-xmpp-1\.BREWERY=\).*|\1${cfg.bridgeMuc}|g' \ + "${sipCommunicatorPropertiesFile}" + sed -i \ + 's|\(org\.jitsi\.jigasi\.ALLOWED_JID=\).*|\1${cfg.bridgeMuc}|g' \ + "${sipCommunicatorPropertiesFile}" + + + # Disable certificate verification for self-signed certificates + sed -i \ + 's|\(# \)\(net.java.sip.communicator.service.gui.ALWAYS_TRUST_MODE_ENABLED=true\)|\2|g' \ + "${sipCommunicatorPropertiesFile}" + ''; + + restartTriggers = [ + config.environment.etc."jitsi/jigasi/sip-communicator.properties".source + ]; + environment.JAVA_SYS_PROPS = concatStringsSep " " (mapAttrsToList (k: v: "${k}=${toString v}") jigasiProps); + + script = '' + ${pkgs.jigasi}/bin/jigasi \ + --host="${cfg.xmppHost}" \ + --domain="${if cfg.xmppDomain == null then cfg.xmppHost else cfg.xmppDomain}" \ + --secret="$(cat ${cfg.componentPasswordFile})" \ + --user_name="${cfg.userName}" \ + --user_domain="${cfg.userDomain}" \ + --user_password="$(cat ${cfg.userPasswordFile})" \ + --configdir="${stateDir}" \ + --configdirname="${homeDirName}" + ''; + + serviceConfig = { + Type = "exec"; + + DynamicUser = true; + User = "jigasi"; + Group = "jitsi-meet"; + + CapabilityBoundingSet = ""; + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; + RestrictNamespaces = true; + LockPersonality = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + StateDirectory = baseNameOf stateDir; + EnvironmentFile = cfg.environmentFile; + }; + }; + + environment.etc."jitsi/jigasi/sip-communicator.properties".source = + mkDefault "${sipCommunicatorPropertiesFile}"; + environment.etc."jitsi/jigasi/logging.properties".source = + mkDefault "${stateDir}/logging.properties-journal"; + }; + + meta.maintainers = lib.teams.jitsi.members; +} diff --git a/nixos/modules/services/networking/jitsi-videobridge.nix b/nixos/modules/services/networking/jitsi-videobridge.nix index 37b0b1e5bf50..00ea5b9da546 100644 --- a/nixos/modules/services/networking/jitsi-videobridge.nix +++ b/nixos/modules/services/networking/jitsi-videobridge.nix @@ -6,16 +6,7 @@ let cfg = config.services.jitsi-videobridge; attrsToArgs = a: concatStringsSep " " (mapAttrsToList (k: v: "${k}=${toString v}") a); - # HOCON is a JSON superset that videobridge2 uses for configuration. - # It can substitute environment variables which we use for passwords here. - # https://github.com/lightbend/config/blob/master/README.md - # - # Substitution for environment variable FOO is represented as attribute set - # { __hocon_envvar = "FOO"; } - toHOCON = x: if isAttrs x && x ? __hocon_envvar then ("\${" + x.__hocon_envvar + "}") - else if isAttrs x then "{${ concatStringsSep "," (mapAttrsToList (k: v: ''"${k}":${toHOCON v}'') x) }}" - else if isList x then "[${ concatMapStringsSep "," toHOCON x }]" - else builtins.toJSON x; + format = pkgs.formats.hocon { }; # We're passing passwords in environment variables that have names generated # from an attribute name, which may not be a valid bash identifier. @@ -38,7 +29,7 @@ let hostname = xmppConfig.hostName; domain = xmppConfig.domain; username = xmppConfig.userName; - password = { __hocon_envvar = toVarName name; }; + password = format.lib.mkSubstitution (toVarName name); muc_jids = xmppConfig.mucJids; muc_nickname = xmppConfig.mucNickname; disable_certificate_verification = xmppConfig.disableCertificateVerification; @@ -221,7 +212,7 @@ in "-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION" = "/etc/jitsi"; "-Dnet.java.sip.communicator.SC_HOME_DIR_NAME" = "videobridge"; "-Djava.util.logging.config.file" = "/etc/jitsi/videobridge/logging.properties"; - "-Dconfig.file" = pkgs.writeText "jvb.conf" (toHOCON jvbConfig); + "-Dconfig.file" = format.generate "jvb.conf" jvbConfig; # Mitigate CVE-2021-44228 "-Dlog4j2.formatMsgNoLookups" = true; } // (mapAttrs' (k: v: nameValuePair "-D${k}" v) cfg.extraProperties); diff --git a/nixos/modules/services/networking/kea.nix b/nixos/modules/services/networking/kea.nix index 2f922a026a3a..656ddd41fd12 100644 --- a/nixos/modules/services/networking/kea.nix +++ b/nixos/modules/services/networking/kea.nix @@ -254,6 +254,8 @@ in DynamicUser = true; User = "kea"; ConfigurationDirectory = "kea"; + RuntimeDirectory = "kea"; + RuntimeDirectoryPreserve = true; StateDirectory = "kea"; UMask = "0077"; }; @@ -288,8 +290,8 @@ in ]; environment = { - KEA_PIDFILE_DIR = "/run/kea-ctrl-agent"; - KEA_LOCKFILE_DIR = "/run/kea-ctrl-agent"; + KEA_PIDFILE_DIR = "/run/kea"; + KEA_LOCKFILE_DIR = "/run/kea"; }; restartTriggers = [ @@ -300,7 +302,6 @@ in ExecStart = "${package}/bin/kea-ctrl-agent -c /etc/kea/ctrl-agent.conf ${lib.escapeShellArgs cfg.ctrl-agent.extraArgs}"; KillMode = "process"; Restart = "on-failure"; - RuntimeDirectory = "kea-ctrl-agent"; } // commonServiceConfig; }; }) @@ -324,13 +325,16 @@ in "network-online.target" "time-sync.target" ]; + wants = [ + "network-online.target" + ]; wantedBy = [ "multi-user.target" ]; environment = { - KEA_PIDFILE_DIR = "/run/kea-dhcp4"; - KEA_LOCKFILE_DIR = "/run/kea-dhcp4"; + KEA_PIDFILE_DIR = "/run/kea"; + KEA_LOCKFILE_DIR = "/run/kea"; }; restartTriggers = [ @@ -348,7 +352,6 @@ in "CAP_NET_BIND_SERVICE" "CAP_NET_RAW" ]; - RuntimeDirectory = "kea-dhcp4"; } // commonServiceConfig; }; }) @@ -372,13 +375,16 @@ in "network-online.target" "time-sync.target" ]; + wants = [ + "network-online.target" + ]; wantedBy = [ "multi-user.target" ]; environment = { - KEA_PIDFILE_DIR = "/run/kea-dhcp6"; - KEA_LOCKFILE_DIR = "/run/kea-dhcp6"; + KEA_PIDFILE_DIR = "/run/kea"; + KEA_LOCKFILE_DIR = "/run/kea"; }; restartTriggers = [ @@ -394,7 +400,6 @@ in CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; - RuntimeDirectory = "kea-dhcp6"; } // commonServiceConfig; }; }) @@ -414,6 +419,7 @@ in "https://kea.readthedocs.io/en/kea-${package.version}/arm/ddns.html" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" "time-sync.target" @@ -423,8 +429,8 @@ in ]; environment = { - KEA_PIDFILE_DIR = "/run/kea-dhcp-ddns"; - KEA_LOCKFILE_DIR = "/run/kea-dhcp-ddns"; + KEA_PIDFILE_DIR = "/run/kea"; + KEA_LOCKFILE_DIR = "/run/kea"; }; restartTriggers = [ @@ -439,7 +445,6 @@ in CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; - RuntimeDirectory = "kea-dhcp-ddns"; } // commonServiceConfig; }; }) diff --git a/nixos/modules/services/networking/keepalived/default.nix b/nixos/modules/services/networking/keepalived/default.nix index 29fbea5545c3..599dfd52e271 100644 --- a/nixos/modules/services/networking/keepalived/default.nix +++ b/nixos/modules/services/networking/keepalived/default.nix @@ -59,9 +59,11 @@ let ${optionalString i.vmacXmitBase "vmac_xmit_base"} ${optionalString (i.unicastSrcIp != null) "unicast_src_ip ${i.unicastSrcIp}"} - unicast_peer { - ${concatStringsSep "\n" i.unicastPeers} - } + ${optionalString (builtins.length i.unicastPeers > 0) '' + unicast_peer { + ${concatStringsSep "\n" i.unicastPeers} + } + ''} virtual_ipaddress { ${concatMapStringsSep "\n" virtualIpLine i.virtualIps} @@ -138,6 +140,7 @@ let in { + meta.maintainers = [ lib.maintainers.raitobezarius ]; options = { services.keepalived = { @@ -150,6 +153,14 @@ in ''; }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether to automatically allow VRRP and AH packets in the firewall. + ''; + }; + enableScriptSecurity = mkOption { type = types.bool; default = false; @@ -282,6 +293,19 @@ in assertions = flatten (map vrrpInstanceAssertions vrrpInstances); + networking.firewall = lib.mkIf cfg.openFirewall { + extraCommands = '' + # Allow VRRP and AH packets + ip46tables -A nixos-fw -p vrrp -m comment --comment "services.keepalived.openFirewall" -j ACCEPT + ip46tables -A nixos-fw -p ah -m comment --comment "services.keepalived.openFirewall" -j ACCEPT + ''; + + extraStopCommands = '' + ip46tables -D nixos-fw -p vrrp -m comment --comment "services.keepalived.openFirewall" -j ACCEPT + ip46tables -D nixos-fw -p ah -m comment --comment "services.keepalived.openFirewall" -j ACCEPT + ''; + }; + systemd.timers.keepalived-boot-delay = { description = "Keepalive Daemon delay to avoid instant transition to MASTER state"; after = [ "network.target" "network-online.target" "syslog.target" ]; diff --git a/nixos/modules/services/networking/knot.nix b/nixos/modules/services/networking/knot.nix index 4f6ac945cf97..6488a159b3b7 100644 --- a/nixos/modules/services/networking/knot.nix +++ b/nixos/modules/services/networking/knot.nix @@ -1,8 +1,36 @@ -{ config, lib, pkgs, ... }: +{ config, lib, pkgs, utils, ... }: -with lib; let + inherit (lib) + attrNames + concatMapStrings + concatMapStringsSep + concatStrings + concatStringsSep + elem + filter + flip + hasAttr + hasPrefix + isAttrs + isBool + isDerivation + isList + mapAttrsToList + mkChangedOptionModule + mkEnableOption + mkIf + mkOption + mkPackageOption + optionals + types + ; + + inherit (utils) + escapeSystemdExecArgs + ; + cfg = config.services.knot; yamlConfig = let @@ -44,6 +72,7 @@ let ++ [ (sec_list_fa "id" nix_def "template") ] ++ [ (sec_list_fa "domain" nix_def "zone") ] ++ [ (sec_plain nix_def "include") ] + ++ [ (sec_plain nix_def "clear") ] ); # A plain section contains directly attributes (we don't really check that ATM). @@ -112,8 +141,7 @@ let mkConfigFile = configString: pkgs.writeTextFile { name = "knot.conf"; text = (concatMapStringsSep "\n" (file: "include: ${file}") cfg.keyFiles) + "\n" + configString; - # TODO: maybe we could do some checks even when private keys complicate this? - checkPhase = lib.optionalString (cfg.keyFiles == []) '' + checkPhase = lib.optionalString cfg.checkConfig '' ${cfg.package}/bin/knotc --config=$out conf-check ''; }; @@ -141,12 +169,45 @@ let in { options = { services.knot = { - enable = mkEnableOption (lib.mdDoc "Knot authoritative-only DNS server"); + enable = mkEnableOption "Knot authoritative-only DNS server"; + + enableXDP = mkOption { + type = types.bool; + default = lib.hasAttrByPath [ "xdp" "listen" ] cfg.settings; + defaultText = '' + Enabled when the `xdp.listen` setting is configured through `settings`. + ''; + example = true; + description = '' + Extends the systemd unit with permissions to allow for the use of + the eXpress Data Path (XDP). + + ::: {.note} + Make sure to read up on functional [limitations](https://www.knot-dns.cz/docs/latest/singlehtml/index.html#mode-xdp-limitations) + when running in XDP mode. + ::: + ''; + }; + + checkConfig = mkOption { + type = types.bool; + # TODO: maybe we could do some checks even when private keys complicate this? + # conf-check fails hard on missing IPs/devices with XDP + default = cfg.keyFiles == [] && !cfg.enableXDP; + defaultText = '' + Disabled when the config uses `keyFiles` or `enableXDP`. + ''; + example = false; + description = '' + Toggles the configuration test at build time. It runs in a + sandbox, and therefore cannot be used in all scenarios. + ''; + }; extraArgs = mkOption { type = types.listOf types.str; default = []; - description = lib.mdDoc '' + description = '' List of additional command line parameters for knotd ''; }; @@ -154,7 +215,7 @@ in { keyFiles = mkOption { type = types.listOf types.path; default = []; - description = lib.mdDoc '' + description = '' A list of files containing additional configuration to be included using the include directive. This option allows to include configuration like TSIG keys without @@ -167,7 +228,7 @@ in { settings = mkOption { type = types.attrs; default = {}; - description = lib.mdDoc '' + description = '' Extra configuration as nix values. ''; }; @@ -175,21 +236,14 @@ in { settingsFile = mkOption { type = types.nullOr types.path; default = null; - description = lib.mdDoc '' + description = '' As alternative to ``settings``, you can provide whole configuration directly in the almost-YAML format of Knot DNS. You might want to utilize ``pkgs.writeText "knot.conf" "longConfigString"`` for this. ''; }; - package = mkOption { - type = types.package; - default = pkgs.knot-dns; - defaultText = literalExpression "pkgs.knot-dns"; - description = lib.mdDoc '' - Which Knot DNS package to use - ''; - }; + package = mkPackageOption pkgs "knot-dns" { }; }; }; imports = [ @@ -216,19 +270,35 @@ in { wants = [ "network.target" ]; after = ["network.target" ]; - serviceConfig = { + serviceConfig = let + # https://www.knot-dns.cz/docs/3.3/singlehtml/index.html#pre-requisites + xdpCapabilities = lib.optionals (cfg.enableXDP) [ + "CAP_NET_ADMIN" + "CAP_NET_RAW" + "CAP_SYS_ADMIN" + "CAP_IPC_LOCK" + ] ++ lib.optionals (lib.versionOlder config.boot.kernelPackages.kernel.version "5.11") [ + "CAP_SYS_RESOURCE" + ]; + in { Type = "notify"; - ExecStart = "${cfg.package}/bin/knotd --config=${configFile} --socket=${socketFile} ${concatStringsSep " " cfg.extraArgs}"; - ExecReload = "${knot-cli-wrappers}/bin/knotc reload"; + ExecStart = escapeSystemdExecArgs ([ + (lib.getExe cfg.package) + "--config=${configFile}" + "--socket=${socketFile}" + ] ++ cfg.extraArgs); + ExecReload = escapeSystemdExecArgs [ + "${knot-cli-wrappers}/bin/knotc" "reload" + ]; User = "knot"; Group = "knot"; AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" - ]; + ] ++ xdpCapabilities; CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" - ]; + ] ++ xdpCapabilities; DeviceAllow = ""; DevicePolicy = "closed"; LockPersonality = true; @@ -253,6 +323,9 @@ in { "AF_INET" "AF_INET6" "AF_UNIX" + ] ++ optionals (cfg.enableXDP) [ + "AF_NETLINK" + "AF_XDP" ]; RestrictNamespaces = true; RestrictRealtime =true; @@ -264,6 +337,8 @@ in { SystemCallFilter = [ "@system-service" "~@privileged" + ] ++ optionals (cfg.enableXDP) [ + "bpf" ]; UMask = "0077"; }; diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix index 3ad757133a60..307414abf170 100644 --- a/nixos/modules/services/networking/kresd.nix +++ b/nixos/modules/services/networking/kresd.nix @@ -11,7 +11,7 @@ let mkListen = kind: addr: let al_v4 = builtins.match "([0-9.]+):([0-9]+)($)" addr; al_v6 = builtins.match "\\[(.+)]:([0-9]+)(%.*|$)" addr; - al_portOnly = 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 ]; @@ -57,14 +57,8 @@ in { and give commands interactively to kresd@1.service. ''; }; - package = mkOption { - type = types.package; - description = lib.mdDoc '' - knot-resolver package to use. - ''; - default = pkgs.knot-resolver; - defaultText = literalExpression "pkgs.knot-resolver"; - example = literalExpression "pkgs.knot-resolver.override { extraFeatures = true; }"; + package = mkPackageOption pkgs "knot-resolver" { + example = "knot-resolver.override { extraFeatures = true; }"; }; extraConfig = mkOption { type = types.lines; diff --git a/nixos/modules/services/networking/lambdabot.nix b/nixos/modules/services/networking/lambdabot.nix index 8609bc971962..01914097ad72 100644 --- a/nixos/modules/services/networking/lambdabot.nix +++ b/nixos/modules/services/networking/lambdabot.nix @@ -24,12 +24,7 @@ in description = lib.mdDoc "Enable the Lambdabot IRC bot"; }; - package = mkOption { - type = types.package; - default = pkgs.lambdabot; - defaultText = literalExpression "pkgs.lambdabot"; - description = lib.mdDoc "Used lambdabot package"; - }; + package = mkPackageOption pkgs "lambdabot" { }; script = mkOption { type = types.str; diff --git a/nixos/modules/services/networking/legit.nix b/nixos/modules/services/networking/legit.nix index 90234f3955e8..ff8e0dd4f93c 100644 --- a/nixos/modules/services/networking/legit.nix +++ b/nixos/modules/services/networking/legit.nix @@ -7,7 +7,7 @@ let mdDoc mkIf mkOption - mkPackageOptionMD + mkPackageOption optionalAttrs optional types; @@ -25,7 +25,7 @@ in options.services.legit = { enable = mkEnableOption (mdDoc "legit git web frontend"); - package = mkPackageOptionMD pkgs "legit-web" { }; + package = mkPackageOption pkgs "legit-web" { }; user = mkOption { type = types.str; diff --git a/nixos/modules/services/networking/libreswan.nix b/nixos/modules/services/networking/libreswan.nix index db4d2f7f0ba0..a44cac93d5f6 100644 --- a/nixos/modules/services/networking/libreswan.nix +++ b/nixos/modules/services/networking/libreswan.nix @@ -133,9 +133,6 @@ in "ipsec.d/01-nixos.conf".source = configFile; } // policyFiles; - # Create NSS database directory - systemd.tmpfiles.rules = [ "d /var/lib/ipsec/nss 755 root root -" ]; - systemd.services.ipsec = { description = "Internet Key Exchange (IKE) Protocol Daemon for IPsec"; wantedBy = [ "multi-user.target" ]; @@ -153,6 +150,10 @@ in echo 0 | tee /proc/sys/net/ipv4/conf/*/send_redirects echo 0 | tee /proc/sys/net/ipv{4,6}/conf/*/accept_redirects ''; + serviceConfig = { + StateDirectory = "ipsec/nss"; + StateDirectoryMode = 0700; + }; }; }; diff --git a/nixos/modules/services/networking/lokinet.nix b/nixos/modules/services/networking/lokinet.nix index f6bc314ed260..8f64d3f0119f 100644 --- a/nixos/modules/services/networking/lokinet.nix +++ b/nixos/modules/services/networking/lokinet.nix @@ -9,12 +9,7 @@ in with lib; { options.services.lokinet = { enable = mkEnableOption (lib.mdDoc "Lokinet daemon"); - package = mkOption { - type = types.package; - default = pkgs.lokinet; - defaultText = literalExpression "pkgs.lokinet"; - description = lib.mdDoc "Lokinet package to use."; - }; + package = mkPackageOption pkgs "lokinet" { }; useLocally = mkOption { type = types.bool; diff --git a/nixos/modules/services/networking/miniupnpd.nix b/nixos/modules/services/networking/miniupnpd.nix index 64aacaf35040..116298dc6b1d 100644 --- a/nixos/modules/services/networking/miniupnpd.nix +++ b/nixos/modules/services/networking/miniupnpd.nix @@ -13,8 +13,17 @@ let listening_ip=${range} '') cfg.internalIPs} + ${lib.optionalString (firewall == "nftables") '' + upnp_table_name=miniupnpd + upnp_nat_table_name=miniupnpd + ''} + ${cfg.appendConfig} ''; + firewall = if config.networking.nftables.enable then "nftables" else "iptables"; + miniupnpd = pkgs.miniupnpd.override { inherit firewall; }; + firewallScripts = lib.optionals (firewall == "iptables") + ([ "iptables"] ++ lib.optional (config.networking.enableIPv6) "ip6tables"); in { options = { @@ -57,20 +66,50 @@ in }; config = mkIf cfg.enable { - networking.firewall.extraCommands = '' - ${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_init.sh -i ${cfg.externalInterface} - ''; + networking.firewall.extraCommands = lib.mkIf (firewallScripts != []) (builtins.concatStringsSep "\n" (map (fw: '' + EXTIF=${cfg.externalInterface} ${pkgs.bash}/bin/bash -x ${miniupnpd}/etc/miniupnpd/${fw}_init.sh + '') firewallScripts)); + + networking.firewall.extraStopCommands = lib.mkIf (firewallScripts != []) (builtins.concatStringsSep "\n" (map (fw: '' + EXTIF=${cfg.externalInterface} ${pkgs.bash}/bin/bash -x ${miniupnpd}/etc/miniupnpd/${fw}_removeall.sh + '') firewallScripts)); - networking.firewall.extraStopCommands = '' - ${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_removeall.sh -i ${cfg.externalInterface} - ''; + networking.nftables = lib.mkIf (firewall == "nftables") { + # see nft_init in ${miniupnpd-nftables}/etc/miniupnpd + tables.miniupnpd = { + family = "inet"; + # The following is omitted because it's expected that the firewall is to be responsible for it. + # + # chain forward { + # type filter hook forward priority filter; policy drop; + # jump miniupnpd + # } + # + # Otherwise, it quickly gets ugly with (potentially) two forward chains with "policy drop". + # This means the chain "miniupnpd" never actually gets triggered and is simply there to satisfy + # miniupnpd. If you're doing it yourself (without networking.firewall), the easiest way to get + # it to work is adding a rule "ct status dnat accept" - this is what networking.firewall does. + # If you don't want to simply accept forwarding for all "ct status dnat" packets, override + # upnp_table_name with whatever your table is, create a chain "miniupnpd" in your table and + # jump into it from your forward chain. + content = '' + chain miniupnpd {} + chain prerouting_miniupnpd { + type nat hook prerouting priority dstnat; policy accept; + } + chain postrouting_miniupnpd { + type nat hook postrouting priority srcnat; policy accept; + } + ''; + }; + }; systemd.services.miniupnpd = { description = "MiniUPnP daemon"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { - ExecStart = "${pkgs.miniupnpd}/bin/miniupnpd -f ${configFile}"; + ExecStart = "${miniupnpd}/bin/miniupnpd -f ${configFile}"; PIDFile = "/run/miniupnpd.pid"; Type = "forking"; }; diff --git a/nixos/modules/services/networking/miredo.nix b/nixos/modules/services/networking/miredo.nix index d15a55b4d7d6..0c43839c15ab 100644 --- a/nixos/modules/services/networking/miredo.nix +++ b/nixos/modules/services/networking/miredo.nix @@ -22,14 +22,7 @@ in enable = mkEnableOption (lib.mdDoc "the Miredo IPv6 tunneling service"); - package = mkOption { - type = types.package; - default = pkgs.miredo; - defaultText = literalExpression "pkgs.miredo"; - description = lib.mdDoc '' - The package to use for the miredo daemon's binary. - ''; - }; + package = mkPackageOption pkgs "miredo" { }; serverAddress = mkOption { default = "teredo.remlab.net"; diff --git a/nixos/modules/services/networking/morty.nix b/nixos/modules/services/networking/morty.nix index 72514764a7c6..6954596addfd 100644 --- a/nixos/modules/services/networking/morty.nix +++ b/nixos/modules/services/networking/morty.nix @@ -42,12 +42,7 @@ in description = lib.mdDoc "Request timeout in seconds."; }; - package = mkOption { - type = types.package; - default = pkgs.morty; - defaultText = literalExpression "pkgs.morty"; - description = lib.mdDoc "morty package to use."; - }; + package = mkPackageOption pkgs "morty" { }; port = mkOption { type = types.port; diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix index c6fcc64b4ca2..0aca263ae5b2 100644 --- a/nixos/modules/services/networking/mosquitto.nix +++ b/nixos/modules/services/networking/mosquitto.nix @@ -471,14 +471,7 @@ let globalOptions = with types; { enable = mkEnableOption (lib.mdDoc "the MQTT Mosquitto broker"); - package = mkOption { - type = package; - default = pkgs.mosquitto; - defaultText = literalExpression "pkgs.mosquitto"; - description = lib.mdDoc '' - Mosquitto package to use. - ''; - }; + package = mkPackageOption pkgs "mosquitto" { }; bridges = mkOption { type = attrsOf bridgeOptions; @@ -592,6 +585,7 @@ in systemd.services.mosquitto = { description = "Mosquitto MQTT Broker Daemon"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { Type = "notify"; diff --git a/nixos/modules/services/networking/mtr-exporter.nix b/nixos/modules/services/networking/mtr-exporter.nix index af694c3e736b..38bc0401a7e6 100644 --- a/nixos/modules/services/networking/mtr-exporter.nix +++ b/nixos/modules/services/networking/mtr-exporter.nix @@ -5,7 +5,7 @@ let maintainers types literalExpression escapeShellArg escapeShellArgs mkEnableOption mkOption mkRemovedOptionModule mkIf mdDoc - optionalString concatMapStrings concatStringsSep; + mkPackageOption optionalString concatMapStrings concatStringsSep; cfg = config.services.mtr-exporter; @@ -44,19 +44,9 @@ in { ''; }; - package = mkOption { - type = types.package; - default = pkgs.mtr-exporter; - defaultText = literalExpression "pkgs.mtr-exporter"; - description = mdDoc "The MTR exporter package to use."; - }; + package = mkPackageOption pkgs "mtr-exporter" { }; - mtrPackage = mkOption { - type = types.package; - default = pkgs.mtr; - defaultText = literalExpression "pkgs.mtr"; - description = mdDoc "The MTR package to use."; - }; + mtrPackage = mkPackageOption pkgs "mtr" { }; jobs = mkOption { description = mdDoc "List of MTR jobs. Will be added to /etc/mtr-exporter.conf"; diff --git a/nixos/modules/services/networking/mullvad-vpn.nix b/nixos/modules/services/networking/mullvad-vpn.nix index 82e68bf92af1..5da4ca1d1d80 100644 --- a/nixos/modules/services/networking/mullvad-vpn.nix +++ b/nixos/modules/services/networking/mullvad-vpn.nix @@ -23,12 +23,10 @@ with lib; ''; }; - package = mkOption { - type = types.package; - default = pkgs.mullvad; - defaultText = literalExpression "pkgs.mullvad"; - description = lib.mdDoc '' - The Mullvad package to use. `pkgs.mullvad` only provides the CLI tool, `pkgs.mullvad-vpn` provides both the CLI and the GUI. + package = mkPackageOption pkgs "mullvad" { + example = "mullvad-vpn"; + extraDescription = '' + `pkgs.mullvad` only provides the CLI tool, `pkgs.mullvad-vpn` provides both the CLI and the GUI. ''; }; }; @@ -55,7 +53,7 @@ with lib; systemd.services.mullvad-daemon = { description = "Mullvad VPN daemon"; wantedBy = [ "multi-user.target" ]; - wants = [ "network.target" ]; + wants = [ "network.target" "network-online.target" ]; after = [ "network-online.target" "NetworkManager.service" @@ -65,7 +63,9 @@ with lib; pkgs.iproute2 # Needed for ping "/run/wrappers" - ]; + # See https://github.com/NixOS/nixpkgs/issues/262681 + ] ++ (lib.optional config.networking.resolvconf.enable + config.networking.resolvconf.package); startLimitBurst = 5; startLimitIntervalSec = 20; serviceConfig = { @@ -76,5 +76,5 @@ with lib; }; }; - meta.maintainers = with maintainers; [ patricksjackson ymarkus ]; + meta.maintainers = with maintainers; [ arcuru ymarkus ]; } diff --git a/nixos/modules/services/networking/multipath.nix b/nixos/modules/services/networking/multipath.nix index bd403e109c2a..42ffc3c88426 100644 --- a/nixos/modules/services/networking/multipath.nix +++ b/nixos/modules/services/networking/multipath.nix @@ -24,12 +24,7 @@ in { enable = mkEnableOption (lib.mdDoc "the device mapper multipath (DM-MP) daemon"); - package = mkOption { - type = package; - description = lib.mdDoc "multipath-tools package to use"; - default = pkgs.multipath-tools; - defaultText = lib.literalExpression "pkgs.multipath-tools"; - }; + package = mkPackageOption pkgs "multipath-tools" { }; devices = mkOption { default = [ ]; @@ -546,8 +541,9 @@ in { # We do not have systemd in stage-1 boot so must invoke `multipathd` # with the `-1` argument which disables systemd calls. Invoke `multipath` # to display the multipath mappings in the output of `journalctl -b`. + # TODO: Implement for systemd stage 1 boot.initrd.kernelModules = [ "dm-multipath" "dm-service-time" ]; - boot.initrd.postDeviceCommands = '' + boot.initrd.postDeviceCommands = mkIf (!config.boot.initrd.systemd.enable) '' modprobe -a dm-multipath dm-service-time multipathd -s (set -x && sleep 1 && multipath -ll) diff --git a/nixos/modules/services/networking/murmur.nix b/nixos/modules/services/networking/murmur.nix index 20c2eff11e62..5805f332a66f 100644 --- a/nixos/modules/services/networking/murmur.nix +++ b/nixos/modules/services/networking/murmur.nix @@ -119,12 +119,7 @@ in description = lib.mdDoc "Host to bind to. Defaults binding on all addresses."; }; - package = mkOption { - type = types.package; - default = pkgs.murmur; - defaultText = literalExpression "pkgs.murmur"; - description = lib.mdDoc "Overridable attribute of the murmur package to use."; - }; + package = mkPackageOption pkgs "murmur" { }; password = mkOption { type = types.str; @@ -331,6 +326,29 @@ in RuntimeDirectoryMode = "0700"; User = "murmur"; Group = "murmur"; + + # service hardening + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "full"; + RestrictAddressFamilies = "~AF_PACKET AF_NETLINK"; + RestrictNamespaces = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + SystemCallArchitectures = "native"; + SystemCallFilter = "@system-service"; }; }; diff --git a/nixos/modules/services/networking/mxisd.nix b/nixos/modules/services/networking/mxisd.nix index 528a51c1f3af..47d2b16a1501 100644 --- a/nixos/modules/services/networking/mxisd.nix +++ b/nixos/modules/services/networking/mxisd.nix @@ -39,12 +39,7 @@ in { services.mxisd = { enable = mkEnableOption (lib.mdDoc "matrix federated identity server"); - package = mkOption { - type = types.package; - default = pkgs.ma1sd; - defaultText = literalExpression "pkgs.ma1sd"; - description = lib.mdDoc "The mxisd/ma1sd package to use"; - }; + package = mkPackageOption pkgs "ma1sd" { }; environmentFile = mkOption { type = types.nullOr types.str; diff --git a/nixos/modules/services/networking/nar-serve.nix b/nixos/modules/services/networking/nar-serve.nix index b8b76120e44f..02b8979bd8bc 100644 --- a/nixos/modules/services/networking/nar-serve.nix +++ b/nixos/modules/services/networking/nar-serve.nix @@ -6,7 +6,7 @@ let in { meta = { - maintainers = [ maintainers.rizary ]; + maintainers = [ maintainers.rizary maintainers.zimbatm ]; }; options = { services.nar-serve = { diff --git a/nixos/modules/services/networking/nat-nftables.nix b/nixos/modules/services/networking/nat-nftables.nix index 4b2317ca2ffc..7aa93d8a64b1 100644 --- a/nixos/modules/services/networking/nat-nftables.nix +++ b/nixos/modules/services/networking/nat-nftables.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, ... }: +{ config, lib, ... }: with lib; @@ -35,26 +35,18 @@ let mkTable = { ipVer, dest, ipSet, forwardPorts, dmzHost }: let - # nftables does not support both port and port range as values in a dnat map. - # e.g. "dnat th dport map { 80 : 10.0.0.1 . 80, 443 : 10.0.0.2 . 900-1000 }" - # So we split them. - fwdPorts = filter (x: length (splitString "-" x.destination) == 1) forwardPorts; - fwdPortsRange = filter (x: length (splitString "-" x.destination) > 1) forwardPorts; - # nftables maps for port forward # l4proto . dport : addr . port - toFwdMap = forwardPorts: toNftSet (map + fwdMap = toNftSet (map (fwd: with (splitIPPorts fwd.destination); "${fwd.proto} . ${toNftRange fwd.sourcePort} : ${IP} . ${ports}" ) forwardPorts); - fwdMap = toFwdMap fwdPorts; - fwdRangeMap = toFwdMap fwdPortsRange; # nftables maps for port forward loopback dnat # daddr . l4proto . dport : addr . port - toFwdLoopDnatMap = forwardPorts: toNftSet (concatMap + fwdLoopDnatMap = toNftSet (concatMap (fwd: map (loopbackip: with (splitIPPorts fwd.destination); @@ -62,8 +54,6 @@ let ) fwd.loopbackIPs) forwardPorts); - fwdLoopDnatMap = toFwdLoopDnatMap fwdPorts; - fwdLoopDnatRangeMap = toFwdLoopDnatMap fwdPortsRange; # nftables set for port forward loopback snat # daddr . l4proto . dport @@ -79,17 +69,11 @@ let type nat hook prerouting priority dstnat; ${optionalString (fwdMap != "") '' - iifname "${cfg.externalInterface}" dnat meta l4proto . th dport map { ${fwdMap} } comment "port forward" - ''} - ${optionalString (fwdRangeMap != "") '' - iifname "${cfg.externalInterface}" dnat meta l4proto . th dport map { ${fwdRangeMap} } comment "port forward" + iifname "${cfg.externalInterface}" meta l4proto { tcp, udp } dnat meta l4proto . th dport map { ${fwdMap} } comment "port forward" ''} ${optionalString (fwdLoopDnatMap != "") '' - dnat ${ipVer} daddr . meta l4proto . th dport map { ${fwdLoopDnatMap} } comment "port forward loopback from other hosts behind NAT" - ''} - ${optionalString (fwdLoopDnatRangeMap != "") '' - dnat ${ipVer} daddr . meta l4proto . th dport map { ${fwdLoopDnatRangeMap} } comment "port forward loopback from other hosts behind NAT" + meta l4proto { tcp, udp } dnat ${ipVer} daddr . meta l4proto . th dport map { ${fwdLoopDnatMap} } comment "port forward loopback from other hosts behind NAT" ''} ${optionalString (dmzHost != null) '' @@ -116,10 +100,7 @@ let type nat hook output priority mangle; ${optionalString (fwdLoopDnatMap != "") '' - dnat ${ipVer} daddr . meta l4proto . th dport map { ${fwdLoopDnatMap} } comment "port forward loopback from the host itself" - ''} - ${optionalString (fwdLoopDnatRangeMap != "") '' - dnat ${ipVer} daddr . meta l4proto . th dport map { ${fwdLoopDnatRangeMap} } comment "port forward loopback from the host itself" + meta l4proto { tcp, udp } dnat ${ipVer} daddr . meta l4proto . th dport map { ${fwdLoopDnatMap} } comment "port forward loopback from the host itself" ''} } ''; diff --git a/nixos/modules/services/networking/nbd.nix b/nixos/modules/services/networking/nbd.nix index 454380aa3154..b4bf7ede8463 100644 --- a/nixos/modules/services/networking/nbd.nix +++ b/nixos/modules/services/networking/nbd.nix @@ -117,6 +117,7 @@ in boot.kernelModules = [ "nbd" ]; systemd.services.nbd-server = { + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; before = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/networking/nebula.nix b/nixos/modules/services/networking/nebula.nix index e1a8c6740f57..e13876172dac 100644 --- a/nixos/modules/services/networking/nebula.nix +++ b/nixos/modules/services/networking/nebula.nix @@ -27,12 +27,7 @@ in description = lib.mdDoc "Enable or disable this network."; }; - package = mkOption { - type = types.package; - default = pkgs.nebula; - defaultText = literalExpression "pkgs.nebula"; - description = lib.mdDoc "Nebula derivation to use."; - }; + package = mkPackageOption pkgs "nebula" { }; ca = mkOption { type = types.path; @@ -201,7 +196,7 @@ in before = [ "sshd.service" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { - Type = "simple"; + Type = "notify"; Restart = "always"; ExecStart = "${netCfg.package}/bin/nebula -config ${configFile}"; UMask = "0027"; diff --git a/nixos/modules/services/networking/netbird.md b/nixos/modules/services/networking/netbird.md new file mode 100644 index 000000000000..a326207becc8 --- /dev/null +++ b/nixos/modules/services/networking/netbird.md @@ -0,0 +1,56 @@ +# Netbird {#module-services-netbird} + +## Quickstart {#module-services-netbird-quickstart} + +The absolute minimal configuration for the netbird daemon looks like this: + +```nix +services.netbird.enable = true; +``` + +This will set up a netbird service listening on the port `51820` associated to the +`wt0` interface. + +It is strictly equivalent to setting: + +```nix +services.netbird.tunnels.wt0.stateDir = "netbird"; +``` + +The `enable` option is mainly kept for backward compatibility, as defining netbird +tunnels through the `tunnels` option is more expressive. + +## Multiple connections setup {#module-services-netbird-multiple-connections} + +Using the `services.netbird.tunnels` option, it is also possible to define more than +one netbird service running at the same time. + +The following configuration will start a netbird daemon using the interface `wt1` and +the port 51830. Its configuration file will then be located at `/var/lib/netbird-wt1/config.json`. + +```nix +services.netbird.tunnels = { + wt1 = { + port = 51830; + }; +}; +``` + +To interact with it, you will need to specify the correct daemon address: + +```bash +netbird --daemon-addr unix:///var/run/netbird-wt1/sock ... +``` + +The address will by default be `unix:///var/run/netbird-<name>`. + +It is also possible to overwrite default options passed to the service, for +example: + +```nix +services.netbird.tunnels.wt1.environment = { + NB_DAEMON_ADDR = "unix:///var/run/toto.sock" +}; +``` + +This will set the socket to interact with the netbird service to `/var/run/toto.sock`. diff --git a/nixos/modules/services/networking/netbird.nix b/nixos/modules/services/networking/netbird.nix index 647c0ce3e6d1..6a1511d4d084 100644 --- a/nixos/modules/services/networking/netbird.nix +++ b/nixos/modules/services/networking/netbird.nix @@ -1,65 +1,171 @@ -{ config, lib, pkgs, ... }: - -with lib; +{ + config, + lib, + pkgs, + ... +}: let - cfg = config.services.netbird; + inherit (lib) + attrNames + getExe + literalExpression + maintainers + mapAttrs' + mkDefault + mkEnableOption + mkIf + mkMerge + mkOption + mkPackageOption + nameValuePair + optional + versionOlder + ; + + inherit (lib.types) + attrsOf + port + str + submodule + ; + kernel = config.boot.kernelPackages; - interfaceName = "wt0"; -in { - meta.maintainers = with maintainers; [ misuzu ]; + + cfg = config.services.netbird; +in +{ + meta.maintainers = with maintainers; [ + misuzu + thubrecht + ]; + meta.doc = ./netbird.md; options.services.netbird = { enable = mkEnableOption (lib.mdDoc "Netbird daemon"); - package = mkOption { - type = types.package; - default = pkgs.netbird; - defaultText = literalExpression "pkgs.netbird"; - description = lib.mdDoc "The package to use for netbird"; + package = mkPackageOption pkgs "netbird" { }; + + tunnels = mkOption { + type = attrsOf ( + submodule ( + { name, config, ... }: + { + options = { + port = mkOption { + type = port; + default = 51820; + description = '' + Port for the ${name} netbird interface. + ''; + }; + + environment = mkOption { + type = attrsOf str; + defaultText = literalExpression '' + { + NB_CONFIG = "/var/lib/''${stateDir}/config.json"; + NB_LOG_FILE = "console"; + NB_WIREGUARD_PORT = builtins.toString port; + NB_INTERFACE_NAME = name; + NB_DAMEON_ADDR = "/var/run/''${stateDir}" + } + ''; + description = '' + Environment for the netbird service, used to pass configuration options. + ''; + }; + + stateDir = mkOption { + type = str; + default = "netbird-${name}"; + description = '' + Directory storing the netbird configuration. + ''; + }; + }; + + config.environment = builtins.mapAttrs (_: mkDefault) { + NB_CONFIG = "/var/lib/${config.stateDir}/config.json"; + NB_LOG_FILE = "console"; + NB_WIREGUARD_PORT = builtins.toString config.port; + NB_INTERFACE_NAME = name; + NB_DAEMON_ADDR = "unix:///var/run/${config.stateDir}/sock"; + }; + } + ) + ); + default = { }; + description = '' + Attribute set of Netbird tunnels, each one will spawn a daemon listening on ... + ''; }; }; - config = mkIf cfg.enable { - boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard; + config = mkMerge [ + (mkIf cfg.enable { + # For backwards compatibility + services.netbird.tunnels.wt0.stateDir = "netbird"; + }) - environment.systemPackages = [ cfg.package ]; + (mkIf (cfg.tunnels != { }) { + boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard; - networking.dhcpcd.denyInterfaces = [ interfaceName ]; + environment.systemPackages = [ cfg.package ]; - systemd.network.networks."50-netbird" = mkIf config.networking.useNetworkd { - matchConfig = { - Name = interfaceName; - }; - linkConfig = { - Unmanaged = true; - ActivationPolicy = "manual"; - }; - }; + networking.dhcpcd.denyInterfaces = attrNames cfg.tunnels; - systemd.services.netbird = { - description = "A WireGuard-based mesh network that connects your devices into a single private network"; - documentation = [ "https://netbird.io/docs/" ]; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - path = with pkgs; [ - openresolv - ]; - serviceConfig = { - Environment = [ - "NB_CONFIG=/var/lib/netbird/config.json" - "NB_LOG_FILE=console" - ]; - ExecStart = "${cfg.package}/bin/netbird service run"; - Restart = "always"; - RuntimeDirectory = "netbird"; - StateDirectory = "netbird"; - WorkingDirectory = "/var/lib/netbird"; - }; - unitConfig = { - StartLimitInterval = 5; - StartLimitBurst = 10; - }; - stopIfChanged = false; - }; - }; + systemd.network.networks = mkIf config.networking.useNetworkd ( + mapAttrs' + ( + name: _: + nameValuePair "50-netbird-${name}" { + matchConfig = { + Name = name; + }; + linkConfig = { + Unmanaged = true; + ActivationPolicy = "manual"; + }; + } + ) + cfg.tunnels + ); + + systemd.services = + mapAttrs' + ( + name: + { environment, stateDir, ... }: + nameValuePair "netbird-${name}" { + description = "A WireGuard-based mesh network that connects your devices into a single private network"; + + documentation = [ "https://netbird.io/docs/" ]; + + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + path = with pkgs; [ openresolv ]; + + inherit environment; + + serviceConfig = { + ExecStart = "${getExe cfg.package} service run"; + Restart = "always"; + RuntimeDirectory = stateDir; + StateDirectory = stateDir; + StateDirectoryMode = "0700"; + WorkingDirectory = "/var/lib/${stateDir}"; + }; + + unitConfig = { + StartLimitInterval = 5; + StartLimitBurst = 10; + }; + + stopIfChanged = false; + } + ) + cfg.tunnels; + }) + ]; } diff --git a/nixos/modules/services/networking/netclient.nix b/nixos/modules/services/networking/netclient.nix index 124735fd716a..43b8f07cca04 100644 --- a/nixos/modules/services/networking/netclient.nix +++ b/nixos/modules/services/networking/netclient.nix @@ -7,7 +7,7 @@ in options.services.netclient = { enable = lib.mkEnableOption (lib.mdDoc "Netclient Daemon"); - package = lib.mkPackageOptionMD pkgs "netclient" { }; + package = lib.mkPackageOption pkgs "netclient" { }; }; config = lib.mkIf cfg.enable { diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index d32712c8243d..c96439cf2641 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -565,7 +565,10 @@ in wantedBy = [ "network-online.target" ]; }; - systemd.services.ModemManager.aliases = [ "dbus-org.freedesktop.ModemManager1.service" ]; + systemd.services.ModemManager = { + aliases = [ "dbus-org.freedesktop.ModemManager1.service" ]; + path = lib.optionals (cfg.fccUnlockScripts != []) [ pkgs.libqmi pkgs.libmbim ]; + }; systemd.services.NetworkManager-dispatcher = { wantedBy = [ "network.target" ]; diff --git a/nixos/modules/services/networking/nftables.nix b/nixos/modules/services/networking/nftables.nix index 424d005dc0b5..2351ebf4b707 100644 --- a/nixos/modules/services/networking/nftables.nix +++ b/nixos/modules/services/networking/nftables.nix @@ -185,6 +185,19 @@ in can be loaded using "nft -f". The ruleset is updated atomically. ''; }; + + networking.nftables.flattenRulesetFile = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Use `builtins.readFile` rather than `include` to handle {option}`networking.nftables.rulesetFile`. It is useful when you want to apply {option}`networking.nftables.preCheckRuleset` to {option}`networking.nftables.rulesetFile`. + + ::: {.note} + It is expected that {option}`networking.nftables.rulesetFile` can be accessed from the build sandbox. + ::: + ''; + }; + networking.nftables.tables = mkOption { type = types.attrsOf (types.submodule tableSubmodule); @@ -252,8 +265,10 @@ in networking.nftables.flushRuleset = mkDefault (versionOlder config.system.stateVersion "23.11" || (cfg.rulesetFile != null || cfg.ruleset != "")); systemd.services.nftables = { description = "nftables firewall"; - before = [ "network-pre.target" ]; - wants = [ "network-pre.target" ]; + after = [ "sysinit.target" ]; + before = [ "network-pre.target" "shutdown.target" ]; + conflicts = [ "shutdown.target" ]; + wants = [ "network-pre.target" "sysinit.target" ]; wantedBy = [ "multi-user.target" ]; reloadIfChanged = true; serviceConfig = let @@ -293,9 +308,13 @@ in } '') enabledTables)} ${cfg.ruleset} - ${lib.optionalString (cfg.rulesetFile != null) '' - include "${cfg.rulesetFile}" - ''} + ${if cfg.rulesetFile != null then + if cfg.flattenRulesetFile then + builtins.readFile cfg.rulesetFile + else '' + include "${cfg.rulesetFile}" + '' + else ""} ''; checkPhase = lib.optionalString cfg.checkRuleset '' cp $out ruleset.conf @@ -315,6 +334,7 @@ in ExecStop = [ deletionsScriptVar cleanupDeletionsScript ]; StateDirectory = "nftables"; }; + unitConfig.DefaultDependencies = false; }; }; } diff --git a/nixos/modules/services/networking/ngircd.nix b/nixos/modules/services/networking/ngircd.nix index 5e721f5aa625..a2fff78fdff8 100644 --- a/nixos/modules/services/networking/ngircd.nix +++ b/nixos/modules/services/networking/ngircd.nix @@ -28,14 +28,7 @@ in { type = types.lines; }; - package = mkOption { - description = lib.mdDoc "The ngircd package."; - - type = types.package; - - default = pkgs.ngircd; - defaultText = literalExpression "pkgs.ngircd"; - }; + package = mkPackageOption pkgs "ngircd" { }; }; }; diff --git a/nixos/modules/services/networking/nix-serve.nix b/nixos/modules/services/networking/nix-serve.nix index f37be31270b7..a0c0be2ff254 100644 --- a/nixos/modules/services/networking/nix-serve.nix +++ b/nixos/modules/services/networking/nix-serve.nix @@ -26,14 +26,7 @@ in ''; }; - package = mkOption { - type = types.package; - default = pkgs.nix-serve; - defaultText = literalExpression "pkgs.nix-serve"; - description = lib.mdDoc '' - nix-serve package to use. - ''; - }; + package = mkPackageOption pkgs "nix-serve" { }; openFirewall = mkOption { type = types.bool; @@ -67,6 +60,10 @@ in }; config = mkIf cfg.enable { + nix.settings = lib.optionalAttrs (lib.versionAtLeast config.nix.package.version "2.4") { + extra-allowed-users = [ "nix-serve" ]; + }; + systemd.services.nix-serve = { description = "nix-serve binary cache server"; after = [ "network.target" ]; diff --git a/nixos/modules/services/networking/nomad.nix b/nixos/modules/services/networking/nomad.nix index b1e51195247a..8cb0264648de 100644 --- a/nixos/modules/services/networking/nomad.nix +++ b/nixos/modules/services/networking/nomad.nix @@ -10,14 +10,7 @@ in services.nomad = { enable = mkEnableOption (lib.mdDoc "Nomad, a distributed, highly available, datacenter-aware scheduler"); - package = mkOption { - type = types.package; - default = pkgs.nomad; - defaultText = literalExpression "pkgs.nomad"; - description = lib.mdDoc '' - The package used for the Nomad agent and CLI. - ''; - }; + package = mkPackageOption pkgs "nomad" { }; extraPackages = mkOption { type = types.listOf types.package; diff --git a/nixos/modules/services/networking/ntp/chrony.nix b/nixos/modules/services/networking/ntp/chrony.nix index afd721e34da5..b56bea4e134f 100644 --- a/nixos/modules/services/networking/ntp/chrony.nix +++ b/nixos/modules/services/networking/ntp/chrony.nix @@ -9,6 +9,7 @@ let stateDir = cfg.directory; driftFile = "${stateDir}/chrony.drift"; keyFile = "${stateDir}/chrony.keys"; + rtcFile = "${stateDir}/chrony.rtc"; configFile = pkgs.writeText "chrony.conf" '' ${concatMapStringsSep "\n" (server: "server " + server + " " + cfg.serverOption + optionalString (cfg.enableNTS) " nts") cfg.servers} @@ -20,8 +21,10 @@ let driftfile ${driftFile} keyfile ${keyFile} + ${optionalString (cfg.enableRTCTrimming) "rtcfile ${rtcFile}"} ${optionalString (cfg.enableNTS) "ntsdumpdir ${stateDir}"} + ${optionalString (cfg.enableRTCTrimming) "rtcautotrim ${builtins.toString cfg.autotrimThreshold}"} ${optionalString (!config.time.hardwareClockInLocalTime) "rtconutc"} ${cfg.extraConfig} @@ -44,14 +47,7 @@ in ''; }; - package = mkOption { - type = types.package; - default = pkgs.chrony; - defaultText = literalExpression "pkgs.chrony"; - description = lib.mdDoc '' - Which chrony package to use. - ''; - }; + package = mkPackageOption pkgs "chrony" { }; servers = mkOption { default = config.networking.timeServers; @@ -85,6 +81,33 @@ in ''; }; + enableRTCTrimming = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Enable tracking of the RTC offset to the system clock and automatic trimming. + See also [](#opt-services.chrony.autotrimThreshold) + + ::: {.note} + This is not compatible with the `rtcsync` directive, which naively syncs the RTC time every 11 minutes. + + Tracking the RTC drift will allow more precise timekeeping, + especially on intermittently running devices, where the RTC is very relevant. + ::: + ''; + }; + + autotrimThreshold = mkOption { + type = types.ints.positive; + default = 30; + example = 10; + description = '' + Maximum estimated error threshold for the `rtcautotrim` command. + When reached, the RTC will be trimmed. + Only used when [](#opt-services.chrony.enableRTCTrimming) is enabled. + ''; + }; + enableNTS = mkOption { type = types.bool; default = false; @@ -132,7 +155,7 @@ in }; extraFlags = mkOption { - default = []; + default = [ ]; example = [ "-s" ]; type = types.listOf types.str; description = lib.mdDoc "Extra flags passed to the chronyd command."; @@ -141,14 +164,15 @@ in }; config = mkIf cfg.enable { - meta.maintainers = with lib.maintainers; [ thoughtpolice ]; + meta.maintainers = with lib.maintainers; [ thoughtpolice vifino ]; environment.systemPackages = [ chronyPkg ]; users.groups.chrony.gid = config.ids.gids.chrony; users.users.chrony = - { uid = config.ids.uids.chrony; + { + uid = config.ids.uids.chrony; group = "chrony"; description = "chrony daemon user"; home = stateDir; @@ -156,21 +180,29 @@ in services.timesyncd.enable = mkForce false; + # If chrony controls and tracks the RTC, writing it externally causes clock error. + systemd.services.save-hwclock = lib.mkIf cfg.enableRTCTrimming { + enable = lib.mkForce false; + }; + systemd.services.systemd-timedated.environment = { SYSTEMD_TIMEDATED_NTP_SERVICES = "chronyd.service"; }; systemd.tmpfiles.rules = [ "d ${stateDir} 0750 chrony chrony - -" "f ${driftFile} 0640 chrony chrony - -" "f ${keyFile} 0640 chrony chrony - -" + ] ++ lib.optionals cfg.enableRTCTrimming [ + "f ${rtcFile} 0640 chrony chrony - -" ]; systemd.services.chronyd = - { description = "chrony NTP daemon"; + { + description = "chrony NTP daemon"; wantedBy = [ "multi-user.target" ]; - wants = [ "time-sync.target" ]; - before = [ "time-sync.target" ]; - after = [ "network.target" "nss-lookup.target" ]; + wants = [ "time-sync.target" ]; + before = [ "time-sync.target" ]; + after = [ "network.target" "nss-lookup.target" ]; conflicts = [ "ntpd.service" "systemd-timesyncd.service" ]; path = [ chronyPkg ]; @@ -218,5 +250,18 @@ in SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @resources" "@clock" "@setuid" "capset" "@chown" ]; }; }; + + assertions = [ + { + assertion = !(cfg.enableRTCTrimming && builtins.any (line: (builtins.match "^ *rtcsync" line) != null) (lib.strings.splitString "\n" cfg.extraConfig)); + message = '' + The chrony module now configures `rtcfile` and `rtcautotrim` for you. + These options conflict with `rtcsync` and cause chrony to crash. + Unless you are very sure the former isn't what you want, please remove + `rtcsync` from `services.chrony.extraConfig`. + Alternatively, disable this behaviour by `services.chrony.enableRTCTrimming = false;` + ''; + } + ]; }; } diff --git a/nixos/modules/services/networking/ntp/ntpd-rs.nix b/nixos/modules/services/networking/ntp/ntpd-rs.nix new file mode 100644 index 000000000000..4643ac146ddb --- /dev/null +++ b/nixos/modules/services/networking/ntp/ntpd-rs.nix @@ -0,0 +1,89 @@ +{ lib, config, pkgs, ... }: + +let + cfg = config.services.ntpd-rs; + format = pkgs.formats.toml { }; + configFile = format.generate "ntpd-rs.toml" cfg.settings; +in +{ + options.services.ntpd-rs = { + enable = lib.mkEnableOption "Network Time Service (ntpd-rs)"; + metrics.enable = lib.mkEnableOption "ntpd-rs Prometheus Metrics Exporter"; + + package = lib.mkPackageOption pkgs "ntpd-rs" { }; + + useNetworkingTimeServers = lib.mkOption { + type = lib.types.bool; + default = true; + description = lib.mdDoc '' + Use source time servers from {var}`networking.timeServers` in config. + ''; + }; + + settings = lib.mkOption { + type = lib.types.submodule { + freeformType = format.type; + }; + default = { }; + description = lib.mdDoc '' + Settings to write to {file}`ntp.toml` + + See <https://docs.ntpd-rs.pendulum-project.org/man/ntp.toml.5> + for more information about available options. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = [ + { + assertion = !config.services.timesyncd.enable; + message = '' + `ntpd-rs` is not compatible with `services.timesyncd`. Please disable one of them. + ''; + } + ]; + + environment.systemPackages = [ cfg.package ]; + systemd.packages = [ cfg.package ]; + + services.timesyncd.enable = false; + systemd.services.systemd-timedated.environment = { + SYSTEMD_TIMEDATED_NTP_SERVICES = "ntpd-rs.service"; + }; + + services.ntpd-rs.settings = { + observability = { + observation-path = lib.mkDefault "/var/run/ntpd-rs/observe"; + }; + source = lib.mkIf cfg.useNetworkingTimeServers (map + (ts: { + mode = "server"; + address = ts; + }) + config.networking.timeServers); + }; + + systemd.services.ntpd-rs = { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = ""; + Group = ""; + DynamicUser = true; + ExecStart = [ "" "${lib.makeBinPath [ cfg.package ]}/ntp-daemon --config=${configFile}" ]; + }; + }; + + systemd.services.ntpd-rs-metrics = lib.mkIf cfg.metrics.enable { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = ""; + Group = ""; + DynamicUser = true; + ExecStart = [ "" "${lib.makeBinPath [ cfg.package ]}/ntp-metrics-exporter --config=${configFile}" ]; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ fpletz ]; +} diff --git a/nixos/modules/services/networking/ntp/ntpd.nix b/nixos/modules/services/networking/ntp/ntpd.nix index 036a8df635db..2bc690cacf09 100644 --- a/nixos/modules/services/networking/ntp/ntpd.nix +++ b/nixos/modules/services/networking/ntp/ntpd.nix @@ -56,7 +56,7 @@ in The default flags prevent external hosts from using ntpd as a DDoS reflector, setting system time, and querying OS/ntpd version. As recommended in section 6.5.1.1.3, answer "No" of - http://support.ntp.org/bin/view/Support/AccessRestrictions + https://support.ntp.org/Support/AccessRestrictions ''; default = [ "limited" "kod" "nomodify" "notrap" "noquery" "nopeer" ]; }; diff --git a/nixos/modules/services/networking/ocserv.nix b/nixos/modules/services/networking/ocserv.nix index 9548fd92dbda..3c61d56b893e 100644 --- a/nixos/modules/services/networking/ocserv.nix +++ b/nixos/modules/services/networking/ocserv.nix @@ -85,6 +85,7 @@ in systemd.services.ocserv = { description = "OpenConnect SSL VPN server"; documentation = [ "man:ocserv(8)" ]; + wants = [ "network-online.target" ]; after = [ "dbus.service" "network-online.target" ]; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/networking/openconnect.nix b/nixos/modules/services/networking/openconnect.nix index 7f9006053b89..d2730faf9381 100644 --- a/nixos/modules/services/networking/openconnect.nix +++ b/nixos/modules/services/networking/openconnect.nix @@ -117,7 +117,7 @@ let }; in { options.networking.openconnect = { - package = mkPackageOptionMD pkgs "openconnect" { }; + package = mkPackageOption pkgs "openconnect" { }; interfaces = mkOption { description = lib.mdDoc "OpenConnect interfaces."; diff --git a/nixos/modules/services/networking/peroxide.nix b/nixos/modules/services/networking/peroxide.nix index 885ee1d96cd0..34c82e2c8b03 100644 --- a/nixos/modules/services/networking/peroxide.nix +++ b/nixos/modules/services/networking/peroxide.nix @@ -11,7 +11,7 @@ in options.services.peroxide = { enable = mkEnableOption (lib.mdDoc "peroxide"); - package = mkPackageOptionMD pkgs "peroxide" { + package = mkPackageOption pkgs "peroxide" { default = [ "peroxide" ]; }; diff --git a/nixos/modules/services/networking/pixiecore.nix b/nixos/modules/services/networking/pixiecore.nix index f410be471646..1f47a1d0b631 100644 --- a/nixos/modules/services/networking/pixiecore.nix +++ b/nixos/modules/services/networking/pixiecore.nix @@ -16,7 +16,7 @@ in type = types.bool; default = false; description = lib.mdDoc '' - Open ports (67, 69 UDP and 4011, 'port', 'statusPort' TCP) in the firewall for Pixiecore. + Open ports (67, 69, 4011 UDP and 'port', 'statusPort' TCP) in the firewall for Pixiecore. ''; }; @@ -103,8 +103,8 @@ in }; networking.firewall = mkIf cfg.openFirewall { - allowedTCPPorts = [ 4011 cfg.port cfg.statusPort ]; - allowedUDPPorts = [ 67 69 ]; + allowedTCPPorts = [ cfg.port cfg.statusPort ]; + allowedUDPPorts = [ 67 69 4011 ]; }; systemd.services.pixiecore = { diff --git a/nixos/modules/services/networking/pleroma.nix b/nixos/modules/services/networking/pleroma.nix index e9db7f3eab8e..8470f5e9cbc0 100644 --- a/nixos/modules/services/networking/pleroma.nix +++ b/nixos/modules/services/networking/pleroma.nix @@ -6,12 +6,7 @@ in { services.pleroma = with lib; { enable = mkEnableOption (lib.mdDoc "pleroma"); - package = mkOption { - type = types.package; - default = pkgs.pleroma; - defaultText = literalExpression "pkgs.pleroma"; - description = lib.mdDoc "Pleroma package to use."; - }; + package = mkPackageOption pkgs "pleroma" { }; user = mkOption { type = types.str; @@ -97,6 +92,7 @@ in { systemd.services.pleroma = { description = "Pleroma social network"; + wants = [ "network-online.target" ]; after = [ "network-online.target" "postgresql.service" ]; wantedBy = [ "multi-user.target" ]; restartTriggers = [ config.environment.etc."/pleroma/config.exs".source ]; @@ -146,6 +142,6 @@ in { }; }; - meta.maintainers = with lib.maintainers; [ ninjatrappeur ]; + meta.maintainers = with lib.maintainers; [ picnoir ]; meta.doc = ./pleroma.md; } diff --git a/nixos/modules/services/networking/pppd.nix b/nixos/modules/services/networking/pppd.nix index 75fc04c67571..855b5358f47f 100644 --- a/nixos/modules/services/networking/pppd.nix +++ b/nixos/modules/services/networking/pppd.nix @@ -14,12 +14,7 @@ in services.pppd = { enable = mkEnableOption (lib.mdDoc "pppd"); - package = mkOption { - default = pkgs.ppp; - defaultText = literalExpression "pkgs.ppp"; - type = types.package; - description = lib.mdDoc "pppd package to use."; - }; + package = mkPackageOption pkgs "ppp" { }; peers = mkOption { default = {}; diff --git a/nixos/modules/services/networking/prayer.nix b/nixos/modules/services/networking/prayer.nix deleted file mode 100644 index 197aa8a6f448..000000000000 --- a/nixos/modules/services/networking/prayer.nix +++ /dev/null @@ -1,90 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - - inherit (pkgs) prayer; - - cfg = config.services.prayer; - - stateDir = "/var/lib/prayer"; - - prayerUser = "prayer"; - prayerGroup = "prayer"; - - prayerExtraCfg = pkgs.writeText "extraprayer.cf" '' - prefix = "${prayer}" - var_prefix = "${stateDir}" - prayer_user = "${prayerUser}" - prayer_group = "${prayerGroup}" - sendmail_path = "/run/wrappers/bin/sendmail" - - use_http_port ${cfg.port} - - ${cfg.extraConfig} - ''; - - prayerCfg = pkgs.runCommand "prayer.cf" { preferLocalBuild = true; } '' - # We have to remove the http_port 80, or it will start a server there - cat ${prayer}/etc/prayer.cf | grep -v http_port > $out - cat ${prayerExtraCfg} >> $out - ''; - -in - -{ - - ###### interface - - options = { - - services.prayer = { - - enable = mkEnableOption (lib.mdDoc "the prayer webmail http server"); - - port = mkOption { - default = 2080; - type = types.port; - description = lib.mdDoc '' - Port the prayer http server is listening to. - ''; - }; - - extraConfig = mkOption { - type = types.lines; - default = "" ; - description = lib.mdDoc '' - Extra configuration. Contents will be added verbatim to the configuration file. - ''; - }; - }; - - }; - - - ###### implementation - - config = mkIf config.services.prayer.enable { - environment.systemPackages = [ prayer ]; - - users.users.${prayerUser} = - { uid = config.ids.uids.prayer; - description = "Prayer daemon user"; - home = stateDir; - }; - - users.groups.${prayerGroup} = - { gid = config.ids.gids.prayer; }; - - systemd.services.prayer = { - wantedBy = [ "multi-user.target" ]; - serviceConfig.Type = "forking"; - preStart = '' - mkdir -m 0755 -p ${stateDir} - chown ${prayerUser}:${prayerGroup} ${stateDir} - ''; - script = "${prayer}/sbin/prayer --config-file=${prayerCfg}"; - }; - }; -} diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix index 0066c77438f4..2952df2a1099 100644 --- a/nixos/modules/services/networking/prosody.nix +++ b/nixos/modules/services/networking/prosody.nix @@ -496,12 +496,8 @@ in ''; }; - package = mkOption { - type = types.package; - description = lib.mdDoc "Prosody package to use"; - default = pkgs.prosody; - defaultText = literalExpression "pkgs.prosody"; - example = literalExpression '' + package = mkPackageOption pkgs "prosody" { + example = '' pkgs.prosody.override { withExtraLibs = [ pkgs.luaPackages.lpty ]; withCommunityModules = [ "auth_external" ]; @@ -779,9 +775,6 @@ in admins = ${toLua cfg.admins} - -- we already build with libevent, so we can just enable it for a more performant server - use_libevent = true - modules_enabled = { ${ lib.concatStringsSep "\n " (lib.mapAttrsToList diff --git a/nixos/modules/services/networking/pyload.nix b/nixos/modules/services/networking/pyload.nix new file mode 100644 index 000000000000..93f8dd7d731a --- /dev/null +++ b/nixos/modules/services/networking/pyload.nix @@ -0,0 +1,166 @@ +{ config, lib, pkgs, utils, ... }: +let + cfg = config.services.pyload; + + stateDir = "/var/lib/pyload"; +in +{ + meta.maintainers = with lib.maintainers; [ ambroisie ]; + + options = with lib; { + services.pyload = { + enable = mkEnableOption "pyLoad download manager"; + + package = mkPackageOption pkgs "pyLoad" { default = [ "pyload-ng" ]; }; + + listenAddress = mkOption { + type = types.str; + default = "localhost"; + example = "0.0.0.0"; + description = "Address to listen on for the web UI."; + }; + + port = mkOption { + type = types.port; + default = 8000; + example = 9876; + description = "Port to listen on for the web UI."; + }; + + downloadDirectory = mkOption { + type = types.path; + default = "${stateDir}/downloads"; + example = "/mnt/downloads"; + description = "Directory to store downloads."; + }; + + user = mkOption { + type = types.str; + default = "pyload"; + description = "User under which pyLoad runs, and which owns the download directory."; + }; + + group = mkOption { + type = types.str; + default = "pyload"; + description = "Group under which pyLoad runs, and which owns the download directory."; + }; + + credentialsFile = mkOption { + type = with types; nullOr path; + default = null; + example = "/run/secrets/pyload-credentials.env"; + description = '' + File containing {env}`PYLOAD_DEFAULT_USERNAME` and + {env}`PYLOAD_DEFAULT_PASSWORD` in the format of an `EnvironmentFile=`, + as described by {manpage}`systemd.exec(5)`. + + If not given, they default to the username/password combo of + pyload/pyload. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.tmpfiles.settings.pyload = { + ${cfg.downloadDirectory}.d = { inherit (cfg) user group; }; + }; + + systemd.services.pyload = { + description = "pyLoad download manager"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + # NOTE: unlike what the documentation says, it looks like `HOME` is not + # defined with this service definition... + # Since pyload tries to do the equivalent of `cd ~`, it needs to be able + # to resolve $HOME, which fails when `RootDirectory` is set. + # FIXME: check if `SetLoginEnvironment` fixes this issue in version 255 + environment = { + HOME = stateDir; + PYLOAD__WEBUI__HOST = cfg.listenAddress; + PYLOAD__WEBUI__PORT = builtins.toString cfg.port; + }; + + serviceConfig = { + ExecStart = utils.escapeSystemdExecArgs [ + (lib.getExe cfg.package) + "--userdir" + "${stateDir}/config" + "--storagedir" + cfg.downloadDirectory + ]; + + User = cfg.user; + Group = cfg.group; + + EnvironmentFile = lib.optional (cfg.credentialsFile != null) cfg.credentialsFile; + + StateDirectory = "pyload"; + WorkingDirectory = stateDir; + RuntimeDirectory = "pyload"; + RuntimeDirectoryMode = "0700"; + RootDirectory = "/run/pyload"; + BindReadOnlyPaths = [ + builtins.storeDir # Needed to run the python interpreter + ]; + BindPaths = [ + cfg.downloadDirectory + ]; + + # Hardening options + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@resources" "~@privileged" ]; + UMask = "0002"; + CapabilityBoundingSet = [ + "~CAP_BLOCK_SUSPEND" + "~CAP_BPF" + "~CAP_CHOWN" + "~CAP_IPC_LOCK" + "~CAP_KILL" + "~CAP_LEASE" + "~CAP_LINUX_IMMUTABLE" + "~CAP_NET_ADMIN" + "~CAP_SYS_ADMIN" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_SYS_NICE" + "~CAP_SYS_PACCT" + "~CAP_SYS_PTRACE" + "~CAP_SYS_RESOURCE" + "~CAP_SYS_TTY_CONFIG" + ]; + }; + }; + + users.users.pyload = lib.mkIf (cfg.user == "pyload") { + isSystemUser = true; + group = cfg.group; + home = stateDir; + }; + + users.groups.pyload = lib.mkIf (cfg.group == "pyload") { }; + }; +} diff --git a/nixos/modules/services/networking/quassel.nix b/nixos/modules/services/networking/quassel.nix index a074023b5ee4..4294d67fffd3 100644 --- a/nixos/modules/services/networking/quassel.nix +++ b/nixos/modules/services/networking/quassel.nix @@ -35,14 +35,7 @@ in ''; }; - package = mkOption { - type = types.package; - default = pkgs.quasselDaemon; - defaultText = literalExpression "pkgs.quasselDaemon"; - description = lib.mdDoc '' - The package of the quassel daemon. - ''; - }; + package = mkPackageOption pkgs "quasselDaemon" { }; interfaces = mkOption { type = types.listOf types.str; diff --git a/nixos/modules/services/networking/quicktun.nix b/nixos/modules/services/networking/quicktun.nix index 7aed972adc88..2d44659f2080 100644 --- a/nixos/modules/services/networking/quicktun.nix +++ b/nixos/modules/services/networking/quicktun.nix @@ -1,94 +1,153 @@ -{ config, pkgs, lib, ... }: +{ options, config, pkgs, lib, ... }: let + inherit (lib) mkOption mdDoc types mkIf; + opt = options.services.quicktun; cfg = config.services.quicktun; - in - -with lib; - { options = { - services.quicktun = mkOption { default = { }; - description = lib.mdDoc "QuickTun tunnels"; - type = types.attrsOf (types.submodule { + description = mdDoc '' + QuickTun tunnels. + + See <http://wiki.ucis.nl/QuickTun> for more information about available options. + ''; + type = types.attrsOf (types.submodule ({ name, ... }: let + qtcfg = cfg.${name}; + in { options = { tunMode = mkOption { - type = types.int; - default = 0; - example = 1; - description = lib.mdDoc ""; + type = with types; coercedTo bool (b: if b then 1 else 0) (ints.between 0 1); + default = false; + example = true; + description = mdDoc "Whether to operate in tun (IP) or tap (Ethernet) mode."; }; remoteAddress = mkOption { type = types.str; + default = "0.0.0.0"; example = "tunnel.example.com"; - description = lib.mdDoc ""; + description = mdDoc '' + IP address or hostname of the remote end (use `0.0.0.0` for a floating/dynamic remote endpoint). + ''; }; localAddress = mkOption { - type = types.str; + type = with types; nullOr str; + default = null; example = "0.0.0.0"; - description = lib.mdDoc ""; + description = mdDoc "IP address or hostname of the local end."; }; localPort = mkOption { - type = types.int; + type = types.port; default = 2998; - description = lib.mdDoc ""; + description = mdDoc "Local UDP port."; }; remotePort = mkOption { - type = types.int; - default = 2998; - description = lib.mdDoc ""; + type = types.port; + default = qtcfg.localPort; + defaultText = lib.literalExpression "config.services.quicktun.<name>.localPort"; + description = mdDoc " remote UDP port"; }; remoteFloat = mkOption { - type = types.int; - default = 0; - description = lib.mdDoc ""; + type = with types; coercedTo bool (b: if b then 1 else 0) (ints.between 0 1); + default = false; + example = true; + description = mdDoc '' + Whether to allow the remote address and port to change when properly encrypted packets are received. + ''; }; protocol = mkOption { - type = types.str; + type = types.enum [ "raw" "nacl0" "nacltai" "salty" ]; default = "nacltai"; - description = lib.mdDoc ""; + description = mdDoc "Which protocol to use."; }; privateKey = mkOption { - type = types.str; - description = lib.mdDoc ""; + type = with types; nullOr str; + default = null; + description = mdDoc '' + Local secret key in hexadecimal form. + + ::: {.warning} + This option is deprecated. Please use {var}`services.quicktun.<name>.privateKeyFile` instead. + ::: + + ::: {.note} + Not needed when {var}`services.quicktun.<name>.protocol` is set to `raw`. + ::: + ''; + }; + + privateKeyFile = mkOption { + type = with types; nullOr path; + # This is a hack to deprecate `privateKey` without using `mkChangedModuleOption` + default = if qtcfg.privateKey == null then null else pkgs.writeText "quickttun-key-${name}" qtcfg.privateKey; + defaultText = "null"; + description = mdDoc '' + Path to file containing local secret key in binary or hexadecimal form. + + ::: {.note} + Not needed when {var}`services.quicktun.<name>.protocol` is set to `raw`. + ::: + ''; }; publicKey = mkOption { - type = types.str; - description = lib.mdDoc ""; + type = with types; nullOr str; + default = null; + description = mdDoc '' + Remote public key in hexadecimal form. + + ::: {.note} + Not needed when {var}`services.quicktun.<name>.protocol` is set to `raw`. + ::: + ''; }; timeWindow = mkOption { - type = types.int; + type = types.ints.unsigned; default = 5; - description = lib.mdDoc ""; + description = mdDoc '' + Allowed time window for first received packet in seconds (positive number allows packets from history) + ''; }; upScript = mkOption { - type = types.lines; - default = ""; - description = lib.mdDoc ""; + type = with types; nullOr lines; + default = null; + description = mdDoc '' + Run specified command or script after the tunnel device has been opened. + ''; }; }; - }); + })); }; - }; - config = mkIf (cfg != []) { - systemd.services = foldr (a: b: a // b) {} ( - mapAttrsToList (name: qtcfg: { + config = { + warnings = lib.pipe cfg [ + (lib.mapAttrsToList (name: value: if value.privateKey != null then name else null)) + (builtins.filter (n: n != null)) + (map (n: " - services.quicktun.${n}.privateKey")) + (services: lib.optional (services != [ ]) '' + `services.quicktun.<name>.privateKey` is deprecated. + Please use `services.quicktun.<name>.privateKeyFile` instead. + + Offending options: + ${lib.concatStringsSep "\n" services} + '') + ]; + + systemd.services = lib.mkMerge ( + lib.mapAttrsToList (name: qtcfg: { "quicktun-${name}" = { wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; @@ -96,14 +155,14 @@ with lib; INTERFACE = name; TUN_MODE = toString qtcfg.tunMode; REMOTE_ADDRESS = qtcfg.remoteAddress; - LOCAL_ADDRESS = qtcfg.localAddress; + LOCAL_ADDRESS = mkIf (qtcfg.localAddress != null) (qtcfg.localAddress); LOCAL_PORT = toString qtcfg.localPort; REMOTE_PORT = toString qtcfg.remotePort; REMOTE_FLOAT = toString qtcfg.remoteFloat; - PRIVATE_KEY = qtcfg.privateKey; - PUBLIC_KEY = qtcfg.publicKey; + PRIVATE_KEY_FILE = mkIf (qtcfg.privateKeyFile != null) qtcfg.privateKeyFile; + PUBLIC_KEY = mkIf (qtcfg.publicKey != null) qtcfg.publicKey; TIME_WINDOW = toString qtcfg.timeWindow; - TUN_UP_SCRIPT = pkgs.writeScript "quicktun-${name}-up.sh" qtcfg.upScript; + TUN_UP_SCRIPT = mkIf (qtcfg.upScript != null) (pkgs.writeScript "quicktun-${name}-up.sh" qtcfg.upScript); SUID = "nobody"; }; serviceConfig = { @@ -114,5 +173,4 @@ with lib; }) cfg ); }; - } diff --git a/nixos/modules/services/networking/radvd.nix b/nixos/modules/services/networking/radvd.nix index 72590eda4ee6..57aa21287050 100644 --- a/nixos/modules/services/networking/radvd.nix +++ b/nixos/modules/services/networking/radvd.nix @@ -32,14 +32,7 @@ in ''; }; - package = mkOption { - type = types.package; - default = pkgs.radvd; - defaultText = literalExpression "pkgs.radvd"; - description = lib.mdDoc '' - The RADVD package to use for the RADVD service. - ''; - }; + package = mkPackageOption pkgs "radvd" { }; config = mkOption { type = types.lines; diff --git a/nixos/modules/services/networking/rosenpass.nix b/nixos/modules/services/networking/rosenpass.nix index d2a264b83d67..487cb6f60142 100644 --- a/nixos/modules/services/networking/rosenpass.nix +++ b/nixos/modules/services/networking/rosenpass.nix @@ -208,6 +208,7 @@ in in rec { wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; path = [ cfg.package pkgs.wireguard-tools ]; diff --git a/nixos/modules/services/networking/routedns.nix b/nixos/modules/services/networking/routedns.nix index 2a29a06700ce..126539702438 100644 --- a/nixos/modules/services/networking/routedns.nix +++ b/nixos/modules/services/networking/routedns.nix @@ -52,12 +52,7 @@ in description = lib.mdDoc "Path to RouteDNS TOML configuration file."; }; - package = mkOption { - default = pkgs.routedns; - defaultText = literalExpression "pkgs.routedns"; - type = types.package; - description = lib.mdDoc "RouteDNS package to use."; - }; + package = mkPackageOption pkgs "routedns" { }; }; config = mkIf cfg.enable { diff --git a/nixos/modules/services/networking/rxe.nix b/nixos/modules/services/networking/rxe.nix index 7dbb4823b4bc..07437ed71195 100644 --- a/nixos/modules/services/networking/rxe.nix +++ b/nixos/modules/services/networking/rxe.nix @@ -33,7 +33,7 @@ in { wantedBy = [ "multi-user.target" ]; after = [ "systemd-modules-load.service" "network-online.target" ]; - wants = [ "network-pre.target" ]; + wants = [ "network-pre.target" "network-online.target" ]; serviceConfig = { Type = "oneshot"; diff --git a/nixos/modules/services/networking/sabnzbd.nix b/nixos/modules/services/networking/sabnzbd.nix index 8f3545df8995..2f0d17ad3d17 100644 --- a/nixos/modules/services/networking/sabnzbd.nix +++ b/nixos/modules/services/networking/sabnzbd.nix @@ -17,12 +17,7 @@ in services.sabnzbd = { enable = mkEnableOption (lib.mdDoc "the sabnzbd server"); - package = mkOption { - type = types.package; - default = pkgs.sabnzbd; - defaultText = lib.literalExpression "pkgs.sabnzbd"; - description = lib.mdDoc "The sabnzbd executable package run by the service."; - }; + package = mkPackageOption pkgs "sabnzbd" { }; configFile = mkOption { type = types.path; @@ -41,6 +36,14 @@ in default = "sabnzbd"; description = lib.mdDoc "Group to run the service as"; }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Open ports in the firewall for the sabnzbd web interface + ''; + }; }; }; @@ -48,17 +51,16 @@ in ###### implementation config = mkIf cfg.enable { - - users.users.sabnzbd = { - uid = config.ids.uids.sabnzbd; - group = "sabnzbd"; - description = "sabnzbd user"; - home = "/var/lib/sabnzbd/"; - createHome = true; + users.users = mkIf (cfg.user == "sabnzbd") { + sabnzbd = { + uid = config.ids.uids.sabnzbd; + group = cfg.group; + description = "sabnzbd user"; + }; }; - users.groups.sabnzbd = { - gid = config.ids.gids.sabnzbd; + users.groups = mkIf (cfg.group == "sabnzbd") { + sabnzbd.gid = config.ids.gids.sabnzbd; }; systemd.services.sabnzbd = { @@ -68,10 +70,15 @@ in serviceConfig = { Type = "forking"; GuessMainPID = "no"; - User = "${cfg.user}"; - Group = "${cfg.group}"; + User = cfg.user; + Group = cfg.group; + StateDirectory = "sabnzbd"; ExecStart = "${lib.getBin cfg.package}/bin/sabnzbd -d -f ${cfg.configFile}"; }; }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ 8080 ]; + }; }; } diff --git a/nixos/modules/services/networking/seafile.nix b/nixos/modules/services/networking/seafile.nix index b07d51b9b49a..b2d12234900a 100644 --- a/nixos/modules/services/networking/seafile.nix +++ b/nixos/modules/services/networking/seafile.nix @@ -32,7 +32,8 @@ let dataDir = "${seafRoot}/data"; seahubDir = "${seafRoot}/seahub"; -in { +in +{ ###### Interface @@ -121,12 +122,7 @@ in { ''; }; - seafilePackage = mkOption { - type = types.package; - description = lib.mdDoc "Which package to use for the seafile server."; - default = pkgs.seafile-server; - defaultText = literalExpression "pkgs.seafile-server"; - }; + seafilePackage = mkPackageOption pkgs "seafile-server" { }; seahubExtraConf = mkOption { default = ""; @@ -152,146 +148,151 @@ in { description = "Seafile components"; }; - systemd.services = let - securityOptions = { - ProtectHome = true; - PrivateUsers = true; - PrivateDevices = true; - ProtectClock = true; - ProtectHostname = true; - ProtectProc = "invisible"; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectKernelLogs = true; - ProtectControlGroups = true; - RestrictNamespaces = true; - LockPersonality = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - MemoryDenyWriteExecute = true; - SystemCallArchitectures = "native"; - RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" ]; - }; - in { - seaf-server = { - description = "Seafile server"; - partOf = [ "seafile.target" ]; - after = [ "network.target" ]; - wantedBy = [ "seafile.target" ]; - restartTriggers = [ ccnetConf seafileConf ]; - path = [ pkgs.sqlite ]; - serviceConfig = securityOptions // { - User = "seafile"; - Group = "seafile"; - DynamicUser = true; - StateDirectory = "seafile"; - RuntimeDirectory = "seafile"; - LogsDirectory = "seafile"; - ConfigurationDirectory = "seafile"; - ExecStart = '' - ${cfg.seafilePackage}/bin/seaf-server \ - --foreground \ - -F /etc/seafile \ - -c ${ccnetDir} \ - -d ${dataDir} \ - -l /var/log/seafile/server.log \ - -P /run/seafile/server.pid \ - -p /run/seafile - ''; + systemd.services = + let + securityOptions = { + ProtectHome = true; + PrivateUsers = true; + PrivateDevices = true; + ProtectClock = true; + ProtectHostname = true; + ProtectProc = "invisible"; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + LockPersonality = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + MemoryDenyWriteExecute = true; + SystemCallArchitectures = "native"; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" ]; }; - preStart = '' - if [ ! -f "${seafRoot}/server-setup" ]; then - mkdir -p ${dataDir}/library-template - mkdir -p ${ccnetDir}/{GroupMgr,misc,OrgMgr,PeerMgr} - sqlite3 ${ccnetDir}/GroupMgr/groupmgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/groupmgr.sql" - sqlite3 ${ccnetDir}/misc/config.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/config.sql" - sqlite3 ${ccnetDir}/OrgMgr/orgmgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/org.sql" - sqlite3 ${ccnetDir}/PeerMgr/usermgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/user.sql" - sqlite3 ${dataDir}/seafile.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/seafile.sql" - echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup - fi - # checking for upgrades and handling them - # WARNING: needs to be extended to actually handle major version migrations - installedMajor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f1) - installedMinor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f2) - pkgMajor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f1) - pkgMinor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f2) - - if [[ $installedMajor == $pkgMajor && $installedMinor == $pkgMinor ]]; then - : - elif [[ $installedMajor == 8 && $installedMinor == 0 && $pkgMajor == 9 && $pkgMinor == 0 ]]; then - # Upgrade from 8.0 to 9.0 - sqlite3 ${dataDir}/seafile.db ".read ${pkgs.seahub}/scripts/upgrade/sql/9.0.0/sqlite3/seafile.sql" - echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup - else - echo "Unsupported upgrade" >&2 - exit 1 - fi - ''; - }; + in + { + seaf-server = { + description = "Seafile server"; + partOf = [ "seafile.target" ]; + after = [ "network.target" ]; + wantedBy = [ "seafile.target" ]; + restartTriggers = [ ccnetConf seafileConf ]; + path = [ pkgs.sqlite ]; + serviceConfig = securityOptions // { + User = "seafile"; + Group = "seafile"; + DynamicUser = true; + StateDirectory = "seafile"; + RuntimeDirectory = "seafile"; + LogsDirectory = "seafile"; + ConfigurationDirectory = "seafile"; + ExecStart = '' + ${cfg.seafilePackage}/bin/seaf-server \ + --foreground \ + -F /etc/seafile \ + -c ${ccnetDir} \ + -d ${dataDir} \ + -l /var/log/seafile/server.log \ + -P /run/seafile/server.pid \ + -p /run/seafile + ''; + }; + preStart = '' + if [ ! -f "${seafRoot}/server-setup" ]; then + mkdir -p ${dataDir}/library-template + mkdir -p ${ccnetDir}/{GroupMgr,misc,OrgMgr,PeerMgr} + sqlite3 ${ccnetDir}/GroupMgr/groupmgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/groupmgr.sql" + sqlite3 ${ccnetDir}/misc/config.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/config.sql" + sqlite3 ${ccnetDir}/OrgMgr/orgmgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/org.sql" + sqlite3 ${ccnetDir}/PeerMgr/usermgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/user.sql" + sqlite3 ${dataDir}/seafile.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/seafile.sql" + echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup + fi + # checking for upgrades and handling them + installedMajor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f1) + installedMinor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f2) + pkgMajor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f1) + pkgMinor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f2) - seahub = { - description = "Seafile Server Web Frontend"; - wantedBy = [ "seafile.target" ]; - partOf = [ "seafile.target" ]; - after = [ "network.target" "seaf-server.service" ]; - requires = [ "seaf-server.service" ]; - restartTriggers = [ seahubSettings ]; - environment = { - PYTHONPATH = "${pkgs.seahub.pythonPath}:${pkgs.seahub}/thirdpart:${pkgs.seahub}"; - DJANGO_SETTINGS_MODULE = "seahub.settings"; - CCNET_CONF_DIR = ccnetDir; - SEAFILE_CONF_DIR = dataDir; - SEAFILE_CENTRAL_CONF_DIR = "/etc/seafile"; - SEAFILE_RPC_PIPE_PATH = "/run/seafile"; - SEAHUB_LOG_DIR = "/var/log/seafile"; + if [[ $installedMajor == $pkgMajor && $installedMinor == $pkgMinor ]]; then + : + elif [[ $installedMajor == 8 && $installedMinor == 0 && $pkgMajor == 9 && $pkgMinor == 0 ]]; then + # Upgrade from 8.0 to 9.0 + sqlite3 ${dataDir}/seafile.db ".read ${pkgs.seahub}/scripts/upgrade/sql/9.0.0/sqlite3/seafile.sql" + echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup + elif [[ $installedMajor == 9 && $installedMinor == 0 && $pkgMajor == 10 && $pkgMinor == 0 ]]; then + # Upgrade from 9.0 to 10.0 + sqlite3 ${dataDir}/seafile.db ".read ${pkgs.seahub}/scripts/upgrade/sql/10.0.0/sqlite3/seafile.sql" + echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup + else + echo "Unsupported upgrade" >&2 + exit 1 + fi + ''; }; - serviceConfig = securityOptions // { - User = "seafile"; - Group = "seafile"; - DynamicUser = true; - RuntimeDirectory = "seahub"; - StateDirectory = "seafile"; - LogsDirectory = "seafile"; - ConfigurationDirectory = "seafile"; - ExecStart = '' - ${pkgs.seahub.python.pkgs.gunicorn}/bin/gunicorn seahub.wsgi:application \ - --name seahub \ - --workers ${toString cfg.workers} \ - --log-level=info \ - --preload \ - --timeout=1200 \ - --limit-request-line=8190 \ - --bind unix:/run/seahub/gunicorn.sock + + seahub = { + description = "Seafile Server Web Frontend"; + wantedBy = [ "seafile.target" ]; + partOf = [ "seafile.target" ]; + after = [ "network.target" "seaf-server.service" ]; + requires = [ "seaf-server.service" ]; + restartTriggers = [ seahubSettings ]; + environment = { + PYTHONPATH = "${pkgs.seahub.pythonPath}:${pkgs.seahub}/thirdpart:${pkgs.seahub}"; + DJANGO_SETTINGS_MODULE = "seahub.settings"; + CCNET_CONF_DIR = ccnetDir; + SEAFILE_CONF_DIR = dataDir; + SEAFILE_CENTRAL_CONF_DIR = "/etc/seafile"; + SEAFILE_RPC_PIPE_PATH = "/run/seafile"; + SEAHUB_LOG_DIR = "/var/log/seafile"; + }; + serviceConfig = securityOptions // { + User = "seafile"; + Group = "seafile"; + DynamicUser = true; + RuntimeDirectory = "seahub"; + StateDirectory = "seafile"; + LogsDirectory = "seafile"; + ConfigurationDirectory = "seafile"; + ExecStart = '' + ${pkgs.seahub.python.pkgs.gunicorn}/bin/gunicorn seahub.wsgi:application \ + --name seahub \ + --workers ${toString cfg.workers} \ + --log-level=info \ + --preload \ + --timeout=1200 \ + --limit-request-line=8190 \ + --bind unix:/run/seahub/gunicorn.sock + ''; + }; + preStart = '' + mkdir -p ${seahubDir}/media + # Link all media except avatars + for m in `find ${pkgs.seahub}/media/ -maxdepth 1 -not -name "avatars"`; do + ln -sf $m ${seahubDir}/media/ + done + if [ ! -e "${seafRoot}/.seahubSecret" ]; then + ${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.seahub}/media/avatars/default.png + install -D -t ${seahubDir}/media/avatars/groups ${pkgs.seahub}/media/avatars/groups/default.png + # init database + ${pkgs.seahub}/manage.py migrate + # create admin account + ${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.seahub.version}" ]; then + # update database + ${pkgs.seahub}/manage.py migrate + echo "${pkgs.seahub.version}-sqlite" > "${seafRoot}/seahub-setup" + fi ''; }; - preStart = '' - mkdir -p ${seahubDir}/media - # Link all media except avatars - for m in `find ${pkgs.seahub}/media/ -maxdepth 1 -not -name "avatars"`; do - ln -sf $m ${seahubDir}/media/ - done - if [ ! -e "${seafRoot}/.seahubSecret" ]; then - ${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.seahub}/media/avatars/default.png - install -D -t ${seahubDir}/media/avatars/groups ${pkgs.seahub}/media/avatars/groups/default.png - # init database - ${pkgs.seahub}/manage.py migrate - # create admin account - ${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.seahub.version}" ]; then - # update database - ${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 8054f01d705f..938d585e3179 100644 --- a/nixos/modules/services/networking/searx.nix +++ b/nixos/modules/services/networking/searx.nix @@ -143,12 +143,7 @@ in ''; }; - package = mkOption { - type = types.package; - default = pkgs.searxng; - defaultText = literalExpression "pkgs.searxng"; - description = lib.mdDoc "searx package to use."; - }; + package = mkPackageOption pkgs "searxng" { }; runInUwsgi = mkOption { type = types.bool; diff --git a/nixos/modules/services/networking/shellhub-agent.nix b/nixos/modules/services/networking/shellhub-agent.nix index 7cce23cb9c4e..ad33c50f9d63 100644 --- a/nixos/modules/services/networking/shellhub-agent.nix +++ b/nixos/modules/services/networking/shellhub-agent.nix @@ -14,7 +14,7 @@ in enable = mkEnableOption (lib.mdDoc "ShellHub Agent daemon"); - package = mkPackageOptionMD pkgs "shellhub-agent" { }; + package = mkPackageOption pkgs "shellhub-agent" { }; preferredHostname = mkOption { type = types.str; diff --git a/nixos/modules/services/networking/sing-box.nix b/nixos/modules/services/networking/sing-box.nix index a884bcd271ec..ea7363713601 100644 --- a/nixos/modules/services/networking/sing-box.nix +++ b/nixos/modules/services/networking/sing-box.nix @@ -13,7 +13,7 @@ in services.sing-box = { enable = lib.mkEnableOption (lib.mdDoc "sing-box universal proxy platform"); - package = lib.mkPackageOptionMD pkgs "sing-box" { }; + package = lib.mkPackageOption pkgs "sing-box" { }; settings = lib.mkOption { type = lib.types.submodule { diff --git a/nixos/modules/services/networking/skydns.nix b/nixos/modules/services/networking/skydns.nix index 84cf6b0deac1..0514bff2767e 100644 --- a/nixos/modules/services/networking/skydns.nix +++ b/nixos/modules/services/networking/skydns.nix @@ -55,12 +55,7 @@ in { example = ["8.8.8.8:53" "8.8.4.4:53"]; }; - package = mkOption { - default = pkgs.skydns; - defaultText = literalExpression "pkgs.skydns"; - type = types.package; - description = lib.mdDoc "Skydns package to use."; - }; + package = mkPackageOption pkgs "skydns" { }; extraConfig = mkOption { default = {}; diff --git a/nixos/modules/services/networking/smokeping.nix b/nixos/modules/services/networking/smokeping.nix index c7aec7d9489f..4ecf411c7496 100644 --- a/nixos/modules/services/networking/smokeping.nix +++ b/nixos/modules/services/networking/smokeping.nix @@ -165,12 +165,7 @@ in example = "no-reply@yourdomain.com"; description = lib.mdDoc "Email contact for owner"; }; - package = mkOption { - type = types.package; - default = pkgs.smokeping; - defaultText = literalExpression "pkgs.smokeping"; - description = lib.mdDoc "Specify a custom smokeping package"; - }; + package = mkPackageOption pkgs "smokeping" { }; host = mkOption { type = types.nullOr types.str; default = "localhost"; diff --git a/nixos/modules/services/networking/softether.nix b/nixos/modules/services/networking/softether.nix index c8e888eafcc2..234832ea0c0f 100644 --- a/nixos/modules/services/networking/softether.nix +++ b/nixos/modules/services/networking/softether.nix @@ -18,14 +18,7 @@ in enable = mkEnableOption (lib.mdDoc "SoftEther VPN services"); - package = mkOption { - type = types.package; - default = pkgs.softether; - defaultText = literalExpression "pkgs.softether"; - description = lib.mdDoc '' - softether derivation to use. - ''; - }; + package = mkPackageOption pkgs "softether" { }; vpnserver.enable = mkEnableOption (lib.mdDoc "SoftEther VPN Server"); diff --git a/nixos/modules/services/networking/soju.nix b/nixos/modules/services/networking/soju.nix index 7f0ac3e3b8e6..d69ec08ca13a 100644 --- a/nixos/modules/services/networking/soju.nix +++ b/nixos/modules/services/networking/soju.nix @@ -110,6 +110,7 @@ in systemd.services.soju = { description = "soju IRC bouncer"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { DynamicUser = true; diff --git a/nixos/modules/services/networking/spacecookie.nix b/nixos/modules/services/networking/spacecookie.nix index b2956edfcb7f..745c942ba60b 100644 --- a/nixos/modules/services/networking/spacecookie.nix +++ b/nixos/modules/services/networking/spacecookie.nix @@ -27,15 +27,8 @@ in { enable = mkEnableOption (lib.mdDoc "spacecookie"); - package = mkOption { - type = types.package; - default = pkgs.spacecookie; - defaultText = literalExpression "pkgs.spacecookie"; - example = literalExpression "pkgs.haskellPackages.spacecookie"; - description = lib.mdDoc '' - The spacecookie derivation to use. This can be used to - override the used package or to use another version. - ''; + package = mkPackageOption pkgs "spacecookie" { + example = "haskellPackages.spacecookie"; }; openFirewall = mkOption { diff --git a/nixos/modules/services/networking/spiped.nix b/nixos/modules/services/networking/spiped.nix index 3e01ace54ad1..547317dbcbe2 100644 --- a/nixos/modules/services/networking/spiped.nix +++ b/nixos/modules/services/networking/spiped.nix @@ -197,8 +197,9 @@ in script = "exec ${pkgs.spiped}/bin/spiped -F `cat /etc/spiped/$1.spec`"; }; - system.activationScripts.spiped = optionalString (cfg.config != {}) - "mkdir -p /var/lib/spiped"; + systemd.tmpfiles.rules = lib.mkIf (cfg.config != { }) [ + "d /var/lib/spiped -" + ]; # Setup spiped config files environment.etc = mapAttrs' (name: cfg: nameValuePair "spiped/${name}.spec" diff --git a/nixos/modules/services/networking/squid.nix b/nixos/modules/services/networking/squid.nix index 914cd7f320c9..68f4dc3d6dc1 100644 --- a/nixos/modules/services/networking/squid.nix +++ b/nixos/modules/services/networking/squid.nix @@ -111,12 +111,7 @@ in description = lib.mdDoc "Whether to run squid web proxy."; }; - package = mkOption { - default = pkgs.squid; - defaultText = literalExpression "pkgs.squid"; - type = types.package; - description = lib.mdDoc "Squid package to use."; - }; + package = mkPackageOption pkgs "squid" { }; proxyAddress = mkOption { type = types.nullOr types.str; @@ -176,7 +171,7 @@ in serviceConfig = { PIDFile="/run/squid.pid"; ExecStart = "${cfg.package}/bin/squid --foreground -YCs -f ${squidConfig}"; - ExecReload="kill -HUP $MAINPID"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; KillMode="mixed"; NotifyAccess="all"; }; diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index daa30fe09b89..aca8343b7d59 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -12,22 +12,44 @@ let then cfgc.package else pkgs.buildPackages.openssh; - # reports boolean as yes / no - mkValueStringSshd = with lib; v: - if isInt v then toString v - else if isString v then v - else if true == v then "yes" - else if false == v then "no" - else if isList v then concatStringsSep "," v - else throw "unsupported type ${builtins.typeOf v}: ${(lib.generators.toPretty {}) v}"; - # dont use the "=" operator - settingsFormat = (pkgs.formats.keyValue { - mkKeyValue = lib.generators.mkKeyValueDefault { - mkValueString = mkValueStringSshd; - } " ";}); + settingsFormat = + let + # reports boolean as yes / no + mkValueString = with lib; v: + if isInt v then toString v + else if isString v then v + else if true == v then "yes" + else if false == v then "no" + else throw "unsupported type ${builtins.typeOf v}: ${(lib.generators.toPretty {}) v}"; + + base = pkgs.formats.keyValue { + mkKeyValue = lib.generators.mkKeyValueDefault { inherit mkValueString; } " "; + }; + # OpenSSH is very inconsistent with options that can take multiple values. + # For some of them, they can simply appear multiple times and are appended, for others the + # values must be separated by whitespace or even commas. + # Consult either sshd_config(5) or, as last resort, the OpehSSH source for parsing + # the options at servconf.c:process_server_config_line_depth() to determine the right "mode" + # for each. But fortunaly this fact is documented for most of them in the manpage. + commaSeparated = [ "Ciphers" "KexAlgorithms" "Macs" ]; + spaceSeparated = [ "AuthorizedKeysFile" "AllowGroups" "AllowUsers" "DenyGroups" "DenyUsers" ]; + in { + inherit (base) type; + generate = name: value: + let transformedValue = mapAttrs (key: val: + if isList val then + if elem key commaSeparated then concatStringsSep "," val + else if elem key spaceSeparated then concatStringsSep " " val + else throw "list value for unknown key ${key}: ${(lib.generators.toPretty {}) val}" + else + val + ) value; + in + base.generate name transformedValue; + }; - configFile = settingsFormat.generate "sshd.conf-settings" cfg.settings; + configFile = settingsFormat.generate "sshd.conf-settings" (filterAttrs (n: v: v != null) cfg.settings); sshconf = pkgs.runCommand "sshd.conf-final" { } '' cat ${configFile} - >$out <<EOL ${cfg.extraConfig} @@ -431,6 +453,42 @@ in <https://infosec.mozilla.org/guidelines/openssh#modern-openssh-67> ''; }; + AllowUsers = mkOption { + type = with types; nullOr (listOf str); + default = null; + description = lib.mdDoc '' + If specified, login is allowed only for the listed users. + See {manpage}`sshd_config(5)` for details. + ''; + }; + DenyUsers = mkOption { + type = with types; nullOr (listOf str); + default = null; + description = lib.mdDoc '' + If specified, login is denied for all listed users. Takes + precedence over [](#opt-services.openssh.settings.AllowUsers). + See {manpage}`sshd_config(5)` for details. + ''; + }; + AllowGroups = mkOption { + type = with types; nullOr (listOf str); + default = null; + description = lib.mdDoc '' + If specified, login is allowed only for users part of the + listed groups. + See {manpage}`sshd_config(5)` for details. + ''; + }; + DenyGroups = mkOption { + type = with types; nullOr (listOf str); + default = null; + description = lib.mdDoc '' + If specified, login is denied for all users part of the listed + groups. Takes precedence over + [](#opt-services.openssh.settings.AllowGroups). See + {manpage}`sshd_config(5)` for details. + ''; + }; }; }); }; @@ -542,7 +600,11 @@ in { description = "SSH Socket"; wantedBy = [ "sockets.target" ]; socketConfig.ListenStream = if cfg.listenAddresses != [] then - map (l: "${l.addr}:${toString (if l.port != null then l.port else 22)}") cfg.listenAddresses + concatMap + ({ addr, port }: + if port != null then [ "${addr}:${toString port}" ] + else map (p: "${addr}:${toString p}") cfg.ports) + cfg.listenAddresses else cfg.ports; socketConfig.Accept = true; @@ -616,7 +678,11 @@ in (lport: "sshd -G -T -C lport=${toString lport} -f ${sshconf} > /dev/null") cfg.ports} ${concatMapStringsSep "\n" - (la: "sshd -G -T -C ${escapeShellArg "laddr=${la.addr},lport=${toString la.port}"} -f ${sshconf} > /dev/null") + (la: + concatMapStringsSep "\n" + (port: "sshd -G -T -C ${escapeShellArg "laddr=${la.addr},lport=${toString port}"} -f ${sshconf} > /dev/null") + (if la.port != null then [ la.port ] else cfg.ports) + ) cfg.listenAddresses} touch $out '') diff --git a/nixos/modules/services/networking/sslh.nix b/nixos/modules/services/networking/sslh.nix index daf2f2f3668e..dd29db510020 100644 --- a/nixos/modules/services/networking/sslh.nix +++ b/nixos/modules/services/networking/sslh.nix @@ -5,81 +5,131 @@ with lib; let cfg = config.services.sslh; user = "sslh"; - configFile = pkgs.writeText "sslh.conf" '' - verbose: ${boolToString cfg.verbose}; - foreground: true; - inetd: false; - numeric: false; - transparent: ${boolToString cfg.transparent}; - timeout: "${toString cfg.timeout}"; - - listen: - ( - ${ - concatMapStringsSep ",\n" - (addr: ''{ host: "${addr}"; port: "${toString cfg.port}"; }'') - cfg.listenAddresses - } - ); - - ${cfg.appendConfig} - ''; - defaultAppendConfig = '' - protocols: - ( - { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; }, - { name: "openvpn"; host: "localhost"; port: "1194"; probe: "builtin"; }, - { name: "xmpp"; host: "localhost"; port: "5222"; probe: "builtin"; }, - { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; }, - { name: "tls"; host: "localhost"; port: "443"; probe: "builtin"; }, - { name: "anyprot"; host: "localhost"; port: "443"; probe: "builtin"; } - ); - ''; + + configFormat = pkgs.formats.libconfig {}; + configFile = configFormat.generate "sslh.conf" cfg.settings; in + { imports = [ (mkRenamedOptionModule [ "services" "sslh" "listenAddress" ] [ "services" "sslh" "listenAddresses" ]) + (mkRenamedOptionModule [ "services" "sslh" "timeout" ] [ "services" "sslh" "settings" "timeout" ]) + (mkRenamedOptionModule [ "services" "sslh" "transparent" ] [ "services" "sslh" "settings" "transparent" ]) + (mkRemovedOptionModule [ "services" "sslh" "appendConfig" ] "Use services.sslh.settings instead") + (mkChangedOptionModule [ "services" "sslh" "verbose" ] [ "services" "sslh" "settings" "verbose-connections" ] + (config: if config.services.sslh.verbose then 1 else 0)) ]; - options = { - services.sslh = { - enable = mkEnableOption (lib.mdDoc "sslh"); + meta.buildDocsInSandbox = false; - verbose = mkOption { - type = types.bool; - default = false; - description = lib.mdDoc "Verbose logs."; - }; + options.services.sslh = { + enable = mkEnableOption (lib.mdDoc "sslh, protocol demultiplexer"); - timeout = mkOption { - type = types.int; - default = 2; - description = lib.mdDoc "Timeout in seconds."; - }; + method = mkOption { + type = types.enum [ "fork" "select" "ev" ]; + default = "fork"; + description = lib.mdDoc '' + The method to use for handling connections: - transparent = mkOption { - type = types.bool; - default = false; - description = lib.mdDoc "Will the services behind sslh (Apache, sshd and so on) see the external IP and ports as if the external world connected directly to them"; - }; + - `fork` forks a new process for each incoming connection. It is + well-tested and very reliable, but incurs the overhead of many + processes. - listenAddresses = mkOption { - type = types.coercedTo types.str singleton (types.listOf types.str); - default = [ "0.0.0.0" "[::]" ]; - description = lib.mdDoc "Listening addresses or hostnames."; - }; + - `select` uses only one thread, which monitors all connections at once. + It has lower overhead per connection, but if it stops, you'll lose all + connections. - port = mkOption { - type = types.port; - default = 443; - description = lib.mdDoc "Listening port."; - }; + - `ev` is implemented using libev, it's similar to `select` but + scales better to a large number of connections. + ''; + }; + + listenAddresses = mkOption { + type = with types; coercedTo str singleton (listOf str); + default = [ "0.0.0.0" "[::]" ]; + description = lib.mdDoc "Listening addresses or hostnames."; + }; + + port = mkOption { + type = types.port; + default = 443; + description = lib.mdDoc "Listening port."; + }; + + settings = mkOption { + type = types.submodule { + freeformType = configFormat.type; + + options.timeout = mkOption { + type = types.ints.unsigned; + default = 2; + description = lib.mdDoc "Timeout in seconds."; + }; + + options.transparent = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether the services behind sslh (Apache, sshd and so on) will see the + external IP and ports as if the external world connected directly to + them. + ''; + }; + + options.verbose-connections = mkOption { + type = types.ints.between 0 4; + default = 0; + description = lib.mdDoc '' + Where to log connections information. Possible values are: + + 0. don't log anything + 1. write log to stdout + 2. write log to syslog + 3. write log to both stdout and syslog + 4. write to a log file ({option}`sslh.settings.logfile`) + ''; + }; + + options.numeric = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether to disable reverse DNS lookups, thus keeping IP + address literals in the log. + ''; + }; + + options.protocols = mkOption { + type = types.listOf configFormat.type; + default = [ + { name = "ssh"; host = "localhost"; port = "22"; service= "ssh"; } + { name = "openvpn"; host = "localhost"; port = "1194"; } + { name = "xmpp"; host = "localhost"; port = "5222"; } + { name = "http"; host = "localhost"; port = "80"; } + { name = "tls"; host = "localhost"; port = "443"; } + { name = "anyprot"; host = "localhost"; port = "443"; } + ]; + description = lib.mdDoc '' + List of protocols sslh will probe for and redirect. + Each protocol entry consists of: + + - `name`: name of the probe. + + - `service`: libwrap service name (see {manpage}`hosts_access(5)`), - appendConfig = mkOption { - type = types.str; - default = defaultAppendConfig; - description = lib.mdDoc "Verbatim configuration file."; + - `host`, `port`: where to connect when this probe succeeds, + + - `log_level`: to log incoming connections, + + - `transparent`: proxy this protocol transparently, + + - etc. + + See the documentation for all options, including probe-specific ones. + ''; + }; }; + description = lib.mdDoc "sslh configuration. See {manpage}`sslh(8)` for available settings."; }; }; @@ -96,20 +146,29 @@ in PermissionsStartOnly = true; Restart = "always"; RestartSec = "1s"; - ExecStart = "${pkgs.sslh}/bin/sslh -F${configFile}"; + ExecStart = "${pkgs.sslh}/bin/sslh-${cfg.method} -F${configFile}"; KillMode = "process"; - AmbientCapabilities = "CAP_NET_BIND_SERVICE CAP_NET_ADMIN CAP_SETGID CAP_SETUID"; + AmbientCapabilities = ["CAP_NET_BIND_SERVICE" "CAP_NET_ADMIN" "CAP_SETGID" "CAP_SETUID"]; PrivateTmp = true; PrivateDevices = true; ProtectSystem = "full"; ProtectHome = true; }; }; + + services.sslh.settings = { + # Settings defined here are not supposed to be changed: doing so will + # break the module, as such you need `lib.mkForce` to override them. + foreground = true; + inetd = false; + listen = map (addr: { host = addr; port = toString cfg.port; }) cfg.listenAddresses; + }; + }) # code from https://github.com/yrutschle/sslh#transparent-proxy-support # the only difference is using iptables mark 0x2 instead of 0x1 to avoid conflicts with nixos/nat module - (mkIf (cfg.enable && cfg.transparent) { + (mkIf (cfg.enable && cfg.settings.transparent) { # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination boot.kernel.sysctl."net.ipv4.conf.default.route_localnet" = 1; boot.kernel.sysctl."net.ipv4.conf.all.route_localnet" = 1; diff --git a/nixos/modules/services/networking/strongswan-swanctl/module.nix b/nixos/modules/services/networking/strongswan-swanctl/module.nix index c51e8ad9f5fc..c1f0aeb64e96 100644 --- a/nixos/modules/services/networking/strongswan-swanctl/module.nix +++ b/nixos/modules/services/networking/strongswan-swanctl/module.nix @@ -5,19 +5,15 @@ with (import ./param-lib.nix lib); let cfg = config.services.strongswan-swanctl; + configFile = pkgs.writeText "swanctl.conf" + ( (paramsToConf cfg.swanctl swanctlParams) + + (concatMapStrings (i: "\ninclude ${i}") cfg.includes)); swanctlParams = import ./swanctl-params.nix lib; in { options.services.strongswan-swanctl = { enable = mkEnableOption (lib.mdDoc "strongswan-swanctl service"); - package = mkOption { - type = types.package; - default = pkgs.strongswan; - defaultText = literalExpression "pkgs.strongswan"; - description = lib.mdDoc '' - The strongswan derivation to use. - ''; - }; + package = mkPackageOption pkgs "strongswan" { }; strongswan.extraConfig = mkOption { type = types.str; @@ -28,6 +24,13 @@ in { }; swanctl = paramsToOptions swanctlParams; + includes = mkOption { + type = types.listOf types.path; + default = []; + description = '' + Extra configuration files to include in the swanctl configuration. This can be used to provide secret values from outside the nix store. + ''; + }; }; config = mkIf cfg.enable { @@ -38,30 +41,30 @@ in { } ]; - environment.etc."swanctl/swanctl.conf".text = - paramsToConf cfg.swanctl swanctlParams; + environment.etc."swanctl/swanctl.conf".source = configFile; # The swanctl command complains when the following directories don't exist: # See: https://wiki.strongswan.org/projects/strongswan/wiki/Swanctldirectory - system.activationScripts.strongswan-swanctl-etc = stringAfter ["etc"] '' - mkdir -p '/etc/swanctl/x509' # Trusted X.509 end entity certificates - mkdir -p '/etc/swanctl/x509ca' # Trusted X.509 Certificate Authority certificates - mkdir -p '/etc/swanctl/x509ocsp' - mkdir -p '/etc/swanctl/x509aa' # Trusted X.509 Attribute Authority certificates - mkdir -p '/etc/swanctl/x509ac' # Attribute Certificates - mkdir -p '/etc/swanctl/x509crl' # Certificate Revocation Lists - mkdir -p '/etc/swanctl/pubkey' # Raw public keys - mkdir -p '/etc/swanctl/private' # Private keys in any format - mkdir -p '/etc/swanctl/rsa' # PKCS#1 encoded RSA private keys - mkdir -p '/etc/swanctl/ecdsa' # Plain ECDSA private keys - mkdir -p '/etc/swanctl/bliss' - mkdir -p '/etc/swanctl/pkcs8' # PKCS#8 encoded private keys of any type - mkdir -p '/etc/swanctl/pkcs12' # PKCS#12 containers - ''; + systemd.tmpfiles.rules = [ + "d /etc/swanctl/x509 -" # Trusted X.509 end entity certificates + "d /etc/swanctl/x509ca -" # Trusted X.509 Certificate Authority certificates + "d /etc/swanctl/x509ocsp -" + "d /etc/swanctl/x509aa -" # Trusted X.509 Attribute Authority certificates + "d /etc/swanctl/x509ac -" # Attribute Certificates + "d /etc/swanctl/x509crl -" # Certificate Revocation Lists + "d /etc/swanctl/pubkey -" # Raw public keys + "d /etc/swanctl/private -" # Private keys in any format + "d /etc/swanctl/rsa -" # PKCS#1 encoded RSA private keys + "d /etc/swanctl/ecdsa -" # Plain ECDSA private keys + "d /etc/swanctl/bliss -" + "d /etc/swanctl/pkcs8 -" # PKCS#8 encoded private keys of any type + "d /etc/swanctl/pkcs12 -" # PKCS#12 containers + ]; systemd.services.strongswan-swanctl = { description = "strongSwan IPsec IKEv1/IKEv2 daemon using swanctl"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; path = with pkgs; [ kmod iproute2 iptables util-linux ]; environment = { diff --git a/nixos/modules/services/networking/strongswan.nix b/nixos/modules/services/networking/strongswan.nix index e58526814d1a..dcf04d2a1917 100644 --- a/nixos/modules/services/networking/strongswan.nix +++ b/nixos/modules/services/networking/strongswan.nix @@ -153,6 +153,7 @@ in description = "strongSwan IPSec Service"; wantedBy = [ "multi-user.target" ]; path = with pkgs; [ kmod iproute2 iptables util-linux ]; # XXX Linux + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; environment = { STRONGSWAN_CONF = strongswanConf { inherit setup connections ca secretsFile managePlugins enabledPlugins; }; diff --git a/nixos/modules/services/networking/syncplay.nix b/nixos/modules/services/networking/syncplay.nix index 0a66d93bf153..151259b6d4ad 100644 --- a/nixos/modules/services/networking/syncplay.nix +++ b/nixos/modules/services/networking/syncplay.nix @@ -107,6 +107,7 @@ in systemd.services.syncplay = { description = "Syncplay Service"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix index bdcdaf056d03..e0425792431e 100644 --- a/nixos/modules/services/networking/syncthing.nix +++ b/nixos/modules/services/networking/syncthing.nix @@ -559,6 +559,15 @@ in { ''; }; + databaseDir = mkOption { + type = types.path; + description = lib.mdDoc '' + The directory containing the database and logs. + ''; + default = cfg.configDir; + defaultText = literalExpression "config.${opt.configDir}"; + }; + extraFlags = mkOption { type = types.listOf types.str; default = []; @@ -583,14 +592,7 @@ in { ''; }; - package = mkOption { - type = types.package; - default = pkgs.syncthing; - defaultText = literalExpression "pkgs.syncthing"; - description = lib.mdDoc '' - The Syncthing package to use. - ''; - }; + package = mkPackageOption pkgs "syncthing" { }; }; }; @@ -666,7 +668,9 @@ in { ${cfg.package}/bin/syncthing \ -no-browser \ -gui-address=${if isUnixGui then "unix://" else ""}${cfg.guiAddress} \ - -home=${cfg.configDir} ${escapeShellArgs cfg.extraFlags} + -config=${cfg.configDir} \ + -data=${cfg.databaseDir} \ + ${escapeShellArgs cfg.extraFlags} ''; MemoryDenyWriteExecute = true; NoNewPrivileges = true; diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix index 8b35cc8d6669..f11fe57d6ce5 100644 --- a/nixos/modules/services/networking/tailscale.nix +++ b/nixos/modules/services/networking/tailscale.nix @@ -29,7 +29,13 @@ in { description = lib.mdDoc "Username or user ID of the user allowed to to fetch Tailscale TLS certificates for the node."; }; - package = lib.mkPackageOptionMD pkgs "tailscale" {}; + package = lib.mkPackageOption pkgs "tailscale" {}; + + openFirewall = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc "Whether to open the firewall for the specified port."; + }; useRoutingFeatures = mkOption { type = types.enum [ "none" "client" "server" "both" ]; @@ -68,11 +74,10 @@ in { systemd.services.tailscaled = { wantedBy = [ "multi-user.target" ]; path = [ - config.networking.resolvconf.package # for configuring DNS in some configs pkgs.procps # for collecting running services (opt-in feature) pkgs.getent # for `getent` to look up user shells pkgs.kmod # required to pass tailscale's v6nat check - ]; + ] ++ lib.optional config.networking.resolvconf.enable config.networking.resolvconf.package; serviceConfig.Environment = [ "PORT=${toString cfg.port}" ''"FLAGS=--tun ${lib.escapeShellArg cfg.interfaceName}"'' @@ -94,8 +99,8 @@ in { }; systemd.services.tailscaled-autoconnect = mkIf (cfg.authKeyFile != null) { - after = ["tailscale.service"]; - wants = ["tailscale.service"]; + after = ["tailscaled.service"]; + wants = ["tailscaled.service"]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "oneshot"; @@ -113,6 +118,8 @@ in { "net.ipv6.conf.all.forwarding" = mkOverride 97 true; }; + networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ cfg.port ]; + networking.firewall.checkReversePath = mkIf (cfg.useRoutingFeatures == "client" || cfg.useRoutingFeatures == "both") "loose"; networking.dhcpcd.denyInterfaces = [ cfg.interfaceName ]; diff --git a/nixos/modules/services/networking/tayga.nix b/nixos/modules/services/networking/tayga.nix index 299ae2777f7c..63423bf02922 100644 --- a/nixos/modules/services/networking/tayga.nix +++ b/nixos/modules/services/networking/tayga.nix @@ -64,12 +64,7 @@ in services.tayga = { enable = mkEnableOption (lib.mdDoc "Tayga"); - package = mkOption { - type = types.package; - default = pkgs.tayga; - defaultText = lib.literalMD "pkgs.tayga"; - description = lib.mdDoc "This option specifies the TAYGA package to use."; - }; + package = mkPackageOption pkgs "tayga" { }; ipv4 = mkOption { type = types.submodule (versionOpts 4); diff --git a/nixos/modules/services/networking/teamspeak3.nix b/nixos/modules/services/networking/teamspeak3.nix index f09ef1a959ed..ff41539a6d9b 100644 --- a/nixos/modules/services/networking/teamspeak3.nix +++ b/nixos/modules/services/networking/teamspeak3.nix @@ -50,7 +50,7 @@ in }; defaultVoicePort = mkOption { - type = types.int; + type = types.port; default = 9987; description = lib.mdDoc '' Default UDP port for clients to connect to virtual servers - used for first virtual server, subsequent ones will open on incrementing port numbers by default. @@ -67,7 +67,7 @@ in }; fileTransferPort = mkOption { - type = types.int; + type = types.port; default = 30033; description = lib.mdDoc '' TCP port opened for file transfers. @@ -84,10 +84,26 @@ in }; queryPort = mkOption { - type = types.int; + type = types.port; default = 10011; description = lib.mdDoc '' - TCP port opened for ServerQuery connections. + TCP port opened for ServerQuery connections using the raw telnet protocol. + ''; + }; + + querySshPort = mkOption { + type = types.port; + default = 10022; + description = lib.mdDoc '' + TCP port opened for ServerQuery connections using the SSH protocol. + ''; + }; + + queryHttpPort = mkOption { + type = types.port; + default = 10080; + description = lib.mdDoc '' + TCP port opened for ServerQuery connections using the HTTP protocol. ''; }; @@ -128,7 +144,9 @@ in ]; networking.firewall = mkIf cfg.openFirewall { - allowedTCPPorts = [ cfg.fileTransferPort ] ++ optionals (cfg.openFirewallServerQuery) [ cfg.queryPort (cfg.queryPort + 11) ]; + allowedTCPPorts = [ cfg.fileTransferPort ] ++ (map (port: + mkIf cfg.openFirewallServerQuery port + ) [cfg.queryPort cfg.querySshPort cfg.queryHttpPort]); # subsequent vServers will use the incremented voice port, let's just open the next 10 allowedUDPPortRanges = [ { from = cfg.defaultVoicePort; to = cfg.defaultVoicePort + 10; } ]; }; @@ -141,13 +159,19 @@ in serviceConfig = { ExecStart = '' ${ts3}/bin/ts3server \ - dbsqlpath=${ts3}/lib/teamspeak/sql/ logpath=${cfg.logPath} \ - ${optionalString (cfg.voiceIP != null) "voice_ip=${cfg.voiceIP}"} \ + dbsqlpath=${ts3}/lib/teamspeak/sql/ \ + logpath=${cfg.logPath} \ + license_accepted=1 \ default_voice_port=${toString cfg.defaultVoicePort} \ - ${optionalString (cfg.fileTransferIP != null) "filetransfer_ip=${cfg.fileTransferIP}"} \ filetransfer_port=${toString cfg.fileTransferPort} \ + query_port=${toString cfg.queryPort} \ + query_ssh_port=${toString cfg.querySshPort} \ + query_http_port=${toString cfg.queryHttpPort} \ + ${optionalString (cfg.voiceIP != null) "voice_ip=${cfg.voiceIP}"} \ + ${optionalString (cfg.fileTransferIP != null) "filetransfer_ip=${cfg.fileTransferIP}"} \ ${optionalString (cfg.queryIP != null) "query_ip=${cfg.queryIP}"} \ - query_port=${toString cfg.queryPort} license_accepted=1 + ${optionalString (cfg.queryIP != null) "query_ssh_ip=${cfg.queryIP}"} \ + ${optionalString (cfg.queryIP != null) "query_http_ip=${cfg.queryIP}"} \ ''; WorkingDirectory = cfg.dataDir; User = user; diff --git a/nixos/modules/services/networking/teleport.nix b/nixos/modules/services/networking/teleport.nix index 399af711c0e1..add6b47315b1 100644 --- a/nixos/modules/services/networking/teleport.nix +++ b/nixos/modules/services/networking/teleport.nix @@ -11,12 +11,8 @@ in services.teleport = with lib.types; { enable = mkEnableOption (lib.mdDoc "the Teleport service"); - package = mkOption { - type = types.package; - default = pkgs.teleport; - defaultText = lib.literalMD "pkgs.teleport"; - example = lib.literalMD "pkgs.teleport_11"; - description = lib.mdDoc "The teleport package to use"; + package = mkPackageOption pkgs "teleport" { + example = "teleport_11"; }; settings = mkOption { diff --git a/nixos/modules/services/networking/thelounge.nix b/nixos/modules/services/networking/thelounge.nix index 321e46fb5d4d..92da2e6c254b 100644 --- a/nixos/modules/services/networking/thelounge.nix +++ b/nixos/modules/services/networking/thelounge.nix @@ -25,7 +25,7 @@ in options.services.thelounge = { enable = mkEnableOption (lib.mdDoc "The Lounge web IRC client"); - package = mkPackageOptionMD pkgs "thelounge" { }; + package = mkPackageOption pkgs "thelounge" { }; public = mkOption { type = types.bool; diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix index 7db83e6a584b..eb769f53901c 100644 --- a/nixos/modules/services/networking/tinc.nix +++ b/nixos/modules/services/networking/tinc.nix @@ -279,14 +279,7 @@ in ''; }; - package = mkOption { - type = types.package; - default = pkgs.tinc_pre; - defaultText = literalExpression "pkgs.tinc_pre"; - description = lib.mdDoc '' - The package to use for the tinc daemon's binary. - ''; - }; + package = mkPackageOption pkgs "tinc_pre" { }; chroot = mkOption { default = false; diff --git a/nixos/modules/services/networking/tinyproxy.nix b/nixos/modules/services/networking/tinyproxy.nix index 9bcd8bfd814b..8ff12b52f10c 100644 --- a/nixos/modules/services/networking/tinyproxy.nix +++ b/nixos/modules/services/networking/tinyproxy.nix @@ -28,7 +28,7 @@ in options = { services.tinyproxy = { enable = mkEnableOption (lib.mdDoc "Tinyproxy daemon"); - package = mkPackageOptionMD pkgs "tinyproxy" {}; + package = mkPackageOption pkgs "tinyproxy" {}; settings = mkOption { description = lib.mdDoc "Configuration for [tinyproxy](https://tinyproxy.github.io/)."; default = { }; @@ -85,7 +85,7 @@ in User = "tinyproxy"; Group = "tinyproxy"; Type = "simple"; - ExecStart = "${getExe pkgs.tinyproxy} -d -c ${configFile}"; + ExecStart = "${getExe cfg.package} -d -c ${configFile}"; ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID"; KillSignal = "SIGINT"; TimeoutStopSec = "30s"; diff --git a/nixos/modules/services/networking/tmate-ssh-server.nix b/nixos/modules/services/networking/tmate-ssh-server.nix index ff4ce0773309..6bee2721f9a7 100644 --- a/nixos/modules/services/networking/tmate-ssh-server.nix +++ b/nixos/modules/services/networking/tmate-ssh-server.nix @@ -18,12 +18,7 @@ in options.services.tmate-ssh-server = { enable = mkEnableOption (mdDoc "tmate ssh server"); - package = mkOption { - type = types.package; - description = mdDoc "The package containing tmate-ssh-server"; - defaultText = literalExpression "pkgs.tmate-ssh-server"; - default = pkgs.tmate-ssh-server; - }; + package = mkPackageOption pkgs "tmate-ssh-server" { }; host = mkOption { type = types.str; @@ -81,12 +76,12 @@ in [ (pkgs.writeShellApplication { name = "tmate-client-config"; - runtimeInputs = with pkgs;[ openssh coreutils sd ]; + runtimeInputs = with pkgs;[ openssh coreutils ]; text = '' RSA_SIG="$(ssh-keygen -l -E SHA256 -f "${keysDir}/ssh_host_rsa_key.pub" | cut -d ' ' -f 2)" ED25519_SIG="$(ssh-keygen -l -E SHA256 -f "${keysDir}/ssh_host_ed25519_key.pub" | cut -d ' ' -f 2)" - sd -sp '@ed25519_fingerprint@' "$ED25519_SIG" ${tmate-config} | \ - sd -sp '@rsa_fingerprint@' "$RSA_SIG" + sed "s|@ed25519_fingerprint@|$ED25519_SIG|g" ${tmate-config} | \ + sed "s|@rsa_fingerprint@|$RSA_SIG|g" ''; }) ]; diff --git a/nixos/modules/services/networking/tox-bootstrapd.nix b/nixos/modules/services/networking/tox-bootstrapd.nix index 5c7e7a4c2208..0f310a28d266 100644 --- a/nixos/modules/services/networking/tox-bootstrapd.nix +++ b/nixos/modules/services/networking/tox-bootstrapd.nix @@ -47,7 +47,7 @@ in lib.mdDoc '' Configuration for bootstrap daemon. See <https://github.com/irungentoo/toxcore/blob/master/other/bootstrap_daemon/tox-bootstrapd.conf> - and <http://wiki.tox.im/Nodes>. + and <https://wiki.tox.chat/users/nodes>. ''; }; }; diff --git a/nixos/modules/services/networking/trickster.nix b/nixos/modules/services/networking/trickster.nix index 0b696e412b4d..4b920ec446e0 100644 --- a/nixos/modules/services/networking/trickster.nix +++ b/nixos/modules/services/networking/trickster.nix @@ -20,14 +20,7 @@ in ''; }; - package = mkOption { - type = types.package; - default = pkgs.trickster; - defaultText = literalExpression "pkgs.trickster"; - description = lib.mdDoc '' - Package that should be used for trickster. - ''; - }; + package = mkPackageOption pkgs "trickster" { }; configFile = mkOption { type = types.nullOr types.path; diff --git a/nixos/modules/services/networking/trust-dns.nix b/nixos/modules/services/networking/trust-dns.nix index 4196d124a2ab..47020341024b 100644 --- a/nixos/modules/services/networking/trust-dns.nix +++ b/nixos/modules/services/networking/trust-dns.nix @@ -48,13 +48,11 @@ in options = { services.trust-dns = with lib; { enable = mkEnableOption (lib.mdDoc "trust-dns"); - package = mkOption { - type = types.package; - default = pkgs.trust-dns; - defaultText = "pkgs.trust-dns"; - description = mdDoc '' - Trust-dns package to use. - Only `bin/trust-dns` need be provided: the other trust-dns utilities (client and resolver) are not needed. + package = mkPackageOption pkgs "trust-dns" { + extraDescription = '' + ::: {.note} + The package must provide `meta.mainProgram` which names the server binayr; any other utilities (client, resolver) are not needed. + ::: ''; }; quiet = mkOption { @@ -135,7 +133,7 @@ in flags = (lib.optional cfg.debug "--debug") ++ (lib.optional cfg.quiet "--quiet"); flagsStr = builtins.concatStringsSep " " flags; in '' - ${cfg.package}/bin/trust-dns --config ${configFile} ${flagsStr} + ${cfg.package}/bin/${cfg.package.meta.mainProgram} --config ${configFile} ${flagsStr} ''; Type = "simple"; Restart = "on-failure"; diff --git a/nixos/modules/services/networking/twingate.nix b/nixos/modules/services/networking/twingate.nix index 03c68fc874f0..6874b1c18b57 100644 --- a/nixos/modules/services/networking/twingate.nix +++ b/nixos/modules/services/networking/twingate.nix @@ -6,7 +6,7 @@ in { options.services.twingate = { enable = lib.mkEnableOption (lib.mdDoc "Twingate Client daemon"); - package = lib.mkPackageOptionMD pkgs "twingate" { }; + package = lib.mkPackageOption pkgs "twingate" { }; }; config = lib.mkIf cfg.enable { diff --git a/nixos/modules/services/networking/ucarp.nix b/nixos/modules/services/networking/ucarp.nix index 1214cec63f54..56799fe00ade 100644 --- a/nixos/modules/services/networking/ucarp.nix +++ b/nixos/modules/services/networking/ucarp.nix @@ -143,16 +143,11 @@ in { default = null; }; - package = mkOption { - type = types.package; - description = lib.mdDoc '' - Package that should be used for ucarp. - + package = mkPackageOption pkgs "ucarp" { + extraDescription = '' Please note that the default package, pkgs.ucarp, has not received any upstream updates for a long time and can be considered as unmaintained. ''; - default = pkgs.ucarp; - defaultText = literalExpression "pkgs.ucarp"; }; }; diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix index 0426dbb0c83c..616b32f11797 100644 --- a/nixos/modules/services/networking/unbound.nix +++ b/nixos/modules/services/networking/unbound.nix @@ -42,12 +42,7 @@ in { enable = mkEnableOption (lib.mdDoc "Unbound domain name server"); - package = mkOption { - type = types.package; - default = pkgs.unbound-with-systemd; - defaultText = literalExpression "pkgs.unbound-with-systemd"; - description = lib.mdDoc "The unbound package to use"; - }; + package = mkPackageOption pkgs "unbound-with-systemd" { }; user = mkOption { type = types.str; @@ -166,7 +161,7 @@ in { services.unbound.settings = { server = { directory = mkDefault cfg.stateDir; - username = cfg.user; + username = ''""''; chroot = ''""''; pidfile = ''""''; # when running under systemd there is no need to daemonize @@ -245,14 +240,13 @@ in { NotifyAccess = "main"; Type = "notify"; - # FIXME: Which of these do we actually need, can we drop the chroot flag? AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" + "CAP_NET_RAW" # needed if ip-transparent is set to true + ]; + CapabilityBoundingSet = [ + "CAP_NET_BIND_SERVICE" "CAP_NET_RAW" - "CAP_SETGID" - "CAP_SETUID" - "CAP_SYS_CHROOT" - "CAP_SYS_RESOURCE" ]; User = cfg.user; @@ -266,22 +260,19 @@ in { ProtectControlGroups = true; ProtectKernelModules = true; ProtectSystem = "strict"; + ProtectClock = true; + ProtectHostname = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + ProtectKernelLogs = true; + ProtectKernelTunables = true; RuntimeDirectory = "unbound"; ConfigurationDirectory = "unbound"; StateDirectory = "unbound"; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_NETLINK" "AF_UNIX" ]; RestrictRealtime = true; SystemCallArchitectures = "native"; - SystemCallFilter = [ - "~@clock" - "@cpu-emulation" - "@debug" - "@keyring" - "@module" - "mount" - "@obsolete" - "@resources" - ]; + SystemCallFilter = [ "@system-service" ]; RestrictNamespaces = true; LockPersonality = true; RestrictSUIDSGID = true; diff --git a/nixos/modules/services/networking/unifi.nix b/nixos/modules/services/networking/unifi.nix index 37a739f41d48..8eb29f2bcdb6 100644 --- a/nixos/modules/services/networking/unifi.nix +++ b/nixos/modules/services/networking/unifi.nix @@ -1,60 +1,54 @@ { config, options, lib, pkgs, utils, ... }: -with lib; let cfg = config.services.unifi; stateDir = "/var/lib/unifi"; - cmd = '' - @${cfg.jrePackage}/bin/java java \ - ${optionalString (lib.versionAtLeast (lib.getVersion cfg.jrePackage) "16") - "--add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED " - + "--add-opens java.base/sun.security.util=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED " - + "--add-opens java.rmi/sun.rmi.transport=ALL-UNNAMED"} \ - ${optionalString (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m"} \ - ${optionalString (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m"} \ - -jar ${stateDir}/lib/ace.jar - ''; + cmd = lib.escapeShellArgs ([ "@${cfg.jrePackage}/bin/java" "java" ] + ++ lib.optionals (lib.versionAtLeast (lib.getVersion cfg.jrePackage) "16") [ + "--add-opens=java.base/java.lang=ALL-UNNAMED" + "--add-opens=java.base/java.time=ALL-UNNAMED" + "--add-opens=java.base/sun.security.util=ALL-UNNAMED" + "--add-opens=java.base/java.io=ALL-UNNAMED" + "--add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED" + ] + ++ (lib.optional (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m") + ++ (lib.optional (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m") + ++ cfg.extraJvmOptions + ++ [ "-jar" "${stateDir}/lib/ace.jar" ]); in { options = { - services.unifi.enable = mkOption { - type = types.bool; + services.unifi.enable = lib.mkOption { + type = lib.types.bool; default = false; description = lib.mdDoc '' Whether or not to enable the unifi controller service. ''; }; - services.unifi.jrePackage = mkOption { - type = types.package; + services.unifi.jrePackage = lib.mkOption { + type = lib.types.package; default = if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.5") then pkgs.jdk17_headless else if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3") then pkgs.jdk11 else pkgs.jre8; - defaultText = literalExpression ''if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.5") then pkgs.jdk17_headless else if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3" then pkgs.jdk11 else pkgs.jre8''; + defaultText = lib.literalExpression ''if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.5") then pkgs.jdk17_headless else if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3" then pkgs.jdk11 else pkgs.jre8''; description = lib.mdDoc '' The JRE package to use. Check the release notes to ensure it is supported. ''; }; - services.unifi.unifiPackage = mkOption { - type = types.package; - default = pkgs.unifi5; - defaultText = literalExpression "pkgs.unifi5"; - description = lib.mdDoc '' - The unifi package to use. - ''; - }; + services.unifi.unifiPackage = lib.mkPackageOption pkgs "unifi5" { }; - services.unifi.mongodbPackage = mkOption { - type = types.package; - default = pkgs.mongodb-4_4; - defaultText = literalExpression "pkgs.mongodb"; - description = lib.mdDoc '' - The mongodb package to use. Please note: unifi7 officially only supports mongodb up until 3.6 but works with 4.4. + services.unifi.mongodbPackage = lib.mkPackageOption pkgs "mongodb" { + default = "mongodb-4_4"; + extraDescription = '' + ::: {.note} + unifi7 officially only supports mongodb up until 3.6 but works with 4.4. + ::: ''; }; - services.unifi.openFirewall = mkOption { - type = types.bool; + services.unifi.openFirewall = lib.mkOption { + type = lib.types.bool; default = false; description = lib.mdDoc '' Whether or not to open the minimum required ports on the firewall. @@ -65,8 +59,8 @@ in ''; }; - services.unifi.initialJavaHeapSize = mkOption { - type = types.nullOr types.int; + services.unifi.initialJavaHeapSize = lib.mkOption { + type = with lib.types; nullOr int; default = null; example = 1024; description = lib.mdDoc '' @@ -75,8 +69,8 @@ in ''; }; - services.unifi.maximumJavaHeapSize = mkOption { - type = types.nullOr types.int; + services.unifi.maximumJavaHeapSize = lib.mkOption { + type = with lib.types; nullOr int; default = null; example = 4096; description = lib.mdDoc '' @@ -85,9 +79,18 @@ in ''; }; + services.unifi.extraJvmOptions = lib.mkOption { + type = with lib.types; listOf str; + default = [ ]; + example = lib.literalExpression ''["-Xlog:gc"]''; + description = lib.mdDoc '' + Set extra options to pass to the JVM. + ''; + }; + }; - config = mkIf cfg.enable { + config = lib.mkIf cfg.enable { users.users.unifi = { isSystemUser = true; @@ -97,7 +100,7 @@ in }; users.groups.unifi = {}; - networking.firewall = mkIf cfg.openFirewall { + networking.firewall = lib.mkIf cfg.openFirewall { # https://help.ubnt.com/hc/en-us/articles/218506997 allowedTCPPorts = [ 8080 # Port for UAP to inform controller. @@ -123,8 +126,8 @@ in serviceConfig = { Type = "simple"; - ExecStart = "${(removeSuffix "\n" cmd)} start"; - ExecStop = "${(removeSuffix "\n" cmd)} stop"; + ExecStart = "${cmd} start"; + ExecStop = "${cmd} stop"; Restart = "on-failure"; TimeoutSec = "5min"; User = "unifi"; @@ -166,7 +169,7 @@ in StateDirectory = "unifi"; RuntimeDirectory = "unifi"; LogsDirectory = "unifi"; - CacheDirectory= "unifi"; + CacheDirectory = "unifi"; TemporaryFileSystem = [ # required as we want to create bind mounts below @@ -176,7 +179,7 @@ in # We must create the binary directories as bind mounts instead of symlinks # This is because the controller resolves all symlinks to absolute paths # to be used as the working directory. - BindPaths = [ + BindPaths = [ "/var/log/unifi:${stateDir}/logs" "/run/unifi:${stateDir}/run" "${cfg.unifiPackage}/dl:${stateDir}/dl" @@ -194,7 +197,7 @@ in }; imports = [ - (mkRemovedOptionModule [ "services" "unifi" "dataDir" ] "You should move contents of dataDir to /var/lib/unifi/data" ) - (mkRenamedOptionModule [ "services" "unifi" "openPorts" ] [ "services" "unifi" "openFirewall" ]) + (lib.mkRemovedOptionModule [ "services" "unifi" "dataDir" ] "You should move contents of dataDir to /var/lib/unifi/data") + (lib.mkRenamedOptionModule [ "services" "unifi" "openPorts" ] [ "services" "unifi" "openFirewall" ]) ]; } diff --git a/nixos/modules/services/networking/v2ray.nix b/nixos/modules/services/networking/v2ray.nix index ba2aa5bc1de7..3e1895fbe20c 100644 --- a/nixos/modules/services/networking/v2ray.nix +++ b/nixos/modules/services/networking/v2ray.nix @@ -16,14 +16,7 @@ with lib; ''; }; - package = mkOption { - type = types.package; - default = pkgs.v2ray; - defaultText = literalExpression "pkgs.v2ray"; - description = lib.mdDoc '' - Which v2ray package to use. - ''; - }; + package = mkPackageOption pkgs "v2ray" { }; configFile = mkOption { type = types.nullOr types.str; diff --git a/nixos/modules/services/networking/vdirsyncer.nix b/nixos/modules/services/networking/vdirsyncer.nix index f9b880c763e3..165dc70f0876 100644 --- a/nixos/modules/services/networking/vdirsyncer.nix +++ b/nixos/modules/services/networking/vdirsyncer.nix @@ -20,9 +20,11 @@ let else pkgs.writeText "vdirsyncer-${name}.conf" (toIniJson ( { - general = cfg'.config.general // (lib.optionalAttrs (cfg'.config.statusPath == null) { - status_path = "/var/lib/vdirsyncer/${name}"; - }); + general = cfg'.config.general // { + status_path = if cfg'.config.statusPath == null + then "/var/lib/vdirsyncer/${name}" + else cfg'.config.statusPath; + }; } // ( mapAttrs' (name: nameValuePair "pair ${name}") cfg'.config.pairs ) // ( @@ -71,7 +73,7 @@ in services.vdirsyncer = { enable = mkEnableOption (mdDoc "vdirsyncer"); - package = mkPackageOptionMD pkgs "vdirsyncer" {}; + package = mkPackageOption pkgs "vdirsyncer" {}; jobs = mkOption { description = mdDoc "vdirsyncer job configurations"; diff --git a/nixos/modules/services/networking/wasabibackend.nix b/nixos/modules/services/networking/wasabibackend.nix index 938145b35ee8..e3a48afd2a2c 100644 --- a/nixos/modules/services/networking/wasabibackend.nix +++ b/nixos/modules/services/networking/wasabibackend.nix @@ -119,6 +119,7 @@ in { systemd.services.wasabibackend = { description = "wasabibackend server"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; environment = { DOTNET_PRINT_TELEMETRY_MESSAGE = "false"; diff --git a/nixos/modules/services/networking/webhook.nix b/nixos/modules/services/networking/webhook.nix index 2a78491941cf..b020db6961c3 100644 --- a/nixos/modules/services/networking/webhook.nix +++ b/nixos/modules/services/networking/webhook.nix @@ -36,7 +36,7 @@ in { which execute configured commands for any person or service that knows the URL ''); - package = mkPackageOptionMD pkgs "webhook" {}; + package = mkPackageOption pkgs "webhook" {}; user = mkOption { type = types.str; default = defaultUser; diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix index d4099be12a27..d36be87daf60 100644 --- a/nixos/modules/services/networking/wireguard.nix +++ b/nixos/modules/services/networking/wireguard.nix @@ -586,6 +586,7 @@ in }) all_peers; boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard; + boot.kernelModules = [ "wireguard" ]; environment.systemPackages = [ pkgs.wireguard-tools ]; systemd.services = diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix index 90d9c68433cf..4586550ed75e 100644 --- a/nixos/modules/services/networking/wpa_supplicant.nix +++ b/nixos/modules/services/networking/wpa_supplicant.nix @@ -107,6 +107,10 @@ let stopIfChanged = false; path = [ package ]; + # if `userControl.enable`, the supplicant automatically changes the permissions + # and owning group of the runtime dir; setting `umask` ensures the generated + # config file isn't readable (except to root); see nixpkgs#267693 + serviceConfig.UMask = "066"; serviceConfig.RuntimeDirectory = "wpa_supplicant"; serviceConfig.RuntimeDirectoryMode = "700"; serviceConfig.EnvironmentFile = mkIf (cfg.environmentFile != null) diff --git a/nixos/modules/services/networking/wstunnel.nix b/nixos/modules/services/networking/wstunnel.nix index 3c3ecc3e04d7..2762c85651f4 100644 --- a/nixos/modules/services/networking/wstunnel.nix +++ b/nixos/modules/services/networking/wstunnel.nix @@ -48,7 +48,7 @@ let default = true; }; - package = mkPackageOptionMD pkgs "wstunnel" {}; + package = mkPackageOption pkgs "wstunnel" {}; autoStart = mkOption { description = mdDoc "Whether this tunnel server should be started automatically."; diff --git a/nixos/modules/services/networking/x2goserver.nix b/nixos/modules/services/networking/x2goserver.nix index 1242229a0b60..f1eba9fafc1c 100644 --- a/nixos/modules/services/networking/x2goserver.nix +++ b/nixos/modules/services/networking/x2goserver.nix @@ -160,5 +160,8 @@ in { security.sudo.extraConfig = '' Defaults env_keep+=QT_GRAPHICSSYSTEM ''; + security.sudo-rs.extraConfig = '' + Defaults env_keep+=QT_GRAPHICSSYSTEM + ''; }; } diff --git a/nixos/modules/services/networking/xandikos.nix b/nixos/modules/services/networking/xandikos.nix index 6d1ddc74c719..147f07ac546d 100644 --- a/nixos/modules/services/networking/xandikos.nix +++ b/nixos/modules/services/networking/xandikos.nix @@ -11,12 +11,7 @@ in services.xandikos = { enable = mkEnableOption (lib.mdDoc "Xandikos CalDAV and CardDAV server"); - package = mkOption { - type = types.package; - default = pkgs.xandikos; - defaultText = literalExpression "pkgs.xandikos"; - description = lib.mdDoc "The Xandikos package to use."; - }; + package = mkPackageOption pkgs "xandikos" { }; address = mkOption { type = types.str; diff --git a/nixos/modules/services/networking/xray.nix b/nixos/modules/services/networking/xray.nix index 83655a2f88ef..56c7887b3308 100644 --- a/nixos/modules/services/networking/xray.nix +++ b/nixos/modules/services/networking/xray.nix @@ -16,14 +16,7 @@ with lib; ''; }; - package = mkOption { - type = types.package; - default = pkgs.xray; - defaultText = literalExpression "pkgs.xray"; - description = lib.mdDoc '' - Which xray package to use. - ''; - }; + package = mkPackageOption pkgs "xray" { }; settingsFile = mkOption { type = types.nullOr types.path; diff --git a/nixos/modules/services/networking/xrdp.nix b/nixos/modules/services/networking/xrdp.nix index 218b440aab3c..7e6634cd239a 100644 --- a/nixos/modules/services/networking/xrdp.nix +++ b/nixos/modules/services/networking/xrdp.nix @@ -4,14 +4,17 @@ with lib; let cfg = config.services.xrdp; + confDir = pkgs.runCommand "xrdp.conf" { preferLocalBuild = true; } '' - mkdir $out + mkdir -p $out - cp ${cfg.package}/etc/xrdp/{km-*,xrdp,sesman,xrdp_keyboard}.ini $out + cp -r ${cfg.package}/etc/xrdp/* $out + chmod -R +w $out cat > $out/startwm.sh <<EOF #!/bin/sh . /etc/profile + ${lib.optionalString cfg.audio.enable "${cfg.audio.package}/libexec/pulsaudio-xrdp-module/pulseaudio_xrdp_init"} ${cfg.defaultWindowManager} EOF chmod +x $out/startwm.sh @@ -25,13 +28,17 @@ let substituteInPlace $out/sesman.ini \ --replace LogFile=xrdp-sesman.log LogFile=/dev/null \ - --replace EnableSyslog=1 EnableSyslog=0 + --replace EnableSyslog=1 EnableSyslog=0 \ + --replace startwm.sh $out/startwm.sh \ + --replace reconnectwm.sh $out/reconnectwm.sh \ # Ensure that clipboard works for non-ASCII characters sed -i -e '/.*SessionVariables.*/ a\ LANG=${config.i18n.defaultLocale}\ LOCALE_ARCHIVE=${config.i18n.glibcLocales}/lib/locale/locale-archive ' $out/sesman.ini + + ${cfg.extraConfDirCommands} ''; in { @@ -44,13 +51,11 @@ in enable = mkEnableOption (lib.mdDoc "xrdp, the Remote Desktop Protocol server"); - package = mkOption { - type = types.package; - default = pkgs.xrdp; - defaultText = literalExpression "pkgs.xrdp"; - description = lib.mdDoc '' - The package to use for the xrdp daemon's binary. - ''; + package = mkPackageOptionMD pkgs "xrdp" { }; + + audio = { + enable = mkEnableOption (lib.mdDoc "audio support for xrdp sessions. So far it only works with PulseAudio sessions on the server side. No PipeWire support yet"); + package = mkPackageOptionMD pkgs "pulseaudio-module-xrdp" {}; }; port = mkOption { @@ -100,86 +105,117 @@ in confDir = mkOption { type = types.path; default = confDir; - defaultText = literalMD "generated from configuration"; - description = lib.mdDoc "The location of the config files for xrdp."; + internal = true; + description = lib.mdDoc '' + Configuration directory of xrdp and sesman. + + Changes to this must be made through extraConfDirCommands. + ''; + readOnly = true; + }; + + extraConfDirCommands = mkOption { + type = types.str; + default = ""; + description = lib.mdDoc '' + Extra commands to run on the default confDir derivation. + ''; + example = '' + substituteInPlace $out/sesman.ini \ + --replace LogLevel=INFO LogLevel=DEBUG \ + --replace LogFile=/dev/null LogFile=/var/log/xrdp.log + ''; }; }; }; - ###### implementation - config = mkIf cfg.enable { + config = lib.mkMerge [ + (mkIf cfg.audio.enable { + environment.systemPackages = [ cfg.audio.package ]; # needed for autostart - networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ]; + hardware.pulseaudio.extraModules = [ cfg.audio.package ]; + }) - # xrdp can run X11 program even if "services.xserver.enable = false" - xdg = { - autostart.enable = true; - menus.enable = true; - mime.enable = true; - icons.enable = true; - }; + (mkIf cfg.enable { - fonts.enableDefaultPackages = mkDefault true; - - systemd = { - services.xrdp = { - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - description = "xrdp daemon"; - requires = [ "xrdp-sesman.service" ]; - preStart = '' - # prepare directory for unix sockets (the sockets will be owned by loggedinuser:xrdp) - mkdir -p /tmp/.xrdp || true - chown xrdp:xrdp /tmp/.xrdp - chmod 3777 /tmp/.xrdp - - # generate a self-signed certificate - if [ ! -s ${cfg.sslCert} -o ! -s ${cfg.sslKey} ]; then - mkdir -p $(dirname ${cfg.sslCert}) || true - mkdir -p $(dirname ${cfg.sslKey}) || true - ${pkgs.openssl.bin}/bin/openssl req -x509 -newkey rsa:2048 -sha256 -nodes -days 365 \ - -subj /C=US/ST=CA/L=Sunnyvale/O=xrdp/CN=www.xrdp.org \ - -config ${cfg.package}/share/xrdp/openssl.conf \ - -keyout ${cfg.sslKey} -out ${cfg.sslCert} - chown root:xrdp ${cfg.sslKey} ${cfg.sslCert} - chmod 440 ${cfg.sslKey} ${cfg.sslCert} - fi - if [ ! -s /run/xrdp/rsakeys.ini ]; then - mkdir -p /run/xrdp - ${cfg.package}/bin/xrdp-keygen xrdp /run/xrdp/rsakeys.ini - fi - ''; - serviceConfig = { - User = "xrdp"; - Group = "xrdp"; - PermissionsStartOnly = true; - ExecStart = "${cfg.package}/bin/xrdp --nodaemon --port ${toString cfg.port} --config ${cfg.confDir}/xrdp.ini"; - }; + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ]; + + # xrdp can run X11 program even if "services.xserver.enable = false" + xdg = { + autostart.enable = true; + menus.enable = true; + mime.enable = true; + icons.enable = true; }; - services.xrdp-sesman = { - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - description = "xrdp session manager"; - restartIfChanged = false; # do not restart on "nixos-rebuild switch". like "display-manager", it can have many interactive programs as children - serviceConfig = { - ExecStart = "${cfg.package}/bin/xrdp-sesman --nodaemon --config ${cfg.confDir}/sesman.ini"; - ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID"; + fonts.enableDefaultPackages = mkDefault true; + + environment.etc."xrdp".source = "${confDir}/*"; + + systemd = { + services.xrdp = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = "xrdp daemon"; + requires = [ "xrdp-sesman.service" ]; + preStart = '' + # prepare directory for unix sockets (the sockets will be owned by loggedinuser:xrdp) + mkdir -p /tmp/.xrdp || true + chown xrdp:xrdp /tmp/.xrdp + chmod 3777 /tmp/.xrdp + + # generate a self-signed certificate + if [ ! -s ${cfg.sslCert} -o ! -s ${cfg.sslKey} ]; then + mkdir -p $(dirname ${cfg.sslCert}) || true + mkdir -p $(dirname ${cfg.sslKey}) || true + ${lib.getExe pkgs.openssl} req -x509 -newkey rsa:2048 -sha256 -nodes -days 365 \ + -subj /C=US/ST=CA/L=Sunnyvale/O=xrdp/CN=www.xrdp.org \ + -config ${cfg.package}/share/xrdp/openssl.conf \ + -keyout ${cfg.sslKey} -out ${cfg.sslCert} + chown root:xrdp ${cfg.sslKey} ${cfg.sslCert} + chmod 440 ${cfg.sslKey} ${cfg.sslCert} + fi + if [ ! -s /run/xrdp/rsakeys.ini ]; then + mkdir -p /run/xrdp + ${pkgs.xrdp}/bin/xrdp-keygen xrdp /run/xrdp/rsakeys.ini + fi + ''; + serviceConfig = { + User = "xrdp"; + Group = "xrdp"; + PermissionsStartOnly = true; + ExecStart = "${pkgs.xrdp}/bin/xrdp --nodaemon --port ${toString cfg.port} --config ${confDir}/xrdp.ini"; + }; + }; + + services.xrdp-sesman = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = "xrdp session manager"; + restartIfChanged = false; # do not restart on "nixos-rebuild switch". like "display-manager", it can have many interactive programs as children + serviceConfig = { + ExecStart = "${pkgs.xrdp}/bin/xrdp-sesman --nodaemon --config ${confDir}/sesman.ini"; + ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID"; + }; }; + }; - }; + users.users.xrdp = { + description = "xrdp daemon user"; + isSystemUser = true; + group = "xrdp"; + }; + users.groups.xrdp = {}; - users.users.xrdp = { - description = "xrdp daemon user"; - isSystemUser = true; - group = "xrdp"; - }; - users.groups.xrdp = {}; + security.pam.services.xrdp-sesman = { + allowNullPassword = true; + startSession = true; + }; - security.pam.services.xrdp-sesman = { allowNullPassword = true; startSession = true; }; - }; + }) + ]; } diff --git a/nixos/modules/services/networking/yggdrasil.nix b/nixos/modules/services/networking/yggdrasil.nix index 56d81fb04013..9173e7eb3457 100644 --- a/nixos/modules/services/networking/yggdrasil.nix +++ b/nixos/modules/services/networking/yggdrasil.nix @@ -108,12 +108,7 @@ in ''; }; - package = mkOption { - type = package; - default = pkgs.yggdrasil; - defaultText = literalExpression "pkgs.yggdrasil"; - description = lib.mdDoc "Yggdrasil package to use."; - }; + package = mkPackageOption pkgs "yggdrasil" { }; persistentKeys = mkEnableOption (lib.mdDoc '' persistent keys. If enabled then keys will be generated once and Yggdrasil @@ -142,16 +137,24 @@ in message = "networking.enableIPv6 must be true for yggdrasil to work"; }]; - system.activationScripts.yggdrasil = mkIf cfg.persistentKeys '' - if [ ! -e ${keysPath} ] - then - mkdir --mode=700 -p ${builtins.dirOf keysPath} - ${binYggdrasil} -genconf -json \ - | ${pkgs.jq}/bin/jq \ - 'to_entries|map(select(.key|endswith("Key")))|from_entries' \ - > ${keysPath} - fi - ''; + # This needs to be a separate service. The yggdrasil service fails if + # this is put into its preStart. + systemd.services.yggdrasil-persistent-keys = lib.mkIf cfg.persistentKeys { + wantedBy = [ "multi-user.target" ]; + before = [ "yggdrasil.service" ]; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + script = '' + if [ ! -e ${keysPath} ] + then + mkdir --mode=700 -p ${builtins.dirOf keysPath} + ${binYggdrasil} -genconf -json \ + | ${pkgs.jq}/bin/jq \ + 'to_entries|map(select(.key|endswith("Key")))|from_entries' \ + > ${keysPath} + fi + ''; + }; systemd.services.yggdrasil = { description = "Yggdrasil Network Service"; diff --git a/nixos/modules/services/networking/zeronet.nix b/nixos/modules/services/networking/zeronet.nix index 1f3711bd0d72..7e88a8b346d9 100644 --- a/nixos/modules/services/networking/zeronet.nix +++ b/nixos/modules/services/networking/zeronet.nix @@ -1,7 +1,8 @@ { config, lib, pkgs, ... }: let - inherit (lib) generators literalExpression mkEnableOption mkIf mkOption recursiveUpdate types; + inherit (lib) generators literalExpression mkEnableOption mkPackageOption + mkIf mkOption recursiveUpdate types; cfg = config.services.zeronet; dataDir = "/var/lib/zeronet"; configFile = pkgs.writeText "zeronet.conf" (generators.toINI {} (recursiveUpdate defaultSettings cfg.settings)); @@ -19,12 +20,7 @@ in with lib; { options.services.zeronet = { enable = mkEnableOption (lib.mdDoc "zeronet"); - package = mkOption { - type = types.package; - default = pkgs.zeronet; - defaultText = literalExpression "pkgs.zeronet"; - description = lib.mdDoc "ZeroNet package to use"; - }; + package = mkPackageOption pkgs "zeronet" { }; settings = mkOption { type = with types; attrsOf (oneOf [ str int bool (listOf str) ]); diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix index f78fd8642ba0..60615d553041 100644 --- a/nixos/modules/services/networking/zerotierone.nix +++ b/nixos/modules/services/networking/zerotierone.nix @@ -4,6 +4,8 @@ with lib; let cfg = config.services.zerotierone; + localConfFile = pkgs.writeText "zt-local.conf" (builtins.toJSON cfg.localConf); + localConfFilePath = "/var/lib/zerotier-one/local.conf"; in { options.services.zerotierone.enable = mkEnableOption (lib.mdDoc "ZeroTierOne"); @@ -27,13 +29,19 @@ in ''; }; - options.services.zerotierone.package = mkOption { - default = pkgs.zerotierone; - defaultText = literalExpression "pkgs.zerotierone"; - type = types.package; - description = lib.mdDoc '' - ZeroTier One package to use. + options.services.zerotierone.package = mkPackageOption pkgs "zerotierone" { }; + + options.services.zerotierone.localConf = mkOption { + default = null; + description = mdDoc '' + Optional configuration to be written to the Zerotier JSON-based local.conf. + If set, the configuration will be symlinked to `/var/lib/zerotier-one/local.conf` at build time. + To understand the configuration format, refer to https://docs.zerotier.com/config/#local-configuration-options. ''; + example = { + settings.allowTcpFallbackRelay = false; + }; + type = types.nullOr types.attrs; }; config = mkIf cfg.enable { @@ -52,7 +60,17 @@ in chown -R root:root /var/lib/zerotier-one '' + (concatMapStrings (netId: '' touch "/var/lib/zerotier-one/networks.d/${netId}.conf" - '') cfg.joinNetworks); + '') cfg.joinNetworks) + optionalString (cfg.localConf != null) '' + if [ -L "${localConfFilePath}" ] + then + rm ${localConfFilePath} + elif [ -f "${localConfFilePath}" ] + then + mv ${localConfFilePath} ${localConfFilePath}.bak + fi + ln -s ${localConfFile} ${localConfFilePath} + ''; + serviceConfig = { ExecStart = "${cfg.package}/bin/zerotier-one -p${toString cfg.port}"; Restart = "always"; diff --git a/nixos/modules/services/networking/znc/default.nix b/nixos/modules/services/networking/znc/default.nix index d3ba4a524197..e15233293cf2 100644 --- a/nixos/modules/services/networking/znc/default.nix +++ b/nixos/modules/services/networking/znc/default.nix @@ -243,6 +243,7 @@ in systemd.services.znc = { description = "ZNC Server"; wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; after = [ "network-online.target" ]; serviceConfig = { User = cfg.user; |