Files
infix/test/case/containers/firewall_basic/test.py
T
Joachim Wiberg 1bbd80d8c7 test: drop leading ietf/infix prefix from directories
Let's drop the leading IETF or Infix prefixes from tests.  Initially the
idea was to mimnic the YANG models, but it's difficult to navigate and
does not provide any real benefit to developers or end-users.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-20 20:23:23 +01:00

195 lines
6.7 KiB
Python
Executable File

#!/usr/bin/env python3
r"""Basic Firewall Container
Verify that an nftables container can be used for IP masquerading and
port forwarding to another container.
....
<--- Docker containers --->
.-------------. .----------------------. .--------..---------------.
| | mgmt |------------| mgmt | | | | fire || | web |
| host | data |------------| ext0 | target | int0 | | wall || eth0 | server |
'-------------'.42 .1'----------------------' '--------''---------------'
\ .1 .2 /
192.168.0.0/24 \ 10.0.0.0/24 /
`-- VETH pair --'
....
The web server container is connected to the target on an internal
network, using a VETH pair, serving HTTP on port 91.
The firewall container sets up a port forward with IP masquerding
to/from `ext0:8080` to 10.0.0.2:91.
Operation is verified using HTTP GET requests for internal port 91 and
external port 8080 to ensure the web page, with a known key phrase, is
only reachable from the public interface `ext0`, on 192.168.0.1:8080.
"""
import infamy
from infamy.util import until, to_binary, curl
with infamy.Test() as test:
NFTABLES = f"oci-archive:{infamy.Container.NFTABLES_IMAGE}"
HTTPD = f"oci-archive:{infamy.Container.HTTPD_IMAGE}"
WEBIP = "10.0.0.2"
INTIP = "10.0.0.1"
EXTIP = "192.168.0.1"
OURIP = "192.168.0.42"
WEBNM = "web"
NFTNM = "firewall"
GOOD_URL = f"http://{EXTIP}:8080/index.html"
BAD_URL = f"http://{EXTIP}:91/index.html"
with test.step("Set up topology and attach to target DUT"):
env = infamy.Env()
target = env.attach("target", "mgmt")
_, ext0 = env.ltop.xlate("target", "ext0")
_, hport = env.ltop.xlate("host", "data")
addr = target.get_mgmt_ip()
if not target.has_model("infix-containers"):
test.skip()
with test.step("Set hostname to 'container-host'"):
target.put_config_dict("ietf-system", {
"system": {
"hostname": "container-host"
}
})
with test.step("Create VETH pair for web server container"):
target.put_config_dict("ietf-interfaces", {
"interfaces": {
"interface": [
{
"name": f"{ext0}",
"ipv4": {
"forwarding": True,
"address": [{
"ip": f"{EXTIP}",
"prefix-length": 24
}]
}
},
{
"name": "int0",
"type": "infix-if-type:veth",
"enabled": True,
"infix-interfaces:veth": {
"peer": f"{WEBNM}"
},
"ipv4": {
"forwarding": True,
"address": [{
"ip": f"{INTIP}",
"prefix-length": 24,
}]
}
},
{
"name": f"{WEBNM}",
"type": "infix-if-type:veth",
"enabled": True,
"infix-interfaces:veth": {
"peer": "int0"
},
"ipv4": {
"address": [{
"ip": f"{WEBIP}",
"prefix-length": 24,
}]
},
"container-network": {}
},
]
}
})
with test.step("Create firewall container from bundled OCI image"):
# Store the nftables .conf file contents as a multi-line string
config = to_binary(f"""#!/usr/sbin/nft -f
flush ruleset
define WAN = "{ext0}"
define INT = "int0"
define WIP = "{WEBIP}"
"""
"""
table ip nat {
chain prerouting {
type nat hook prerouting priority 0; policy accept;
iifname $WAN tcp dport 8080 dnat to $WIP:91
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname $WAN masquerade
oifname $INT masquerade
}
}
""")
target.put_config_dict("infix-containers", {
"containers": {
"container": [
{
"name": f"{NFTNM}",
"image": f"{NFTABLES}",
"network": {
"host": True
},
"mount": [
{
"name": "nftables.conf",
"content": config,
"target": "/etc/nftables.conf"
}
],
"privileged": True
}
]
}
})
with test.step("Create web server container from bundled OCI image"):
target.put_config_dict("infix-containers", {
"containers": {
"container": [
{
"name": f"{WEBNM}",
"image": f"{HTTPD}",
"command": "/usr/sbin/httpd -f -v -p 91",
"network": {
"interface": [
{"name": f"{WEBNM}"}
]
}
}
]
}
})
with test.step("Verify firewall container has started"):
c = infamy.Container(target)
until(lambda: c.running(NFTNM), attempts=60)
with test.step("Verify web container has started"):
c = infamy.Container(target)
until(lambda: c.running(WEBNM), attempts=60)
with infamy.IsolatedMacVlan(hport) as ns:
MESG = "tiny web server from the curiOS docker"
ns.addip(OURIP)
with test.step("Verify connectivity, host can reach target:ext0"):
ns.must_reach(EXTIP)
with test.step("Verify 'web' is NOT reachable on http://container-host.local:91"):
until(lambda: not ns.call(lambda: curl(BAD_URL)))
with test.step("Verify 'web' is reachable on http://container-host.local:8080"):
until(lambda: MESG in ns.call(lambda: curl(GOOD_URL)))
test.succeed()