Commit bc1dfbdeb233

Vincent Demeester <vincent@sbr.pm>
2025-12-01 15:19:11
refactor: Split DNS zones for local and public resolution
- Enable local network to resolve sbr.pm to 192.168.1.x addresses - Configure Gandi DNS to publish VPN IPs (10.100.0.x) for external access - Extract common zone structure to eliminate duplication Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent fdeba48
Changed files (4)
systems/common/services/dns/sbr.pm-common.nix
@@ -0,0 +1,120 @@
+# Common DNS zone builder for sbr.pm
+# Takes an IP selector function to allow different IP selection strategies
+{
+  dns,
+  globals,
+  getIPForMachine,
+}:
+with dns.lib.combinators;
+let
+  # Helper to generate service DNS records
+  mkServiceRecords =
+    services:
+    builtins.listToAttrs (
+      builtins.concatMap (
+        serviceName:
+        let
+          service = services.${serviceName};
+          hostName = if builtins.isAttrs service then service.host else service;
+          ip = getIPForMachine globals.machines.${hostName};
+          aliases = if builtins.isAttrs service then (service.aliases or [ ]) else [ ];
+        in
+        [
+          {
+            name = serviceName;
+            value.A = [ ip ];
+          }
+        ]
+        ++ (map (alias: {
+          name = alias;
+          value.A = [ ip ];
+        }) aliases)
+      ) (builtins.attrNames services)
+    );
+
+  # Only include machines that should be in sbr.pm zone
+  machineList = [
+    "shikoku"
+    "sakhalin"
+    "aix"
+    "rhea"
+    "aion"
+    "demeter"
+    "athena"
+    "nagoya"
+    "kerkouane"
+    "aomi"
+    "kyushu"
+    "wakasu"
+  ];
+
+  mkMachineRecords = builtins.listToAttrs (
+    map (machineName: {
+      name = machineName;
+      value = {
+        A = [ (getIPForMachine globals.machines.${machineName}) ];
+        subdomains."*".A = [ (getIPForMachine globals.machines.${machineName}) ];
+      };
+    }) machineList
+  );
+in
+{
+  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."
+  ];
+
+  # Root domain points to public endpoint
+  A = [ "167.99.17.238" ];
+
+  # Email (Gandi)
+  MX = [
+    {
+      preference = 10;
+      exchange = "spool.mail.gandi.net.";
+    }
+    {
+      preference = 50;
+      exchange = "fb.mail.gandi.net.";
+    }
+  ];
+
+  subdomains = {
+    # Name servers (demeter and athena)
+    ns1.A = [ (getIPForMachine globals.machines.demeter) ];
+    ns2.A = [ (getIPForMachine globals.machines.athena) ];
+
+    # Wildcard for public endpoint
+    "*".A = [
+      {
+        address = "167.99.17.238";
+        ttl = 10800;
+      }
+    ];
+
+    # Email CNAMEs (Gandi mail service)
+    imap.CNAME = [ "access.mail.gandi.net." ];
+    pop.CNAME = [ "access.mail.gandi.net." ];
+    smtp.CNAME = [ "relay.mail.gandi.net." ];
+    webmail.CNAME = [ "webmail.gandi.net." ];
+
+    # Shortcuts
+    p.A = [ "167.99.17.238" ]; # public endpoint shortcut
+    www = {
+      A = [ "167.99.17.238" ];
+      subdomains."*".A = [ "167.99.17.238" ];
+    };
+  }
+  // mkMachineRecords
+  // mkServiceRecords globals.services;
+}
systems/common/services/dns/sbr.pm-gandi.nix
@@ -0,0 +1,10 @@
+# Gandi (public) DNS zone for sbr.pm - uses VPN IPs (10.100.0.x) only
+{ dns, globals, ... }:
+let
+  dnsHelpers = import ../../../../lib/dns-helpers.nix { inherit globals; };
+  inherit (dnsHelpers) getMachineIP;
+in
+import ./sbr.pm-common.nix {
+  inherit dns globals;
+  getIPForMachine = getMachineIP;
+}
systems/common/services/dns/sbr.pm.nix
@@ -1,92 +1,17 @@
+# Local DNS zone for sbr.pm - uses local IPs (192.168.1.x) with VPN fallback
 { dns, globals, ... }:
