mob next [ci-skip] [ci skip] [skip ci]

lastFile:flake.nix
This commit is contained in:
2026-08-06 11:49:57 +07:00
parent 5642a0f6fc
commit a46b83cbc7
3 changed files with 230 additions and 236 deletions
+172 -152
View File
@@ -1,3 +1,4 @@
{ inputs, self, ... }:
let let
hosts = import ./modules/hosts.nix; hosts = import ./modules/hosts.nix;
@@ -17,173 +18,192 @@ let
}; };
in in
{ {
# Ensure this is unique among all clans you want to use. clan = {
meta.name = "cnx-network-clan"; # Ensure this is unique among all clans you want to use.
meta.domain = "cnx-network.internal"; meta.name = "cnx-network-clan";
meta.domain = "cnx-network.internal";
inventory.machines = fleet; specialArgs = { inherit inputs self; };
inventory.instances = { # Customize nixpkgs
# pkgsForSystem =
# system:
# import nixpkgs {
# inherit system;
# config = {
# allowUnfree = true;
# };
# overlays = [];
# };
secrets.age.plugins = [
"age-plugin-yubikey"
"age-plugin-fido2-hmac"
];
# Admin SSH keys + root password, split per the clan-core migration off inventory.machines = fleet;
# the deprecated `admin` service (sshd handles keys, users the password).
sshd = { inventory.instances = {
roles.server.tags.all = { };
roles.server.settings.authorizedKeys = { # Admin SSH keys + root password, split per the clan-core migration off
"berwn" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIENAjhGQGraQoAjJzsomKP8GAmQPeGL1rNRNHgRcLqtT"; # the deprecated `admin` service (sshd handles keys, users the password).
"kurogeek" = sshd = {
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcZ/p1Ofa9liwIzPWzNtONhJ7+FUWd2lCz33r81t8+w kurogeek@kurogeek"; roles.server.tags.all = { };
roles.server.settings.authorizedKeys = {
"berwn" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIENAjhGQGraQoAjJzsomKP8GAmQPeGL1rNRNHgRcLqtT";
"kurogeek" =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcZ/p1Ofa9liwIzPWzNtONhJ7+FUWd2lCz33r81t8+w kurogeek@kurogeek";
};
}; };
};
gw-router = { gw-router = {
module = { module = {
name = "gw-router"; name = "gw-router";
input = "self"; input = "self";
}; };
roles.default.machines."gw-cnx-1" = { roles.default.machines."gw-cnx-1" = {
settings = { settings = {
wan = { wan = {
interface = "enp1s0"; interface = "enp1s0";
vlanId = null; vlanId = null;
pppInterface = "enp1s0"; pppInterface = "enp1s0";
}; };
vlans = { vlans = {
mgmt = { mgmt = {
id = 10; id = 10;
address = "10.1.10.1"; address = "10.1.10.1";
prefixLength = 24; prefixLength = 24;
subnet = "10.1.10.0/24"; subnet = "10.1.10.0/24";
dhcp.fixIPs.storinator01 = { dhcp.fixIPs.storinator01 = {
hwAddress = "7c:c2:55:e0:d6:40"; hwAddress = "7c:c2:55:e0:d6:40";
ipAddress = "10.1.10.53"; ipAddress = "10.1.10.53";
};
};
lan = {
id = 20;
address = "10.1.20.1";
prefixLength = 24;
subnet = "10.1.20.0/24";
};
iot = {
id = 40;
address = "10.1.40.1";
prefixLength = 24;
subnet = "10.1.40.0/24";
};
voip = {
id = 50;
address = "10.1.50.1";
prefixLength = 24;
subnet = "10.1.50.0/24";
};
dmz = {
id = 60;
address = "10.1.60.1";
prefixLength = 24;
subnet = "10.1.60.0/24";
};
unit1 = {
id = 110;
address = "10.1.110.1";
prefixLength = 24;
subnet = "10.1.110.0/24";
};
unit2 = {
id = 120;
address = "10.1.120.1";
prefixLength = 24;
subnet = "10.1.120.0/24";
};
unit3 = {
id = 130;
address = "10.1.130.1";
prefixLength = 24;
subnet = "10.1.130.0/24";
};
unit4 = {
id = 140;
address = "10.1.140.1";
prefixLength = 24;
subnet = "10.1.140.0/24";
};
unit5 = {
id = 150;
address = "10.1.150.1";
prefixLength = 24;
subnet = "10.1.150.0/24";
dhcp.fixIPs.newt = {
hwAddress = "7c:d3:0a:21:58:0b";
ipAddress = "10.1.150.22";
};
}; };
}; };
lan = { trunkPorts = [ "enp2s0" ];
id = 20; accessPorts = {
address = "10.1.20.1"; enp4s0.vlanId = 10;
prefixLength = 24;
subnet = "10.1.20.0/24";
}; };
iot = {
id = 40;
address = "10.1.40.1";
prefixLength = 24;
subnet = "10.1.40.0/24";
};
voip = {
id = 50;
address = "10.1.50.1";
prefixLength = 24;
subnet = "10.1.50.0/24";
};
dmz = {
id = 60;
address = "10.1.60.1";
prefixLength = 24;
subnet = "10.1.60.0/24";
};
unit1 = {
id = 110;
address = "10.1.110.1";
prefixLength = 24;
subnet = "10.1.110.0/24";
};
unit2 = {
id = 120;
address = "10.1.120.1";
prefixLength = 24;
subnet = "10.1.120.0/24";
};
unit3 = {
id = 130;
address = "10.1.130.1";
prefixLength = 24;
subnet = "10.1.130.0/24";
};
unit4 = {
id = 140;
address = "10.1.140.1";
prefixLength = 24;
subnet = "10.1.140.0/24";
};
unit5 = {
id = 150;
address = "10.1.150.1";
prefixLength = 24;
subnet = "10.1.150.0/24";
dhcp.fixIPs.newt = {
hwAddress = "7c:d3:0a:21:58:0b";
ipAddress = "10.1.150.22";
};
};
};
trunkPorts = [ "enp2s0" ];
accessPorts = {
enp4s0.vlanId = 10;
}; };
}; };
}; };
};
root-user = { root-user = {
module = { module = {
name = "users"; name = "users";
input = "clan-core"; 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
};
}; };
roles.default.tags.all = { };
roles.default.settings = { zerotier = {
user = "root"; roles.controller.machines."control" = { };
prompt = false; # auto-generate, like the old admin service roles.peer.tags.all = { };
share = false; # per-machine password, not fleet-wide # 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 = { };
}; };
}; };
zerotier = { # Fleet-wide baseline applied to every machine. Secrets minted by
roles.controller.machines."control" = { }; # `clan vars generate` are encrypted for the admins group from the very
roles.peer.tags.all = { }; # first run — generating before this took effect is what forced the
# External members admitted by ZeroTier node id (stable per device). # re-encryption dance (`clan vars fix`) on gw-cnx-1.
# Inventory machines are auto-accepted; this is only for peers outside the machines = builtins.mapAttrs (_: _: {
# clan. Node id comes from `zerotier-cli info` on the joining device. clan.core.sops.defaultGroups = [ "admins" ];
roles.controller.settings.allowedIds = [ }) fleet;
"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;
} }
+3 -29
View File
@@ -13,34 +13,10 @@
inputs@{ inputs@{
self, self,
clan-core, clan-core,
nixpkgs,
flake-parts, flake-parts,
... ...
}: }:
let let
# Usage see: https://docs.clan.lol
clan = clan-core.lib.clan {
inherit self;
imports = [
./clan.nix
];
specialArgs = { inherit inputs self; };
# Customize nixpkgs
# pkgsForSystem =
# system:
# import nixpkgs {
# inherit system;
# config = {
# allowUnfree = true;
# };
# overlays = [];
# };
secrets.age.plugins = [
"age-plugin-yubikey"
"age-plugin-fido2-hmac"
];
};
in in
flake-parts.lib.mkFlake { inherit inputs; } { flake-parts.lib.mkFlake { inherit inputs; } {
systems = [ systems = [
@@ -51,14 +27,12 @@
]; ];
imports = [ imports = [
inputs.clan-core.flakeModules.default
./clan.nix
./modules/clan/flake-module.nix ./modules/clan/flake-module.nix
]; ];
# flake = {
# inherit (clan.config) nixosConfigurations nixosModules clanInternals;
# clan = clan.config;
# };
perSystem = perSystem =
{ system, ... }: { system, ... }:
let let
+55 -55
View File
@@ -4,7 +4,7 @@
{ config, lib, ... }: { config, lib, ... }:
{ {
imports = [ imports = [
../../modules/router # ../../modules/router
../../modules/monitoring/exporters.nix ../../modules/monitoring/exporters.nix
(import ../../modules/dns/acme-gw-secret.nix "gw-cnx-1") (import ../../modules/dns/acme-gw-secret.nix "gw-cnx-1")
]; ];
@@ -20,60 +20,60 @@
builtins.hashString "sha256" config.networking.hostName builtins.hashString "sha256" config.networking.hostName
); );
cnx.router = { # cnx.router = {
enable = true; # enable = true;
site = "cnx"; # site = "cnx";
siteId = 1; # siteId = 1;
wan.interface = "enp1s0"; # wan.interface = "enp1s0";
wan.vlanId = null; # this ISP runs PPPoE untagged on the port # wan.vlanId = null; # this ISP runs PPPoE untagged on the port
trunkPorts = [ # trunkPorts = [
"enp2s0" # "enp2s0"
# "enp3s0" # STAGING: serves as the uplink below until cutover # # "enp3s0" # STAGING: serves as the uplink below until cutover
]; # ];
# Dedicated on-site recovery port: untagged mgmt, always available even # # Dedicated on-site recovery port: untagged mgmt, always available even
# if the switch config is broken. # # if the switch config is broken.
accessPorts.enp4s0 = "mgmt"; # accessPorts.enp4s0 = "mgmt";
# Replaces the newedge.house OPNsense box; renumbered to the fleet # # Replaces the newedge.house OPNsense box; renumbered to the fleet
# convention (10.1.<vlanId>.0/24, router .1, pool .100-.199). The old # # convention (10.1.<vlanId>.0/24, router .1, pool .100-.199). The old
# untagged LAN becomes tagged mgmt — infra switch ports get PVID 10. # # untagged LAN becomes tagged mgmt — infra switch ports get PVID 10.
vlans = { # vlans = {
mgmt = { # mgmt = {
id = 10; # 10.1.10.0/24 — servers, APs, switches, Omada, admin # id = 10; # 10.1.10.0/24 — servers, APs, switches, Omada, admin
dhcp.reservations.storinator01 = { # dhcp.reservations.storinator01 = {
hwAddress = "7c:c2:55:e0:d6:40"; # hwAddress = "7c:c2:55:e0:d6:40";
ipAddress = "10.1.10.53"; # ipAddress = "10.1.10.53";
}; # };
}; # };
lan.id = 20; # 10.1.20.0/24 — trusted clients # lan.id = 20; # 10.1.20.0/24 — trusted clients
iot.id = 40; # 10.1.40.0/24 # iot.id = 40; # 10.1.40.0/24
voip.id = 50; # 10.1.50.0/24 # voip.id = 50; # 10.1.50.0/24
dmz.id = 60; # 10.1.60.0/24 # dmz.id = 60; # 10.1.60.0/24
unit1.id = 110; # 10.1.110.0/24 # unit1.id = 110; # 10.1.110.0/24
unit2.id = 120; # 10.1.120.0/24 # unit2.id = 120; # 10.1.120.0/24
unit3.id = 130; # 10.1.130.0/24 # unit3.id = 130; # 10.1.130.0/24
unit4.id = 140; # 10.1.140.0/24 # unit4.id = 140; # 10.1.140.0/24
unit5 = { # unit5 = {
id = 150; # 10.1.150.0/24 # id = 150; # 10.1.150.0/24
dhcp.reservations.newt = { # dhcp.reservations.newt = {
hwAddress = "7c:d3:0a:21:58:0b"; # hwAddress = "7c:d3:0a:21:58:0b";
ipAddress = "10.1.150.22"; # ipAddress = "10.1.150.22";
}; # };
}; # };
}; # };
# This site runs the Omada controller for its APs/switches. # # This site runs the Omada controller for its APs/switches.
omada.enable = true; # omada.enable = true;
#
# Internal reverse proxy: real wildcard cert *.cnx1.cnx.network; Blocky # # Internal reverse proxy: real wildcard cert *.cnx1.cnx.network; Blocky
# resolves the names to the router's LAN address for mgmt+lan clients. # # resolves the names to the router's LAN address for mgmt+lan clients.
proxy = { # proxy = {
enable = true; # enable = true;
services.omada = { # services.omada = {
# Omada's UI is HTTPS with a self-signed cert on the host network. # # Omada's UI is HTTPS with a self-signed cert on the host network.
backend = "https://127.0.0.1:8043"; # backend = "https://127.0.0.1:8043";
insecureSkipVerify = true; # insecureSkipVerify = true;
}; # };
}; # };
}; # };
# STAGING (remove at cutover, and restore enp3s0 to trunkPorts): DHCP-client # STAGING (remove at cutover, and restore enp3s0 to trunkPorts): DHCP-client
# uplink into the existing OPNsense LAN so the box has internet + mesh while # uplink into the existing OPNsense LAN so the box has internet + mesh while