diff options
author | Alyssa Ross <hi@alyssa.is> | 2021-06-22 15:01:47 +0000 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2021-06-22 16:57:59 +0000 |
commit | 633cab0ecb07627706c6b523e219490f019eaab5 (patch) | |
tree | 4fb472bdfe2723037dad53dc1b8a87c939015f5e /nixpkgs/nixos/modules/services/networking | |
parent | ffb691c199e7e0cbc4e45e5310779c9e3f7c2a73 (diff) | |
parent | 432fc2d9a67f92e05438dff5fdc2b39d33f77997 (diff) | |
download | nixlib-633cab0ecb07627706c6b523e219490f019eaab5.tar nixlib-633cab0ecb07627706c6b523e219490f019eaab5.tar.gz nixlib-633cab0ecb07627706c6b523e219490f019eaab5.tar.bz2 nixlib-633cab0ecb07627706c6b523e219490f019eaab5.tar.lz nixlib-633cab0ecb07627706c6b523e219490f019eaab5.tar.xz nixlib-633cab0ecb07627706c6b523e219490f019eaab5.tar.zst nixlib-633cab0ecb07627706c6b523e219490f019eaab5.zip |
Merge commit '432fc2d9a67f92e05438dff5fdc2b39d33f77997'
# Conflicts: # nixpkgs/pkgs/applications/editors/emacs/elisp-packages/elpa-generated.nix # nixpkgs/pkgs/applications/networking/mailreaders/thunderbird/default.nix # nixpkgs/pkgs/applications/window-managers/sway/default.nix # nixpkgs/pkgs/build-support/rust/default.nix # nixpkgs/pkgs/development/go-modules/generic/default.nix
Diffstat (limited to 'nixpkgs/nixos/modules/services/networking')
29 files changed, 1263 insertions, 559 deletions
diff --git a/nixpkgs/nixos/modules/services/networking/adguardhome.nix b/nixpkgs/nixos/modules/services/networking/adguardhome.nix new file mode 100644 index 000000000000..4388ef2b7e57 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/adguardhome.nix @@ -0,0 +1,78 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.adguardhome; + + args = concatStringsSep " " ([ + "--no-check-update" + "--pidfile /run/AdGuardHome/AdGuardHome.pid" + "--work-dir /var/lib/AdGuardHome/" + "--config /var/lib/AdGuardHome/AdGuardHome.yaml" + "--host ${cfg.host}" + "--port ${toString cfg.port}" + ] ++ cfg.extraArgs); + +in +{ + options.services.adguardhome = with types; { + enable = mkEnableOption "AdGuard Home network-wide ad blocker"; + + host = mkOption { + default = "0.0.0.0"; + type = str; + description = '' + Host address to bind HTTP server to. + ''; + }; + + port = mkOption { + default = 3000; + type = port; + description = '' + Port to serve HTTP pages on. + ''; + }; + + openFirewall = mkOption { + default = false; + type = bool; + description = '' + Open ports in the firewall for the AdGuard Home web interface. Does not + open the port needed to access the DNS resolver. + ''; + }; + + extraArgs = mkOption { + default = [ ]; + type = listOf str; + description = '' + Extra command line parameters to be passed to the adguardhome binary. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.adguardhome = { + description = "AdGuard Home: Network-level blocker"; + after = [ "syslog.target" "network.target" ]; + wantedBy = [ "multi-user.target" ]; + unitConfig = { + StartLimitIntervalSec = 5; + StartLimitBurst = 10; + }; + serviceConfig = { + DynamicUser = true; + ExecStart = "${pkgs.adguardhome}/bin/adguardhome ${args}"; + AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; + Restart = "always"; + RestartSec = 10; + RuntimeDirectory = "AdGuardHome"; + StateDirectory = "AdGuardHome"; + }; + }; + + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/babeld.nix b/nixpkgs/nixos/modules/services/networking/babeld.nix index 97dca002a007..5e14283179ac 100644 --- a/nixpkgs/nixos/modules/services/networking/babeld.nix +++ b/nixpkgs/nixos/modules/services/networking/babeld.nix @@ -32,6 +32,8 @@ in { + meta.maintainers = with maintainers; [ hexa ]; + ###### interface options = { diff --git a/nixpkgs/nixos/modules/services/networking/bind.nix b/nixpkgs/nixos/modules/services/networking/bind.nix index e507e8ce9eeb..20eef2c3455b 100644 --- a/nixpkgs/nixos/modules/services/networking/bind.nix +++ b/nixpkgs/nixos/modules/services/networking/bind.nix @@ -8,32 +8,37 @@ let bindUser = "named"; - bindZoneOptions = { - name = mkOption { - type = types.str; - description = "Name of the zone."; - }; - master = mkOption { - description = "Master=false means slave server"; - type = types.bool; - }; - file = mkOption { - type = types.either types.str types.path; - description = "Zone file resource records contain columns of data, separated by whitespace, that define the record."; - }; - masters = mkOption { - type = types.listOf types.str; - description = "List of servers for inclusion in stub and secondary zones."; - }; - slaves = mkOption { - type = types.listOf types.str; - description = "Addresses who may request zone transfers."; - default = []; - }; - extraConfig = mkOption { - type = types.str; - description = "Extra zone config to be appended at the end of the zone section."; - default = ""; + bindZoneCoerce = list: builtins.listToAttrs (lib.forEach list (zone: { name = zone.name; value = zone; })); + + bindZoneOptions = { name, config, ... }: { + options = { + name = mkOption { + type = types.str; + default = name; + description = "Name of the zone."; + }; + master = mkOption { + description = "Master=false means slave server"; + type = types.bool; + }; + file = mkOption { + type = types.either types.str types.path; + description = "Zone file resource records contain columns of data, separated by whitespace, that define the record."; + }; + masters = mkOption { + type = types.listOf types.str; + description = "List of servers for inclusion in stub and secondary zones."; + }; + slaves = mkOption { + type = types.listOf types.str; + description = "Addresses who may request zone transfers."; + default = [ ]; + }; + extraConfig = mkOption { + type = types.str; + description = "Extra zone config to be appended at the end of the zone section."; + default = ""; + }; }; }; @@ -84,7 +89,7 @@ let ${extraConfig} }; '') - cfg.zones } + (attrValues cfg.zones) } ''; in @@ -100,7 +105,7 @@ in enable = mkEnableOption "BIND domain name server"; cacheNetworks = mkOption { - default = ["127.0.0.0/24"]; + default = [ "127.0.0.0/24" ]; type = types.listOf types.str; description = " What networks are allowed to use us as a resolver. Note @@ -112,7 +117,7 @@ in }; blockedNetworks = mkOption { - default = []; + default = [ ]; type = types.listOf types.str; description = " What networks are just blocked. @@ -136,7 +141,7 @@ in }; listenOn = mkOption { - default = ["any"]; + default = [ "any" ]; type = types.listOf types.str; description = " Interfaces to listen on. @@ -144,7 +149,7 @@ in }; listenOnIpv6 = mkOption { - default = ["any"]; + default = [ "any" ]; type = types.listOf types.str; description = " Ipv6 interfaces to listen on. @@ -152,19 +157,20 @@ in }; zones = mkOption { - default = []; - type = types.listOf (types.submodule [ { options = bindZoneOptions; } ]); + default = [ ]; + type = with types; coercedTo (listOf attrs) bindZoneCoerce (attrsOf (types.submodule bindZoneOptions)); description = " List of zones we claim authority over. "; - example = [{ - name = "example.com"; - master = false; - file = "/var/dns/example.com"; - masters = ["192.168.0.1"]; - slaves = []; - extraConfig = ""; - }]; + example = { + "example.com" = { + master = false; + file = "/var/dns/example.com"; + masters = [ "192.168.0.1" ]; + slaves = [ ]; + extraConfig = ""; + }; + }; }; extraConfig = mkOption { @@ -206,7 +212,8 @@ in networking.resolvconf.useLocalResolver = mkDefault true; users.users.${bindUser} = - { uid = config.ids.uids.bind; + { + uid = config.ids.uids.bind; description = "BIND daemon user"; }; @@ -226,9 +233,9 @@ in ''; serviceConfig = { - ExecStart = "${pkgs.bind.out}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f"; + ExecStart = "${pkgs.bind.out}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f"; ExecReload = "${pkgs.bind.out}/sbin/rndc -k '/etc/bind/rndc.key' reload"; - ExecStop = "${pkgs.bind.out}/sbin/rndc -k '/etc/bind/rndc.key' stop"; + ExecStop = "${pkgs.bind.out}/sbin/rndc -k '/etc/bind/rndc.key' stop"; }; unitConfig.Documentation = "man:named(8)"; diff --git a/nixpkgs/nixos/modules/services/networking/cjdns.nix b/nixpkgs/nixos/modules/services/networking/cjdns.nix index e9a0e5af1a47..f1a504b3e3f4 100644 --- a/nixpkgs/nixos/modules/services/networking/cjdns.nix +++ b/nixpkgs/nixos/modules/services/networking/cjdns.nix @@ -12,8 +12,18 @@ let { ... }: { options = { password = mkOption { - type = types.str; - description = "Authorized password to the opposite end of the tunnel."; + type = types.str; + description = "Authorized password to the opposite end of the tunnel."; + }; + login = mkOption { + default = ""; + type = types.str; + description = "(optional) name your peer has for you"; + }; + peerName = mkOption { + default = ""; + type = types.str; + description = "(optional) human-readable name for peer"; }; publicKey = mkOption { type = types.str; diff --git a/nixpkgs/nixos/modules/services/networking/croc.nix b/nixpkgs/nixos/modules/services/networking/croc.nix index b218fab2196d..9466adf71d8c 100644 --- a/nixpkgs/nixos/modules/services/networking/croc.nix +++ b/nixpkgs/nixos/modules/services/networking/croc.nix @@ -72,9 +72,7 @@ in RuntimeDirectoryMode = "700"; SystemCallFilter = [ "@system-service" - "~@aio" "~@chown" "~@keyring" "~@memlock" - "~@privileged" "~@resources" "~@setuid" - "~@sync" "~@timer" + "~@aio" "~@keyring" "~@memlock" "~@privileged" "~@resources" "~@setuid" "~@sync" "~@timer" ]; SystemCallArchitectures = "native"; SystemCallErrorNumber = "EPERM"; diff --git a/nixpkgs/nixos/modules/services/networking/gale.nix b/nixpkgs/nixos/modules/services/networking/gale.nix deleted file mode 100644 index cb954fd836bc..000000000000 --- a/nixpkgs/nixos/modules/services/networking/gale.nix +++ /dev/null @@ -1,181 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - cfg = config.services.gale; - # we convert the path to a string to avoid it being copied to the nix store, - # otherwise users could read the private key as all files in the store are - # world-readable - keyPath = toString cfg.keyPath; - # ...but we refer to the pubkey file using a path so that we can ensure the - # config gets rebuilt if the public key changes (we can assume the private key - # will never change without the public key having changed) - gpubFile = cfg.keyPath + "/${cfg.domain}.gpub"; - home = "/var/lib/gale"; - keysPrepared = cfg.keyPath != null && lib.pathExists cfg.keyPath; -in -{ - options = { - services.gale = { - enable = mkEnableOption "the Gale messaging daemon"; - - user = mkOption { - default = "gale"; - type = types.str; - description = "Username for the Gale daemon."; - }; - - group = mkOption { - default = "gale"; - type = types.str; - description = "Group name for the Gale daemon."; - }; - - setuidWrapper = mkOption { - default = null; - description = "Configuration for the Gale gksign setuid wrapper."; - }; - - domain = mkOption { - default = ""; - type = types.str; - description = "Domain name for the Gale system."; - }; - - keyPath = mkOption { - default = null; - type = types.nullOr types.path; - description = '' - Directory containing the key pair for this Gale domain. The expected - filename will be taken from the domain option with ".gpri" and ".gpub" - appended. - ''; - }; - - extraConfig = mkOption { - type = types.lines; - default = ""; - description = '' - Additional text to be added to <filename>/etc/gale/conf</filename>. - ''; - }; - }; - }; - - config = mkMerge [ - (mkIf cfg.enable { - assertions = [{ - assertion = cfg.domain != ""; - message = "A domain must be set for Gale."; - }]; - - warnings = mkIf (!keysPrepared) [ - "You must run gale-install in order to generate a domain key." - ]; - - system.activationScripts.gale = mkIf cfg.enable ( - stringAfter [ "users" "groups" ] '' - chmod 755 ${home} - mkdir -m 0777 -p ${home}/auth/cache - mkdir -m 1777 -p ${home}/auth/local # GALE_DOMAIN.gpub - mkdir -m 0700 -p ${home}/auth/private # ROOT.gpub - mkdir -m 0755 -p ${home}/auth/trusted # ROOT - mkdir -m 0700 -p ${home}/.gale - mkdir -m 0700 -p ${home}/.gale/auth - mkdir -m 0700 -p ${home}/.gale/auth/private # GALE_DOMAIN.gpri - - ln -sf ${pkgs.gale}/etc/gale/auth/trusted/ROOT "${home}/auth/trusted/ROOT" - chown ${cfg.user}:${cfg.group} ${home} ${home}/auth ${home}/auth/* - chown ${cfg.user}:${cfg.group} ${home}/.gale ${home}/.gale/auth ${home}/.gale/auth/private - '' - ); - - environment = { - etc = { - "gale/auth".source = home + "/auth"; # symlink /var/lib/gale/auth - "gale/conf".text = '' - GALE_USER ${cfg.user} - GALE_DOMAIN ${cfg.domain} - ${cfg.extraConfig} - ''; - }; - - systemPackages = [ pkgs.gale ]; - }; - - users.users.${cfg.user} = { - description = "Gale daemon"; - uid = config.ids.uids.gale; - group = cfg.group; - home = home; - createHome = true; - }; - - users.groups = [{ - name = cfg.group; - gid = config.ids.gids.gale; - }]; - }) - (mkIf (cfg.enable && keysPrepared) { - assertions = [ - { - assertion = cfg.keyPath != null - && lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpub"); - message = "Couldn't find a Gale public key for ${cfg.domain}."; - } - { - assertion = cfg.keyPath != null - && lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpri"); - message = "Couldn't find a Gale private key for ${cfg.domain}."; - } - ]; - - services.gale.setuidWrapper = { - program = "gksign"; - source = "${pkgs.gale}/bin/gksign"; - owner = cfg.user; - group = cfg.group; - setuid = true; - setgid = false; - }; - - security.wrappers.gksign = cfg.setuidWrapper; - - systemd.services.gale-galed = { - description = "Gale messaging daemon"; - wantedBy = [ "multi-user.target" ]; - wants = [ "gale-gdomain.service" ]; - after = [ "network.target" ]; - - preStart = '' - install -m 0640 -o ${cfg.user} -g ${cfg.group} ${keyPath}/${cfg.domain}.gpri "${home}/.gale/auth/private/" - install -m 0644 -o ${cfg.user} -g ${cfg.group} ${gpubFile} "${home}/.gale/auth/private/${cfg.domain}.gpub" - install -m 0644 -o ${cfg.user} -g ${cfg.group} ${gpubFile} "${home}/auth/local/${cfg.domain}.gpub" - ''; - - serviceConfig = { - Type = "forking"; - ExecStart = "@${pkgs.gale}/bin/galed galed"; - User = cfg.user; - Group = cfg.group; - PermissionsStartOnly = true; - }; - }; - - systemd.services.gale-gdomain = { - description = "Gale AKD daemon"; - wantedBy = [ "multi-user.target" ]; - requires = [ "gale-galed.service" ]; - after = [ "gale-galed.service" ]; - - serviceConfig = { - Type = "forking"; - ExecStart = "@${pkgs.gale}/bin/gdomain gdomain"; - User = cfg.user; - Group = cfg.group; - }; - }; - }) - ]; -} diff --git a/nixpkgs/nixos/modules/services/networking/ghostunnel.nix b/nixpkgs/nixos/modules/services/networking/ghostunnel.nix new file mode 100644 index 000000000000..58a51df6cca2 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ghostunnel.nix @@ -0,0 +1,242 @@ +{ config, lib, pkgs, ... }: +let + inherit (lib) + attrValues + concatMap + concatStringsSep + escapeShellArg + literalExample + mapAttrs' + mkDefault + mkEnableOption + mkIf + mkOption + nameValuePair + optional + types + ; + + mainCfg = config.services.ghostunnel; + + module = { config, name, ... }: + { + options = { + + listen = mkOption { + description = '' + Address and port to listen on (can be HOST:PORT, unix:PATH). + ''; + type = types.str; + }; + + target = mkOption { + description = '' + Address to forward connections to (can be HOST:PORT or unix:PATH). + ''; + type = types.str; + }; + + keystore = mkOption { + description = '' + Path to keystore (combined PEM with cert/key, or PKCS12 keystore). + + NB: storepass is not supported because it would expose credentials via <code>/proc/*/cmdline</code>. + + Specify this or <code>cert</code> and <code>key</code>. + ''; + type = types.nullOr types.str; + default = null; + }; + + cert = mkOption { + description = '' + Path to certificate (PEM with certificate chain). + + Not required if <code>keystore</code> is set. + ''; + type = types.nullOr types.str; + default = null; + }; + + key = mkOption { + description = '' + Path to certificate private key (PEM with private key). + + Not required if <code>keystore</code> is set. + ''; + type = types.nullOr types.str; + default = null; + }; + + cacert = mkOption { + description = '' + Path to CA bundle file (PEM/X509). Uses system trust store if <code>null</code>. + ''; + type = types.nullOr types.str; + }; + + disableAuthentication = mkOption { + description = '' + Disable client authentication, no client certificate will be required. + ''; + type = types.bool; + default = false; + }; + + allowAll = mkOption { + description = '' + If true, allow all clients, do not check client cert subject. + ''; + type = types.bool; + default = false; + }; + + allowCN = mkOption { + description = '' + Allow client if common name appears in the list. + ''; + type = types.listOf types.str; + default = []; + }; + + allowOU = mkOption { + description = '' + Allow client if organizational unit name appears in the list. + ''; + type = types.listOf types.str; + default = []; + }; + + allowDNS = mkOption { + description = '' + Allow client if DNS subject alternative name appears in the list. + ''; + type = types.listOf types.str; + default = []; + }; + + allowURI = mkOption { + description = '' + Allow client if URI subject alternative name appears in the list. + ''; + type = types.listOf types.str; + default = []; + }; + + extraArguments = mkOption { + description = "Extra arguments to pass to <code>ghostunnel server</code>"; + type = types.separatedString " "; + default = ""; + }; + + unsafeTarget = mkOption { + description = '' + If set, does not limit target to localhost, 127.0.0.1, [::1], or UNIX sockets. + + This is meant to protect against accidental unencrypted traffic on + untrusted networks. + ''; + type = types.bool; + default = false; + }; + + # Definitions to apply at the root of the NixOS configuration. + atRoot = mkOption { + internal = true; + }; + }; + + # Clients should not be authenticated with the public root certificates + # (afaict, it doesn't make sense), so we only provide that default when + # client cert auth is disabled. + config.cacert = mkIf config.disableAuthentication (mkDefault null); + + config.atRoot = { + assertions = [ + { message = '' + services.ghostunnel.servers.${name}: At least one access control flag is required. + Set at least one of: + - services.ghostunnel.servers.${name}.disableAuthentication + - services.ghostunnel.servers.${name}.allowAll + - services.ghostunnel.servers.${name}.allowCN + - services.ghostunnel.servers.${name}.allowOU + - services.ghostunnel.servers.${name}.allowDNS + - services.ghostunnel.servers.${name}.allowURI + ''; + assertion = config.disableAuthentication + || config.allowAll + || config.allowCN != [] + || config.allowOU != [] + || config.allowDNS != [] + || config.allowURI != [] + ; + } + ]; + + systemd.services."ghostunnel-server-${name}" = { + after = [ "network.target" ]; + wants = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Restart = "always"; + AmbientCapabilities = ["CAP_NET_BIND_SERVICE"]; + DynamicUser = true; + LoadCredential = optional (config.keystore != null) "keystore:${config.keystore}" + ++ optional (config.cert != null) "cert:${config.cert}" + ++ optional (config.key != null) "key:${config.key}" + ++ optional (config.cacert != null) "cacert:${config.cacert}"; + }; + script = concatStringsSep " " ( + [ "${mainCfg.package}/bin/ghostunnel" ] + ++ optional (config.keystore != null) "--keystore=$CREDENTIALS_DIRECTORY/keystore" + ++ optional (config.cert != null) "--cert=$CREDENTIALS_DIRECTORY/cert" + ++ optional (config.key != null) "--key=$CREDENTIALS_DIRECTORY/key" + ++ optional (config.cacert != null) "--cacert=$CREDENTIALS_DIRECTORY/cacert" + ++ [ + "server" + "--listen ${config.listen}" + "--target ${config.target}" + ] ++ optional config.allowAll "--allow-all" + ++ map (v: "--allow-cn=${escapeShellArg v}") config.allowCN + ++ map (v: "--allow-ou=${escapeShellArg v}") config.allowOU + ++ map (v: "--allow-dns=${escapeShellArg v}") config.allowDNS + ++ map (v: "--allow-uri=${escapeShellArg v}") config.allowURI + ++ optional config.disableAuthentication "--disable-authentication" + ++ optional config.unsafeTarget "--unsafe-target" + ++ [ config.extraArguments ] + ); + }; + }; + }; + +in +{ + + options = { + services.ghostunnel.enable = mkEnableOption "ghostunnel"; + + services.ghostunnel.package = mkOption { + description = "The ghostunnel package to use."; + type = types.package; + default = pkgs.ghostunnel; + defaultText = literalExample ''pkgs.ghostunnel''; + }; + + services.ghostunnel.servers = mkOption { + description = '' + Server mode ghostunnels (TLS listener -> plain TCP/UNIX target) + ''; + type = types.attrsOf (types.submodule module); + default = {}; + }; + }; + + config = mkIf mainCfg.enable { + assertions = lib.mkMerge (map (v: v.atRoot.assertions) (attrValues mainCfg.servers)); + systemd = lib.mkMerge (map (v: v.atRoot.systemd) (attrValues mainCfg.servers)); + }; + + meta.maintainers = with lib.maintainers; [ + roberth + ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/hans.nix b/nixpkgs/nixos/modules/services/networking/hans.nix index 8334dc68d623..84147db00f61 100644 --- a/nixpkgs/nixos/modules/services/networking/hans.nix +++ b/nixpkgs/nixos/modules/services/networking/hans.nix @@ -141,5 +141,5 @@ in }; }; - meta.maintainers = with maintainers; [ gnidorah ]; + meta.maintainers = with maintainers; [ ]; } diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/faxq-wait.sh b/nixpkgs/nixos/modules/services/networking/hylafax/faxq-wait.sh index 8c39e9d20c18..1826aa30e627 100755 --- a/nixpkgs/nixos/modules/services/networking/hylafax/faxq-wait.sh +++ b/nixpkgs/nixos/modules/services/networking/hylafax/faxq-wait.sh @@ -1,4 +1,4 @@ -#! @shell@ -e +#! @runtimeShell@ -e # skip this if there are no modems at all if ! stat -t "@spoolAreaPath@"/etc/config.* >/dev/null 2>&1 diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/options.nix b/nixpkgs/nixos/modules/services/networking/hylafax/options.nix index 7f18c0d39ab4..74960e69b9ac 100644 --- a/nixpkgs/nixos/modules/services/networking/hylafax/options.nix +++ b/nixpkgs/nixos/modules/services/networking/hylafax/options.nix @@ -3,7 +3,7 @@ let inherit (lib.options) literalExample mkEnableOption mkOption; - inherit (lib.types) bool enum int lines attrsOf nullOr path str submodule; + inherit (lib.types) bool enum ints lines attrsOf nullOr path str submodule; inherit (lib.modules) mkDefault mkIf mkMerge; commonDescr = '' @@ -18,7 +18,6 @@ let ''; str1 = lib.types.addCheck str (s: s!=""); # non-empty string - int1 = lib.types.addCheck int (i: i>0); # positive integer configAttrType = # Options in HylaFAX configuration files can be @@ -27,7 +26,7 @@ let # This type definition resolves all # those types into a list of strings. let - inherit (lib.types) attrsOf coercedTo listOf; + inherit (lib.types) attrsOf coercedTo int listOf; innerType = coercedTo bool (x: if x then "Yes" else "No") (coercedTo int (toString) str); in @@ -290,7 +289,7 @@ in ''; }; faxcron.infoDays = mkOption { - type = int1; + type = ints.positive; default = 30; description = '' Set the expiration time for data in the @@ -298,7 +297,7 @@ in ''; }; faxcron.logDays = mkOption { - type = int1; + type = ints.positive; default = 30; description = '' Set the expiration time for @@ -306,7 +305,7 @@ in ''; }; faxcron.rcvDays = mkOption { - type = int1; + type = ints.positive; default = 7; description = '' Set the expiration time for files in @@ -343,7 +342,7 @@ in ''; }; faxqclean.doneqMinutes = mkOption { - type = int1; + type = ints.positive; default = 15; example = literalExample "24*60"; description = '' @@ -353,7 +352,7 @@ in ''; }; faxqclean.docqMinutes = mkOption { - type = int1; + type = ints.positive; default = 60; example = literalExample "24*60"; description = '' diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/spool.sh b/nixpkgs/nixos/modules/services/networking/hylafax/spool.sh index 31e930e8c597..8b723df77df9 100755 --- a/nixpkgs/nixos/modules/services/networking/hylafax/spool.sh +++ b/nixpkgs/nixos/modules/services/networking/hylafax/spool.sh @@ -1,4 +1,4 @@ -#! @shell@ -e +#! @runtimeShell@ -e # The following lines create/update the HylaFAX spool directory: # Subdirectories/files with persistent data are kept, @@ -80,7 +80,7 @@ touch clientlog faxcron.lastrun xferfaxlog chown @faxuser@:@faxgroup@ clientlog faxcron.lastrun xferfaxlog # create symlinks for frozen directories/files -lnsym --target-directory=. "@hylafax@"/spool/{COPYRIGHT,bin,config} +lnsym --target-directory=. "@hylafaxplus@"/spool/{COPYRIGHT,bin,config} # create empty temporary directories update --mode=0700 -d client dev status @@ -93,7 +93,7 @@ install -d "@spoolAreaPath@/etc" cd "@spoolAreaPath@/etc" # create symlinks to all files in template's etc -lnsym --target-directory=. "@hylafax@/spool/etc"/* +lnsym --target-directory=. "@hylafaxplus@/spool/etc"/* # set LOCKDIR in setup.cache sed --regexp-extended 's|^(UUCP_LOCKDIR=).*$|\1'"'@lockPath@'|g" --in-place setup.cache diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/systemd.nix b/nixpkgs/nixos/modules/services/networking/hylafax/systemd.nix index f63f7c97ad1c..4506bbbc5eb7 100644 --- a/nixpkgs/nixos/modules/services/networking/hylafax/systemd.nix +++ b/nixpkgs/nixos/modules/services/networking/hylafax/systemd.nix @@ -13,11 +13,10 @@ let # creates hylafax config file, # makes sure "Include" is listed *first* let - mkLines = conf: - (lib.concatLists - (lib.flip lib.mapAttrsToList conf - (k: map (v: "${k}: ${v}") - ))); + mkLines = lib.flip lib.pipe [ + (lib.mapAttrsToList (key: map (val: "${key}: ${val}"))) + lib.concatLists + ]; include = mkLines { Include = conf.Include or []; }; other = mkLines ( conf // { Include = []; } ); in @@ -48,13 +47,12 @@ let name = "hylafax-setup-spool.sh"; src = ./spool.sh; isExecutable = true; - inherit (pkgs.stdenv) shell; - hylafax = pkgs.hylafaxplus; faxuser = "uucp"; faxgroup = "uucp"; lockPath = "/var/lock"; inherit globalConfigPath modemConfigPath; inherit (cfg) sendmailPath spoolAreaPath userAccessFile; + inherit (pkgs) hylafaxplus runtimeShell; }; waitFaxqScript = pkgs.substituteAll { @@ -64,8 +62,8 @@ let src = ./faxq-wait.sh; isExecutable = true; timeoutSec = toString 10; - inherit (pkgs.stdenv) shell; inherit (cfg) spoolAreaPath; + inherit (pkgs) runtimeShell; }; sockets.hylafax-hfaxd = { @@ -108,8 +106,10 @@ let PrivateDevices = true; # breaks /dev/tty... PrivateNetwork = true; PrivateTmp = true; + #ProtectClock = true; # breaks /dev/tty... (why?) ProtectControlGroups = true; #ProtectHome = true; # breaks custom spool dirs + ProtectKernelLogs = true; ProtectKernelModules = true; ProtectKernelTunables = true; #ProtectSystem = "strict"; # breaks custom spool dirs diff --git a/nixpkgs/nixos/modules/services/networking/kresd.nix b/nixpkgs/nixos/modules/services/networking/kresd.nix index 3f9be6172f1b..6882a315f616 100644 --- a/nixpkgs/nixos/modules/services/networking/kresd.nix +++ b/nixpkgs/nixos/modules/services/networking/kresd.nix @@ -29,8 +29,6 @@ let + concatMapStrings (mkListen "doh2") cfg.listenDoH + cfg.extraConfig ); - - package = pkgs.knot-resolver; in { meta.maintainers = [ maintainers.vcunat /* upstream developer */ ]; @@ -58,6 +56,15 @@ in { and give commands interactively to kresd@1.service. ''; }; + package = mkOption { + type = types.package; + description = " + knot-resolver package to use. + "; + default = pkgs.knot-resolver; + defaultText = "pkgs.knot-resolver"; + example = literalExample "pkgs.knot-resolver.override { extraFeatures = true; }"; + }; extraConfig = mkOption { type = types.lines; default = ""; @@ -108,6 +115,8 @@ in { config = mkIf cfg.enable { environment.etc."knot-resolver/kresd.conf".source = configFile; # not required + networking.resolvconf.useLocalResolver = mkDefault true; + users.users.knot-resolver = { isSystemUser = true; group = "knot-resolver"; @@ -115,7 +124,7 @@ in { }; users.groups.knot-resolver.gid = null; - systemd.packages = [ package ]; # the units are patched inside the package a bit + systemd.packages = [ cfg.package ]; # the units are patched inside the package a bit systemd.targets.kresd = { # configure units started by default wantedBy = [ "multi-user.target" ]; @@ -123,8 +132,8 @@ in { ++ map (i: "kresd@${toString i}.service") (range 1 cfg.instances); }; systemd.services."kresd@".serviceConfig = { - ExecStart = "${package}/bin/kresd --noninteractive " - + "-c ${package}/lib/knot-resolver/distro-preconfig.lua -c ${configFile}"; + ExecStart = "${cfg.package}/bin/kresd --noninteractive " + + "-c ${cfg.package}/lib/knot-resolver/distro-preconfig.lua -c ${configFile}"; # Ensure /run/knot-resolver exists RuntimeDirectory = "knot-resolver"; RuntimeDirectoryMode = "0770"; diff --git a/nixpkgs/nixos/modules/services/networking/libreswan.nix b/nixpkgs/nixos/modules/services/networking/libreswan.nix index 81bc4e1cf95c..1f0423ac3d84 100644 --- a/nixpkgs/nixos/modules/services/networking/libreswan.nix +++ b/nixpkgs/nixos/modules/services/networking/libreswan.nix @@ -9,21 +9,22 @@ let libexec = "${pkgs.libreswan}/libexec/ipsec"; ipsec = "${pkgs.libreswan}/sbin/ipsec"; - trim = chars: str: let - nonchars = filter (x : !(elem x.value chars)) - (imap0 (i: v: {ind = i; value = v;}) (stringToCharacters str)); - in - if length nonchars == 0 then "" - else substring (head nonchars).ind (add 1 (sub (last nonchars).ind (head nonchars).ind)) str; + trim = chars: str: + let + nonchars = filter (x : !(elem x.value chars)) + (imap0 (i: v: {ind = i; value = v;}) (stringToCharacters str)); + in + if length nonchars == 0 then "" + else substring (head nonchars).ind (add 1 (sub (last nonchars).ind (head nonchars).ind)) str; indent = str: concatStrings (concatMap (s: [" " (trim [" " "\t"] s) "\n"]) (splitString "\n" str)); configText = indent (toString cfg.configSetup); connectionText = concatStrings (mapAttrsToList (n: v: '' conn ${n} ${indent v} - '') cfg.connections); - configFile = pkgs.writeText "ipsec.conf" + + configFile = pkgs.writeText "ipsec-nixos.conf" '' config setup ${configText} @@ -31,6 +32,11 @@ let ${connectionText} ''; + policyFiles = mapAttrs' (name: text: + { name = "ipsec.d/policies/${name}"; + value.source = pkgs.writeText "ipsec-policy-${name}" text; + }) cfg.policies; + in { @@ -41,41 +47,71 @@ in services.libreswan = { - enable = mkEnableOption "libreswan ipsec service"; + enable = mkEnableOption "Libreswan IPsec service"; configSetup = mkOption { type = types.lines; default = '' protostack=netkey - nat_traversal=yes virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:25.0.0.0/8,%v4:100.64.0.0/10,%v6:fd00::/8,%v6:fe80::/10 ''; example = '' secretsfile=/root/ipsec.secrets protostack=netkey - nat_traversal=yes virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:25.0.0.0/8,%v4:100.64.0.0/10,%v6:fd00::/8,%v6:fe80::/10 ''; - description = "Options to go in the 'config setup' section of the libreswan ipsec configuration"; + description = "Options to go in the 'config setup' section of the Libreswan IPsec configuration"; }; connections = mkOption { type = types.attrsOf types.lines; default = {}; - example = { - myconnection = '' - auto=add - left=%defaultroute - leftid=@user - - right=my.vpn.com - - ikev2=no - ikelifetime=8h - ''; - }; - description = "A set of connections to define for the libreswan ipsec service"; + example = literalExample '' + { myconnection = ''' + auto=add + left=%defaultroute + leftid=@user + + right=my.vpn.com + + ikev2=no + ikelifetime=8h + '''; + } + ''; + description = "A set of connections to define for the Libreswan IPsec service"; + }; + + policies = mkOption { + type = types.attrsOf types.lines; + default = {}; + example = literalExample '' + { private-or-clear = ''' + # Attempt opportunistic IPsec for the entire Internet + 0.0.0.0/0 + ::/0 + '''; + } + ''; + description = '' + A set of policies to apply to the IPsec connections. + + <note><para> + The policy name must match the one of connection it needs to apply to. + </para></note> + ''; }; + + disableRedirects = mkOption { + type = types.bool; + default = true; + description = '' + Whether to disable send and accept redirects for all nework interfaces. + See the Libreswan <link xlink:href="https://libreswan.org/wiki/FAQ#Why_is_it_recommended_to_disable_send_redirects_in_.2Fproc.2Fsys.2Fnet_.3F"> + FAQ</link> page for why this is recommended. + ''; + }; + }; }; @@ -85,43 +121,38 @@ in config = mkIf cfg.enable { + # Install package, systemd units, etc. environment.systemPackages = [ pkgs.libreswan pkgs.iproute2 ]; + systemd.packages = [ pkgs.libreswan ]; + systemd.tmpfiles.packages = [ pkgs.libreswan ]; + + # Install configuration files + environment.etc = { + "ipsec.secrets".source = "${pkgs.libreswan}/etc/ipsec.secrets"; + "ipsec.conf".source = "${pkgs.libreswan}/etc/ipsec.conf"; + "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"; - path = [ - "${pkgs.libreswan}" - "${pkgs.iproute2}" - "${pkgs.procps}" - "${pkgs.nssTools}" - "${pkgs.iptables}" - "${pkgs.nettools}" - ]; - - wants = [ "network-online.target" ]; - after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; - - serviceConfig = { - Type = "simple"; - Restart = "always"; - EnvironmentFile = "-${pkgs.libreswan}/etc/sysconfig/pluto"; - ExecStartPre = [ - "${libexec}/addconn --config ${configFile} --checkconfig" - "${libexec}/_stackmanager start" - "${ipsec} --checknss" - "${ipsec} --checknflog" - ]; - ExecStart = "${libexec}/pluto --config ${configFile} --nofork \$PLUTO_OPTIONS"; - ExecStop = "${libexec}/whack --shutdown"; - ExecStopPost = [ - "${pkgs.iproute2}/bin/ip xfrm policy flush" - "${pkgs.iproute2}/bin/ip xfrm state flush" - "${ipsec} --stopnflog" - ]; - ExecReload = "${libexec}/whack --listen"; - }; - + restartTriggers = [ configFile ] ++ mapAttrsToList (n: v: v.source) policyFiles; + path = with pkgs; [ + libreswan + iproute2 + procps + nssTools + iptables + nettools + ]; + preStart = optionalString cfg.disableRedirects '' + # Disable send/receive redirects + echo 0 | tee /proc/sys/net/ipv4/conf/*/send_redirects + echo 0 | tee /proc/sys/net/ipv{4,6}/conf/*/accept_redirects + ''; }; }; diff --git a/nixpkgs/nixos/modules/services/networking/monero.nix b/nixpkgs/nixos/modules/services/networking/monero.nix index fde3293fc131..952d1d47ca67 100644 --- a/nixpkgs/nixos/modules/services/networking/monero.nix +++ b/nixpkgs/nixos/modules/services/networking/monero.nix @@ -4,7 +4,6 @@ with lib; let cfg = config.services.monero; - dataDir = "/var/lib/monero"; listToConf = option: list: concatMapStrings (value: "${option}=${value}\n") list; @@ -53,11 +52,19 @@ in enable = mkEnableOption "Monero node daemon"; + dataDir = mkOption { + type = types.str; + default = "/var/lib/monero"; + description = '' + The directory where Monero stores its data files. + ''; + }; + mining.enable = mkOption { type = types.bool; default = false; description = '' - Whether to mine moneroj. + Whether to mine monero. ''; }; @@ -198,15 +205,14 @@ in config = mkIf cfg.enable { users.users.monero = { - uid = config.ids.uids.monero; + isSystemUser = true; + group = "monero"; description = "Monero daemon user"; - home = dataDir; + home = cfg.dataDir; createHome = true; }; - users.groups.monero = { - gid = config.ids.gids.monero; - }; + users.groups.monero = { }; systemd.services.monero = { description = "monero daemon"; diff --git a/nixpkgs/nixos/modules/services/networking/mosquitto.nix b/nixpkgs/nixos/modules/services/networking/mosquitto.nix index 10b49d9b2206..8e814ffd0b9b 100644 --- a/nixpkgs/nixos/modules/services/networking/mosquitto.nix +++ b/nixpkgs/nixos/modules/services/networking/mosquitto.nix @@ -20,8 +20,7 @@ let acl_file ${aclFile} persistence true allow_anonymous ${boolToString cfg.allowAnonymous} - bind_address ${cfg.host} - port ${toString cfg.port} + listener ${toString cfg.port} ${cfg.host} ${passwordConf} ${listenerConf} ${cfg.extraConf} @@ -233,15 +232,50 @@ in ExecStart = "${pkgs.mosquitto}/bin/mosquitto -c ${mosquittoConf}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - ProtectSystem = "strict"; - ProtectHome = true; + # Hardening + CapabilityBoundingSet = ""; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; PrivateDevices = true; PrivateTmp = true; - ReadWritePaths = "${cfg.dataDir}"; + PrivateUsers = true; + ProtectClock = true; ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; ProtectKernelModules = true; ProtectKernelTunables = true; - NoNewPrivileges = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + ProtectSystem = "strict"; + ReadWritePaths = [ + cfg.dataDir + "/tmp" # mosquitto_passwd creates files in /tmp before moving them + ]; + ReadOnlyPaths = with cfg.ssl; lib.optionals (enable) [ + certfile + keyfile + cafile + ]; + RemoveIPC = true; + RestrictAddressFamilies = [ + "AF_UNIX" # for sd_notify() call + "AF_INET" + "AF_INET6" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "~@resources" + ]; + UMask = "0077"; }; preStart = '' rm -f ${cfg.dataDir}/passwd diff --git a/nixpkgs/nixos/modules/services/networking/networkmanager.nix b/nixpkgs/nixos/modules/services/networking/networkmanager.nix index 135f29be58c0..064018057cdb 100644 --- a/nixpkgs/nixos/modules/services/networking/networkmanager.nix +++ b/nixpkgs/nixos/modules/services/networking/networkmanager.nix @@ -22,36 +22,51 @@ let enableIwd = cfg.wifi.backend == "iwd"; - configFile = pkgs.writeText "NetworkManager.conf" '' - [main] - plugins=keyfile - dhcp=${cfg.dhcp} - dns=${cfg.dns} - # If resolvconf is disabled that means that resolv.conf is managed by some other module. - rc-manager=${if config.networking.resolvconf.enable then "resolvconf" else "unmanaged"} - - [keyfile] - ${optionalString (cfg.unmanaged != []) - ''unmanaged-devices=${lib.concatStringsSep ";" cfg.unmanaged}''} - - [logging] - level=${cfg.logLevel} - audit=${lib.boolToString config.security.audit.enable} - - [connection] - ipv6.ip6-privacy=2 - ethernet.cloned-mac-address=${cfg.ethernet.macAddress} - wifi.cloned-mac-address=${cfg.wifi.macAddress} - ${optionalString (cfg.wifi.powersave != null) - ''wifi.powersave=${if cfg.wifi.powersave then "3" else "2"}''} - - [device] - wifi.scan-rand-mac-address=${if cfg.wifi.scanRandMacAddress then "yes" else "no"} - wifi.backend=${cfg.wifi.backend} - - ${cfg.extraConfig} + mkValue = v: + if v == true then "yes" + else if v == false then "no" + else if lib.isInt v then toString v + else v; + + mkSection = name: attrs: '' + [${name}] + ${ + lib.concatStringsSep "\n" + (lib.mapAttrsToList + (k: v: "${k}=${mkValue v}") + (lib.filterAttrs + (k: v: v != null) + attrs)) + } ''; + configFile = pkgs.writeText "NetworkManager.conf" (lib.concatStringsSep "\n" [ + (mkSection "main" { + plugins = "keyfile"; + dhcp = cfg.dhcp; + dns = cfg.dns; + # If resolvconf is disabled that means that resolv.conf is managed by some other module. + rc-manager = + if config.networking.resolvconf.enable then "resolvconf" + else "unmanaged"; + }) + (mkSection "keyfile" { + unmanaged-devices = + if cfg.unmanaged == [] then null + else lib.concatStringsSep ";" cfg.unmanaged; + }) + (mkSection "logging" { + audit = config.security.audit.enable; + level = cfg.logLevel; + }) + (mkSection "connection" cfg.connectionConfig) + (mkSection "device" { + "wifi.scan-rand-mac-address" = cfg.wifi.scanRandMacAddress; + "wifi.backend" = cfg.wifi.backend; + }) + cfg.extraConfig + ]); + /* [network-manager] Identity=unix-group:networkmanager @@ -154,6 +169,28 @@ in { ''; }; + connectionConfig = mkOption { + type = with types; attrsOf (nullOr (oneOf [ + bool + int + str + ])); + default = {}; + description = '' + Configuration for the [connection] section of NetworkManager.conf. + Refer to + <link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html"> + https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#id-1.2.3.11 + </link> + or + <citerefentry> + <refentrytitle>NetworkManager.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> + for more information. + ''; + }; + extraConfig = mkOption { type = types.lines; default = ""; @@ -482,6 +519,18 @@ in { (mkIf enableIwd { wireless.iwd.enable = true; }) + + { + networkmanager.connectionConfig = { + "ipv6.ip6-privacy" = 2; + "ethernet.cloned-mac-address" = cfg.ethernet.macAddress; + "wifi.cloned-mac-address" = cfg.wifi.macAddress; + "wifi.powersave" = + if cfg.wifi.powersave == null then null + else if cfg.wifi.powersave then 3 + else 2; + }; + } ]; boot.kernelModules = [ "ctr" ]; diff --git a/nixpkgs/nixos/modules/services/networking/nsd.nix b/nixpkgs/nixos/modules/services/networking/nsd.nix index f33c350a257a..2ac0a8c7922e 100644 --- a/nixpkgs/nixos/modules/services/networking/nsd.nix +++ b/nixpkgs/nixos/modules/services/networking/nsd.nix @@ -20,6 +20,15 @@ let mkZoneFileName = name: if name == "." then "root" else name; + # replaces include: directives for keys with fake keys for nsd-checkconf + injectFakeKeys = keys: concatStrings + (mapAttrsToList + (keyName: keyOptions: '' + fakeKey="$(${pkgs.bind}/bin/tsig-keygen -a ${escapeShellArgs [ keyOptions.algorithm keyName ]} | grep -oP "\s*secret \"\K.*(?=\";)")" + sed "s@^\s*include:\s*\"${stateDir}/private/${keyName}\"\$@secret: $fakeKey@" -i $out/nsd.conf + '') + keys); + nsdEnv = pkgs.buildEnv { name = "nsd-env"; @@ -34,9 +43,9 @@ let echo "|- checking zone '$out/zones/$zoneFile'" ${nsdPkg}/sbin/nsd-checkzone "$zoneFile" "$zoneFile" || { if grep -q \\\\\\$ "$zoneFile"; then - echo zone "$zoneFile" contains escaped dollar signes \\\$ - echo Escaping them is not needed any more. Please make shure \ - to unescape them where they prefix a variable name + echo zone "$zoneFile" contains escaped dollar signs \\\$ + echo Escaping them is not needed any more. Please make sure \ + to unescape them where they prefix a variable name. fi exit 1 @@ -44,7 +53,14 @@ let done echo "checking configuration file" + # Save original config file including key references... + cp $out/nsd.conf{,.orig} + # ...inject mock keys into config + ${injectFakeKeys cfg.keys} + # ...do the checkconf ${nsdPkg}/sbin/nsd-checkconf $out/nsd.conf + # ... and restore original config file. + mv $out/nsd.conf{.orig,} ''; }; diff --git a/nixpkgs/nixos/modules/services/networking/radicale.nix b/nixpkgs/nixos/modules/services/networking/radicale.nix index 5af035fd59e0..8c632c319d3c 100644 --- a/nixpkgs/nixos/modules/services/networking/radicale.nix +++ b/nixpkgs/nixos/modules/services/networking/radicale.nix @@ -3,56 +3,103 @@ with lib; let - cfg = config.services.radicale; - confFile = pkgs.writeText "radicale.conf" cfg.config; - - defaultPackage = if versionAtLeast config.system.stateVersion "20.09" then { - pkg = pkgs.radicale3; - text = "pkgs.radicale3"; - } else if versionAtLeast config.system.stateVersion "17.09" then { - pkg = pkgs.radicale2; - text = "pkgs.radicale2"; - } else { - pkg = pkgs.radicale1; - text = "pkgs.radicale1"; + format = pkgs.formats.ini { + listToValue = concatMapStringsSep ", " (generators.mkValueStringDefault { }); }; -in -{ + pkg = if isNull cfg.package then + pkgs.radicale + else + cfg.package; + + confFile = if cfg.settings == { } then + pkgs.writeText "radicale.conf" cfg.config + else + format.generate "radicale.conf" cfg.settings; + + rightsFile = format.generate "radicale.rights" cfg.rights; - options = { - services.radicale.enable = mkOption { - type = types.bool; - default = false; + bindLocalhost = cfg.settings != { } && !hasAttrByPath [ "server" "hosts" ] cfg.settings; + +in { + options.services.radicale = { + enable = mkEnableOption "Radicale CalDAV and CardDAV server"; + + package = mkOption { + description = "Radicale package to use."; + # Default cannot be pkgs.radicale because non-null values suppress + # warnings about incompatible configuration and storage formats. + type = with types; nullOr package // { inherit (package) description; }; + default = null; + defaultText = "pkgs.radicale"; + }; + + config = mkOption { + type = types.str; + default = ""; description = '' - Enable Radicale CalDAV and CardDAV server. + Radicale configuration, this will set the service + configuration file. + This option is mutually exclusive with <option>settings</option>. + This option is deprecated. Use <option>settings</option> instead. ''; }; - services.radicale.package = mkOption { - type = types.package; - default = defaultPackage.pkg; - defaultText = defaultPackage.text; + settings = mkOption { + type = format.type; + default = { }; description = '' - Radicale package to use. This defaults to version 1.x if - <literal>system.stateVersion < 17.09</literal>, version 2.x if - <literal>17.09 ≤ system.stateVersion < 20.09</literal>, and - version 3.x otherwise. + Configuration for Radicale. See + <link xlink:href="https://radicale.org/3.0.html#documentation/configuration" />. + This option is mutually exclusive with <option>config</option>. + ''; + example = literalExample '' + server = { + hosts = [ "0.0.0.0:5232" "[::]:5232" ]; + }; + auth = { + type = "htpasswd"; + htpasswd_filename = "/etc/radicale/users"; + htpasswd_encryption = "bcrypt"; + }; + storage = { + filesystem_folder = "/var/lib/radicale/collections"; + }; ''; }; - services.radicale.config = mkOption { - type = types.str; - default = ""; + rights = mkOption { + type = format.type; description = '' - Radicale configuration, this will set the service - configuration file. + Configuration for Radicale's rights file. See + <link xlink:href="https://radicale.org/3.0.html#documentation/authentication-and-rights" />. + This option only works in conjunction with <option>settings</option>. + Setting this will also set <option>settings.rights.type</option> and + <option>settings.rights.file</option> to approriate values. + ''; + default = { }; + example = literalExample '' + root = { + user = ".+"; + collection = ""; + permissions = "R"; + }; + principal = { + user = ".+"; + collection = "{user}"; + permissions = "RW"; + }; + calendars = { + user = ".+"; + collection = "{user}/[^/]+"; + permissions = "rw"; + }; ''; }; - services.radicale.extraArgs = mkOption { + extraArgs = mkOption { type = types.listOf types.str; default = []; description = "Extra arguments passed to the Radicale daemon."; @@ -60,33 +107,94 @@ in }; config = mkIf cfg.enable { - environment.systemPackages = [ cfg.package ]; + assertions = [ + { + assertion = cfg.settings == { } || cfg.config == ""; + message = '' + The options services.radicale.config and services.radicale.settings + are mutually exclusive. + ''; + } + ]; - users.users.radicale = - { uid = config.ids.uids.radicale; - description = "radicale user"; - home = "/var/lib/radicale"; - createHome = true; - }; + warnings = optional (isNull cfg.package && versionOlder config.system.stateVersion "17.09") '' + The configuration and storage formats of your existing Radicale + installation might be incompatible with the newest version. + For upgrade instructions see + https://radicale.org/2.1.html#documentation/migration-from-1xx-to-2xx. + Set services.radicale.package to suppress this warning. + '' ++ optional (isNull cfg.package && versionOlder config.system.stateVersion "20.09") '' + The configuration format of your existing Radicale installation might be + incompatible with the newest version. For upgrade instructions see + https://github.com/Kozea/Radicale/blob/3.0.6/NEWS.md#upgrade-checklist. + Set services.radicale.package to suppress this warning. + '' ++ optional (cfg.config != "") '' + The option services.radicale.config is deprecated. + Use services.radicale.settings instead. + ''; + + services.radicale.settings.rights = mkIf (cfg.rights != { }) { + type = "from_file"; + file = toString rightsFile; + }; + + environment.systemPackages = [ pkg ]; + + users.users.radicale.uid = config.ids.uids.radicale; - users.groups.radicale = - { gid = config.ids.gids.radicale; }; + users.groups.radicale.gid = config.ids.gids.radicale; systemd.services.radicale = { description = "A Simple Calendar and Contact Server"; after = [ "network.target" ]; + requires = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { ExecStart = concatStringsSep " " ([ - "${cfg.package}/bin/radicale" "-C" confFile + "${pkg}/bin/radicale" "-C" confFile ] ++ ( map escapeShellArg cfg.extraArgs )); User = "radicale"; Group = "radicale"; + StateDirectory = "radicale/collections"; + StateDirectoryMode = "0750"; + # Hardening + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ "/dev/stdin" ]; + DevicePolicy = "strict"; + IPAddressAllow = mkIf bindLocalhost "localhost"; + IPAddressDeny = mkIf bindLocalhost "any"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = 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"; + ReadWritePaths = lib.optional + (hasAttrByPath [ "storage" "filesystem_folder" ] cfg.settings) + cfg.settings.storage.filesystem_folder; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; + UMask = "0027"; }; }; }; - meta.maintainers = with lib.maintainers; [ aneeshusa infinisil ]; + meta.maintainers = with lib.maintainers; [ aneeshusa infinisil dotlambda ]; } diff --git a/nixpkgs/nixos/modules/services/networking/searx.nix b/nixpkgs/nixos/modules/services/networking/searx.nix index a515e4a3dc3b..04f7d7e31f46 100644 --- a/nixpkgs/nixos/modules/services/networking/searx.nix +++ b/nixpkgs/nixos/modules/services/networking/searx.nix @@ -4,23 +4,25 @@ with lib; let runDir = "/run/searx"; + cfg = config.services.searx; + settingsFile = pkgs.writeText "settings.yml" + (builtins.toJSON cfg.settings); + generateConfig = '' cd ${runDir} # write NixOS settings as JSON - cat <<'EOF' > settings.yml - ${builtins.toJSON cfg.settings} - EOF + ( + umask 077 + cp --no-preserve=mode ${settingsFile} settings.yml + ) # substitute environment variables env -0 | while IFS='=' read -r -d ''' n v; do sed "s#@$n@#$v#g" -i settings.yml done - - # set strict permissions - chmod 400 settings.yml ''; settingType = with types; (oneOf diff --git a/nixpkgs/nixos/modules/services/networking/solanum.nix b/nixpkgs/nixos/modules/services/networking/solanum.nix new file mode 100644 index 000000000000..b6496fb8b35a --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/solanum.nix @@ -0,0 +1,101 @@ +{ config, lib, pkgs, ... }: + +let + inherit (lib) mkEnableOption mkIf mkOption types; + inherit (pkgs) solanum; + cfg = config.services.solanum; + + configFile = pkgs.writeText "solanum.conf" cfg.config; +in + +{ + + ###### interface + + options = { + + services.solanum = { + + enable = mkEnableOption "Solanum IRC daemon"; + + config = mkOption { + type = types.str; + default = '' + serverinfo { + name = "irc.example.com"; + sid = "1ix"; + description = "irc!"; + + vhost = "0.0.0.0"; + vhost6 = "::"; + }; + + listen { + host = "0.0.0.0"; + port = 6667; + }; + + auth { + user = "*@*"; + class = "users"; + flags = exceed_limit; + }; + channel { + default_split_user_count = 0; + }; + ''; + description = '' + Solanum IRC daemon configuration file. + check <link xlink:href="https://github.com/solanum-ircd/solanum/blob/main/doc/reference.conf"/> for all options. + ''; + }; + + openFilesLimit = mkOption { + type = types.int; + default = 1024; + description = '' + Maximum number of open files. Limits the clients and server connections. + ''; + }; + + motd = mkOption { + type = types.nullOr types.lines; + default = null; + description = '' + Solanum MOTD text. + + Solanum will read its MOTD from <literal>/etc/solanum/ircd.motd</literal>. + If set, the value of this option will be written to this path. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable (lib.mkMerge [ + { + systemd.services.solanum = { + description = "Solanum IRC daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${solanum}/bin/solanum -foreground -logfile /dev/stdout -configfile ${configFile} -pidfile /run/solanum/ircd.pid"; + DynamicUser = true; + User = "solanum"; + StateDirectory = "solanum"; + RuntimeDirectory = "solanum"; + LimitNOFILE = "${toString cfg.openFilesLimit}"; + }; + }; + + } + + (mkIf (cfg.motd != null) { + environment.etc."solanum/ircd.motd".text = cfg.motd; + }) + ]); +} diff --git a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix index 174c80f73d2d..b3fe496e41c0 100644 --- a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix @@ -122,6 +122,15 @@ in ''; }; + sftpServerExecutable = mkOption { + type = types.str; + example = "internal-sftp"; + description = '' + The sftp server executable. Can be a path or "internal-sftp" to use + the sftp server built into the sshd binary. + ''; + }; + sftpFlags = mkOption { type = with types; listOf str; default = []; @@ -397,6 +406,7 @@ in }); services.openssh.moduliFile = mkDefault "${cfgc.package}/etc/ssh/moduli"; + services.openssh.sftpServerExecutable = mkDefault "${cfgc.package}/libexec/sftp-server"; environment.etc = authKeysFiles // { "ssh/moduli".source = cfg.moduliFile; @@ -516,7 +526,7 @@ in ''} ${optionalString cfg.allowSFTP '' - Subsystem sftp ${cfgc.package}/libexec/sftp-server ${concatStringsSep " " cfg.sftpFlags} + Subsystem sftp ${cfg.sftpServerExecutable} ${concatStringsSep " " cfg.sftpFlags} ''} PermitRootLogin ${cfg.permitRootLogin} diff --git a/nixpkgs/nixos/modules/services/networking/supplicant.nix b/nixpkgs/nixos/modules/services/networking/supplicant.nix index 20704be9b36f..4f4b5cef3741 100644 --- a/nixpkgs/nixos/modules/services/networking/supplicant.nix +++ b/nixpkgs/nixos/modules/services/networking/supplicant.nix @@ -44,19 +44,10 @@ let preStart = '' ${optionalString (suppl.configFile.path!=null) '' - touch -a ${suppl.configFile.path} - chmod 600 ${suppl.configFile.path} + (umask 077 && touch -a "${suppl.configFile.path}") ''} ${optionalString suppl.userControlled.enable '' - if ! test -e ${suppl.userControlled.socketDir}; then - mkdir -m 0770 -p ${suppl.userControlled.socketDir} - chgrp ${suppl.userControlled.group} ${suppl.userControlled.socketDir} - fi - - if test "$(stat --printf '%G' ${suppl.userControlled.socketDir})" != "${suppl.userControlled.group}"; then - echo "ERROR: bad ownership on ${suppl.userControlled.socketDir}" >&2 - exit 1 - fi + install -dm770 -g "${suppl.userControlled.group}" "${suppl.userControlled.socketDir}" ''} ''; diff --git a/nixpkgs/nixos/modules/services/networking/unbound.nix b/nixpkgs/nixos/modules/services/networking/unbound.nix index 622c3d8ea434..09aef9a1dcf1 100644 --- a/nixpkgs/nixos/modules/services/networking/unbound.nix +++ b/nixpkgs/nixos/modules/services/networking/unbound.nix @@ -4,51 +4,28 @@ with lib; let cfg = config.services.unbound; - stateDir = "/var/lib/unbound"; - - access = concatMapStringsSep "\n " (x: "access-control: ${x} allow") cfg.allowedAccess; - - interfaces = concatMapStringsSep "\n " (x: "interface: ${x}") cfg.interfaces; - - isLocalAddress = x: substring 0 3 x == "::1" || substring 0 9 x == "127.0.0.1"; - - forward = - optionalString (any isLocalAddress cfg.forwardAddresses) '' - do-not-query-localhost: no - '' - + optionalString (cfg.forwardAddresses != []) '' - forward-zone: - name: . - '' - + concatMapStringsSep "\n" (x: " forward-addr: ${x}") cfg.forwardAddresses; - - rootTrustAnchorFile = "${stateDir}/root.key"; - - trustAnchor = optionalString cfg.enableRootTrustAnchor - "auto-trust-anchor-file: ${rootTrustAnchorFile}"; - - confFile = pkgs.writeText "unbound.conf" '' - server: - ip-freebind: yes - directory: "${stateDir}" - username: unbound - chroot: "" - pidfile: "" - # when running under systemd there is no need to daemonize - do-daemonize: no - ${interfaces} - ${access} - ${trustAnchor} - ${lib.optionalString (cfg.localControlSocketPath != null) '' - remote-control: - control-enable: yes - control-interface: ${cfg.localControlSocketPath} - ''} - ${cfg.extraConfig} - ${forward} - ''; -in -{ + yesOrNo = v: if v then "yes" else "no"; + + toOption = indent: n: v: "${indent}${toString n}: ${v}"; + + toConf = indent: n: v: + if builtins.isFloat v then (toOption indent n (builtins.toJSON v)) + else if isInt v then (toOption indent n (toString v)) + else if isBool v then (toOption indent n (yesOrNo v)) + else if isString v then (toOption indent n v) + else if isList v then (concatMapStringsSep "\n" (toConf indent n) v) + else if isAttrs v then (concatStringsSep "\n" ( + ["${indent}${n}:"] ++ ( + mapAttrsToList (toConf "${indent} ") v + ) + )) + else throw (traceSeq v "services.unbound.settings: unexpected type"); + + confFile = pkgs.writeText "unbound.conf" (concatStringsSep "\n" ((mapAttrsToList (toConf "") cfg.settings) ++ [""])); + + rootTrustAnchorFile = "${cfg.stateDir}/root.key"; + +in { ###### interface @@ -64,25 +41,30 @@ in description = "The unbound package to use"; }; - allowedAccess = mkOption { - default = [ "127.0.0.0/24" ]; - type = types.listOf types.str; - description = "What networks are allowed to use unbound as a resolver."; + user = mkOption { + type = types.str; + default = "unbound"; + description = "User account under which unbound runs."; }; - interfaces = mkOption { - default = [ "127.0.0.1" ] ++ optional config.networking.enableIPv6 "::1"; - type = types.listOf types.str; - description = '' - What addresses the server should listen on. This supports the interface syntax documented in - <citerefentry><refentrytitle>unbound.conf</refentrytitle><manvolnum>8</manvolnum></citerefentry>. - ''; + group = mkOption { + type = types.str; + default = "unbound"; + description = "Group under which unbound runs."; }; - forwardAddresses = mkOption { - default = []; - type = types.listOf types.str; - description = "What servers to forward queries to."; + stateDir = mkOption { + default = "/var/lib/unbound"; + description = "Directory holding all state for unbound to run."; + }; + + resolveLocalQueries = mkOption { + type = types.bool; + default = true; + description = '' + Whether unbound should resolve local queries (i.e. add 127.0.0.1 to + /etc/resolv.conf). + ''; }; enableRootTrustAnchor = mkOption { @@ -106,23 +88,66 @@ in and group will be <literal>nogroup</literal>. Users that should be permitted to access the socket must be in the - <literal>unbound</literal> group. + <literal>config.services.unbound.group</literal> group. If this option is <literal>null</literal> remote control will not be - configured at all. Unbounds default values apply. + enabled. Unbounds default values apply. ''; }; - extraConfig = mkOption { - default = ""; - type = types.lines; + settings = mkOption { + default = {}; + type = with types; submodule { + + freeformType = let + validSettingsPrimitiveTypes = oneOf [ int str bool float ]; + validSettingsTypes = oneOf [ validSettingsPrimitiveTypes (listOf validSettingsPrimitiveTypes) ]; + settingsType = oneOf [ str (attrsOf validSettingsTypes) ]; + in attrsOf (oneOf [ settingsType (listOf settingsType) ]) + // { description = '' + unbound.conf configuration type. The format consist of an attribute + set of settings. Each settings can be either one value, a list of + values or an attribute set. The allowed values are integers, + strings, booleans or floats. + ''; + }; + + options = { + remote-control.control-enable = mkOption { + type = bool; + default = false; + internal = true; + }; + }; + }; + example = literalExample '' + { + server = { + interface = [ "127.0.0.1" ]; + }; + forward-zone = [ + { + name = "."; + forward-addr = "1.1.1.1@853#cloudflare-dns.com"; + } + { + name = "example.org."; + forward-addr = [ + "1.1.1.1@853#cloudflare-dns.com" + "1.0.0.1@853#cloudflare-dns.com" + ]; + } + ]; + remote-control.control-enable = true; + }; + ''; description = '' - Extra unbound config. See - <citerefentry><refentrytitle>unbound.conf</refentrytitle><manvolnum>8 - </manvolnum></citerefentry>. + Declarative Unbound configuration + See the <citerefentry><refentrytitle>unbound.conf</refentrytitle> + <manvolnum>5</manvolnum></citerefentry> manpage for a list of + available options. ''; }; - }; }; @@ -130,23 +155,56 @@ in config = mkIf cfg.enable { + services.unbound.settings = { + server = { + directory = mkDefault cfg.stateDir; + username = cfg.user; + chroot = ''""''; + pidfile = ''""''; + # when running under systemd there is no need to daemonize + do-daemonize = false; + interface = mkDefault ([ "127.0.0.1" ] ++ (optional config.networking.enableIPv6 "::1")); + access-control = mkDefault ([ "127.0.0.0/8 allow" ] ++ (optional config.networking.enableIPv6 "::1/128 allow")); + auto-trust-anchor-file = mkIf cfg.enableRootTrustAnchor rootTrustAnchorFile; + tls-cert-bundle = mkDefault "/etc/ssl/certs/ca-certificates.crt"; + # prevent race conditions on system startup when interfaces are not yet + # configured + ip-freebind = mkDefault true; + }; + remote-control = { + control-enable = mkDefault false; + control-interface = mkDefault ([ "127.0.0.1" ] ++ (optional config.networking.enableIPv6 "::1")); + server-key-file = mkDefault "${cfg.stateDir}/unbound_server.key"; + server-cert-file = mkDefault "${cfg.stateDir}/unbound_server.pem"; + control-key-file = mkDefault "${cfg.stateDir}/unbound_control.key"; + control-cert-file = mkDefault "${cfg.stateDir}/unbound_control.pem"; + } // optionalAttrs (cfg.localControlSocketPath != null) { + control-enable = true; + control-interface = cfg.localControlSocketPath; + }; + }; + environment.systemPackages = [ cfg.package ]; - users.users.unbound = { - description = "unbound daemon user"; - isSystemUser = true; - group = lib.mkIf (cfg.localControlSocketPath != null) (lib.mkDefault "unbound"); + users.users = mkIf (cfg.user == "unbound") { + unbound = { + description = "unbound daemon user"; + isSystemUser = true; + group = cfg.group; + }; }; - # We need a group so that we can give users access to the configured - # control socket. Unbound allows access to the socket only to the unbound - # user and the primary group. - users.groups = lib.mkIf (cfg.localControlSocketPath != null) { + users.groups = mkIf (cfg.group == "unbound") { unbound = {}; }; - networking.resolvconf.useLocalResolver = mkDefault true; + networking = mkIf cfg.resolveLocalQueries { + resolvconf = { + useLocalResolver = mkDefault true; + }; + networkmanager.dns = "unbound"; + }; environment.etc."unbound/unbound.conf".source = confFile; @@ -156,8 +214,15 @@ in before = [ "nss-lookup.target" ]; wantedBy = [ "multi-user.target" "nss-lookup.target" ]; - preStart = lib.mkIf cfg.enableRootTrustAnchor '' - ${cfg.package}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!" + path = mkIf cfg.settings.remote-control.control-enable [ pkgs.openssl ]; + + preStart = '' + ${optionalString cfg.enableRootTrustAnchor '' + ${cfg.package}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!" + ''} + ${optionalString cfg.settings.remote-control.control-enable '' + ${cfg.package}/bin/unbound-control-setup -d ${cfg.stateDir} + ''} ''; restartTriggers = [ @@ -181,8 +246,8 @@ in "CAP_SYS_RESOURCE" ]; - User = "unbound"; - Group = lib.mkIf (cfg.localControlSocketPath != null) (lib.mkDefault "unbound"); + User = cfg.user; + Group = cfg.group; MemoryDenyWriteExecute = true; NoNewPrivileges = true; @@ -211,9 +276,29 @@ in RestrictNamespaces = true; LockPersonality = true; RestrictSUIDSGID = true; + + Restart = "on-failure"; + RestartSec = "5s"; }; }; - # If networkmanager is enabled, ask it to interface with unbound. - networking.networkmanager.dns = "unbound"; }; + + imports = [ + (mkRenamedOptionModule [ "services" "unbound" "interfaces" ] [ "services" "unbound" "settings" "server" "interface" ]) + (mkChangedOptionModule [ "services" "unbound" "allowedAccess" ] [ "services" "unbound" "settings" "server" "access-control" ] ( + config: map (value: "${value} allow") (getAttrFromPath [ "services" "unbound" "allowedAccess" ] config) + )) + (mkRemovedOptionModule [ "services" "unbound" "forwardAddresses" ] '' + Add a new setting: + services.unbound.settings.forward-zone = [{ + name = "."; + forward-addr = [ # Your current services.unbound.forwardAddresses ]; + }]; + If any of those addresses are local addresses (127.0.0.1 or ::1), you must + also set services.unbound.settings.server.do-not-query-localhost to false. + '') + (mkRemovedOptionModule [ "services" "unbound" "extraConfig" ] '' + You can use services.unbound.settings to add any configuration you want. + '') + ]; } diff --git a/nixpkgs/nixos/modules/services/networking/wireguard.nix b/nixpkgs/nixos/modules/services/networking/wireguard.nix index 34c869345357..2b51770a5aa1 100644 --- a/nixpkgs/nixos/modules/services/networking/wireguard.nix +++ b/nixpkgs/nixos/modules/services/networking/wireguard.nix @@ -198,7 +198,32 @@ let example = "demo.wireguard.io:12913"; type = with types; nullOr str; description = ''Endpoint IP or hostname of the peer, followed by a colon, - and then a port number of the peer.''; + and then a port number of the peer. + + Warning for endpoints with changing IPs: + The WireGuard kernel side cannot perform DNS resolution. + Thus DNS resolution is done once by the <literal>wg</literal> userspace + utility, when setting up WireGuard. Consequently, if the IP address + behind the name changes, WireGuard will not notice. + This is especially common for dynamic-DNS setups, but also applies to + any other DNS-based setup. + If you do not use IP endpoints, you likely want to set + <option>networking.wireguard.dynamicEndpointRefreshSeconds</option> + to refresh the IPs periodically. + ''; + }; + + dynamicEndpointRefreshSeconds = mkOption { + default = 0; + example = 5; + type = with types; int; + description = '' + Periodically re-execute the <literal>wg</literal> utility every + this many seconds in order to let WireGuard notice DNS / hostname + changes. + + Setting this to <literal>0</literal> disables periodic reexecution. + ''; }; persistentKeepalive = mkOption { @@ -219,17 +244,6 @@ let }; - generatePathUnit = name: values: - assert (values.privateKey == null); - assert (values.privateKeyFile != null); - nameValuePair "wireguard-${name}" - { - description = "WireGuard Tunnel - ${name} - Private Key"; - requiredBy = [ "wireguard-${name}.service" ]; - before = [ "wireguard-${name}.service" ]; - pathConfig.PathExists = values.privateKeyFile; - }; - generateKeyServiceUnit = name: values: assert values.generatePrivateKeyFile; nameValuePair "wireguard-${name}-key" @@ -246,22 +260,31 @@ let }; script = '' - mkdir --mode 0644 -p "${dirOf values.privateKeyFile}" + set -e + + # If the parent dir does not already exist, create it. + # Otherwise, does nothing, keeping existing permisions intact. + mkdir -p --mode 0755 "${dirOf values.privateKeyFile}" + if [ ! -f "${values.privateKeyFile}" ]; then - touch "${values.privateKeyFile}" - chmod 0600 "${values.privateKeyFile}" - wg genkey > "${values.privateKeyFile}" - chmod 0400 "${values.privateKeyFile}" + # Write private key file with atomically-correct permissions. + (set -e; umask 077; wg genkey > "${values.privateKeyFile}") fi ''; }; - generatePeerUnit = { interfaceName, interfaceCfg, peer }: + peerUnitServiceName = interfaceName: publicKey: dynamicRefreshEnabled: let keyToUnitName = replaceChars [ "/" "-" " " "+" "=" ] [ "-" "\\x2d" "\\x20" "\\x2b" "\\x3d" ]; - unitName = keyToUnitName peer.publicKey; + unitName = keyToUnitName publicKey; + refreshSuffix = optionalString dynamicRefreshEnabled "-refresh"; + in + "wireguard-${interfaceName}-peer-${unitName}${refreshSuffix}"; + + generatePeerUnit = { interfaceName, interfaceCfg, peer }: + let psk = if peer.presharedKey != null then pkgs.writeText "wg-psk" peer.presharedKey @@ -270,7 +293,12 @@ let dst = interfaceCfg.interfaceNamespace; ip = nsWrap "ip" src dst; wg = nsWrap "wg" src dst; - in nameValuePair "wireguard-${interfaceName}-peer-${unitName}" + dynamicRefreshEnabled = peer.dynamicEndpointRefreshSeconds != 0; + # We generate a different name (a `-refresh` suffix) when `dynamicEndpointRefreshSeconds` + # to avoid that the same service switches `Type` (`oneshot` vs `simple`), + # with the intent to make scripting more obvious. + serviceName = peerUnitServiceName interfaceName peer.publicKey dynamicRefreshEnabled; + in nameValuePair serviceName { description = "WireGuard Peer - ${interfaceName} - ${peer.publicKey}"; requires = [ "wireguard-${interfaceName}.service" ]; @@ -280,36 +308,59 @@ let environment.WG_ENDPOINT_RESOLUTION_RETRIES = "infinity"; path = with pkgs; [ iproute2 wireguard-tools ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; + serviceConfig = + if !dynamicRefreshEnabled + then + { + Type = "oneshot"; + RemainAfterExit = true; + } + else + { + Type = "simple"; # re-executes 'wg' indefinitely + # Note that `Type = "oneshot"` services with `RemainAfterExit = true` + # cannot be used with systemd timers (see `man systemd.timer`), + # which is why `simple` with a loop is the best choice here. + # It also makes starting and stopping easiest. + }; script = let - wg_setup = "${wg} set ${interfaceName} peer ${peer.publicKey}" + - optionalString (psk != null) " preshared-key ${psk}" + - optionalString (peer.endpoint != null) " endpoint ${peer.endpoint}" + - optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" + - optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}"; + wg_setup = concatStringsSep " " ( + [ ''${wg} set ${interfaceName} peer "${peer.publicKey}"'' ] + ++ optional (psk != null) ''preshared-key "${psk}"'' + ++ optional (peer.endpoint != null) ''endpoint "${peer.endpoint}"'' + ++ optional (peer.persistentKeepalive != null) ''persistent-keepalive "${toString peer.persistentKeepalive}"'' + ++ optional (peer.allowedIPs != []) ''allowed-ips "${concatStringsSep "," peer.allowedIPs}"'' + ); route_setup = optionalString interfaceCfg.allowedIPsAsRoutes (concatMapStringsSep "\n" (allowedIP: - "${ip} route replace ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}" + ''${ip} route replace "${allowedIP}" dev "${interfaceName}" table "${interfaceCfg.table}"'' ) peer.allowedIPs); in '' ${wg_setup} ${route_setup} + + ${optionalString (peer.dynamicEndpointRefreshSeconds != 0) '' + # Re-execute 'wg' periodically to notice DNS / hostname changes. + # Note this will not time out on transient DNS failures such as DNS names + # because we have set 'WG_ENDPOINT_RESOLUTION_RETRIES=infinity'. + # Also note that 'wg' limits its maximum retry delay to 20 seconds as of writing. + while ${wg_setup}; do + sleep "${toString peer.dynamicEndpointRefreshSeconds}"; + done + ''} ''; postStop = let route_destroy = optionalString interfaceCfg.allowedIPsAsRoutes (concatMapStringsSep "\n" (allowedIP: - "${ip} route delete ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}" + ''${ip} route delete "${allowedIP}" dev "${interfaceName}" table "${interfaceCfg.table}"'' ) peer.allowedIPs); in '' - ${wg} set ${interfaceName} peer ${peer.publicKey} remove + ${wg} set "${interfaceName}" peer "${peer.publicKey}" remove ${route_destroy} ''; }; @@ -345,23 +396,25 @@ let ${values.preSetup} - ${ipPreMove} link add dev ${name} type wireguard - ${optionalString (values.interfaceNamespace != null && values.interfaceNamespace != values.socketNamespace) "${ipPreMove} link set ${name} netns ${ns}"} + ${ipPreMove} link add dev "${name}" type wireguard + ${optionalString (values.interfaceNamespace != null && values.interfaceNamespace != values.socketNamespace) ''${ipPreMove} link set "${name}" netns "${ns}"''} ${concatMapStringsSep "\n" (ip: - "${ipPostMove} address add ${ip} dev ${name}" + ''${ipPostMove} address add "${ip}" dev "${name}"'' ) values.ips} - ${wg} set ${name} private-key ${privKey} ${ - optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}"} + ${concatStringsSep " " ( + [ ''${wg} set "${name}" private-key "${privKey}"'' ] + ++ optional (values.listenPort != null) ''listen-port "${toString values.listenPort}"'' + )} - ${ipPostMove} link set up dev ${name} + ${ipPostMove} link set up dev "${name}" ${values.postSetup} ''; postStop = '' - ${ipPostMove} link del dev ${name} + ${ipPostMove} link del dev "${name}" ${values.postShutdown} ''; }; @@ -371,7 +424,7 @@ let nsList = filter (ns: ns != null) [ src dst ]; ns = last nsList; in - if (length nsList > 0 && ns != "init") then "ip netns exec ${ns} ${cmd}" else cmd; + if (length nsList > 0 && ns != "init") then ''ip netns exec "${ns}" "${cmd}"'' else cmd; in { @@ -445,9 +498,6 @@ in // (mapAttrs' generateKeyServiceUnit (filterAttrs (name: value: value.generatePrivateKeyFile) cfg.interfaces)); - systemd.paths = mapAttrs' generatePathUnit - (filterAttrs (name: value: value.privateKeyFile != null) cfg.interfaces); - }); } diff --git a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix index 61482596763a..c0a4ce40760a 100644 --- a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix +++ b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix @@ -3,6 +3,10 @@ with lib; let + package = if cfg.allowAuxiliaryImperativeNetworks + then pkgs.wpa_supplicant_ro_ssids + else pkgs.wpa_supplicant; + cfg = config.networking.wireless; configFile = if cfg.networks != {} || cfg.extraConfig != "" || cfg.userControlled.enable then pkgs.writeText "wpa_supplicant.conf" '' ${optionalString cfg.userControlled.enable '' @@ -38,6 +42,11 @@ in { description = '' The interfaces <command>wpa_supplicant</command> will use. If empty, it will automatically use all wireless interfaces. + <warning><para> + The automatic discovery of interfaces does not work reliably on boot: + it may fail and leave the system without network. When possible, specify + a known interface name. + </para></warning> ''; }; @@ -47,6 +56,16 @@ in { description = "Force a specific wpa_supplicant driver."; }; + allowAuxiliaryImperativeNetworks = mkEnableOption "support for imperative & declarative networks" // { + description = '' + Whether to allow configuring networks "imperatively" (e.g. via + <package>wpa_supplicant_gui</package>) and declaratively via + <xref linkend="opt-networking.wireless.networks" />. + + Please note that this adds a custom patch to <package>wpa_supplicant</package>. + ''; + }; + networks = mkOption { type = types.attrsOf (types.submodule { options = { @@ -211,9 +230,17 @@ in { message = ''options networking.wireless."${name}".{psk,pskRaw,auth} are mutually exclusive''; }); - environment.systemPackages = [ pkgs.wpa_supplicant ]; + warnings = + optional (cfg.interfaces == [] && config.systemd.services.wpa_supplicant.wantedBy != []) + '' + No network interfaces for wpa_supplicant have been configured: the service + may randomly fail to start at boot. You should specify at least one using the option + networking.wireless.interfaces. + ''; + + environment.systemPackages = [ package ]; - services.dbus.packages = [ pkgs.wpa_supplicant ]; + services.dbus.packages = [ package ]; services.udev.packages = [ pkgs.crda ]; # FIXME: start a separate wpa_supplicant instance per interface. @@ -230,13 +257,17 @@ in { wantedBy = [ "multi-user.target" ]; stopIfChanged = false; - path = [ pkgs.wpa_supplicant ]; + path = [ package ]; - script = '' + script = let + configStr = if cfg.allowAuxiliaryImperativeNetworks + then "-c /etc/wpa_supplicant.conf -I ${configFile}" + else "-c ${configFile}"; + in '' if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ] then echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead." fi - iface_args="-s -u -D${cfg.driver} -c ${configFile}" + iface_args="-s -u -D${cfg.driver} ${configStr}" ${if ifaces == [] then '' for i in $(cd /sys/class/net && echo *); do DEVTYPE= diff --git a/nixpkgs/nixos/modules/services/networking/yggdrasil.nix b/nixpkgs/nixos/modules/services/networking/yggdrasil.nix index a71c635c9f6e..47a7152f6fe6 100644 --- a/nixpkgs/nixos/modules/services/networking/yggdrasil.nix +++ b/nixpkgs/nixos/modules/services/networking/yggdrasil.nix @@ -64,7 +64,7 @@ in { type = types.str; default = "root"; example = "wheel"; - description = "Group to grant acces to the Yggdrasil control socket."; + description = "Group to grant access to the Yggdrasil control socket."; }; openMulticastPort = mkOption { @@ -122,12 +122,11 @@ in { system.activationScripts.yggdrasil = mkIf cfg.persistentKeys '' if [ ! -e ${keysPath} ] then - mkdir -p ${builtins.dirOf keysPath} + mkdir --mode=700 -p ${builtins.dirOf keysPath} ${binYggdrasil} -genconf -json \ | ${pkgs.jq}/bin/jq \ 'to_entries|map(select(.key|endswith("Key")))|from_entries' \ > ${keysPath} - chmod 600 ${keysPath} fi ''; diff --git a/nixpkgs/nixos/modules/services/networking/znc/default.nix b/nixpkgs/nixos/modules/services/networking/znc/default.nix index a7315896c506..aa79ed27dcef 100644 --- a/nixpkgs/nixos/modules/services/networking/znc/default.nix +++ b/nixpkgs/nixos/modules/services/networking/znc/default.nix @@ -103,8 +103,8 @@ in }; dataDir = mkOption { - default = "/var/lib/znc/"; - example = "/home/john/.znc/"; + default = "/var/lib/znc"; + example = "/home/john/.znc"; type = types.path; description = '' The state directory for ZNC. The config and the modules will be linked @@ -258,6 +258,34 @@ in ExecStart = "${pkgs.znc}/bin/znc --foreground --datadir ${cfg.dataDir} ${escapeShellArgs cfg.extraFlags}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID"; + # Hardening + CapabilityBoundingSet = [ "" ]; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = 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"; + ReadWritePaths = [ cfg.dataDir ]; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; + UMask = "0027"; }; preStart = '' mkdir -p ${cfg.dataDir}/configs @@ -271,9 +299,8 @@ in # Ensure essential files exist. if [[ ! -f ${cfg.dataDir}/configs/znc.conf ]]; then echo "No znc.conf file found in ${cfg.dataDir}. Creating one now." - cp --no-clobber ${cfg.configFile} ${cfg.dataDir}/configs/znc.conf + cp --no-preserve=ownership --no-clobber ${cfg.configFile} ${cfg.dataDir}/configs/znc.conf chmod u+rw ${cfg.dataDir}/configs/znc.conf - chown ${cfg.user} ${cfg.dataDir}/configs/znc.conf fi if [[ ! -f ${cfg.dataDir}/znc.pem ]]; then diff --git a/nixpkgs/nixos/modules/services/networking/znc/options.nix b/nixpkgs/nixos/modules/services/networking/znc/options.nix index 048dbd738630..7a43b45fabba 100644 --- a/nixpkgs/nixos/modules/services/networking/znc/options.nix +++ b/nixpkgs/nixos/modules/services/networking/znc/options.nix @@ -44,7 +44,7 @@ let modules = mkOption { type = types.listOf types.str; default = [ "simple_away" ]; - example = literalExample "[ simple_away sasl ]"; + example = literalExample ''[ "simple_away" "sasl" ]''; description = '' ZNC network modules to load. ''; |