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
82 lines
2.7 KiB
Nix
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")
|
|
'';
|
|
}
|