summary refs log tree commit diff
path: root/nixos/modules/services/networking
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/networking')
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.nix321
-rw-r--r--nixos/modules/services/networking/dnscrypt-proxy.xml69
-rw-r--r--nixos/modules/services/networking/dnscrypt-wrapper.nix10
-rw-r--r--nixos/modules/services/networking/kresd.nix21
-rw-r--r--nixos/modules/services/networking/mosquitto.nix14
-rw-r--r--nixos/modules/services/networking/openvpn.nix28
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix12
7 files changed, 458 insertions, 17 deletions
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix
new file mode 100644
index 000000000000..857657eea4db
--- /dev/null
+++ b/nixos/modules/services/networking/dnscrypt-proxy.nix
@@ -0,0 +1,321 @@
+{ config, lib, pkgs, ... }:
+with lib;
+
+let
+  cfg = config.services.dnscrypt-proxy;
+
+  stateDirectory = "/var/lib/dnscrypt-proxy";
+
+  # The minisign public key used to sign the upstream resolver list.
+  # This is somewhat more flexible than preloading the key as an
+  # embedded string.
+  upstreamResolverListPubKey = pkgs.fetchurl {
+    url = https://raw.githubusercontent.com/dyne/dnscrypt-proxy/master/minisign.pub;
+    sha256 = "18lnp8qr6ghfc2sd46nn1rhcpr324fqlvgsp4zaigw396cd7vnnh";
+  };
+
+  # Internal flag indicating whether the upstream resolver list is used.
+  useUpstreamResolverList = cfg.customResolver == null;
+
+  # The final local address.
+  localAddress = "${cfg.localAddress}:${toString cfg.localPort}";
+
+  # The final resolvers list path.
+  resolverList = "${stateDirectory}/dnscrypt-resolvers.csv";
+
+  # Build daemon command line
+
+  resolverArgs =
+    if (cfg.customResolver == null)
+      then
+        [ "-L ${resolverList}"
+          "-R ${cfg.resolverName}"
+        ]
+      else with cfg.customResolver;
+        [ "-N ${name}"
+          "-k ${key}"
+          "-r ${address}:${toString port}"
+        ];
+
+  daemonArgs =
+       [ "-a ${localAddress}" ]
+    ++ resolverArgs
+    ++ cfg.extraArgs;
+in
+
+{
+  meta = {
+    maintainers = with maintainers; [ joachifm ];
+    doc = ./dnscrypt-proxy.xml;
+  };
+
+  options = {
+    # Before adding another option, consider whether it could
+    # equally well be passed via extraArgs.
+
+    services.dnscrypt-proxy = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Whether to enable the DNSCrypt client proxy";
+      };
+
+      localAddress = mkOption {
+        default = "127.0.0.1";
+        type = types.str;
+        description = ''
+          Listen for DNS queries to relay on this address. The only reason to
+          change this from its default value is to proxy queries on behalf
+          of other machines (typically on the local network).
+        '';
+      };
+
+      localPort = mkOption {
+        default = 53;
+        type = types.int;
+        description = ''
+          Listen for DNS queries to relay on this port. The default value
+          assumes that the DNSCrypt proxy should relay DNS queries directly.
+          When running as a forwarder for another DNS client, set this option
+          to a different value; otherwise leave the default.
+        '';
+      };
+
+      resolverName = mkOption {
+        default = "random";
+        example = "dnscrypt.eu-nl";
+        type = types.nullOr types.str;
+        description = ''
+          The name of the DNSCrypt resolver to use, taken from
+          <filename>${resolverList}</filename>.  The default is to
+          pick a random non-logging resolver that supports DNSSEC.
+        '';
+      };
+
+      customResolver = mkOption {
+        default = null;
+        description = ''
+          Use an unlisted resolver (e.g., a private DNSCrypt provider). For
+          advanced users only. If specified, this option takes precedence.
+        '';
+        type = types.nullOr (types.submodule ({ ... }: { options = {
+          address = mkOption {
+            type = types.str;
+            description = "IP address";
+            example = "208.67.220.220";
+          };
+
+          port = mkOption {
+            type = types.int;
+            description = "Port";
+            default = 443;
+          };
+
+          name = mkOption {
+            type = types.str;
+            description = "Fully qualified domain name";
+            example = "2.dnscrypt-cert.example.com";
+          };
+
+          key = mkOption {
+            type = types.str;
+            description = "Public key";
+            example = "B735:1140:206F:225D:3E2B:D822:D7FD:691E:A1C3:3CC8:D666:8D0C:BE04:BFAB:CA43:FB79";
+          };
+        }; }));
+      };
+
+      extraArgs = mkOption {
+        default = [];
+        type = types.listOf types.str;
+        description = ''
+          Additional command-line arguments passed verbatim to the daemon.
+          See <citerefentry><refentrytitle>dnscrypt-proxy</refentrytitle>
+          <manvolnum>8</manvolnum></citerefentry> for details.
+        '';
+        example = [ "-X libdcplugin_example_cache.so,--min-ttl=60" ];
+      };
+    };
+  };
+
+  config = mkIf cfg.enable (mkMerge [{
+    assertions = [
+      { assertion = (cfg.customResolver != null) || (cfg.resolverName != null);
+        message   = "please configure upstream DNSCrypt resolver";
+      }
+    ];
+
+    users.users.dnscrypt-proxy = {
+      description = "dnscrypt-proxy daemon user";
+      isSystemUser = true;
+      group = "dnscrypt-proxy";
+    };
+    users.groups.dnscrypt-proxy = {};
+
+    systemd.sockets.dnscrypt-proxy = {
+      description = "dnscrypt-proxy listening socket";
+      documentation = [ "man:dnscrypt-proxy(8)" ];
+
+      wantedBy = [ "sockets.target" ];
+
+      socketConfig = {
+        ListenStream = localAddress;
+        ListenDatagram = localAddress;
+      };
+    };
+
+    systemd.services.dnscrypt-proxy = {
+      description = "dnscrypt-proxy daemon";
+      documentation = [ "man:dnscrypt-proxy(8)" ];
+
+      before = [ "nss-lookup.target" ];
+      after = [ "network.target" ];
+      requires = [ "dnscrypt-proxy.socket "];
+
+      serviceConfig = {
+        NonBlocking = "true";
+        ExecStart = "${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy ${toString daemonArgs}";
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+
+        User = "dnscrypt-proxy";
+
+        PrivateTmp = true;
+        PrivateDevices = true;
+        ProtectHome = true;
+      };
+    };
+    }
+
+    (mkIf config.security.apparmor.enable {
+    systemd.services.dnscrypt-proxy.after = [ "apparmor.service" ];
+
+    security.apparmor.profiles = singleton (pkgs.writeText "apparmor-dnscrypt-proxy" ''
+      ${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy {
+        /dev/null rw,
+        /dev/urandom r,
+
+        /etc/passwd r,
+        /etc/group r,
+        ${config.environment.etc."nsswitch.conf".source} r,
+
+        ${getLib pkgs.glibc}/lib/*.so mr,
+        ${pkgs.tzdata}/share/zoneinfo/** r,
+
+        network inet stream,
+        network inet6 stream,
+        network inet dgram,
+        network inet6 dgram,
+
+        ${getLib pkgs.dnscrypt-proxy}/lib/dnscrypt-proxy/libdcplugin*.so mr,
+
+        ${getLib pkgs.gcc.cc}/lib/libssp.so.* mr,
+        ${getLib pkgs.libsodium}/lib/libsodium.so.* mr,
+        ${getLib pkgs.systemd}/lib/libsystemd.so.* mr,
+        ${getLib pkgs.xz}/lib/liblzma.so.* mr,
+        ${getLib pkgs.libgcrypt}/lib/libgcrypt.so.* mr,
+        ${getLib pkgs.libgpgerror}/lib/libgpg-error.so.* mr,
+        ${getLib pkgs.libcap}/lib/libcap.so.* mr,
+        ${getLib pkgs.lz4}/lib/liblz4.so.* mr,
+        ${getLib pkgs.attr}/lib/libattr.so.* mr, # */
+
+        ${resolverList} r,
+
+        /run/systemd/notify rw,
+      }
+    '');
+    })
+
+    (mkIf useUpstreamResolverList {
+    systemd.services.init-dnscrypt-proxy-statedir = {
+      description = "Initialize dnscrypt-proxy state directory";
+
+      wantedBy = [ "dnscrypt-proxy.service" ];
+      before = [ "dnscrypt-proxy.service" ];
+
+      script = ''
+        mkdir -pv ${stateDirectory}
+        chown -c dnscrypt-proxy:dnscrypt-proxy ${stateDirectory}
+        cp -uv \
+          ${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv \
+          ${stateDirectory}
+      '';
+
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+      };
+    };
+
+    systemd.services.update-dnscrypt-resolvers = {
+      description = "Update list of DNSCrypt resolvers";
+
+      requires = [ "init-dnscrypt-proxy-statedir.service" ];
+      after = [ "init-dnscrypt-proxy-statedir.service" ];
+
+      path = with pkgs; [ curl diffutils dnscrypt-proxy minisign ];
+      script = ''
+        cd ${stateDirectory}
+        domain=raw.githubusercontent.com
+        get="curl -fSs --resolve $domain:443:$(hostip -r 8.8.8.8 $domain | head -1)"
+        $get -o dnscrypt-resolvers.csv.tmp \
+          https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv
+        $get -o dnscrypt-resolvers.csv.minisig.tmp \
+          https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv.minisig
+        mv dnscrypt-resolvers.csv.minisig{.tmp,}
+        if ! minisign -q -V -p ${upstreamResolverListPubKey} \
+          -m dnscrypt-resolvers.csv.tmp -x dnscrypt-resolvers.csv.minisig ; then
+          echo "failed to verify resolver list!" >&2
+          exit 1
+        fi
+        [[ -f dnscrypt-resolvers.csv ]] && mv dnscrypt-resolvers.csv{,.old}
+        mv dnscrypt-resolvers.csv{.tmp,}
+        if cmp dnscrypt-resolvers.csv{,.old} ; then
+          echo "no change"
+        else
+          echo "resolver list updated"
+        fi
+      '';
+
+      serviceConfig = {
+        PrivateTmp = true;
+        PrivateDevices = true;
+        ProtectHome = true;
+        ProtectSystem = "strict";
+        ReadWritePaths = "${dirOf stateDirectory} ${stateDirectory}";
+        SystemCallFilter = "~@mount";
+      };
+    };
+
+    systemd.timers.update-dnscrypt-resolvers = {
+      wantedBy = [ "timers.target" ];
+      timerConfig = {
+        OnBootSec = "5min";
+        OnUnitActiveSec = "6h";
+      };
+    };
+    })
+    ]);
+
+  imports = [
+    (mkRenamedOptionModule [ "services" "dnscrypt-proxy" "port" ] [ "services" "dnscrypt-proxy" "localPort" ])
+
+    (mkChangedOptionModule
+      [ "services" "dnscrypt-proxy" "tcpOnly" ]
+      [ "services" "dnscrypt-proxy" "extraArgs" ]
+      (config:
+        let val = getAttrFromPath [ "services" "dnscrypt-proxy" "tcpOnly" ] config; in
+        optional val "-T"))
+
+    (mkChangedOptionModule
+      [ "services" "dnscrypt-proxy" "ephemeralKeys" ]
+      [ "services" "dnscrypt-proxy" "extraArgs" ]
+      (config:
+        let val = getAttrFromPath [ "services" "dnscrypt-proxy" "ephemeralKeys" ] config; in
+        optional val "-E"))
+
+    (mkRemovedOptionModule [ "services" "dnscrypt-proxy" "resolverList" ] ''
+      The current resolver listing from upstream is always used
+      unless a custom resolver is specified.
+    '')
+  ];
+}
diff --git a/nixos/modules/services/networking/dnscrypt-proxy.xml b/nixos/modules/services/networking/dnscrypt-proxy.xml
new file mode 100644
index 000000000000..555c6df4d551
--- /dev/null
+++ b/nixos/modules/services/networking/dnscrypt-proxy.xml
@@ -0,0 +1,69 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="sec-dnscrypt-proxy">
+
+  <title>DNSCrypt client proxy</title>
+
+  <para>
+    The DNSCrypt client proxy relays DNS queries to a DNSCrypt enabled
+    upstream resolver. The traffic between the client and the upstream
+    resolver is encrypted and authenticated, mitigating the risk of MITM
+    attacks, DNS poisoning attacks, and third-party snooping (assuming the
+    upstream is trustworthy).
+  </para>
+
+  <sect1><title>Basic configuration</title>
+
+  <para>
+    To enable the client proxy, set
+    <programlisting>
+      services.dnscrypt-proxy.enable = true;
+    </programlisting>
+  </para>
+
+  <para>
+    Enabling the client proxy does not alter the system nameserver; to
+    relay local queries, prepend <literal>127.0.0.1</literal> to
+    <option>networking.nameservers</option>.
+  </para>
+
+  </sect1>
+
+  <sect1><title>As a forwarder for another DNS client</title>
+
+  <para>
+    To run the DNSCrypt proxy client as a forwarder for another
+    DNS client, change the default proxy listening port to a
+    non-standard value and point the other client to it:
+    <programlisting>
+      services.dnscrypt-proxy.localPort = 43;
+    </programlisting>
+  </para>
+
+  <sect2><title>dnsmasq</title>
+  <para>
+    <programlisting>
+      {
+        services.dnsmasq.enable = true;
+        services.dnsmasq.servers = [ "127.0.0.1#43" ];
+      }
+    </programlisting>
+  </para>
+  </sect2>
+
+  <sect2><title>unbound</title>
+  <para>
+    <programlisting>
+      {
+        services.unbound.enable = true;
+        services.unbound.forwardAddresses = [ "127.0.0.1@43" ];
+      }
+    </programlisting>
+  </para>
+  </sect2>
+
+  </sect1>
+
+</chapter>
diff --git a/nixos/modules/services/networking/dnscrypt-wrapper.nix b/nixos/modules/services/networking/dnscrypt-wrapper.nix
index 23cc92946e41..bf13d5c6f5fe 100644
--- a/nixos/modules/services/networking/dnscrypt-wrapper.nix
+++ b/nixos/modules/services/networking/dnscrypt-wrapper.nix
@@ -145,6 +145,16 @@ in {
     };
     users.groups.dnscrypt-wrapper = { };
 
