48bf7fb250
web01 terminates TLS for grafana.cnx.network and proxies to Grafana on control over the mesh. Caddy serves a *.cnx.network wildcard cert obtained via ACME DNS-01, using a dedicated acme_web01 TSIG key scoped on ns1 to _acme-challenge on the cnx.network zone only. Ports 80/443 are the only public exposure (80 just redirects); admin and the backend ride ZeroTier. Also reload Caddy on cert renewal for both web01 and mx1, since both reference the cert via explicit tls file paths and would otherwise keep serving a stale cert after a silent renewal.
84 lines
2.8 KiB
Nix
84 lines
2.8 KiB
Nix
let
|
|
hosts = import ./modules/hosts.nix;
|
|
|
|
# This clan-core pins the zerotier `allowedIps` interface (admit by network
|
|
# IPv6), but node IDs are the stable per-device handle (what `zerotier-cli
|
|
# info` prints). Derive a member's IP on THIS network from the controller's
|
|
# network id so external members can be listed by node id, as below.
|
|
ztNetworkId = builtins.readFile ./vars/per-machine/control/zerotier/zerotier-network-id/value;
|
|
ztMemberIp =
|
|
nodeId:
|
|
let
|
|
full = "fd" + ztNetworkId + "9993" + nodeId;
|
|
h = i: builtins.substring (i * 4) 4 full;
|
|
in
|
|
"${h 0}:${h 1}:${h 2}:${h 3}:${h 4}:${h 5}:${h 6}:${h 7}";
|
|
in
|
|
{
|
|
# Ensure this is unique among all clans you want to use.
|
|
meta.name = "cnx-network-clan";
|
|
meta.domain = "cnx-network.internal";
|
|
|
|
inventory.machines = {
|
|
control = { };
|
|
ns1 = { };
|
|
ns2 = { };
|
|
mx1 = { };
|
|
web01 = { };
|
|
};
|
|
|
|
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.allowedIps = map ztMemberIp [
|
|
"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 = { };
|
|
};
|
|
};
|
|
|
|
machines = {
|
|
};
|
|
}
|