summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--system/modules/darwin/kresd.nix45
-rw-r--r--system/modules/darwin/stubby.nix218
-rw-r--r--system/modules/nextdns.nix92
-rw-r--r--system/settings/base.nix12
-rw-r--r--system/settings/hardware/network-manager.nix8
-rw-r--r--system/settings/services/kresd.nix22
-rw-r--r--system/trillian.nix13
7 files changed, 375 insertions, 35 deletions
diff --git a/system/modules/darwin/kresd.nix b/system/modules/darwin/kresd.nix
new file mode 100644
index 00000000..6bce8af1
--- /dev/null
+++ b/system/modules/darwin/kresd.nix
@@ -0,0 +1,45 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.kresd;
+  package = pkgs.knot-resolver;
+
+  configFile = pkgs.writeText "kresd.conf" cfg.extraConfig;
+in
+{
+  options = {
+    services.kresd.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Whether to enable knot-resolver daemon.";
+    };
+
+    services.kresd.extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Extra configuration to be added to the generated configuration file.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    launchd.daemons.kresd = {
+      command = "${package}/bin/kresd -c ${configFile}";
+
+      serviceConfig = {
+        ProcessType = "Interactive";
+        # Sockets = {
+        #   Listeners = {
+        #     SockServiceName = "dns";
+        #     SockFamily = "IPv4";
+        #   };
+        # };
+      };
+    };
+
+    environment.systemPackages = [ package ];
+  };
+}
diff --git a/system/modules/darwin/stubby.nix b/system/modules/darwin/stubby.nix
new file mode 100644
index 00000000..b3b67755
--- /dev/null
+++ b/system/modules/darwin/stubby.nix
@@ -0,0 +1,218 @@
+{ config, lib, pkgs, ...}:
+
+with lib;
+
+let
+  cfg = config.services.stubby;
+  package = pkgs.stubby;
+
+  fallbacks = concatMapStringsSep "\n  " (x: "- ${x}") cfg.fallbackProtocols;
+  listeners = concatMapStringsSep "\n  " (x: "- ${x}") cfg.listenAddresses;
+
+  # By default, the recursive resolvers maintained by the getdns
+  # project itself are enabled. More information about both getdns's servers,
+  # as well as third party options for upstream resolvers, can be found here:
+  # https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Test+Servers
+  #
+  # You can override these values by supplying a yaml-formatted array of your
+  # preferred upstream resolvers in the following format:
+  #
+  # 106 # - address_data: IPv4 or IPv6 address of the upstream
+  #   port: Port for UDP/TCP (default is 53)
+  #   tls_auth_name: Authentication domain name checked against the server
+  #                  certificate
+  #   tls_pubkey_pinset: An SPKI pinset verified against the keys in the server
+  #                      certificate
+  #     - digest: Only "sha256" is currently supported
+  #       value: Base64 encoded value of the sha256 fingerprint of the public
+  #              key
+  #   tls_port: Port for TLS (default is 853)
+
+  defaultUpstream = ''
+    - address_data: 145.100.185.15
+      tls_auth_name: "dnsovertls.sinodun.com"
+      tls_pubkey_pinset:
+        - digest: "sha256"
+          value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4=
+    - address_data: 145.100.185.16
+      tls_auth_name: "dnsovertls1.sinodun.com"
+      tls_pubkey_pinset:
+        - digest: "sha256"
+          value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA=
+    - address_data: 185.49.141.37
+      tls_auth_name: "getdnsapi.net"
+      tls_pubkey_pinset:
+        - digest: "sha256"
+          value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q=
+    - address_data: 2001:610:1:40ba:145:100:185:15
+      tls_auth_name: "dnsovertls.sinodun.com"
+      tls_pubkey_pinset:
+        - digest: "sha256"
+          value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4=
+    - address_data: 2001:610:1:40ba:145:100:185:16
+      tls_auth_name: "dnsovertls1.sinodun.com"
+      tls_pubkey_pinset:
+        - digest: "sha256"
+          value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA=
+    - address_data: 2a04:b900:0:100::38
+      tls_auth_name: "getdnsapi.net"
+      tls_pubkey_pinset:
+        - digest: "sha256"
+          value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q=
+  '';
+
+  # Resolution type is not changeable here because it is required per the
+  # stubby documentation:
+  #
+  # "resolution_type: Work in stub mode only (not recursive mode) - required for Stubby
+  # operation."
+  #
+  # https://dnsprivacy.org/wiki/display/DP/Configuring+Stubby
+
+  confFile = pkgs.writeText "stubby.yml" ''
+    resolution_type: GETDNS_RESOLUTION_STUB
+    dns_transport_list:
+      ${fallbacks}
+    tls_authentication: ${cfg.authenticationMode}
+    tls_query_padding_blocksize: ${toString cfg.queryPaddingBlocksize}
+    edns_client_subnet_private: ${if cfg.subnetPrivate then "1" else "0"}
+    idle_timeout: ${toString cfg.idleTimeout}
+    listen_addresses:
+      ${listeners}
+    round_robin_upstreams: ${if cfg.roundRobinUpstreams then "1" else "0"}
+    ${cfg.extraConfig}
+    upstream_recursive_servers:
+    ${cfg.upstreamServers}
+  '';
+in
+
+{
+  options = {
+    services.stubby = {
+
+      enable = mkEnableOption "Stubby DNS resolver";
+
+      fallbackProtocols = mkOption {
+        default = [ "GETDNS_TRANSPORT_TLS" ];
+        type = with types; listOf (enum [
+          "GETDNS_TRANSPORT_TLS"
+          "GETDNS_TRANSPORT_TCP"
+          "GETDNS_TRANSPORT_UDP"
+        ]);
+        description = ''
+          Ordered list composed of one or more transport protocols.
+          Strict mode should only use <literal>GETDNS_TRANSPORT_TLS</literal>.
+          Other options are <literal>GETDNS_TRANSPORT_UDP</literal> and
+          <literal>GETDNS_TRANSPORT_TCP</literal>.
+        '';
+      };
+
+      authenticationMode = mkOption {
+        default = "GETDNS_AUTHENTICATION_REQUIRED";
+        type = types.enum [
+          "GETDNS_AUTHENTICATION_REQUIRED"
+          "GETDNS_AUTHENTICATION_NONE"
+        ];
+        description = ''
+          Selects the Strict or Opportunistic usage profile.
+          For strict, set to <literal>GETDNS_AUTHENTICATION_REQUIRED</literal>.
+          for opportunistic, use <literal>GETDNS_AUTHENTICATION_NONE</literal>.
+        '';
+      };
+
+      queryPaddingBlocksize = mkOption {
+        default = 128;
+        type = types.int;
+        description = ''
+          EDNS0 option to pad the size of the DNS query to the given blocksize.
+        '';
+      };
+
+      subnetPrivate = mkOption {
+        default = true;
+        type = types.bool;
+        description = ''
+          EDNS0 option for ECS client privacy. Default is
+          <literal>true</literal>. If set, this option prevents the client
+          subnet from being sent to authoritative nameservers.
+        '';
+      };
+
+      idleTimeout = mkOption {
+        default = 10000;
+        type = types.int;
+        description = "EDNS0 option for keepalive idle timeout expressed in
+        milliseconds.";
+      };
+
+      listenAddresses = mkOption {
+        default = [ "127.0.0.1" "0::1" ];
+        type = with types; listOf str;
+        description = ''
+          Sets the listen address for the stubby daemon.
+          Uses port 53 by default.
+          Ise IP@port to specify a different port.
+        '';
+      };
+
+      roundRobinUpstreams = mkOption {
+        default = true;
+        type = types.bool;
+        description = ''
+          Instructs stubby to distribute queries across all available name
+          servers. Default is <literal>true</literal>. Set to
+          <literal>false</literal> in order to use the first available.
+        '';
+      };
+
+      upstreamServers = mkOption {
+        default = defaultUpstream;
+        type = types.lines;
+        description = ''
+          Replace default upstreams. See <citerefentry><refentrytitle>stubby
+          </refentrytitle><manvolnum>1</manvolnum></citerefentry> for an
+          example of the entry formatting. In Strict mode, at least one of the
+          following settings must be supplied for each nameserver:
+          <literal>tls_auth_name</literal> or
+          <literal>tls_pubkey_pinset</literal>.
+        '';
+      };
+
+      debugLogging = mkOption {
+        default = false;
+        type = types.bool;
+        description = "Enable or disable debug level logging.";
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        type = types.lines;
+        description = ''
+          Add additional configuration options. see <citerefentry>
+          <refentrytitle>stubby</refentrytitle><manvolnum>1</manvolnum>
+          </citerefentry>for more options.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    launchd.daemons.stubby = {
+      command = "${package}/bin/stubby -C ${confFile} ${optionalString cfg.debugLogging "-l"}";
+
+      serviceConfig = {
+        ProcessType = "Interactive";
+        RunAtLoad = true;
+        KeepAlive = true;
+        # Sockets = {
+        #   Listeners = {
+        #     SockServiceName = "dns";
+        #     SockFamily = "IPv4";
+        #   };
+        # };
+      };
+    };
+
+    environment.systemPackages = [ package ];
+  };
+}
diff --git a/system/modules/nextdns.nix b/system/modules/nextdns.nix
new file mode 100644
index 00000000..6de4acdb
--- /dev/null
+++ b/system/modules/nextdns.nix
@@ -0,0 +1,92 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  inherit (pkgs) stdenv;
+
+  cfg = config.networking.nextdns;
+
+  identifyingPrefix = if cfg.identifyDevice then "${config.networking.hostName}-" else "";
+in
+{
+  options = {
+    networking.nextdns.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Whether to enable DNS resolution via NextDNS";
+    };
+
+    networking.nextdns.configID = mkOption {
+      type = types.str;
+      default = "";
+      example = literalExample "abcdef";
+      description = "NextDNS configuration ID";
+    };
+
+    networking.nextdns.identifyDevice = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Whether to send hostname for identifying in your logs";
+    };
+
+    networking.nextdns.resolver = mkOption {
+      type = types.enum [ "kresd" "stubby" ];
+      default = if stdenv.isDarwin then "stubby" else "kresd";
+      description = "Resolver to use";
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      {
+        assertion = !(stdenv.isDarwin && cfg.resolver == "kresd");
+        message = "kresd is not supported on Darwin";
+      }
+    ];
+    networking = if stdenv.isDarwin then
+    {
+      dns = [
+        "::1"
+        "127.0.0.1"
+        "2a07:a8c0::ab:d6e5"
+        "2a07:a8c1::ab:d6e5"
+        "45.90.28.25"
+        "45.90.30.25"
+      ];
+    } else {
+      networking.networkmanager.dns = "none";
+      resolvconf.useLocalResolver = true;
+    };
+    services = if cfg.resolver == "kresd" then {
+      kresd = {
+        enable = true;
+        extraConfig = ''
+          policy.add(policy.all(policy.TLS_FORWARD({
+            {'45.90.28.0', hostname='${identifyingPrefix}${cfg.configID}.dns1.nextdns.io'},
+            {'2a07:a8c0::', hostname='${identifyingPrefix}${cfg.configID}.dns1.nextdns.io'},
+            {'45.90.30.0', hostname='${identifyingPrefix}${cfg.configID}.dns2.nextdns.io'},
+            {'2a07:a8c1::', hostname='${identifyingPrefix}${cfg.configID}.dns2.nextdns.io'}
+          })))
+        '';
+      };
+    } else if cfg.resolver == "stubby" then {
+      stubby = {
+        enable = cfg.resolver == "stubby";
+        fallbackProtocols = lib.mkDefault [ "GETDNS_TRANSPORT_TLS" ];
+        roundRobinUpstreams = lib.mkDefault false;
+        upstreamServers = ''
+          - address_data: 45.90.28.0
+            tls_auth_name: "${identifyingPrefix}${cfg.configID}.dns1.nextdns.io"
+          - address_data: 2a07:a8c0::0
+            tls_auth_name: "${identifyingPrefix}${cfg.configID}.dns1.nextdns.io"
+          - address_data: 45.90.30.0
+            tls_auth_name: "${identifyingPrefix}${cfg.configID}.dns2.nextdns.io"
+          - address_data: 2a07:a8c1::0
+            tls_auth_name: "${identifyingPrefix}${cfg.configID}.dns2.nextdns.io"
+        '';
+      };
+    } else abort "Cannot configure resolver ${cfg.resolver}";
+  };
+}
diff --git a/system/settings/base.nix b/system/settings/base.nix
index 9e94a5a2..8f0c32b2 100644
--- a/system/settings/base.nix
+++ b/system/settings/base.nix
@@ -1,11 +1,21 @@
 { config, pkgs, ... }:
 