+    security.polkit.extraConfig = ''
+      // Allow dnscrypt-wrapper user to restart dnscrypt-wrapper.service
+      polkit.addRule(function(action, subject) {
+          if (action.id == "org.freedesktop.systemd1.manage-units" &&
+              action.lookup("unit") == "dnscrypt-wrapper.service" &&
+              subject.user == "dnscrypt-wrapper") {
+              return polkit.Result.YES;
+          }
+        });
+    '';
 
     systemd.services.dnscrypt-wrapper = {
       description = "dnscrypt-wrapper daemon";
diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix
index 18e2ab9aebf1..011a9b2f58ea 100644
--- a/nixos/modules/services/networking/kresd.nix
+++ b/nixos/modules/services/networking/kresd.nix
@@ -72,6 +72,7 @@ in
         (iface: if elem ":" (stringToCharacters iface) then "[${iface}]:53" else "${iface}:53")
         cfg.interfaces;
       socketConfig.ListenDatagram = listenStreams;
+      socketConfig.FreeBind = true;
     };
 
     systemd.sockets.kresd-control = rec {
@@ -82,20 +83,11 @@ in
       socketConfig = {
         FileDescriptorName = "control";
         Service = "kresd.service";
-        SocketMode = "0660"; # only root user/group may connect
+        SocketMode = "0660"; # only root user/group may connect and control kresd
       };
     };
 
