mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-30 12:33:02 +02:00
178 lines
6.7 KiB
Python
Executable File
178 lines
6.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""LAN-WAN Firewall with Masquerading
|
|
|
|
Typical home/office router scenario where the DUT acts as a gateway with
|
|
LAN-to-WAN traffic forwarding and masquerading (SNAT).
|
|
|
|
image::lan-wan.svg[align=center, scaledwidth=50%]
|
|
|
|
- DUT/Gateway with firewall and NAT
|
|
- Test host has two interfaces: a LAN-side and a WAN-side (Internet)
|
|
- Test host's LAN interface acts as a client behind the router
|
|
- Test host's WAN interface acts as an Internet server/destination
|
|
"""
|
|
|
|
import time
|
|
import infamy
|
|
from infamy.util import until
|
|
|
|
|
|
with infamy.Test() as test:
|
|
with test.step("Set up topology and attach to gateway"):
|
|
env = infamy.Env()
|
|
gateway = env.attach("gateway", "mgmt")
|
|
_, lan_if = env.ltop.xlate("gateway", "lan")
|
|
_, wan_if = env.ltop.xlate("gateway", "wan")
|
|
_, mgmt_if = env.ltop.xlate("gateway", "mgmt")
|
|
_, host_lan = env.ltop.xlate("host", "lan") # Host LAN-side interface
|
|
_, host_wan = env.ltop.xlate("host", "wan") # Host WAN-side interface
|
|
|
|
LAN_NET = "192.168.1.0/24"
|
|
LAN_ROUTER_IP = "192.168.1.1" # Router's LAN interface
|
|
LAN_CLIENT_IP = "192.168.1.100" # Client on LAN side
|
|
|
|
WAN_NET = "203.0.113.0/24" # RFC 5737 test network
|
|
WAN_ROUTER_IP = "203.0.113.1" # Router's WAN interface
|
|
WAN_SERVER_IP = "203.0.113.100" # Server on WAN side
|
|
|
|
with test.step("Configure gateway with firewall and SNAT"):
|
|
gateway.put_config_dicts({
|
|
"ietf-interfaces": {
|
|
"interfaces": {
|
|
"interface": [
|
|
{
|
|
"name": lan_if,
|
|
"enabled": True,
|
|
"ipv4": {
|
|
"forwarding": True,
|
|
"address": [{
|
|
"ip": LAN_ROUTER_IP,
|
|
"prefix-length": 24
|
|
}]
|
|
}
|
|
},
|
|
{
|
|
"name": wan_if,
|
|
"enabled": True,
|
|
"ipv4": {
|
|
"forwarding": True,
|
|
"address": [{
|
|
"ip": WAN_ROUTER_IP,
|
|
"prefix-length": 24
|
|
}]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"infix-firewall": {
|
|
"firewall": {
|
|
"default": "wan",
|
|
"logging": "all",
|
|
"zone": [
|
|
{
|
|
"name": "lan",
|
|
"description": "Internal LAN network - trusted",
|
|
"action": "accept",
|
|
"interface": [lan_if, mgmt_if],
|
|
"service": ["ssh", "dhcp", "dns"]
|
|
}, {
|
|
"name": "wan",
|
|
"description": "External WAN interface - untrusted",
|
|
"action": "drop",
|
|
"interface": [wan_if]
|
|
}
|
|
],
|
|
"policy": [
|
|
{
|
|
"name": "lan-to-wan",
|
|
"description": "Allow LAN to WAN traffic with SNAT",
|
|
"ingress": ["lan"],
|
|
"egress": ["wan"],
|
|
"action": "accept",
|
|
"masquerade": True
|
|
}
|
|
]
|
|
}
|
|
}
|
|
})
|
|
|
|
# Wait for configuration to be activated
|
|
infamy.Firewall.wait_for_operational(gateway, {
|
|
"lan": {"action": "accept"},
|
|
"wan": {"action": "drop"}
|
|
})
|
|
|
|
# Verify firewall operational state
|
|
data = gateway.get_data("/infix-firewall:firewall")
|
|
fw = data["firewall"]
|
|
zones = {z["name"]: z for z in fw["zone"]}
|
|
|
|
# Verify LAN zone
|
|
lan_zone = zones["lan"]
|
|
assert lan_zone["action"] == "accept"
|
|
assert lan_if in lan_zone["interface"]
|
|
|
|
# Verify WAN zone
|
|
wan_zone = zones["wan"]
|
|
assert wan_zone["action"] == "drop"
|
|
assert wan_if in wan_zone["interface"]
|
|
|
|
# Verify policy
|
|
policies = {p["name"]: p for p in fw["policy"]}
|
|
lan_wan_policy = policies["lan-to-wan"]
|
|
assert lan_wan_policy["ingress"] == ["lan"]
|
|
assert lan_wan_policy["egress"] == ["wan"]
|
|
assert lan_wan_policy["action"] == "accept"
|
|
assert lan_wan_policy["masquerade"] is True
|
|
|
|
with infamy.IsolatedMacVlan(host_lan) as lan_client:
|
|
lan_client.addip(LAN_CLIENT_IP)
|
|
lan_client.addroute("0.0.0.0", LAN_ROUTER_IP, prefix_length="0")
|
|
|
|
with infamy.IsolatedMacVlan(host_wan) as wan_server:
|
|
wan_server.addip(WAN_SERVER_IP)
|
|
|
|
with test.step("Verify LAN access to router"):
|
|
lan_client.must_reach(LAN_ROUTER_IP, timeout=3)
|
|
|
|
with test.step("Verify LAN services accessibility"):
|
|
firewall = infamy.Firewall(lan_client, None)
|
|
svc = [
|
|
(22, "tcp", "ssh"),
|
|
(53, "udp", "dns"),
|
|
(67, "udp", "dhcp"),
|
|
]
|
|
|
|
ok, ports = firewall.verify_allowed(LAN_ROUTER_IP, svc)
|
|
if not ok:
|
|
print(f" ⚠ Some LAN services are filtered: {', '.join(ports)}")
|
|
test.fail()
|
|
|
|
with test.step("Verify WAN access to router is blocked"):
|
|
wan_server.must_not_reach(WAN_ROUTER_IP, timeout=3)
|
|
|
|
with test.step("Verify WAN blocks all well-known ports"):
|
|
firewall = infamy.Firewall(wan_server, None)
|
|
|
|
ok, ports, _ = firewall.verify_blocked(WAN_ROUTER_IP)
|
|
if not ok:
|
|
print(f" ⚠ Some ports are unexpectedly open from WAN: {', '.join(ports)}")
|
|
test.fail()
|
|
|
|
with test.step("Verify LAN-to-WAN connectivity (outbound)"):
|
|
lan_client.must_reach(WAN_SERVER_IP, timeout=3)
|
|
|
|
with test.step("Verify LAN-to-WAN masquerading"):
|
|
firewall = infamy.Firewall(lan_client, wan_server)
|
|
|
|
ok, info = firewall.verify_snat(WAN_SERVER_IP, WAN_ROUTER_IP)
|
|
if not ok:
|
|
print(f" ⚠ {info}")
|
|
test.fail()
|
|
|
|
with test.step("Verify WAN-to-LAN blocking (inbound)"):
|
|
wan_server.must_not_reach(LAN_CLIENT_IP, timeout=3)
|
|
|
|
test.succeed()
|