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
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# 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")
|
||||
'';
|
||||
}
|
||||
Reference in New Issue
Block a user