diff --git a/flake.lock b/flake.lock index 26687d1..b5493ce 100644 --- a/flake.lock +++ b/flake.lock @@ -38,6 +38,22 @@ "type": "github" } }, + "blobs": { + "flake": false, + "locked": { + "lastModified": 1604995301, + "narHash": "sha256-wcLzgLec6SGJA8fx1OEN1yV/Py5b+U5iyYpksUY/yLw=", + "owner": "simple-nixos-mailserver", + "repo": "blobs", + "rev": "2cccdf1ca48316f2cfd1c9a0017e8de5a7156265", + "type": "gitlab" + }, + "original": { + "owner": "simple-nixos-mailserver", + "repo": "blobs", + "type": "gitlab" + } + }, "call-flake": { "locked": { "lastModified": 1687380775, @@ -148,6 +164,37 @@ "url": "https://git.clan.lol/clan/clan-core" } }, + "cnx-network": { + "inputs": { + "clan-core": [ + "clan-core" + ], + "flake-parts": [ + "flake-parts" + ], + "nixos-mailserver": "nixos-mailserver", + "nixpkgs": [ + "nixpkgs" + ], + "treefmt-nix": [ + "treefmt-nix" + ] + }, + "locked": { + "lastModified": 1789610252, + "narHash": "sha256-k8rD1LMf17vAf+fS9lVC4Yp0C6ok5mYVtfZAsKC6Wy8=", + "ref": "clanService-router-ai", + "rev": "60aac6efb277a12c24fde29eb77422b3c3519df4", + "revCount": 194, + "type": "git", + "url": "https://git.b4l.co.th/B4L/cnx-network-clan" + }, + "original": { + "ref": "clanService-router-ai", + "type": "git", + "url": "https://git.b4l.co.th/B4L/cnx-network-clan" + } + }, "data-mesher": { "inputs": { "flake-parts": [ @@ -249,6 +296,22 @@ "type": "github" } }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "NixOS", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "flake-compat", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": [ @@ -370,6 +433,57 @@ "type": "github" } }, + "git-hooks": { + "inputs": { + "flake-compat": [ + "cnx-network", + "nixos-mailserver", + "flake-compat" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "cnx-network", + "nixos-mailserver", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778507602, + "narHash": "sha256-kTwur1wV+01SdqskVMSo6JMEpg71ps3HpbFY2GsflKs=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "61ab0e80d9c7ab14c256b5b453d8b3fb0189ba0a", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "cnx-network", + "nixos-mailserver", + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "haskell-flake": { "locked": { "lastModified": 1675296942, @@ -719,6 +833,31 @@ "type": "github" } }, + "nixos-mailserver": { + "inputs": { + "blobs": "blobs", + "flake-compat": "flake-compat", + "git-hooks": "git-hooks", + "nixpkgs": [ + "cnx-network", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1785198005, + "narHash": "sha256-falBPi+PJtMx0vwII8L24wjDGXNZMwRiVXsw8WTXLEo=", + "owner": "simple-nixos-mailserver", + "repo": "nixos-mailserver", + "rev": "d357b9f048c5532ec81b0e0034c0b8463d5ddd46", + "type": "gitlab" + }, + "original": { + "owner": "simple-nixos-mailserver", + "ref": "nixos-26.05", + "repo": "nixos-mailserver", + "type": "gitlab" + } + }, "nixpkgs": { "locked": { "lastModified": 1712920918, @@ -854,6 +993,7 @@ "inputs": { "clan-community": "clan-community", "clan-core": "clan-core", + "cnx-network": "cnx-network", "devshell": "devshell", "flake-parts": "flake-parts", "frappix": "frappix", diff --git a/flake.nix b/flake.nix index 23542f8..7dee08a 100644 --- a/flake.nix +++ b/flake.nix @@ -58,6 +58,16 @@ inputs.nixpkgs.follows = "nixpkgs"; inputs.disko.follows = "clan-core/disko"; }; + + # `router` clan service (site gateway: PPPoE, VLAN bridge, Kea/Blocky, + # nftables). Consumed via `module.input = "cnx-network"` in inventories/router.nix. + cnx-network = { + url = "git+https://git.b4l.co.th/B4L/cnx-network-clan?ref=clanService-router-ai"; + inputs.clan-core.follows = "clan-core"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.flake-parts.follows = "flake-parts"; + inputs.treefmt-nix.follows = "treefmt-nix"; + }; }; outputs = { diff --git a/inventories/default.nix b/inventories/default.nix index 9b84258..e73f108 100644 --- a/inventories/default.nix +++ b/inventories/default.nix @@ -2,6 +2,7 @@ imports = [ ./personal-computer.nix ./emmie.nix + ./router.nix ]; clan = { inventory = { @@ -16,7 +17,10 @@ "deneb" "bosona" ]; - w = [ "sirius" ]; + w = [ + "sirius" + "stellio" + ]; b4l = [ "rigel" "rana" diff --git a/inventories/router.nix b/inventories/router.nix new file mode 100644 index 0000000..1edb50d --- /dev/null +++ b/inventories/router.nix @@ -0,0 +1,61 @@ +# Site gateways: the `router` clan service from cnx-network-clan (see +# flake.nix input `cnx-network`, and that repo's modules/clan/router/README.md). +# One instance for the fleet; each site adds its gateway machine with the +# site's port / VLAN layout. `clan vars generate ` then prompts for the +# PPPoE username/password. +{ lib, ... }: +let + # Admin mesh = the site's ZeroTier network. The service only opens SSH, + # metrics and iperf3 to this prefix, so the gateway must also be a peer of + # the same network (tag membership in default.nix). Prefix derivation as in + # cnx-network's modules/mesh-hosts.nix: fd + 8-byte network id + 0x9993, + # /88 covers every peer. + meshSubnet = + instance: + let + networkId = lib.fileContents ../vars/shared/zerotier-network-${instance}/network-id/value; + full = "fd" + networkId + "9993"; + hextet = i: builtins.substring (i * 4) 4 full; + in + "${hextet 0}:${hextet 1}:${hextet 2}:${hextet 3}:${hextet 4}:${builtins.substring 20 2 full}00::/88"; +in +{ + clan.inventory.instances.router = { + module = { + name = "router"; + input = "cnx-network"; + }; + + roles.default.settings.mesh.subnet = meshSubnet "w-network"; + + # stellio: GL.iNet Flint 2 (GL-MT6000, MT7986a). Ports as named by the + # device tree in matthew-hardware: eth1 = the 2.5G "WAN" port, lan1 = the + # 2.5G LAN port, lan2-lan5 = the 1G LAN ports (DSA on the MT7531 switch). + roles.default.machines.stellio.settings = { + site = "w"; + siteId = 1; + + # PPPoE on the ISP's VLAN 10, as on the liminix router it replaces + # (routers/white-house); set to null if the ISP runs PPPoE untagged. + wan.interface = "eth1"; + wan.vlanId = 10; + + # 2.5G port carries all VLANs tagged (towards a managed switch / AP). + trunkPorts = [ "lan1" ]; + # 1G ports: untagged client ports on lan, and one always-available + # untagged mgmt port for on-site recovery. + accessPorts = { + lan2 = "lan"; + lan3 = "lan"; + lan4 = "lan"; + lan5 = "mgmt"; + }; + + # 10.1..0/24, router .1, DHCP pool .100-.199. + vlans = { + mgmt.id = 10; + lan.id = 20; + }; + }; + }; +}