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:
@@ -227,6 +227,64 @@ let
|
||||
};
|
||||
expect = [ "WAN interface `eth0` is also used by a LAN" ];
|
||||
};
|
||||
namesValid = {
|
||||
router = {
|
||||
interfaces = {
|
||||
wan.mac = "52:54:00:12:34:56";
|
||||
lan.mac = "52:54:00:12:34:57";
|
||||
};
|
||||
wan.interface = "wan";
|
||||
lans = {
|
||||
main = lan { interface = "lan"; };
|
||||
dmz = lan2 {
|
||||
interface = "lan";
|
||||
vlan = 1002;
|
||||
deviceName = "dmz";
|
||||
};
|
||||
};
|
||||
};
|
||||
expect = [ ];
|
||||
};
|
||||
nameKernelLike = {
|
||||
router.lans.main = lan { };
|
||||
router.interfaces = {
|
||||
eth7.mac = "52:54:00:12:34:56";
|
||||
wlp1s0.mac = "52:54:00:12:34:57";
|
||||
};
|
||||
expect = [ "do not look like a kernel name" ];
|
||||
};
|
||||
nameTooLong = {
|
||||
router.lans.main = lan { };
|
||||
router.interfaces.a-very-long-interface-name.mac = "52:54:00:12:34:56";
|
||||
expect = [ "at most 15 characters" ];
|
||||
};
|
||||
sameMacTwice = {
|
||||
router.lans.main = lan { };
|
||||
router.interfaces = {
|
||||
wan.mac = "52:54:00:12:34:56";
|
||||
lan.mac = "52:54:00:12:34:56";
|
||||
};
|
||||
expect = [ "same MAC address" ];
|
||||
};
|
||||
vlanNameClash = {
|
||||
router = {
|
||||
interfaces.dmz.mac = "52:54:00:12:34:56";
|
||||
lans.a = lan {
|
||||
vlan = 20;
|
||||
deviceName = "dmz";
|
||||
};
|
||||
};
|
||||
expect = [ "used twice among `interfaces`" ];
|
||||
};
|
||||
deviceNameWithoutVlan = {
|
||||
router.lans.main = lan { deviceName = "dmz"; };
|
||||
expect = [ "deviceName needs a vlan" ];
|
||||
};
|
||||
pppoeVlanNameWithoutVlan = {
|
||||
router.lans.main = lan { };
|
||||
router.wan.ipv4.pppoe.vlanName = "isp";
|
||||
expect = [ "vlanName needs pppoe.vlan" ];
|
||||
};
|
||||
deviceNameTooLong = {
|
||||
router.lans.main = lan {
|
||||
interface = "enp1s0f1np1";
|
||||
@@ -764,6 +822,62 @@ let
|
||||
};
|
||||
expect = [ ];
|
||||
};
|
||||
localValid = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
firewall.rules = [
|
||||
{
|
||||
from = "wan";
|
||||
to = "local";
|
||||
protocol = "tcp";
|
||||
ports = [ 22 ];
|
||||
sources = [ "198.51.100.0/24" ];
|
||||
rateLimit = "5/minute";
|
||||
}
|
||||
{
|
||||
from = "main";
|
||||
to = "local";
|
||||
protocol = "tcp";
|
||||
ports = [ 8443 ];
|
||||
}
|
||||
];
|
||||
};
|
||||
expect = [ ];
|
||||
};
|
||||
localNatOnly = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
firewall.rules = [
|
||||
{
|
||||
from = "wan";
|
||||
to = "local";
|
||||
protocol = "tcp";
|
||||
ports = [ 22 ];
|
||||
natOnly = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
expect = [ "natOnly does not apply to the router itself" ];
|
||||
};
|
||||
localNoPorts = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
firewall.rules = [
|
||||
{
|
||||
from = "wan";
|
||||
to = "local";
|
||||
protocol = "tcp";
|
||||
}
|
||||
];
|
||||
};
|
||||
expect = [ "must list the ports they open" ];
|
||||
};
|
||||
localZoneName = {
|
||||
router = {
|
||||
lans.main = lan { zone = "local"; };
|
||||
};
|
||||
expect = [ "`local` is a reserved zone name" ];
|
||||
};
|
||||
wanNoDestination = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
@@ -1446,6 +1560,7 @@ let
|
||||
"pdOnOneLan"
|
||||
"pppoeValid"
|
||||
"vlansOnOneTrunk"
|
||||
"namesValid"
|
||||
"ruleValid"
|
||||
"wgValid"
|
||||
"wanValid"
|
||||
|
||||
Reference in New Issue
Block a user