let hosts = import ./modules/hosts.nix; # Single source of the machine list: inventory AND the per-machine baseline # below are both derived from it, so no machine can be added without the # baseline (e.g. admins group encryption for all its generated secrets). fleet = { control = { }; ns1 = { }; ns2 = { }; mx1 = { }; web01 = { }; # Site gateways (Topton 1U routers): dynamic PPPoE WAN, so they are NOT in # modules/hosts.nix / the `internet` instance — clan reaches them over the # zerotier mesh (or Tor) instead. gw-cnx-1 = { }; }; in { # Ensure this is unique among all clans you want to use. meta.name = "cnx-network-clan"; meta.domain = "cnx-network.internal"; inventory.machines = fleet; inventory.instances = { # Admin SSH keys + root password, split per the clan-core migration off # the deprecated `admin` service (sshd handles keys, users the password). sshd = { roles.server.tags.all = { }; roles.server.settings.authorizedKeys = { "berwn" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIENAjhGQGraQoAjJzsomKP8GAmQPeGL1rNRNHgRcLqtT"; "kurogeek" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcZ/p1Ofa9liwIzPWzNtONhJ7+FUWd2lCz33r81t8+w kurogeek@kurogeek"; }; }; root-user = { module = { name = "users"; input = "clan-core"; }; roles.default.tags.all = { }; roles.default.settings = { user = "root"; prompt = false; # auto-generate, like the old admin service share = false; # per-machine password, not fleet-wide }; }; zerotier = { roles.controller.machines."control" = { }; roles.peer.tags.all = { }; # External members admitted by ZeroTier node id (stable per device). # Inventory machines are auto-accepted; this is only for peers outside the # clan. Node id comes from `zerotier-cli info` on the joining device. roles.controller.settings.allowedIds = [ "8802c8d7e0" # alex-nixos "2bd36db8cc" # kurogeek-thinkpad ]; }; tor = { roles.server.tags.nixos = { }; }; # Direct SSH to public IPs — clan's priority-1 connection path, with the # ZeroTier mesh and Tor kept as automatic fallbacks. Raw IPs (from # modules/hosts.nix, not the ns1/ns2 DNS names) so reaching these hosts never # depends on their own DNS being up. internet.roles.default.machines = builtins.mapAttrs (_: h: { settings.host = h.ipv4; }) hosts; # Recovery root password for console access when a machine fails to boot. emergency-access = { roles.default.tags.nixos = { }; }; # Encrypted, deduplicating backups. control hosts the repos; ns1 is the # only client, backing up its declared clan.core.state (the Knot DNSSEC # keystore) over the mesh. Repo lives at /var/lib/borgbackup/ns1 on control. # Cross-host so an ns1 loss is recoverable; repokey encryption means control # never holds plaintext. Run `clan vars generate ns1` (YubiKey) before deploy. borgbackup = { roles.server.machines.control = { }; roles.client.machines.ns1 = { }; }; }; # Fleet-wide baseline applied to every machine. Secrets minted by # `clan vars generate` are encrypted for the admins group from the very # first run — generating before this took effect is what forced the # re-encryption dance (`clan vars fix`) on gw-cnx-1. machines = builtins.mapAttrs (_: _: { clan.core.sops.defaultGroups = [ "admins" ]; }) fleet; }