From 807785cdab63e2511828d91713e13997c14578c3 Mon Sep 17 00:00:00 2001 From: Berwn Date: Sun, 14 Jun 2026 13:24:23 +0700 Subject: [PATCH] Add authoritative DNS on ns1/ns2 and finalize clan config - Knot authoritative DNS: ns1 primary, ns2 secondary serving cnx.network, buildfor.life and cnx.email over TSIG-secured zone transfer (modules/dns) - Knot listens publicly + over ZeroTier; firewall opens port 53 - Complete clan inventory: name/domain, admin SSH key, control as the zerotier controller, tor on all nixos machines - Enable age yubikey/fido2-hmac secret plugins --- clan.nix | 39 +++++--------------------- flake.nix | 4 +++ machines/ns1/configuration.nix | 17 ++++++++++-- machines/ns2/configuration.nix | 13 +++++++-- modules/dns/authoritative.nix | 41 ++++++++++++++++++++++++++++ modules/dns/domains.nix | 7 +++++ modules/dns/zones/buildfor.life.zone | 17 ++++++++++++ modules/dns/zones/cnx.email.zone | 18 ++++++++++++ modules/dns/zones/cnx.network.zone | 26 ++++++++++++++++++ 9 files changed, 146 insertions(+), 36 deletions(-) create mode 100644 modules/dns/authoritative.nix create mode 100644 modules/dns/domains.nix create mode 100644 modules/dns/zones/buildfor.life.zone create mode 100644 modules/dns/zones/cnx.email.zone create mode 100644 modules/dns/zones/cnx.network.zone diff --git a/clan.nix b/clan.nix index e3eb659..d39d491 100644 --- a/clan.nix +++ b/clan.nix @@ -1,58 +1,33 @@ { # Ensure this is unique among all clans you want to use. - meta.name = "__CHANGE_ME__"; - meta.domain = "changeme"; + meta.name = "cnx-network-clan"; + meta.domain = "cnx-network.internal"; inventory.machines = { - # Define machines here. - # jon = { }; + control = {}; + ns1 = {}; + ns2 = {}; }; - # Docs: See https://docs.clan.lol/services/definition/ inventory.instances = { - # Docs: https://docs.clan.lol/services/official/admin/ - # Admin service for managing machines - # This service adds a root password and SSH access. admin = { roles.default.tags.all = { }; roles.default.settings.allowedKeys = { - # Insert the public key that you want to use for SSH access. - # All keys will have ssh access to all machines ("tags.all" means 'all machines'). - # Alternatively set 'users.users.root.openssh.authorizedKeys.keys' in each machine - "admin-machine-1" = "__YOUR_PUBLIC_KEY__"; + "berwn" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIENAjhGQGraQoAjJzsomKP8GAmQPeGL1rNRNHgRcLqtT"; }; }; - # Docs: https://docs.clan.lol/services/official/zerotier/ - # The lines below will define a zerotier network and add all machines as 'peer' to it. - # !!! Manual steps required: - # - Define a controller machine for the zerotier network. - # - Deploy the controller machine first to initialize the network. zerotier = { - # Replace with the name (string) of your machine that you will use as zerotier-controller - # See: https://docs.zerotier.com/controller/ - # Deploy this machine first to create the network secrets - roles.controller.machines."__YOUR_CONTROLLER__" = { }; - # Peers of the network - # tags.all means 'all machines' will joined + roles.controller.machines."control" = { }; roles.peer.tags.all = { }; }; - # Docs: https://docs.clan.lol/services/official/tor/ - # Tor network provides secure, anonymous connections to your machines - # All machines will be accessible via Tor as a fallback connection method tor = { roles.server.tags.nixos = { }; }; }; - # Additional NixOS configuration can be added here. - # machines/jon/configuration.nix will be automatically imported. - # See: https://docs.clan.lol/guides/inventory/autoincludes/ machines = { - # jon = { config, ... }: { - # environment.systemPackages = [ pkgs.asciinema ]; - # }; }; } diff --git a/flake.nix b/flake.nix index 6edbc22..3999538 100644 --- a/flake.nix +++ b/flake.nix @@ -26,6 +26,10 @@ # }; # overlays = []; # }; + secrets.age.plugins = [ + "age-plugin-yubikey" + "age-plugin-fido2-hmac" + ]; }; in { diff --git a/machines/ns1/configuration.nix b/machines/ns1/configuration.nix index 090666a..4595f44 100644 --- a/machines/ns1/configuration.nix +++ b/machines/ns1/configuration.nix @@ -1,7 +1,20 @@ +{ ... }: +let + domains = import ../../modules/dns/domains.nix; +in { imports = [ - + ../../modules/dns/authoritative.nix ]; - # New machine! + # ns1 = primary (master): holds each master zone file, notifies ns2 and + # allows it to pull the zone via AXFR/IXFR. + services.knot.settings.zone = map (d: { + domain = d; + file = ../../modules/dns/zones + "/${d}.zone"; + "zonefile-load" = "whole"; + "zonefile-sync" = "-1"; + notify = [ "ns2" ]; + acl = [ "acl_ns2" ]; + }) domains; } diff --git a/machines/ns2/configuration.nix b/machines/ns2/configuration.nix index 090666a..f9465f4 100644 --- a/machines/ns2/configuration.nix +++ b/machines/ns2/configuration.nix @@ -1,7 +1,16 @@ +{ ... }: +let + domains = import ../../modules/dns/domains.nix; +in { imports = [ - + ../../modules/dns/authoritative.nix ]; - # New machine! + # ns2 = secondary (slave): pulls every zone from ns1 and accepts its NOTIFY. + services.knot.settings.zone = map (d: { + domain = d; + master = [ "ns1" ]; + acl = [ "acl_ns1" ]; + }) domains; } diff --git a/modules/dns/authoritative.nix b/modules/dns/authoritative.nix new file mode 100644 index 0000000..0eeb93c --- /dev/null +++ b/modules/dns/authoritative.nix @@ -0,0 +1,41 @@ +{ config, pkgs, ... }: +let + # ZeroTier addresses — zone transfers run over the mesh, not the public net. + ns1zt = "fd06:1bad:ece2:92ad:ba99:939d:766d:8974"; + ns2zt = "fd06:1bad:ece2:92ad:ba99:9323:61be:a09e"; +in +{ + # Shared TSIG key, generated once and copied to every machine that imports + # this module, so primary and secondary authenticate transfers with the same key. + clan.core.vars.generators.dns-tsig = { + share = true; + files."tsig.conf".secret = true; + runtimeInputs = [ pkgs.knot-dns ]; + script = '' + keymgr -t cnx_xfr hmac-sha256 > "$out"/tsig.conf + ''; + }; + + networking.firewall.allowedTCPPorts = [ 53 ]; + networking.firewall.allowedUDPPorts = [ 53 ]; + + services.knot = { + enable = true; + # Including the key via keyFiles keeps the secret out of the Nix store. + keyFiles = [ config.clan.core.vars.generators.dns-tsig.files."tsig.conf".path ]; + settings = { + server.listen = [ "0.0.0.0@53" "::@53" ]; + log = [ { target = "syslog"; any = "info"; } ]; + + remote = [ + { id = "ns1"; address = [ ns1zt ]; key = "cnx_xfr"; } + { id = "ns2"; address = [ ns2zt ]; key = "cnx_xfr"; } + ]; + + acl = [ + { id = "acl_ns1"; address = [ ns1zt ]; key = "cnx_xfr"; action = [ "transfer" "notify" ]; } + { id = "acl_ns2"; address = [ ns2zt ]; key = "cnx_xfr"; action = [ "transfer" "notify" ]; } + ]; + }; + }; +} diff --git a/modules/dns/domains.nix b/modules/dns/domains.nix new file mode 100644 index 0000000..5311c1f --- /dev/null +++ b/modules/dns/domains.nix @@ -0,0 +1,7 @@ +# Public zones served authoritatively by ns1 (primary) and ns2 (secondary). +# Add a domain here AND drop a matching .zone file in ./zones/. +[ + "cnx.network" + "buildfor.life" + "cnx.email" +] diff --git a/modules/dns/zones/buildfor.life.zone b/modules/dns/zones/buildfor.life.zone new file mode 100644 index 0000000..1434919 --- /dev/null +++ b/modules/dns/zones/buildfor.life.zone @@ -0,0 +1,17 @@ +$ORIGIN buildfor.life. +$TTL 3600 + +@ IN SOA ns1.cnx.network. hostmaster.cnx.network. ( + 2026061401 ; serial (bump on every edit: YYYYMMDDnn) + 3600 ; refresh + 900 ; retry + 604800 ; expire + 300 ) ; negative-cache TTL + +; Served by the same nameservers (out-of-bailiwick, no glue needed here). +@ IN NS ns1.cnx.network. +@ IN NS ns2.cnx.network. + +; ---- Web / apex (fill in once you have a web host) ---- +;@ IN A +;www IN CNAME buildfor.life. diff --git a/modules/dns/zones/cnx.email.zone b/modules/dns/zones/cnx.email.zone new file mode 100644 index 0000000..8dcc236 --- /dev/null +++ b/modules/dns/zones/cnx.email.zone @@ -0,0 +1,18 @@ +$ORIGIN cnx.email. +$TTL 3600 + +@ IN SOA ns1.cnx.network. hostmaster.cnx.network. ( + 2026061401 ; serial (bump on every edit: YYYYMMDDnn) + 3600 ; refresh + 900 ; retry + 604800 ; expire + 300 ) ; negative-cache TTL + +@ IN NS ns1.cnx.network. +@ IN NS ns2.cnx.network. + +; ---- Mail (fill in once the mail host exists) ---- +;@ IN MX 10 mail.cnx.email. +;mail IN A +;@ IN TXT "v=spf1 mx -all" +;_dmarc IN TXT "v=DMARC1; p=quarantine; rua=mailto:postmaster@cnx.email" diff --git a/modules/dns/zones/cnx.network.zone b/modules/dns/zones/cnx.network.zone new file mode 100644 index 0000000..cd4754a --- /dev/null +++ b/modules/dns/zones/cnx.network.zone @@ -0,0 +1,26 @@ +$ORIGIN cnx.network. +$TTL 3600 + +@ IN SOA ns1.cnx.network. hostmaster.cnx.network. ( + 2026061402 ; serial (bump on every edit: YYYYMMDDnn) + 3600 ; refresh + 900 ; retry + 604800 ; expire + 300 ) ; negative-cache TTL + +; ---- Nameservers (used by every zone we serve) ---- +@ IN NS ns1.cnx.network. +@ IN NS ns2.cnx.network. + +; ---- Glue for the nameservers ---- +ns1 IN A 46.224.170.206 +ns1 IN AAAA fd06:1bad:ece2:92ad:ba99:939d:766d:8974 +ns2 IN A 157.180.70.82 +ns2 IN AAAA fd06:1bad:ece2:92ad:ba99:9323:61be:a09e + +; ---- control (ZeroTier controller) ---- +control IN AAAA fd06:1bad:ece2:92ad:ba99:9306:1bad:ece2 + +; ---- Web / apex (fill in once you have a web host) ---- +;@ IN A +;www IN CNAME cnx.network.