diff --git a/devShells.nix b/devShells.nix index 72b2695..f7e9627 100644 --- a/devShells.nix +++ b/devShells.nix @@ -1,12 +1,12 @@ -{ ... }: +{ inputs, ... }: { perSystem = - { pkgs, ... }: + { pkgs, system, ... }: { devShells.default = pkgs.mkShell { packages = with pkgs; [ - agenix-cli + inputs.agenix.packages.${system}.default deploy-rs nil nixfmt-rfc-style diff --git a/hosts/alexandria/forgejo.nix b/hosts/alexandria/forgejo.nix index 909d1d1..d9860f6 100644 --- a/hosts/alexandria/forgejo.nix +++ b/hosts/alexandria/forgejo.nix @@ -32,4 +32,13 @@ in domains."git.baduhai.dev".locations."/".proxyPass = "http://unix:${config.services.forgejo.settings.server.HTTP_ADDR}:/"; }; + + # Register this domain for split DNS + services.splitDNS.entries = [ + { + domain = "git.baduhai.dev"; + lanIP = "192.168.15.142"; + tailscaleIP = "100.76.19.50"; + } + ]; } diff --git a/hosts/alexandria/jellyfin.nix b/hosts/alexandria/jellyfin.nix index 9555c89..994f972 100644 --- a/hosts/alexandria/jellyfin.nix +++ b/hosts/alexandria/jellyfin.nix @@ -13,4 +13,13 @@ in acmeHost = "baduhai.dev"; domains."jellyfin.baduhai.dev".locations."/".proxyPass = "http://127.0.0.1:8096/"; }; + + # Register this domain for split DNS + services.splitDNS.entries = [ + { + domain = "jellyfin.baduhai.dev"; + lanIP = "192.168.15.142"; + tailscaleIP = "100.76.19.50"; + } + ]; } diff --git a/hosts/alexandria/librespeed.nix b/hosts/alexandria/librespeed.nix index e36a81d..9b59685 100644 --- a/hosts/alexandria/librespeed.nix +++ b/hosts/alexandria/librespeed.nix @@ -27,4 +27,13 @@ in acmeHost = "baduhai.dev"; domains."speedtest.baduhai.dev".locations."/".proxyPass = "http://127.0.0.1:58080/"; }; + + # Register this domain for split DNS + services.splitDNS.entries = [ + { + domain = "speedtest.baduhai.dev"; + lanIP = "192.168.15.142"; + tailscaleIP = "100.76.19.50"; + } + ]; } diff --git a/hosts/alexandria/nextcloud.nix b/hosts/alexandria/nextcloud.nix index 9e606d9..e12d12b 100644 --- a/hosts/alexandria/nextcloud.nix +++ b/hosts/alexandria/nextcloud.nix @@ -79,6 +79,15 @@ in acmeHost = "baduhai.dev"; domains."cloud.baduhai.dev" = { }; }; + + # Register this domain for split DNS + splitDNS.entries = [ + { + domain = "cloud.baduhai.dev"; + lanIP = "192.168.15.142"; + tailscaleIP = "100.76.19.50"; + } + ]; }; age.secrets = { diff --git a/hosts/alexandria/nginx.nix b/hosts/alexandria/nginx.nix index 0a0a261..54640c1 100644 --- a/hosts/alexandria/nginx.nix +++ b/hosts/alexandria/nginx.nix @@ -38,6 +38,11 @@ in users.users.nginx.extraGroups = [ "acme" ]; + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + age.secrets.cloudflare = { file = ../../secrets/cloudflare.age; owner = "nginx"; diff --git a/hosts/alexandria/unbound.nix b/hosts/alexandria/unbound.nix new file mode 100644 index 0000000..e923318 --- /dev/null +++ b/hosts/alexandria/unbound.nix @@ -0,0 +1,67 @@ +{ config, inputs, lib, ... }: + +let + utils = import ../../utils.nix { inherit inputs lib; }; + inherit (utils) mkSplitDNS; +in + +{ + imports = [ ../modules/split-dns.nix ]; + + services.unbound = { + enable = true; + enableRootTrustAnchor = true; + settings = { + server = { + interface = [ + "0.0.0.0" + "::" + ]; + access-control = [ + "127.0.0.0/8 allow" + "192.168.0.0/16 allow" + "100.64.0.0/10 allow" # Tailscale CGNAT range + "::1/128 allow" + "fd7a:115c:a1e0::/48 allow" # Tailscale IPv6 + ]; + + # Enable views for split DNS + access-control-view = [ + "100.64.0.0/10 tailscale" + "fd7a:115c:a1e0::/48 tailscale" + "192.168.0.0/16 lan" + ]; + + num-threads = 2; + msg-cache-size = "50m"; + rrset-cache-size = "100m"; + cache-min-ttl = 300; + cache-max-ttl = 86400; + prefetch = true; + prefetch-key = true; + hide-identity = true; + hide-version = true; + so-rcvbuf = "1m"; + so-sndbuf = "1m"; + }; + # Split DNS views - automatically collected from all service files + view = mkSplitDNS config.services.splitDNS.entries; + + forward-zone = [ + { + name = "."; + forward-addr = [ + "1.1.1.1@853#cloudflare-dns.com" + "1.0.0.1@853#cloudflare-dns.com" + ]; + forward-tls-upstream = true; + } + ]; + }; + }; + + networking.firewall = { + allowedTCPPorts = [ 53 ]; + allowedUDPPorts = [ 53 ]; + }; +} diff --git a/hosts/alexandria/vaultwarden.nix b/hosts/alexandria/vaultwarden.nix index fd10d6b..68387ee 100644 --- a/hosts/alexandria/vaultwarden.nix +++ b/hosts/alexandria/vaultwarden.nix @@ -24,4 +24,13 @@ in domains."pass.baduhai.dev".locations."/".proxyPass = "http://${config.services.vaultwarden.config.ROCKET_ADDRESS}:${toString config.services.vaultwarden.config.ROCKET_PORT}/"; }; + + # Register this domain for split DNS + services.splitDNS.entries = [ + { + domain = "pass.baduhai.dev"; + lanIP = "192.168.15.142"; + tailscaleIP = "100.76.19.50"; + } + ]; } diff --git a/hosts/modules/split-dns.nix b/hosts/modules/split-dns.nix new file mode 100644 index 0000000..0998816 --- /dev/null +++ b/hosts/modules/split-dns.nix @@ -0,0 +1,28 @@ +{ config, lib, ... }: + +{ + options.services.splitDNS = { + entries = lib.mkOption { + type = lib.types.listOf ( + lib.types.submodule { + options = { + domain = lib.mkOption { + type = lib.types.str; + description = "The domain name to configure"; + }; + lanIP = lib.mkOption { + type = lib.types.str; + description = "IP address to return for LAN requests"; + }; + tailscaleIP = lib.mkOption { + type = lib.types.str; + description = "IP address to return for Tailscale requests"; + }; + }; + } + ); + default = [ ]; + description = "List of domains to configure for split DNS"; + }; + }; +} diff --git a/utils.nix b/utils.nix index 38cf968..c803fe5 100644 --- a/utils.nix +++ b/utils.nix @@ -190,4 +190,33 @@ in }; in lib.mapAttrs (_: lib.recursiveUpdate commonVHostConfig) domains; + + # Split DNS utilities for unbound + # Generates unbound view config from a list of DNS entries + mkSplitDNS = + entries: + let + # Generate view entries for a single domain + mkEntry = + { + domain, + lanIP, + tailscaleIP, + }: + [ + { + name = "tailscale"; + view-first = true; + local-zone = ''"baduhai.dev." transparent''; + local-data = ''"${domain}. IN A ${tailscaleIP}"''; + } + { + name = "lan"; + view-first = true; + local-zone = ''"baduhai.dev." transparent''; + local-data = ''"${domain}. IN A ${lanIP}"''; + } + ]; + in + builtins.concatMap mkEntry entries; }