-    # Create the cacheDir; tmpfiles don't work on nixos-rebuild switch.
-    systemd.services.kresd-cachedir = {
-      serviceConfig.Type = "oneshot";
-      script = ''
-        if [ ! -d '${cfg.cacheDir}' ]; then
-          mkdir -p '${cfg.cacheDir}'
-          chown kresd:kresd '${cfg.cacheDir}'
-        fi
-      '';
-    };
+    systemd.tmpfiles.rules = [ "d '${cfg.cacheDir}' 0770 kresd kresd - -" ];
 
     systemd.services.kresd = {
       description = "Knot-resolver daemon";
@@ -104,16 +96,15 @@ in
         User = "kresd";
         Type = "notify";
         WorkingDirectory = cfg.cacheDir;
+        Restart = "on-failure";
       };
 
       script = ''
         exec '${package}/bin/kresd' --config '${configFile}' \
-          -k '${cfg.cacheDir}/root.key'
+          -k '${pkgs.dns-root-data}/root.key'
       '';
 
-      after = [ "kresd-cachedir.service" ];
-      requires = [ "kresd.socket" "kresd-cachedir.service" ];
-      wantedBy = [ "sockets.target" ];
+      requires = [ "kresd.socket" ];
     };
   };
 }
diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix
index 81915b5a2ef8..273ca797b98d 100644
--- a/nixos/modules/services/networking/mosquitto.nix
+++ b/nixos/modules/services/networking/mosquitto.nix
@@ -12,6 +12,10 @@ let
     keyfile ${cfg.ssl.keyfile}
   '';
 
