Commit 6560b614e3a7

Vincent Demeester <vincent@sbr.pm>
2025-11-23 13:38:42
refactor(dns): Migrate bind zones to dns.nix library
- Enable type-safe DNS zone management with Nix expressions - Replace static zone files with programmatic zone definitions - Support wildcard subdomains and reverse DNS using dns.nix DSL Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 19466e6
systems/common/services/dns/10.100.0.nix
@@ -0,0 +1,32 @@
+{ dns, ... }:
+with dns.lib.combinators;
+{
+  SOA = {
+    nameServer = "ns1.vpn.";
+    adminEmail = "admin.vpn";
+    serial = 3;
+    refresh = 604800;
+    retry = 86400;
+    expire = 2419200;
+    minimum = 604800;
+  };
+
+  NS = [
+    "ns1.vpn."
+    "ns2.vpn."
+  ];
+
+  subdomains = {
+    # Name servers (note: shikoku is both ns1 and a regular host)
+    "2".PTR = [ "shikoku.vpn." ];
+    "14".PTR = [ "okinawa.vpn." ];
+    "8".PTR = [ "wakasu.vpn." ];
+    "17".PTR = [ "aomi.vpn." ];
+    "16".PTR = [ "sakhalin.vpn." ];
+    "50".PTR = [ "rhea.vpn." ];
+    "49".PTR = [ "aion.vpn." ];
+    "83".PTR = [ "athena.vpn." ];
+    "82".PTR = [ "demeter.vpn." ];
+    "81".PTR = [ "hass.vpn." ];
+  };
+}
systems/common/services/dns/192.168.1.nix
@@ -0,0 +1,63 @@
+{ dns, ... }:
+with dns.lib.combinators;
+{
+  SOA = {
+    nameServer = "ns1.home.";
+    adminEmail = "admin.home";
+    serial = 3;
+    refresh = 604800;
+    retry = 86400;
+    expire = 2419200;
+    minimum = 604800;
+  };
+
+  NS = [
+    "ns1.home."
+    "ns2.home."
+  ];
+
+  subdomains = {
+    # Workstations and devices
+    "11".PTR = [ "hokkaido.home." ];
+    "17".PTR = [ "honshu.home." ];
+    "18".PTR = [ "kobe.home." ];
+    "19".PTR = [ "okinawa.home." ];
+    "70".PTR = [ "sakhalin.home." ];
+    "20".PTR = [ "synodine.home." ];
+    "77".PTR = [ "wakasu.home." ];
+    "23".PTR = [ "aomi.home." ];
+    "50".PTR = [ "rhea.home." ];
+    "49".PTR = [ "aion.home." ];
+    "24".PTR = [ "shikoku.home." ];
+    "57".PTR = [ "remarkable.home." ];
+    "15".PTR = [ "honshu.home." ];
+    "181".PTR = [ "hass.home." ];
+
+    # Servers (primary PTR)
+    "182".PTR = [ "demeter.home." ];
+    "183".PTR = [ "athena.home." ];
+
+    # OpenShift VMs - Load Balancer (primary PTR)
+    "120".PTR = [ "vm0.home." ];
+
+    # Masters (primary PTR)
+    "121".PTR = [ "vm1.home." ];
+    "122".PTR = [ "vm2.home." ];
+    "123".PTR = [ "vm3.home." ];
+
+    # Workers (primary PTR)
+    "124".PTR = [ "vm4.home." ];
+    "125".PTR = [ "vm5.home." ];
+    "126".PTR = [ "vm6.home." ];
+    "127".PTR = [ "vm7.home." ];
+    "128".PTR = [ "vm8.home." ];
+
+    # Bootstrap (primary PTR)
+    "129".PTR = [ "vm9.home." ];
+
+    # k8s nodes (primary PTR)
+    "130".PTR = [ "ubnt1.home." ];
+    "131".PTR = [ "ubnt2.home." ];
+    "132".PTR = [ "k8sn3.home." ];
+  };
+}
systems/common/services/dns/home.nix
@@ -0,0 +1,140 @@
+{ dns, ... }:
+with dns.lib.combinators;
+{
+  SOA = {
+    nameServer = "ns1.home.";
+    adminEmail = "admin.home";
+    serial = 3;
+    refresh = 604800;
+    retry = 86400;
+    expire = 2419200;
+    minimum = 604800;
+  };
+
+  NS = [
+    "ns1.home."
+    "ns2.home."
+  ];
+
+  subdomains = {
+    # Name servers
+    ns1.A = [ "192.168.1.182" ];
+    ns2.A = [ "192.168.1.183" ];
+
+    # Cache wildcard
+    cache.subdomains."*".A = [ "192.168.1.70" ];
+
+    # Machines with wildcards
+    okinawa = {
+      A = [ "192.168.1.19" ];
+      subdomains."*".A = [ "192.168.1.19" ];
+    };
+    hokkaido.A = [ "192.168.1.11" ];
+    honshu.A = [ "192.168.1.17" ];
+    kobe.A = [ "192.168.1.18" ];
+    sakhalin = {
+      A = [ "192.168.1.70" ];
+      subdomains."*".A = [ "192.168.1.70" ];
+    };
+    synodine.A = [ "192.168.1.20" ];
+    wakasu = {
+      A = [ "192.168.1.77" ];
+      subdomains."*".A = [ "192.168.1.77" ];
+    };
+    aomi = {
+      A = [ "192.168.1.23" ];
+      subdomains."*".A = [ "192.168.1.23" ];
+    };
+    rhea = {
+      A = [ "192.168.1.50" ];
+      subdomains."*".A = [ "192.168.1.50" ];
+    };
+    aion = {
+      A = [ "192.168.1.49" ];
+      subdomains."*".A = [ "192.168.1.49" ];
+    };
+    shikoku = {
+      A = [ "192.168.1.24" ];
+      subdomains."*".A = [ "192.168.1.24" ];
+    };
+    athena = {
+      A = [ "192.168.1.183" ];
+      subdomains."*".A = [ "192.168.1.183" ];
+    };
+    demeter = {
+      A = [ "192.168.1.182" ];
+      subdomains."*".A = [ "192.168.1.182" ];
+    };
+    nagoya = {
+      A = [ "192.168.1.80" ];
+      subdomains."*".A = [ "192.168.1.80" ];
+    };
+    remakrable.A = [ "192.168.1.57" ];
+    hass.A = [ "192.168.1.181" ];
+
+    # OpenShift infrastructure
+    vm0.A = [ "192.168.1.120" ];
+    vm1.A = [ "192.168.1.121" ];
+    vm2.A = [ "192.168.1.122" ];
+    vm3.A = [ "192.168.1.123" ];
+    vm4.A = [ "192.168.1.124" ];
+    vm5.A = [ "192.168.1.125" ];
+    vm6.A = [ "192.168.1.126" ];
+    vm7.A = [ "192.168.1.127" ];
+    vm8.A = [ "192.168.1.128" ];
+    vm9.A = [ "192.168.1.129" ];
+
+    ocp = {
+      subdomains = {
+        api.A = [ "192.168.1.120" ];
+        api-int.A = [ "192.168.1.120" ];
+        apps.subdomains."*".A = [ "192.168.1.120" ];
+        master0.A = [ "192.168.1.121" ];
+        master1.A = [ "192.168.1.122" ];
+        master3.A = [ "192.168.1.123" ];
+        worker1.A = [ "192.168.1.124" ];
+        worker2.A = [ "192.168.1.125" ];
+        worker3.A = [ "192.168.1.126" ];
+        worker4.A = [ "192.168.1.127" ];
+        worker5.A = [ "192.168.1.128" ];
+        bootstrap.A = [ "192.168.1.129" ];
+        etcd-0.A = [ "192.168.1.121" ];
+        etcd-1.A = [ "192.168.1.122" ];
+        etcd-2.A = [ "192.168.1.123" ];
+      };
+      SRV = [
+        {
+          service = "etcd-server-ssl";
+          proto = "tcp";
+          priority = 0;
+          weight = 10;
+          port = 2380;
+          target = "etcd-0.ocp.home.";
+        }
+        {
+          service = "etcd-server-ssl";
+          proto = "tcp";
+          priority = 0;
+          weight = 10;
+          port = 2380;
+          target = "etcd-1.ocp.home.";
+        }
+        {
+          service = "etcd-server-ssl";
+          proto = "tcp";
+          priority = 0;
+          weight = 10;
+          port = 2380;
+          target = "etcd-2.ocp.home.";
+        }
+      ];
+    };
+
+    # k8s nodes
+    ubnt1.A = [ "192.168.1.130" ];
+    ubnt2.A = [ "192.168.1.131" ];
+    k8sn1.A = [ "192.168.1.130" ];
+    k8sn2.A = [ "192.168.1.131" ];
+    k8sn3.A = [ "192.168.1.132" ];
+  };
+}
systems/common/services/dns/sbr.pm.nix
@@ -0,0 +1,88 @@
+{ dns, ... }:
+with dns.lib.combinators;
+{
+  SOA = {
+    nameServer = "ns1.sbr.pm.";
+    adminEmail = "admin.sbr.pm";
+    serial = 3;
+    refresh = 604800;
+    retry = 86400;
+    expire = 2419200;
+    minimum = 604800;
+  };
+
+  NS = [
+    "ns1.sbr.pm."
+    "ns2.sbr.pm."
+  ];
+
+  subdomains = {
+    # Name servers
+    ns1.A = [ "192.168.1.182" ];
+    ns2.A = [ "192.168.1.183" ];
+
+    # Wildcard for public endpoint
+    "*".A = [
+      {
+        address = "167.99.17.238";
+        ttl = 10800;
+      }
+    ];
+
+    # Machines
+    wakasu = {
+      A = [ "192.168.1.77" ];
+      subdomains."*".A = [ "192.168.1.77" ];
+    };
+    shikoku = {
+      A = [ "192.168.1.24" ];
+      subdomains."*".A = [ "192.168.1.24" ];
+    };
+    sakhalin = {
+      A = [ "192.168.1.70" ];
+      subdomains."*".A = [ "192.168.1.70" ];
+    };
+    aix = {
+      A = [ "10.100.0.89" ];
+      subdomains."*".A = [ "10.100.0.89" ];
+    };
+    rhea = {
+      A = [ "192.168.1.50" ];
+      subdomains."*".A = [ "192.168.1.50" ];
+    };
+    aion = {
+      A = [ "192.168.1.49" ];
+      subdomains."*".A = [ "192.168.1.49" ];
+    };
+    demeter = {
+      A = [ "192.168.1.182" ];
+      subdomains."*".A = [ "192.168.1.182" ];
+    };
+    athena = {
+      A = [ "192.168.1.183" ];
+      subdomains."*".A = [ "192.168.1.183" ];
+    };
+    honshu = {
+      A = [ "192.168.1.15" ];
+      subdomains."*".A = [ "192.168.1.15" ];
+    };
+    nagoya = {
+      A = [ "192.168.1.80" ];
+      subdomains."*".A = [ "192.168.1.80" ];
+    };
+    kerkouane = {
+      A = [ "10.100.0.1" ];
+      subdomains."*".A = [ "10.100.0.1" ];
+    };
+
+    # Rhea media services
+    jellyfin.A = [ "192.168.1.50" ];
+    jellyseerr.A = [ "192.168.1.50" ];
+    sonarr.A = [ "192.168.1.50" ];
+    radarr.A = [ "192.168.1.50" ];
+    lidarr.A = [ "192.168.1.50" ];
+    bazarr.A = [ "192.168.1.50" ];
+    transmission.A = [ "192.168.1.50" ];
+    t.A = [ "192.168.1.50" ];
+  };
+}
systems/common/services/dns/vpn.nix
@@ -0,0 +1,71 @@
+{ dns, ... }:
+with dns.lib.combinators;
+{
+  SOA = {
+    nameServer = "ns1.vpn.";
+    adminEmail = "admin.vpn";
+    serial = 3;
+    refresh = 604800;
+    retry = 86400;
+    expire = 2419200;
+    minimum = 604800;
+  };
+
+  NS = [
+    "ns1.vpn."
+    "ns2.vpn."
+  ];
+
+  subdomains = {
+    # Name servers
+    ns1.A = [ "10.100.0.2" ];
+    ns2.A = [ "10.100.0.16" ];
+
+    # Cache/Massimo wildcards
+    cache.subdomains."*".A = [ "10.100.0.6" ];
+    massimo.subdomains."*".A = [ "10.100.0.6" ];
+
+    # Machines with wildcards
+    okinawa = {
+      A = [ "10.100.0.14" ];
+      subdomains."*".A = [ "10.100.0.14" ];
+    };
+    aomi = {
+      A = [ "10.100.0.17" ];
+      subdomains."*".A = [ "10.100.0.17" ];
+    };
+    shikoku = {
+      A = [ "10.100.0.2" ];
+      subdomains."*".A = [ "10.100.0.2" ];
+    };
+    sakhalin = {
+      A = [ "10.100.0.16" ];
+      subdomains."*".A = [ "10.100.0.16" ];
+    };
+    rhea = {
+      A = [ "10.100.0.50" ];
+      subdomains."*".A = [ "10.100.0.50" ];
+    };
+    aion = {
+      A = [ "10.100.0.49" ];
+      subdomains."*".A = [ "10.100.0.49" ];
+    };
+    athena = {
+      A = [ "10.100.0.83" ];
+      subdomains."*".A = [ "10.100.0.83" ];
+    };
+    demeter = {
+      A = [ "10.100.0.82" ];
+      subdomains."*".A = [ "10.100.0.82" ];
+    };
+    nagoya = {
+      A = [ "10.100.0.80" ];
+      subdomains."*".A = [ "10.100.0.80" ];
+    };
+    kyushu = {
+      A = [ "10.100.0.19" ];
+      subdomains."*".A = [ "10.100.0.19" ];
+    };
+    hass.A = [ "10.100.0.81" ];
+  };
+}
systems/common/services/bind.nix
@@ -1,6 +1,15 @@
-{ globals, ... }:
 {
-  # FIXME move the "networks" to globals
+  globals,
+  inputs,
+  ...
+}:
+let
+  dns = inputs.dns;
+
+  # Generate zone file content using dns.nix
+  mkZone = zoneName: zoneFile: dns.lib.toString zoneName (import zoneFile { inherit dns globals; });
+in
+{
   services.bind = {
     enable = true;
     forwarders = [
@@ -10,10 +19,39 @@
     extraOptions = ''
       dnssec-validation no;
     '';
-    cacheNetworks = [
-      "127.0.0.0/8"
-    ]
-    ++ globals.net.dns.cacheNetworks;
-    inherit (globals.net.dns) zones;
+    cacheNetworks = [ "127.0.0.0/8" ] ++ globals.net.dns.cacheNetworks;
+
+    zones = [
+      # sbr.pm zone
+      {
+        name = "sbr.pm";
+        master = true;
+        file = mkZone "sbr.pm" ./dns/sbr.pm.nix;
+      }
+      # home zone
+      {
+        name = "home";
+        master = true;
+        file = mkZone "home" ./dns/home.nix;
+      }
+      # home reverse zone
+      {
+        name = "192.168.1.in-addr.arpa";
+        master = true;
+        file = mkZone "192.168.1.in-addr.arpa" ./dns/192.168.1.nix;
+      }
+      # vpn zone
+      {
+        name = "vpn";
+        master = true;
+        file = mkZone "vpn" ./dns/vpn.nix;
+      }
+      # vpn reverse zone
+      {
+        name = "10.100.0.in-addr.arpa";
+        master = true;
+        file = mkZone "10.100.0.in-addr.arpa" ./dns/10.100.0.nix;
+      }
+    ];
   };
 }
flake.lock
@@ -256,6 +256,27 @@
         "type": "github"
       }
     },
