Reusable cnx.router.* module for the Topton 1U boxes replacing OPNsense: PPPoE WAN (optionally VLAN-tagged, AIS: 10, secret credentials incl. username), VLAN-filtering bridge, nftables NAT/firewall with MSS clamp, Kea DHCP with per-VLAN lease time, Blocky DNS, DHCPv6-PD, CrowdSec with the ZeroTier mesh whitelisted, optional Omada controller, ZFS disk. Fleet baseline rides along: admins sops group is now derived for every machine in clan.nix (secrets encrypt to it from the first vars generate) and time sync is chrony everywhere instead of systemd-timesyncd.
85 lines
3.0 KiB
Nix
85 lines
3.0 KiB
Nix
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 = {
|
|
roles.default.tags.all = { };
|
|
roles.default.settings.allowedKeys = {
|
|
"berwn" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIENAjhGQGraQoAjJzsomKP8GAmQPeGL1rNRNHgRcLqtT";
|
|
"kurogeek" =
|
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcZ/p1Ofa9liwIzPWzNtONhJ7+FUWd2lCz33r81t8+w kurogeek@kurogeek";
|
|
};
|
|
};
|
|
|
|
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;
|
|
}
|