-{ boot.loader.timeout = 1;
+{
+  imports = [
+    ../modules/nextdns.nix
+  ];
+  boot.loader.timeout = 1;
 
   environment.systemPackages = with pkgs; [
     nix-index
   ];
 
+  networking.nextdns = {
+    enable = true;
+    configID = "abd6e5";
+    identifyDevice = true;
+  };
+
   networking.extraHosts = ''
     127.0.0.1 ${config.networking.hostName}
     ::1 ${config.networking.hostName}
diff --git a/system/settings/hardware/network-manager.nix b/system/settings/hardware/network-manager.nix
index 4daea7be..6ab0c818 100644
--- a/system/settings/hardware/network-manager.nix
+++ b/system/settings/hardware/network-manager.nix
@@ -1,17 +1,9 @@
 { config, lib, pkgs, ... }:
 
 {
-  imports = [
-    ../services/kresd.nix
-  ];
-
   networking = {
     networkmanager = {
       enable = true;
-      dns = lib.mkForce "none";
-    };
-    resolvconf = {
-      useLocalResolver = true;
     };
   };
 
diff --git a/system/settings/services/kresd.nix b/system/settings/services/kresd.nix
deleted file mode 100644
index 335d96cc..00000000
--- a/system/settings/services/kresd.nix
+++ /dev/null
@@ -1,22 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-let
-  nextdnsConfig = "abd6e5";
-  hostname = config.networking.hostName;
-in
-{
-  services.kresd = {
-    enable = true;
-    extraConfig = ''
-      cache.size = 100*MB
-      cache.min_ttl(3 * 3600)
-
-      policy.add(policy.all(policy.TLS_FORWARD({
-        {'45.90.28.0', hostname='${hostname}-${nextdnsConfig}.dns1.nextdns.io'},
-        {'2a07:a8c0::', hostname='${hostname}-${nextdnsConfig}.dns1.nextdns.io'},
-        {'45.90.30.0', hostname='${hostname}-${nextdnsConfig}.dns2.nextdns.io'},
-        {'2a07:a8c1::', hostname='${hostname}-${nextdnsConfig}.dns2.nextdns.io'}
-      })))
-    '';
-  };
-}
diff --git a/system/trillian.nix b/system/trillian.nix
index 21245fe8..276a2559 100644
--- a/system/trillian.nix
+++ b/system/trillian.nix
@@ -4,6 +4,9 @@
   imports = [
     ./private/default.nix
 
+    ./modules/darwin/stubby.nix
+    ./modules/nextdns.nix
+
     ./settings/programs/accounting.nix
     ./settings/programs/shell.nix
   ];
@@ -15,10 +18,12 @@
   networking = {
     hostName = "trillian";
     knownNetworkServices = [ "Wi-Fi" "USB 10/100/1000 LAN" ];
-    dns = [
-      "::1"
-      "127.0.0.1"
-    ];
+    nextdns = {
+      enable = true;
+      resolver = "stubby";
+      configID = "abd6e5";
+      identifyDevice = true;
+    };
   };
 
   # Use a custom configuration.nix location.