+    "dns": {
+      "inputs": {
+        "flake-utils": "flake-utils",
+        "nixpkgs": [
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1759510210,
+        "narHash": "sha256-rR3BuhcSyQ3bQ0rS14I53O7gWzlPEs15skl1TWx+TeI=",
+        "owner": "nix-community",
+        "repo": "dns.nix",
+        "rev": "f3cb11f642d4fa6224e2b1ddfd2c3ba42e9ffea2",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "dns.nix",
+        "type": "github"
+      }
+    },
     "emacs-overlay": {
       "inputs": {
         "nixpkgs": [
@@ -371,6 +392,21 @@
         "type": "github"
       }
     },
+    "flake-utils": {
+      "locked": {
+        "lastModified": 1614513358,
+        "narHash": "sha256-LakhOx3S1dRjnh0b5Dg3mbZyH0ToC9I8Y2wKSkBaTzU=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "5466c5bbece17adaab2d82fae80b46e807611bf3",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
     "gitignore": {
       "inputs": {
         "nixpkgs": [
@@ -843,6 +879,7 @@
         "copilot-cli": "copilot-cli",
         "dagger": "dagger",
         "disko": "disko",
+        "dns": "dns",
         "emacs-overlay": "emacs-overlay",
         "flake-compat": "flake-compat",
         "go-org-readwise": "go-org-readwise",
flake.nix
@@ -286,6 +286,10 @@
       repo = "home-manager";
       inputs.nixpkgs.follows = "nixpkgs";
     };
+    dns = {
+      url = "github:nix-community/dns.nix";
+      inputs.nixpkgs.follows = "nixpkgs";
+    };
     home-manager-25_05 = {
       type = "github";
       owner = "nix-community";