Files
nixos-router/tests/names.nix
T
nilsandClaude Sonnet 5.5 586274af4e Add the appliance web UI look, interface names, MCP and REST API, local zone
Web UI: dark/light themes (appliance layout, sidebar, switch checkboxes),
theme preference on the Advanced page, Changes page with the pending diff.

Interface names: router.interfaces.<name>.mac, lans.<l>.deviceName and
pppoe.vlanName; a rename is staged for the next reboot by router-apply.

Remote access: an MCP server (POST /mcp) and an OpenAPI REST API
(/api/v1, /api/openapi.json) with the same tokens, scopes, limits and audit
log; tools to read status, settings, leases, logs (get_logs), and to edit
and check the saved settings (never applied). Tokens on the API access page.

Firewall: a reserved `local` zone (the router itself) as the destination of
rules, so the web UI or SSH can be allowed from the WAN.

PLAN.md lists what is still left.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
2026-11-01 16:56:59 +01:00

82 lines
2.7 KiB
Nix

# Network cards are named by MAC address: the WAN and the LAN card get the
# names wan and lan, and a VLAN on the LAN card gets the name dmz.
{ self, pkgs }:
let
common = import ./common.nix;
# The test driver gives the NIC on VLAN n of machine m the MAC 52:54:00:12:0n:0m.
mac = net: "52:54:00:12:0${toString net}:01";
in
pkgs.testers.runNixOSTest {
name = "router-names";
defaults.imports = [ common.tools ];
interactive.defaults = common.interactiveDefaults;
nodes.router =
{ lib, ... }:
{
imports = [ self.nixosModules.router ];
virtualisation.vlans = [
1
2
];
networking.interfaces.eth1.ipv4.addresses = lib.mkForce [ ];
networking.interfaces.eth2.ipv4.addresses = lib.mkForce [ ];
router = {
enable = true;
interfaces = {
wan.mac = mac 1;
lan.mac = mac 2;
};
wan.interface = "wan";
lans = {
main = {
interface = "lan";
ipv4.address = "192.168.10.1";
dhcp.pool = {
start = "192.168.10.100";
end = "192.168.10.199";
};
};
guests = {
interface = "lan";
vlan = 1002;
deviceName = "dmz";
ipv4.address = "192.168.20.1";
dhcp.pool = {
start = "192.168.20.100";
end = "192.168.20.199";
};
};
};
};
};
testScript = ''
router.start()
router.wait_for_unit("network-online.target")
with subtest("the cards have the names given by MAC address"):
router.succeed("ip -br link show wan | grep -qi ${mac 1}")
router.succeed("ip -br link show lan | grep -qi ${mac 2}")
router.fail("ip link show eth1")
router.fail("ip link show eth2")
with subtest("the VLAN device has the name given to it"):
out = router.succeed("ip -d link show dmz")
assert "vlan protocol 802.1Q id 1002" in out and "lan" in out, out
router.fail("ip link show lan.1002")
router.wait_until_succeeds("ip -4 addr show dev lan | grep -q 192.168.10.1/24")
router.wait_until_succeeds("ip -4 addr show dev dmz | grep -q 192.168.20.1/24")
with subtest("services use the names"):
router.wait_for_unit("nftables.service")
rules = router.succeed("nft list ruleset")
assert '"lan"' in rules and '"dmz"' in rules and "eth2" not in rules, rules
router.wait_for_unit("kea-dhcp4-server.service")
assert sorted(router.succeed("cat /etc/router/dhcp-interfaces").split()) == ["dmz", "lan"], "dhcp interfaces"
router.succeed("grep -q '^nic ${mac 2} lan$' /etc/router/interface-names")
'';
}