Commit Graph
2 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5.5 d6c207f395 fix(svc-swarm-node): reload the ingress guard without a role include
Every swarm deploy of PR #1114 stopped at the sys-svc-compose handler that
reloads the guard after `docker stack deploy`:

    [ERROR]: Using 'ansible.builtin.include_role' as a handler is not supported.
    Origin: roles/svc-swarm-node/tasks/utils/ingress_guard/refresh.yml:9:3

The handler includes refresh.yml, and ansible-core refuses a role include
anywhere below a handler (playbook/helpers.py: `use_handlers` propagates
into included task files). The join path ran the same file as a plain task
and passed, which is why the guard loaded before the join and failed only
after the first stack deploy (CI run 39754950746: SuiteCRM#2 on CentOS and
the workspace job's MariaDB swarm drill on Fedora).

refresh.yml now includes svc-net-firewall's 02_fragment.yml as a task file.
The fragment's naming (directory, source, file, table) lives in the role's
vars/main.yml, which a task include does not load, so refresh.yml reads that
file through include_vars under its own name and hands the four values to the
include. The vars file stays the single definition, and the namespaced load
cannot shadow FIREWALL_FRAGMENT_ROLE for a later include of the role.

Refs #1089

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 18:39:25 +02:00
kevinveenbirkenbachandClaude Opus 5.5 788eabffbf fix(svc-swarm-node): drop outside traffic to non-public routing-mesh ports
In swarm mode `docker stack deploy` ignores the host IP of a short port
mapping, so every `127.0.0.1:<port>:<port>` that container_ports renders for
a `ports.local` entry is published by the ingress routing mesh on every
node, via docker-proxy on 0.0.0.0 and [::] plus DNAT in nat PREROUTING.
Databases, LDAP and application backends answered from outside the cluster.

svc-swarm-node now loads an nftables table through svc-net-firewall. Its
prerouting filter chain (priority mangle, ahead of Docker's dstnat) drops
packets addressed to the node for any ingress-published port that no
application declares under `ports.public`. Loopback, docker0,
docker_gwbridge, the user bridges and the swarm nodes' own addresses stay
admitted. Host-side provisioning over 127.0.0.1, containers that reach a
host port through host-gateway, and node-to-node consumers such as the
registry-cache mirror keep working.

Both sets are read live on the manager (`docker service inspect` for
`.Endpoint.Ports`, `docker node inspect` for `.Status.Addr`), so ephemeral
ingress ports and nodes outside `--limit` are covered. The table is loaded
on a node before it joins, reloaded on every node after the join, and
reloaded after each stack deploy by the sys-svc-compose swarm handler.

Refs #1089

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 20:56:29 +02:00