+  passwordConf = optionalString cfg.checkPasswords ''
+    password_file ${cfg.dataDir}/passwd
+  '';
+
   mosquittoConf = pkgs.writeText "mosquitto.conf" ''
     pid_file /run/mosquitto/pid
     acl_file ${aclFile}
@@ -19,6 +23,7 @@ let
     allow_anonymous ${boolToString cfg.allowAnonymous}
     bind_address ${cfg.host}
     port ${toString cfg.port}
+    ${passwordConf}
     ${listenerConf}
     ${cfg.extraConf}
   '';
@@ -153,6 +158,15 @@ in
         '';
       };
 
+      checkPasswords = mkOption {
+        default = false;
+        example = true;
+        type = types.bool;
+        description = ''
+          Refuse connection when clients provide incorrect passwords.
+        '';
+      };
+
       extraConf = mkOption {
         default = "";
         type = types.lines;
diff --git a/nixos/modules/services/networking/openvpn.nix b/nixos/modules/services/networking/openvpn.nix
index 3fbf5a9f0227..7a96b673c51e 100644
--- a/nixos/modules/services/networking/openvpn.nix
+++ b/nixos/modules/services/networking/openvpn.nix
@@ -50,6 +50,11 @@ let
               "up ${pkgs.writeScript "openvpn-${name}-up" upScript}"}
           ${optionalString (cfg.down != "" || cfg.updateResolvConf)
               "down ${pkgs.writeScript "openvpn-${name}-down" downScript}"}
