mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-02 05:43:02 +02:00
test: new test, IPv6 version of lan-wan firewall
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -12,3 +12,7 @@ include::lan-wan/Readme.adoc[]
|
||||
<<<
|
||||
|
||||
include::wan-dmz-lan/Readme.adoc[]
|
||||
|
||||
<<<
|
||||
|
||||
include::ipv6-lan-wan/Readme.adoc[]
|
||||
|
||||
@@ -7,3 +7,6 @@
|
||||
|
||||
- name: WAN-DMZ-LAN Firewall with Port Forwarding
|
||||
case: wan-dmz-lan/test.py
|
||||
|
||||
- name: IPv6 LAN-WAN Firewall
|
||||
case: ipv6-lan-wan/test.py
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
test.adoc
|
||||
@@ -0,0 +1 @@
|
||||
../lan-wan/lan-wan.svg
|
||||
@@ -0,0 +1,31 @@
|
||||
=== IPv6 LAN-WAN Firewall
|
||||
|
||||
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/infix_firewall/ipv6-lan-wan]
|
||||
|
||||
==== Description
|
||||
|
||||
IPv6 version of the typical home/office router scenario where the DUT acts as
|
||||
a gateway with LAN-to-WAN traffic forwarding and IPv6 prefix delegation.
|
||||
|
||||
image::lan-wan.svg[align=center, scaledwidth=50%]
|
||||
|
||||
- DUT/Gateway with IPv6 firewall and forwarding
|
||||
- Test host has two interfaces: a LAN-side and a WAN-side (Internet)
|
||||
- Test host's LAN interface acts as an IPv6 client behind the router
|
||||
- Test host's WAN interface acts as an IPv6 Internet server/destination
|
||||
- Demonstrates IPv6 policy-based forwarding between zones
|
||||
|
||||
==== Topology
|
||||
|
||||
image::topology.svg[IPv6 LAN-WAN Firewall topology, align=center, scaledwidth=75%]
|
||||
|
||||
==== Sequence
|
||||
|
||||
. Set up topology and attach to gateway
|
||||
. Configure gateway with firewall and forwarding
|
||||
. Test connectivity to gateway
|
||||
. Test LAN-to-WAN forwarding
|
||||
. Test WAN-to-LAN blocking
|
||||
. Verify LAN services accessibility
|
||||
|
||||
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env python3
|
||||
"""IPv6 LAN-WAN Firewall
|
||||
|
||||
IPv6 version of the typical home/office router scenario where the DUT acts as
|
||||
a gateway with LAN-to-WAN traffic forwarding and IPv6 prefix delegation.
|
||||
|
||||
image::lan-wan.svg[align=center, scaledwidth=50%]
|
||||
|
||||
- DUT/Gateway with IPv6 firewall and forwarding
|
||||
- Test host has two interfaces: a LAN-side and a WAN-side (Internet)
|
||||
- Test host's LAN interface acts as an IPv6 client behind the router
|
||||
- Test host's WAN interface acts as an IPv6 Internet server/destination
|
||||
- Demonstrates IPv6 policy-based forwarding between zones
|
||||
"""
|
||||
|
||||
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 = "fd01:db8:1::/64"
|
||||
LAN_ROUTER_IP = "fd01:db8:1::1" # Router's LAN interface
|
||||
LAN_CLIENT_IP = "fd01:db8:1::100" # Client on LAN side
|
||||
|
||||
WAN_NET = "2001:db8:2::/64" # RFC 3849 documentation prefix
|
||||
WAN_ROUTER_IP = "2001:db8:2::1" # Router's WAN interface
|
||||
WAN_SERVER_IP = "2001:db8:2::100" # Server on WAN side
|
||||
|
||||
with test.step("Configure gateway with firewall and forwarding"):
|
||||
gateway.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": lan_if,
|
||||
"enabled": True,
|
||||
"ipv6": {
|
||||
"enabled": True,
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": LAN_ROUTER_IP,
|
||||
"prefix-length": 64
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": wan_if,
|
||||
"enabled": True,
|
||||
"ipv6": {
|
||||
"enabled": True,
|
||||
"forwarding": True,
|
||||
"address": [{
|
||||
"ip": WAN_ROUTER_IP,
|
||||
"prefix-length": 64
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"infix-firewall": {
|
||||
"firewall": {
|
||||
"default": "wan",
|
||||
"logging": "all",
|
||||
"zone": [{
|
||||
"name": "lan",
|
||||
"description": "Internal LAN network - trusted",
|
||||
"action": "accept",
|
||||
"interface": [lan_if, mgmt_if],
|
||||
"service": ["ssh", "dhcpv6", "dns"]
|
||||
}, {
|
||||
"name": "wan",
|
||||
"description": "External WAN interface - untrusted",
|
||||
"action": "drop",
|
||||
"interface": [wan_if]
|
||||
}],
|
||||
"policy": [{
|
||||
"name": "lan-to-wan",
|
||||
"description": "Allow LAN to WAN traffic",
|
||||
"ingress": ["lan"],
|
||||
"egress": ["wan"],
|
||||
"action": "accept"
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
# 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 exists
|
||||
policies = {p["name"]: p for p in fw.get("policy", [])}
|
||||
assert "lan-to-wan" in policies
|
||||
policy = policies["lan-to-wan"]
|
||||
assert "lan" in policy["ingress"]
|
||||
assert "wan" in policy["egress"]
|
||||
assert policy["action"] == "accept"
|
||||
|
||||
with infamy.IsolatedMacVlan(host_lan) as lan_ns:
|
||||
lan_ns.addip(LAN_CLIENT_IP, prefix_length=64, proto="ipv6")
|
||||
lan_ns.addroute("default", LAN_ROUTER_IP, proto="ipv6")
|
||||
|
||||
with infamy.IsolatedMacVlan(host_wan) as wan_ns:
|
||||
wan_ns.addip(WAN_SERVER_IP, prefix_length=64, proto="ipv6")
|
||||
wan_ns.addroute("default", WAN_ROUTER_IP, proto="ipv6")
|
||||
|
||||
with test.step("Test connectivity to gateway"):
|
||||
lan_ns.must_reach(LAN_ROUTER_IP, timeout=5)
|
||||
wan_ns.must_not_reach(WAN_ROUTER_IP, timeout=5)
|
||||
|
||||
with test.step("Test LAN-to-WAN forwarding"):
|
||||
lan_ns.must_reach(WAN_SERVER_IP, timeout=10)
|
||||
|
||||
with test.step("Test WAN-to-LAN blocking"):
|
||||
wan_ns.must_not_reach(LAN_CLIENT_IP, timeout=5)
|
||||
|
||||
with test.step("Verify LAN services accessibility"):
|
||||
firewall_lan = infamy.Firewall(lan_ns, None)
|
||||
svc = [
|
||||
(22, "tcp", "ssh"),
|
||||
(53, "tcp", "dns")
|
||||
]
|
||||
|
||||
ok, ports = firewall_lan.verify_allowed(LAN_ROUTER_IP, svc)
|
||||
if not ok:
|
||||
print(f" ⚠ Some LAN services are filtered: {', '.join(ports)}")
|
||||
test.fail()
|
||||
|
||||
test.succeed()
|
||||
@@ -0,0 +1,24 @@
|
||||
graph "lan-wan-v6" {
|
||||
layout = "neato";
|
||||
overlap = false;
|
||||
esep = "+80";
|
||||
|
||||
node [shape=record, fontname="DejaVu Sans Mono, Book"];
|
||||
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
|
||||
|
||||
host [
|
||||
label="host | { <mgmt> mgmt | <lan> lan | <wan> wan }",
|
||||
pos="1,1!",
|
||||
requires="controller"
|
||||
];
|
||||
|
||||
gateway [
|
||||
label="{ <mgmt> mgmt | <lan> lan | <wan> wan } | gateway",
|
||||
pos="3,1!",
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
host:mgmt -- gateway:mgmt [requires="mgmt", color="lightgray"]
|
||||
host:lan -- gateway:lan [color=green, fontcolor=green, taillabel="fd01:db8:1::/64"]
|
||||
host:wan -- gateway:wan [color=red, fontcolor=red, taillabel="2001:db8:2::/64"]
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Title: lan-wan-v6 Pages: 1 -->
|
||||
<svg width="432pt" height="78pt"
|
||||
viewBox="0.00 0.00 432.03 78.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 74)">
|
||||
<title>lan-wan-v6</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-74 428.03,-74 428.03,4 -4,4"/>
|
||||
<!-- host -->
|
||||
<g id="node1" class="node">
|
||||
<title>host</title>
|
||||
<polygon fill="none" stroke="black" points="0,-0.5 0,-69.5 100,-69.5 100,-0.5 0,-0.5"/>
|
||||
<text text-anchor="middle" x="25" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
|
||||
<polyline fill="none" stroke="black" points="50,-0.5 50,-69.5 "/>
|
||||
<text text-anchor="middle" x="75" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="50,-46.5 100,-46.5 "/>
|
||||
<text text-anchor="middle" x="75" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">lan</text>
|
||||
<polyline fill="none" stroke="black" points="50,-23.5 100,-23.5 "/>
|
||||
<text text-anchor="middle" x="75" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">wan</text>
|
||||
</g>
|
||||
<!-- gateway -->
|
||||
<g id="node2" class="node">
|
||||
<title>gateway</title>
|
||||
<polygon fill="none" stroke="black" points="300.03,-0.5 300.03,-69.5 424.03,-69.5 424.03,-0.5 300.03,-0.5"/>
|
||||
<text text-anchor="middle" x="325.03" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="300.03,-46.5 350.03,-46.5 "/>
|
||||
<text text-anchor="middle" x="325.03" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">lan</text>
|
||||
<polyline fill="none" stroke="black" points="300.03,-23.5 350.03,-23.5 "/>
|
||||
<text text-anchor="middle" x="325.03" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">wan</text>
|
||||
<polyline fill="none" stroke="black" points="350.03,-0.5 350.03,-69.5 "/>
|
||||
<text text-anchor="middle" x="387.03" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">gateway</text>
|
||||
</g>
|
||||
<!-- host--gateway -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>host:mgmt--gateway:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M100,-58C100,-58 300.03,-58 300.03,-58"/>
|
||||
</g>
|
||||
<!-- host--gateway -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>host:lan--gateway:lan</title>
|
||||
<path fill="none" stroke="green" stroke-width="2" d="M100,-35C100,-35 300.03,-35 300.03,-35"/>
|
||||
<text text-anchor="middle" x="154.5" y="-38.8" font-family="DejaVu Serif, Book" font-size="14.00" fill="green">fd01:db8:1::/64</text>
|
||||
</g>
|
||||
<!-- host--gateway -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>host:wan--gateway:wan</title>
|
||||
<path fill="none" stroke="red" stroke-width="2" d="M100,-12C100,-12 300.03,-12 300.03,-12"/>
|
||||
<text text-anchor="middle" x="156.5" y="-15.8" font-family="DejaVu Serif, Book" font-size="14.00" fill="red">2001:db8:2::/64</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -60,8 +60,12 @@ class PortScanner:
|
||||
else:
|
||||
raise ValueError(f"Unsupported protocol: {protocol}")
|
||||
|
||||
cmd = f"nmap -n {proto} -Pn -p {port} --host-timeout={timeout} " \
|
||||
f"--min-rate=1000 --max-retries=1 --disable-arp-ping {host}"
|
||||
in6 = ""
|
||||
if ":" in host:
|
||||
in6 = "-6"
|
||||
|
||||
cmd = f"nmap -n {proto} {in6} -Pn -p {port} --host-timeout={timeout}" \
|
||||
f" --min-rate=1000 --max-retries=1 --disable-arp-ping {host}"
|
||||
result = self.netns.runsh(cmd)
|
||||
|
||||
# Parse nmap output to determine port state
|
||||
|
||||
Reference in New Issue
Block a user