diff options
Diffstat (limited to 'nixos/modules/services/networking')
-rw-r--r-- | nixos/modules/services/networking/firewall.nix | 10 | ||||
-rw-r--r-- | nixos/modules/services/networking/iodine.nix | 163 | ||||
-rw-r--r-- | nixos/modules/services/networking/resilio.nix | 2 | ||||
-rw-r--r-- | nixos/modules/services/networking/smartdns.nix | 61 | ||||
-rw-r--r-- | nixos/modules/services/networking/ssh/sshd.nix | 26 | ||||
-rw-r--r-- | nixos/modules/services/networking/stubby.nix | 1 | ||||
-rw-r--r-- | nixos/modules/services/networking/supplicant.nix | 2 | ||||
-rw-r--r-- | nixos/modules/services/networking/supybot.nix | 109 | ||||
-rw-r--r-- | nixos/modules/services/networking/tailscale.nix | 46 | ||||
-rw-r--r-- | nixos/modules/services/networking/zerotierone.nix | 17 |
10 files changed, 344 insertions, 93 deletions
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix index b0045ff795e..cdc3a172ea7 100644 --- a/nixos/modules/services/networking/firewall.nix +++ b/nixos/modules/services/networking/firewall.nix @@ -546,9 +546,13 @@ in options nf_conntrack nf_conntrack_helper=1 ''; - assertions = [ { assertion = cfg.checkReversePath -> kernelHasRPFilter; - message = "This kernel does not support rpfilter"; } - ]; + assertions = [ + # This is approximately "checkReversePath -> kernelHasRPFilter", + # but the checkReversePath option can include non-boolean + # values. + { assertion = cfg.checkReversePath == false || kernelHasRPFilter; + message = "This kernel does not support rpfilter"; } + ]; systemd.services.firewall = { description = "Firewall"; diff --git a/nixos/modules/services/networking/iodine.nix b/nixos/modules/services/networking/iodine.nix index f9ca26c2796..46051d7044b 100644 --- a/nixos/modules/services/networking/iodine.nix +++ b/nixos/modules/services/networking/iodine.nix @@ -9,6 +9,8 @@ let iodinedUser = "iodined"; + /* is this path made unreadable by ProtectHome = true ? */ + isProtected = x: hasPrefix "/root" x || hasPrefix "/home" x; in { imports = [ @@ -35,45 +37,48 @@ in corresponding attribute name. ''; example = literalExample '' - { - foo = { - server = "tunnel.mdomain.com"; - relay = "8.8.8.8"; - extraConfig = "-v"; + { + foo = { + server = "tunnel.mdomain.com"; + relay = "8.8.8.8"; + extraConfig = "-v"; + } } - } ''; - type = types.attrsOf (types.submodule ( - { - options = { - server = mkOption { - type = types.str; - default = ""; - description = "Domain or Subdomain of server running iodined"; - example = "tunnel.mydomain.com"; - }; - - relay = mkOption { - type = types.str; - default = ""; - description = "DNS server to use as a intermediate relay to the iodined server"; - example = "8.8.8.8"; - }; - - extraConfig = mkOption { - type = types.str; - default = ""; - description = "Additional command line parameters"; - example = "-l 192.168.1.10 -p 23"; - }; - - passwordFile = mkOption { - type = types.str; - default = ""; - description = "File that contains password"; - }; - }; - })); + type = types.attrsOf ( + types.submodule ( + { + options = { + server = mkOption { + type = types.str; + default = ""; + description = "Hostname of server running iodined"; + example = "tunnel.mydomain.com"; + }; + + relay = mkOption { + type = types.str; + default = ""; + description = "DNS server to use as an intermediate relay to the iodined server"; + example = "8.8.8.8"; + }; + + extraConfig = mkOption { + type = types.str; + default = ""; + description = "Additional command line parameters"; + example = "-l 192.168.1.10 -p 23"; + }; + + passwordFile = mkOption { + type = types.str; + default = ""; + description = "Path to a file containing the password."; + }; + }; + } + ) + ); }; server = { @@ -121,31 +126,67 @@ in boot.kernelModules = [ "tun" ]; systemd.services = - let - createIodineClientService = name: cfg: - { - description = "iodine client - ${name}"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - script = "exec ${pkgs.iodine}/bin/iodine -f -u ${iodinedUser} ${cfg.extraConfig} ${optionalString (cfg.passwordFile != "") "< \"${cfg.passwordFile}\""} ${cfg.relay} ${cfg.server}"; - serviceConfig = { - RestartSec = "30s"; - Restart = "always"; + let + createIodineClientService = name: cfg: + { + description = "iodine client - ${name}"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = "exec ${pkgs.iodine}/bin/iodine -f -u ${iodinedUser} ${cfg.extraConfig} ${optionalString (cfg.passwordFile != "") "< \"${builtins.toString cfg.passwordFile}\""} ${cfg.relay} ${cfg.server}"; + serviceConfig = { + RestartSec = "30s"; + Restart = "always"; + + # hardening : + # Filesystem access + ProtectSystem = "strict"; + ProtectHome = if isProtected cfg.passwordFile then "read-only" else "true" ; + PrivateTmp = true; + ReadWritePaths = "/dev/net/tun"; + PrivateDevices = false; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + # Caps + NoNewPrivileges = true; + # Misc. + LockPersonality = true; + RestrictRealtime = true; + PrivateMounts = true; + MemoryDenyWriteExecute = true; + }; + }; + in + listToAttrs ( + mapAttrsToList + (name: value: nameValuePair "iodine-${name}" (createIodineClientService name value)) + cfg.clients + ) // { + iodined = mkIf (cfg.server.enable) { + description = "iodine, ip over dns server daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = "exec ${pkgs.iodine}/bin/iodined -f -u ${iodinedUser} ${cfg.server.extraConfig} ${optionalString (cfg.server.passwordFile != "") "< \"${builtins.toString cfg.server.passwordFile}\""} ${cfg.server.ip} ${cfg.server.domain}"; + serviceConfig = { + # Filesystem access + ProtectSystem = "strict"; + ProtectHome = if isProtected cfg.server.passwordFile then "read-only" else "true" ; + PrivateTmp = true; + ReadWritePaths = "/dev/net/tun"; + PrivateDevices = false; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + # Caps + NoNewPrivileges = true; + # Misc. + LockPersonality = true; + RestrictRealtime = true; + PrivateMounts = true; + MemoryDenyWriteExecute = true; + }; + }; }; - }; - in - listToAttrs ( - mapAttrsToList - (name: value: nameValuePair "iodine-${name}" (createIodineClientService name value)) - cfg.clients - ) // { - iodined = mkIf (cfg.server.enable) { - description = "iodine, ip over dns server daemon"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - script = "exec ${pkgs.iodine}/bin/iodined -f -u ${iodinedUser} ${cfg.server.extraConfig} ${optionalString (cfg.server.passwordFile != "") "< \"${cfg.server.passwordFile}\""} ${cfg.server.ip} ${cfg.server.domain}"; - }; - }; users.users.${iodinedUser} = { uid = config.ids.uids.iodined; diff --git a/nixos/modules/services/networking/resilio.nix b/nixos/modules/services/networking/resilio.nix index 9b25aa57583..e74e03fc0b0 100644 --- a/nixos/modules/services/networking/resilio.nix +++ b/nixos/modules/services/networking/resilio.nix @@ -244,7 +244,7 @@ in group = "rslsync"; }; - users.groups = [ { name = "rslsync"; } ]; + users.groups.rslsync = {}; systemd.services.resilio = with pkgs; { description = "Resilio Sync Service"; diff --git a/nixos/modules/services/networking/smartdns.nix b/nixos/modules/services/networking/smartdns.nix new file mode 100644 index 00000000000..f1888af7041 --- /dev/null +++ b/nixos/modules/services/networking/smartdns.nix @@ -0,0 +1,61 @@ +{ lib, pkgs, config, ... }: + +with lib; + +let + inherit (lib.types) attrsOf coercedTo listOf oneOf str int bool; + cfg = config.services.smartdns; + + confFile = pkgs.writeText "smartdns.conf" (with generators; + toKeyValue { + mkKeyValue = mkKeyValueDefault { + mkValueString = v: + if isBool v then + if v then "yes" else "no" + else + mkValueStringDefault { } v; + } " "; + listsAsDuplicateKeys = + true; # Allowing duplications because we need to deal with multiple entries with the same key. + } cfg.settings); +in { + options.services.smartdns = { + enable = mkEnableOption "SmartDNS DNS server"; + + bindPort = mkOption { + type = types.port; + default = 53; + description = "DNS listening port number."; + }; + + settings = mkOption { + type = + let atom = oneOf [ str int bool ]; + in attrsOf (coercedTo atom toList (listOf atom)); + example = literalExample '' + { + bind = ":5353 -no-rule -group example"; + cache-size = 4096; + server-tls = [ "8.8.8.8:853" "1.1.1.1:853" ]; + server-https = "https://cloudflare-dns.com/dns-query -exclude-default-group"; + prefetch-domain = true; + speed-check-mode = "ping,tcp:80"; + }; + ''; + description = '' + A set that will be generated into configuration file, see the <link xlink:href="https://github.com/pymumu/smartdns/blob/master/ReadMe_en.md#configuration-parameter">SmartDNS README</link> for details of configuration parameters. + You could override the options here like <option>services.smartdns.bindPort</option> by writing <literal>settings.bind = ":5353 -no-rule -group example";</literal>. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + services.smartdns.settings.bind = mkDefault ":${toString cfg.bindPort}"; + + systemd.packages = [ pkgs.smartdns ]; + systemd.services.smartdns.wantedBy = [ "multi-user.target" ]; + environment.etc."smartdns/smartdns.conf".source = confFile; + environment.etc."default/smartdns".source = + "${pkgs.smartdns}/etc/default/smartdns"; + }; +} diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index b0e2e303cbc..464e9ed38c4 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -17,7 +17,7 @@ let ${cfg.extraConfig} EOL - ssh-keygen -f mock-hostkey -N "" + ssh-keygen -q -f mock-hostkey -N "" sshd -t -f $out -h mock-hostkey ''; @@ -238,6 +238,26 @@ in description = "Files from which authorized keys are read."; }; + authorizedKeysCommand = mkOption { + type = types.str; + default = "none"; + description = '' + Specifies a program to be used to look up the user's public + keys. The program must be owned by root, not writable by group + or others and specified by an absolute path. + ''; + }; + + authorizedKeysCommandUser = mkOption { + type = types.str; + default = "nobody"; + description = '' + Specifies the user under whose account the AuthorizedKeysCommand + is run. It is recommended to use a dedicated user that has no + other role on the host than running authorized keys commands. + ''; + }; + kexAlgorithms = mkOption { type = types.listOf types.str; default = [ @@ -485,6 +505,10 @@ in PrintMotd no # handled by pam_motd AuthorizedKeysFile ${toString cfg.authorizedKeysFiles} + ${optionalString (cfg.authorizedKeysCommand != "none") '' + AuthorizedKeysCommand ${cfg.authorizedKeysCommand} + AuthorizedKeysCommandUser ${cfg.authorizedKeysCommandUser} + ''} ${flip concatMapStrings cfg.hostKeys (k: '' HostKey ${k.path} diff --git a/nixos/modules/services/networking/stubby.nix b/nixos/modules/services/networking/stubby.nix index 849d266576d..c5e0f929a12 100644 --- a/nixos/modules/services/networking/stubby.nix +++ b/nixos/modules/services/networking/stubby.nix @@ -205,6 +205,7 @@ in wantedBy = [ "multi-user.target" ]; serviceConfig = { + Type = "notify"; AmbientCapabilities = "CAP_NET_BIND_SERVICE"; CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; ExecStart = "${pkgs.stubby}/bin/stubby -C ${confFile} ${optionalString cfg.debugLogging "-l"}"; diff --git a/nixos/modules/services/networking/supplicant.nix b/nixos/modules/services/networking/supplicant.nix index 35c1e649e2e..b5b9989ce18 100644 --- a/nixos/modules/services/networking/supplicant.nix +++ b/nixos/modules/services/networking/supplicant.nix @@ -39,8 +39,6 @@ let bindsTo = deps; after = deps; before = [ "network.target" ]; - # Receive restart event after resume - partOf = [ "post-resume.target" ]; path = [ pkgs.coreutils ]; diff --git a/nixos/modules/services/networking/supybot.nix b/nixos/modules/services/networking/supybot.nix index d5b9a97a1c1..dc9fb31ffd0 100644 --- a/nixos/modules/services/networking/supybot.nix +++ b/nixos/modules/services/networking/supybot.nix @@ -3,32 +3,35 @@ with lib; let - cfg = config.services.supybot; - + isStateDirHome = hasPrefix "/home/" cfg.stateDir; + isStateDirVar = cfg.stateDir == "/var/lib/supybot"; + pyEnv = pkgs.python3.withPackages (p: [ p.limnoria ] ++ (cfg.extraPackages p)); in - { - options = { services.supybot = { enable = mkOption { + type = types.bool; default = false; - description = "Enable Supybot, an IRC bot"; + description = "Enable Supybot, an IRC bot (also known as Limnoria)."; }; stateDir = mkOption { - # Setting this to /var/lib/supybot caused useradd to fail - default = "/home/supybot"; + type = types.path; + default = if versionAtLeast config.system.stateVersion "20.09" + then "/var/lib/supybot" + else "/home/supybot"; + defaultText = "/var/lib/supybot"; description = "The root directory, logs and plugins are stored here"; }; configFile = mkOption { type = types.path; description = '' - Path to a supybot config file. This can be generated by + Path to initial supybot config file. This can be generated by running supybot-wizard. Note: all paths should include the full path to the stateDir @@ -36,21 +39,54 @@ in ''; }; + plugins = mkOption { + type = types.attrsOf types.path; + default = {}; + description = '' + Attribute set of additional plugins that will be symlinked to the + <filename>plugin</filename> subdirectory. + + Please note that you still need to add the plugins to the config + file (or with <literal>!load</literal>) using their attribute name. + ''; + example = literalExample '' + let + plugins = pkgs.fetchzip { + url = "https://github.com/ProgVal/Supybot-plugins/archive/57c2450c.zip"; + sha256 = "077snf84ibnva3sbpzdfpfma6hcdw7dflwnhg6pw7mgnf0nd84qd"; + }; + in + { + Wikipedia = "''${plugins}/Wikipedia"; + Decide = ./supy-decide; + } + ''; + }; + + extraPackages = mkOption { + default = p: []; + description = '' + Extra Python packages available to supybot plugins. The + value must be a function which receives the attrset defined + in <varname>python3Packages</varname> as the sole argument. + ''; + example = literalExample ''p: [ p.lxml p.requests ]''; + }; + }; }; - config = mkIf cfg.enable { - environment.systemPackages = [ pkgs.pythonPackages.limnoria ]; + environment.systemPackages = [ pkgs.python3Packages.limnoria ]; users.users.supybot = { uid = config.ids.uids.supybot; group = "supybot"; description = "Supybot IRC bot user"; home = cfg.stateDir; - createHome = true; + isSystemUser = true; }; users.groups.supybot = { @@ -59,19 +95,16 @@ in systemd.services.supybot = { description = "Supybot, an IRC bot"; + documentation = [ "https://limnoria.readthedocs.io/" ]; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - path = [ pkgs.pythonPackages.limnoria ]; preStart = '' - cd ${cfg.stateDir} - mkdir -p backup conf data plugins logs/plugins tmp web - ln -sf ${cfg.configFile} supybot.cfg # This needs to be created afresh every time - rm -f supybot.cfg.bak + rm -f '${cfg.stateDir}/supybot.cfg.bak' ''; serviceConfig = { - ExecStart = "${pkgs.pythonPackages.limnoria}/bin/supybot ${cfg.stateDir}/supybot.cfg"; + ExecStart = "${pyEnv}/bin/supybot ${cfg.stateDir}/supybot.cfg"; PIDFile = "/run/supybot.pid"; User = "supybot"; Group = "supybot"; @@ -79,8 +112,50 @@ in Restart = "on-abort"; StartLimitInterval = "5m"; StartLimitBurst = "1"; + + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + ProtectControlGroups = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + RestrictNamespaces = true; + RestrictRealtime = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RemoveIPC = true; + ProtectHostname = true; + CapabilityBoundingSet = ""; + ProtectSystem = "full"; + } + // optionalAttrs isStateDirVar { + StateDirectory = "supybot"; + ProtectSystem = "strict"; + } + // optionalAttrs (!isStateDirHome) { + ProtectHome = true; }; }; + systemd.tmpfiles.rules = [ + "d '${cfg.stateDir}' 0700 supybot supybot - -" + "d '${cfg.stateDir}/backup' 0750 supybot supybot - -" + "d '${cfg.stateDir}/conf' 0750 supybot supybot - -" + "d '${cfg.stateDir}/data' 0750 supybot supybot - -" + "d '${cfg.stateDir}/plugins' 0750 supybot supybot - -" + "d '${cfg.stateDir}/logs' 0750 supybot supybot - -" + "d '${cfg.stateDir}/logs/plugins' 0750 supybot supybot - -" + "d '${cfg.stateDir}/tmp' 0750 supybot supybot - -" + "d '${cfg.stateDir}/web' 0750 supybot supybot - -" + "L '${cfg.stateDir}/supybot.cfg' - - - - ${cfg.configFile}" + ] + ++ (flip mapAttrsToList cfg.plugins (name: dest: + "L+ '${cfg.stateDir}/plugins/${name}' - - - - ${dest}" + )); + }; } diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix new file mode 100644 index 00000000000..513c42b4011 --- /dev/null +++ b/nixos/modules/services/networking/tailscale.nix @@ -0,0 +1,46 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.services.tailscale; +in { + meta.maintainers = with maintainers; [ danderson mbaillie ]; + + options.services.tailscale = { + enable = mkEnableOption "Tailscale client daemon"; + + port = mkOption { + type = types.port; + default = 41641; + description = "The port to listen on for tunnel traffic (0=autoselect)."; + }; + }; + + config = mkIf cfg.enable { + systemd.services.tailscale = { + description = "Tailscale client daemon"; + + after = [ "network-pre.target" ]; + wants = [ "network-pre.target" ]; + wantedBy = [ "multi-user.target" ]; + + unitConfig = { + StartLimitIntervalSec = 0; + StartLimitBurst = 0; + }; + + serviceConfig = { + ExecStart = + "${pkgs.tailscale}/bin/tailscaled --port ${toString cfg.port}"; + + RuntimeDirectory = "tailscale"; + RuntimeDirectoryMode = 755; + + StateDirectory = "tailscale"; + StateDirectoryMode = 700; + + Restart = "on-failure"; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix index 042c4d5addd..cf39ed065a7 100644 --- a/nixos/modules/services/networking/zerotierone.nix +++ b/nixos/modules/services/networking/zerotierone.nix @@ -69,13 +69,14 @@ in environment.systemPackages = [ cfg.package ]; # Prevent systemd from potentially changing the MAC address - environment.etc."systemd/network/50-zerotier.link".text = '' - [Match] - OriginalName=zt* - - [Link] - AutoNegotiation=false - MACAddressPolicy=none - ''; + systemd.network.links."50-zerotier" = { + matchConfig = { + OriginalName = "zt*"; + }; + linkConfig = { + AutoNegotiation = false; + MACAddressPolicy = "none"; + }; + }; }; } |