-with dns.lib.combinators;
 let
-  dnsHelpers = import ../../../../lib/dns-helpers.nix { inherit globals; };
-  inherit (dnsHelpers) getMachineIP mkServiceRecords;
-
-  # Only include machines that should be in sbr.pm zone
-  machineList = [
-    "shikoku"
-    "sakhalin"
-    "aix"
-    "rhea"
-    "aion"
-    "demeter"
-    "athena"
-    "nagoya"
-    "kerkouane"
-    "aomi"
-    "kyushu"
-    "wakasu"
-  ];
-
-  mkMachineRecords = builtins.listToAttrs (
-    map (machineName: {
-      name = machineName;
-      value = {
-        A = [ (getMachineIP globals.machines.${machineName}) ];
-        subdomains."*".A = [ (getMachineIP globals.machines.${machineName}) ];
-      };
-    }) machineList
-  );
+  # Helper to get local IP, fallback to VPN IP
+  getLocalMachineIP =
+    machine:
+    let
+      localIps = machine.net.ips or [ ];
+      vpnIps = machine.net.vpn.ips or [ ];
+      ips = if localIps != [ ] then localIps else vpnIps;
+    in
+    if builtins.isList ips then builtins.head ips else ips;
 in
-{
-  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."
-  ];
-
-  # Root domain points to public endpoint
-  A = [ "167.99.17.238" ];
-
-  # Email (Gandi)
-  MX = [
-    {
-      preference = 10;
-      exchange = "spool.mail.gandi.net.";
-    }
-    {
-      preference = 50;
-      exchange = "fb.mail.gandi.net.";
-    }
-  ];
-
-  subdomains = {
-    # Name servers (demeter and athena)
-    ns1.A = [ (getMachineIP globals.machines.demeter) ];
-    ns2.A = [ (getMachineIP globals.machines.athena) ];
-
-    # Wildcard for public endpoint
-    "*".A = [
-      {
-        address = "167.99.17.238";
-        ttl = 10800;
-      }
-    ];
-
-    # Email CNAMEs (Gandi mail service)
-    imap.CNAME = [ "access.mail.gandi.net." ];
-    pop.CNAME = [ "access.mail.gandi.net." ];
-    smtp.CNAME = [ "relay.mail.gandi.net." ];
-    webmail.CNAME = [ "webmail.gandi.net." ];
-
-    # Shortcuts
-    p.A = [ "167.99.17.238" ]; # public endpoint shortcut
-    www = {
-      A = [ "167.99.17.238" ];
-      subdomains."*".A = [ "167.99.17.238" ];
-    };
-  }
-  // mkMachineRecords
-  // mkServiceRecords globals.services;
+import ./sbr.pm-common.nix {
+  inherit dns globals;
+  getIPForMachine = getLocalMachineIP;
 }
tools/update-gandi-dns.sh
@@ -42,10 +42,17 @@ if [[ "$DRY_RUN" == "true" ]]; then
 fi
 echo
 
-# Get the DNS zone file content from Nix
-echo -e "${CYAN}Extracting DNS records from Nix configuration...${RESET}"
-ZONE_FILE=$(nix eval --raw '.#nixosConfigurations.demeter.config.services.bind.zones."sbr.pm".file' --apply 'path: builtins.readFile path' 2>&1 | \
-           grep -v "^warning:" | grep -v "^Using saved setting" | grep -v "^building ")
+# Get the DNS zone file content from Nix (using Gandi-specific config with VPN IPs)
+echo -e "${CYAN}Extracting DNS records from Nix configuration (Gandi/VPN version)...${RESET}"
+ZONE_FILE=$(nix eval --impure --raw --expr '
+  let
+    pkgs = import <nixpkgs> {};
+    dns = (builtins.getFlake (toString ./.)).inputs.dns;
+    globals = (import ./globals.nix) {};
+    zone = import ./systems/common/services/dns/sbr.pm-gandi.nix { inherit dns globals; };
+  in
+  dns.lib.toString "sbr.pm" zone
+' 2>&1 | grep -v "^warning:" | grep -v "^Using saved setting" | grep -v "^building ")
 
 if [[ -z "$ZONE_FILE" ]]; then
     echo -e "${RED}Error: Could not generate zone file${RESET}"