+          ${optionalString (cfg.authUserPass != null)
+              "auth-user-pass ${pkgs.writeText "openvpn-credentials-${name}" ''
+                ${cfg.authUserPass.username}
+                ${cfg.authUserPass.password}
+              ''}"}
         '';
 
     in {
@@ -161,6 +166,29 @@ in
             '';
           };
 
+          authUserPass = mkOption {
+            default = null;
+            description = ''
+              This option can be used to store the username / password credentials
+              with the "auth-user-pass" authentication method.
+
+              WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store!
+            '';
+            type = types.nullOr (types.submodule {
+
+              options = {
+                username = mkOption {
+                  description = "The username to store inside the credentials file.";
+                  type = types.string;
+                };
+
+                password = mkOption {
+                  description = "The password to store inside the credentials file.";
+                  type = types.string;
+                };
+              };
+            });
+          };
         };
 
       });
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index aa9c0fa1c09f..d9b12d278160 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -21,7 +21,7 @@ let
           daemon reads in addition to the the user's authorized_keys file.
           You can combine the <literal>keys</literal> and
           <literal>keyFiles</literal> options.
-          Warning: If you are using <literal>NixOps</literal> then don't use this 
+          Warning: If you are using <literal>NixOps</literal> then don't use this
           option since it will replace the key required for deployment via ssh.
         '';
       };
@@ -137,6 +137,14 @@ in
         '';
       };
 
+      openFirewall = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to automatically open the specified ports in the firewall.
+        '';
+      };
+
       listenAddresses = mkOption {
         type = with types; listOf (submodule {
           options = {
@@ -302,7 +310,7 @@ in
 
       };
 
-    networking.firewall.allowedTCPPorts = cfg.ports;
+    networking.firewall.allowedTCPPorts = if cfg.openFirewall then cfg.ports else [];
 
     security.pam.services.sshd =
       { startSession = true;