Files
fips/testing/firewall/docker-compose.yml
Johnathan Corgan 70dba3d2f0 Report IPv6 disabled on the TUN device, and test the firewall report and TUN tests with any TUN name
On Linux the check before creating the TUN read only
net.ipv6.conf.all.disable_ipv6. A new interface takes its own
disable_ipv6 from net.ipv6.conf.default, and a reopened persistent TUN
keeps its own, so with default set and all clear the check passed, the
kernel refused the address, and the node ran without a TUN after logging
a netlink "Permission denied". With the ipv6.disable=1 boot parameter
there is no /proc/sys/net/ipv6 at all, the failed read counted as
enabled, and the node logged "Operation not supported" from removing a
leftover route.

Create now refuses a kernel with no /proc/sys/net/ipv6 before making the
device, and after making it reads the device's own disable_ipv6, the
value the kernel acts on. The error says which setting is to blame and
how to clear it. The check on all alone is gone: it refused a TUN whose
own setting had been cleared since, which the kernel accepts.

TunError::Ipv6Disabled now carries the reason as a string, so code
matching the variant needs a pattern for it. On macOS and FreeBSD the
reason names their own sysctls rather than the Linux ones.

The panel's only test of the name it judges the firewall against used
the default, fips0, so a panel that always used fips0, or used tun.name
while the kernel had given the TUN another name, passed it. Two tests now
give it a configured name other than fips0 with no TUN up, and a TUN up
under a name other than the configured one.

Every case in the firewall suite ran with the TUN named fips0, the name
fips.nft ships with. Removing the daemon's startup check of the loaded
table, or judging the listener report against fips0 instead of the TUN's
name, left the suite green.

A third node now names its TUN mesh0 and loads the shipped fips.nft
before its daemon starts. The suite requires the startup warning naming
fips0 and mesh0 exactly once, a listener report saying the table does
not police the TUN, and one saying it does once the file is reloaded
scoped to mesh0. GitHub's failure step lists the third node's table
beside the second's.

The tests that create network namespaces to check TUN startup, its
fd00::/8 policy rule and the IPv6-disabled refusal need root, so they
are ignored and no CI job ran them. Removing the device check from
create, moving it before the device exists, or bringing back the check
on net.ipv6.conf.all alone passed every run.

The Linux unit-test job now builds the library tests as the runner user
and runs these thirteen under sudo, failing unless all thirteen pass.
Local CI still does not run them.
2026-10-28 00:03:36 +00:00

83 lines
3.5 KiB
YAML

networks:
# No subnet is requested: docker assigns one from the daemon's address pool.
# A fixed request is honoured verbatim, so two runs asking for the same range
# collide on "Pool overlaps" — which is why the generated peer addresses are
# docker hostnames (host-a, host-b) rather than literal IPs. Docker's embedded
# DNS is per-network, so the same hostname in two concurrent runs resolves
# inside each run's own subnet.
#
# That removes one of the two obstacles to running this suite twice at once,
# not both. The compose project name is still fixed, so two runs that do not
# set COMPOSE_PROJECT_NAME share a project, and the second `up` recreates the
# first's containers while either `down` removes both. The local CI runner
# scopes it externally (run_firewall in ci-local.sh); a bare hand run does
# not. Do not read the floating subnet as making concurrent bare runs safe.
fw-net:
driver: bridge
labels:
- "com.corganlabs.fips-ci=1"
x-fips-common: &fips-common
build:
# The harness scopes its build context per run and passes it here; the
# shared directory is the hand-run default. Compose resolves a relative
# value against THIS file's directory, so the harness must export an
# absolute path.
context: ${FIPS_BUILD_CONTEXT:-../docker}
image: ${FIPS_TEST_IMAGE:-fips-test:latest}
entrypoint: ["/usr/local/bin/entrypoint.sh"]
cap_add:
- NET_ADMIN
- NET_RAW
devices:
- /dev/net/tun:/dev/net/tun
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
restart: "no"
environment:
- FIPS_TEST_MODE=default
- RUST_LOG=info,fips::node=debug,fips::ipv6tun::icmp=debug,fips::ipv6tun::lifecycle=debug
services:
service-a:
<<: *fips-common
container_name: fips-fw-container-a${FIPS_CI_NAME_SUFFIX:-}
hostname: host-a
volumes:
- ../docker/resolv.conf:/etc/resolv.conf:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-a/hosts:/etc/fips/hosts:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-a/fips.yaml:/etc/fips/fips.yaml:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-a/fips.key:/etc/fips/fips.key:ro
networks:
- fw-net
service-b:
<<: *fips-common
container_name: fips-fw-container-b${FIPS_CI_NAME_SUFFIX:-}
hostname: host-b
volumes:
- ../docker/resolv.conf:/etc/resolv.conf:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-b/hosts:/etc/fips/hosts:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-b/fips.yaml:/etc/fips/fips.yaml:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-b/fips.key:/etc/fips/fips.key:ro
- ../../packaging/common/fips.nft:/etc/fips/fips.nft:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-b/fips.d:/etc/fips/fips.d:ro
networks:
- fw-net
# Case (f): a TUN named mesh0, with the production fips.nft (scoped to
# fips0) loaded before the daemon starts, so that the daemon's startup
# check finds a table policing another interface.
service-c:
<<: *fips-common
container_name: fips-fw-container-c${FIPS_CI_NAME_SUFFIX:-}
hostname: host-c
entrypoint: ["/bin/sh", "-c", "nft -f /etc/fips/fips.nft && exec /usr/local/bin/entrypoint.sh"]
volumes:
- ../docker/resolv.conf:/etc/resolv.conf:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-c/fips.yaml:/etc/fips/fips.yaml:ro
- ../../packaging/common/fips.nft:/etc/fips/fips.nft:ro
- ./generated-configs${FIPS_CI_NAME_SUFFIX:-}/node-c/fips.d:/etc/fips/fips.d:ro
networks:
- fw-net