I am running two Docker containers on the same host living in Docker the same bridge network, and I have nftables set up to restrict more or less all traffic (rules below).
My only problem (that I know of) is that traffic from one container to the other is blocked, if a container uses the public URL of the service provided by the other container.
E.g. service A is an SMTP server which has a public DNS entry mail.acme.com
and service B is an app that wants to send mails to the webserver using this URL. The SMTP server is configured s.t. it is accessible from the outside (i.e. port-forwarding like 0.0.0.0:25:25
in docker compose).
In this particular case, the input interface in nftables is not my external interface, but rather the interface of the Docker network the container is in. The traffic is not routed through FORWARD
, the rules added by Docker don't apply and the traffic ends up in INPUT
where it is dropped.
I don't want to set up individual rules to allow traffic from the Docker interfaces to the allowed ports.
I am looking for a solution that allows the services to use their respective URLs and which is agnostic of ports and interfaces used in the Docker setup. Is this even possible with nftables alone?
The only thing I have come up with so far is to jump from INPUT
to FORWARD
if the input interface is not an external interface, but that feels like I should not do it.
Here are my INPUT
rules:
chain INPUT {
type filter hook input priority filter; policy drop;
ct state vmap { invalid : drop, established : accept, related : accept }
iifname "lo" accept
iifname "externalif0" icmp type echo-request accept
iifname "externalif0" icmpv6 type echo-request accept
}
This is what happens when a container tries to reach another container on the same host:
(I have truncated a bit of the trace output which did not seem too relevant)
trace id 196c1ae6 ip filter trace_chain packet: iif "dockerbr0" ip saddr <containerip> ip daddr <public host ip> tcp sport 33546 tcp dport 443
trace id 196c1ae6 ip nat PREROUTING packet: iif "dockerbr0" ip saddr <containerip> ip daddr <public host ip> tcp sport 33546 tcp dport 443
trace id e7c2ca4b ip filter INPUT packet: iif "dockerbr0" ip saddr <containerip> ip daddr <public host ip> tcp sport 33546 tcp dport 443
And here is outside traffic to the same service:
trace id 29f9da6c ip filter trace_chain packet: iif "externalif0" ip saddr <someip> ip daddr <public host ip> sport 36382 tcp dport 443
trace id 29f9da6c ip nat PREROUTING packet: iif "externalif0" ip saddr <someip> ip daddr <public host ip> sport 36382 tcp dport 443
trace id 29f9da6c ip nat DOCKER rule iifname != "dockerbr0" dport 443 dnat to <dockerip>:443 (verdict accept)
trace id 29f9da6c ip filter FORWARD packet: iif "externalif0" oif "dockerbr0" ip saddr <someip> daddr <containerip> sport 36382 tcp dport 443
trace id 29f9da6c ip filter DOCKER rule iifname != "dockerbr0" oifname "dockerbr0" daddr <containerip> tcp dport 443 accept (verdict accept)
trace id 29f9da6c ip nat POSTROUTING packet: iif "externalif0" oif "dockerbr0" ip saddr <someip> ip daddr <containerip> sport 36382 tcp dport 443
Also on the host:
iptables-legacy -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT