Add a generic NAT table: port forward, source NAT, masquerade
router.firewall.nat replaces the dnat/toPort/hairpin fields of the filter rules. Filter rules gain natOnly, and may use wan with log and rate limit in the web UI. The Firewall page edits the NAT table and can add the matching allow rule. Settings schema 1 (migrations/1.jq) converts old forwards. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
This commit is contained in:
+170
-33
@@ -609,13 +609,11 @@ let
|
||||
to = "main";
|
||||
destination = "192.168.10.5";
|
||||
protocol = "tcp";
|
||||
ports = [ 8080 ];
|
||||
dnat = true;
|
||||
toPort = 8081;
|
||||
ports = [ 8081 ];
|
||||
natOnly = true;
|
||||
sources = [ "198.51.100.0/24" ];
|
||||
rateLimit = "3/minute";
|
||||
log = true;
|
||||
hairpin = true;
|
||||
}
|
||||
{
|
||||
from = "wan";
|
||||
@@ -685,91 +683,230 @@ let
|
||||
};
|
||||
expect = [ "`wan` cannot be a destination" ];
|
||||
};
|
||||
dnatNeedsHost = {
|
||||
natDnatNeedsHost = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
firewall.rules = [
|
||||
firewall.nat = [
|
||||
{
|
||||
type = "dnat";
|
||||
from = "wan";
|
||||
to = "main";
|
||||
destination = "192.168.10.0/24";
|
||||
protocol = "tcp";
|
||||
ports = [ 8080 ];
|
||||
dnat = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
expect = [ "dnat needs a single IPv4 host" ];
|
||||
expect = [ "dnat and snat need a toAddress" ];
|
||||
};
|
||||
dnatV6Sources = {
|
||||
natDnatHostOutsideZone = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
firewall.rules = [
|
||||
firewall.nat = [
|
||||
{
|
||||
type = "dnat";
|
||||
from = "wan";
|
||||
to = "main";
|
||||
destination = "192.168.10.5";
|
||||
protocol = "tcp";
|
||||
ports = [ 8080 ];
|
||||
dnat = true;
|
||||
toAddress = "10.9.9.9";
|
||||
}
|
||||
];
|
||||
};
|
||||
expect = [ "is not inside any network of zone `main`" ];
|
||||
};
|
||||
natV6Sources = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
firewall.nat = [
|
||||
{
|
||||
type = "dnat";
|
||||
from = "wan";
|
||||
to = "main";
|
||||
protocol = "tcp";
|
||||
ports = [ 8080 ];
|
||||
toAddress = "192.168.10.5";
|
||||
sources = [ "2001:db8::/32" ];
|
||||
}
|
||||
];
|
||||
};
|
||||
expect = [ "its sources must be IPv4" ];
|
||||
expect = [ "translation is IPv4 only" ];
|
||||
};
|
||||
toPortWithoutDnat = {
|
||||
natToPortNeedsOnePort = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
firewall.rules = [
|
||||
firewall.nat = [
|
||||
{
|
||||
type = "dnat";
|
||||
from = "wan";
|
||||
to = "main";
|
||||
destination = "192.168.10.5";
|
||||
protocol = "tcp";
|
||||
ports = [ 8080 ];
|
||||
toPort = 8081;
|
||||
ports = [
|
||||
8080
|
||||
8081
|
||||
];
|
||||
toAddress = "192.168.10.5";
|
||||
toPort = 80;
|
||||
}
|
||||
];
|
||||
};
|
||||
expect = [ "toPort needs dnat" ];
|
||||
expect = [ "toPort needs exactly one" ];
|
||||
};
|
||||
hairpinNoDnat = {
|
||||
natToPortOnMasquerade = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
firewall.rules = [
|
||||
lans = {
|
||||
main = lan { };
|
||||
iot = lan2 { };
|
||||
};
|
||||
firewall.nat = [
|
||||
{
|
||||
from = "wan";
|
||||
type = "masquerade";
|
||||
from = "iot";
|
||||
to = "main";
|
||||
toPort = 80;
|
||||
}
|
||||
];
|
||||
};
|
||||
expect = [ "toPort and hairpin only apply to port forwards" ];
|
||||
};
|
||||
natHairpinFromLan = {
|
||||
router = {
|
||||
lans = {
|
||||
main = lan { internet = true; };
|
||||
iot = lan2 { };
|
||||
};
|
||||
firewall.nat = [
|
||||
{
|
||||
type = "dnat";
|
||||
from = "iot";
|
||||
to = "main";
|
||||
destination = "192.168.10.5";
|
||||
protocol = "tcp";
|
||||
ports = [ 8080 ];
|
||||
toAddress = "192.168.10.5";
|
||||
hairpin = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
expect = [
|
||||
"hairpin only applies to dnat"
|
||||
"hairpin needs at least one"
|
||||
];
|
||||
expect = [ "hairpin only applies to forwards from `wan`" ];
|
||||
};
|
||||
hairpinNoInternet = {
|
||||
natHairpinNoInternet = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
firewall.rules = [
|
||||
firewall.nat = [
|
||||
{
|
||||
type = "dnat";
|
||||
from = "wan";
|
||||
to = "main";
|
||||
destination = "192.168.10.5";
|
||||
protocol = "tcp";
|
||||
ports = [ 8080 ];
|
||||
dnat = true;
|
||||
toAddress = "192.168.10.5";
|
||||
hairpin = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
expect = [ "hairpin needs at least one" ];
|
||||
};
|
||||
natMasqueradeWithAddress = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
firewall.nat = [
|
||||
{
|
||||
type = "masquerade";
|
||||
to = "wan";
|
||||
toAddress = "203.0.113.5";
|
||||
}
|
||||
];
|
||||
};
|
||||
expect = [ "dnat and snat need a toAddress; masquerade has none" ];
|
||||
};
|
||||
natUnknownZones = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
firewall.nat = [
|
||||
{
|
||||
type = "snat";
|
||||
from = "ghost";
|
||||
to = "nowhere";
|
||||
toAddress = "203.0.113.5";
|
||||
}
|
||||
];
|
||||
};
|
||||
expect = [
|
||||
"unknown source zone `ghost`"
|
||||
"unknown zone `nowhere`"
|
||||
];
|
||||
};
|
||||
natDnatToWan = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
firewall.nat = [
|
||||
{
|
||||
type = "dnat";
|
||||
from = "main";
|
||||
to = "wan";
|
||||
protocol = "tcp";
|
||||
ports = [ 80 ];
|
||||
toAddress = "192.168.10.5";
|
||||
}
|
||||
];
|
||||
};
|
||||
expect = [ "unknown zone `wan`" ];
|
||||
};
|
||||
natValid = {
|
||||
router = {
|
||||
lans = {
|
||||
main = lan { internet = true; };
|
||||
iot = lan2 { };
|
||||
};
|
||||
firewall = {
|
||||
rules = [
|
||||
{
|
||||
from = "wan";
|
||||
to = "main";
|
||||
destination = "192.168.10.5";
|
||||
protocol = "tcp";
|
||||
ports = [ 8081 ];
|
||||
natOnly = true;
|
||||
rateLimit = "3/minute";
|
||||
log = true;
|
||||
}
|
||||
];
|
||||
nat = [
|
||||
{
|
||||
type = "dnat";
|
||||
from = "wan";
|
||||
to = "main";
|
||||
protocol = "tcp";
|
||||
ports = [ 8080 ];
|
||||
sources = [ "198.51.100.0/24" ];
|
||||
toAddress = "192.168.10.5";
|
||||
toPort = 8081;
|
||||
hairpin = true;
|
||||
}
|
||||
{
|
||||
type = "dnat";
|
||||
from = "iot";
|
||||
to = "main";
|
||||
protocol = "udp";
|
||||
ports = [ "5000-5100" ];
|
||||
destination = "192.168.20.1";
|
||||
toAddress = "192.168.10.6";
|
||||
}
|
||||
{
|
||||
type = "masquerade";
|
||||
from = "iot";
|
||||
to = "main";
|
||||
}
|
||||
{
|
||||
type = "snat";
|
||||
to = "wan";
|
||||
sources = [ "192.168.10.0/24" ];
|
||||
toAddress = "203.0.113.5";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
expect = [ ];
|
||||
};
|
||||
badPortRange = {
|
||||
router = {
|
||||
lans.main = lan { };
|
||||
|
||||
Reference in New Issue
Block a user