On 8/4/2020 2:24 PM, Preston A. Elder wrote:
> So it's great that shorewall has a DOCKER option. It saves docker's
> rules on restart, and docker can do it's own iptables thing when a new
> docker starts up and ports need to be forwarded, etc. Awesome.
>
> However, the current implementation of DOCKER in shorewall introduces a
> huge security flaw. And it's one essentially mentioned on the DOCKER
> website about iptables use:
>
>> Docker installs two custom iptables chains named DOCKER-USER and
>> DOCKER, and it ensures that incoming packets are always checked by
>> these two chains first.
>
>> All of Docker's iptables rules are added to the DOCKER chain. Do not
>> manipulate this chain manually. If you need to add rules which load
>> before Docker's rules, add them to the DOCKER-USER chain. These rules
>> are applied before any rules Docker creates automatically.
>
>> Rules added to the FORWARD chain -- either manually, or by another
>> iptables-based firewall -- are evaluated after these chains. This
>> means that if you expose a port through Docker, this port gets exposed
>> no matter what rules your firewall has configured. If you want those
>> rules to apply even when a port gets exposed through Docker, you must
>> add these rules to the DOCKER-USER chain.
>
> The crux of this problem is, because shorewall adds it's rules to the
> FORWARD chain after the jumps to DOCKER-USER and DOCKER chains, any
> rules one might have had to drop/reject connections to ports exposed by
> docker are NEVER evaluated - as the connection has already been ACCEPTED.
>
> Example:
>
>> Chain FORWARD (policy DROP 0 packets, 0 bytes)
>> pkts bytes target prot opt in out source
>> destination
>> 1261K 1560M DOCKER-USER all -- * * 0.0.0.0/0
>> 0.0.0.0/0
>> 1261K 1560M DOCKER-ISOLATION-STAGE-1 all -- * *
>> 0.0.0.0/0 0.0.0.0/0
>> 404 464K ACCEPT all -- * br-2cba0ae1535f
>> 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
>> 3 180 DOCKER all -- * br-2cba0ae1535f
>> 0.0.0.0/0 0.0.0.0/0
>> 404 30528 ACCEPT all -- br-2cba0ae1535f !br-2cba0ae1535f
>> 0.0.0.0/0 0.0.0.0/0
>> 0 0 ACCEPT all -- br-2cba0ae1535f br-2cba0ae1535f
>> 0.0.0.0/0 0.0.0.0/0
>> 596K 775M ACCEPT all -- * br-02873a83aa96
>> 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
>> 1754 107K DOCKER all -- * br-02873a83aa96
>> 0.0.0.0/0 0.0.0.0/0
>> 663K 785M ACCEPT all -- br-02873a83aa96 !br-02873a83aa96
>> 0.0.0.0/0 0.0.0.0/0
>> 0 0 ACCEPT all -- br-02873a83aa96 br-02873a83aa96
>> 0.0.0.0/0 0.0.0.0/0
>> 0 0 ACCEPT all -- * docker0 0.0.0.0/0
>> 0.0.0.0/0 ctstate RELATED,ESTABLISHED
>> 0 0 DOCKER all -- * docker0 0.0.0.0/0
>> 0.0.0.0/0
>> 0 0 ACCEPT all -- docker0 !docker0 0.0.0.0/0
>> 0.0.0.0/0
>> 0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0
>> 0.0.0.0/0
>> 0 0 ACCEPT all -- * docker_gwbridge
>> 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
>> 0 0 DOCKER all -- * docker_gwbridge
>> 0.0.0.0/0 0.0.0.0/0
>> 0 0 ACCEPT all -- docker_gwbridge !docker_gwbridge
>> 0.0.0.0/0 0.0.0.0/0
>> 91640 151M eno4_fwd all -- eno4 * 0.0.0.0/0
>> 0.0.0.0/0
>> 89503 74M bond0_fwd all -- bond0 * 0.0.0.0/0
>> 0.0.0.0/0
>> 172K 83M br_fwd all -- br-+ * 0.0.0.0/0
>> 0.0.0.0/0
>> 0 0 dock_frwd all -- docker0 * 0.0.0.0/0
>> 0.0.0.0/0
>> 0 0 DROP all -- * * 0.0.0.0/0
>> 0.0.0.0/0 ADDRTYPE match dst-type BROADCAST
>> 0 0 DROP all -- * * 0.0.0.0/0
>> 0.0.0.0/0 ADDRTYPE match dst-type ANYCAST
>> 0 0 DROP all -- * * 0.0.0.0/0
>> 0.0.0.0/0 ADDRTYPE match dst-type MULTICAST
>> 0 0 LOG all -- * * 0.0.0.0/0
>> 0.0.0.0/0 limit: up to 1/sec burst 10 mode srcip LOG flags
>> 0 level 6 prefix "FORWARD REJECT "
>> 0 0 reject all -- * * 0.0.0.0/0
>> 0.0.0.0/0 [goto]
>> 0 0 DROP all -- docker_gwbridge docker_gwbridge
>> 0.0.0.0/0 0.0.0.0/0
>
> My firewall rules denying connections from eno4 -> docker, or eno4 -> a
> bridge (which is also created by docker, I have more than one docker
> bridge for service network isolation), are not evaluated until you get
> to the last 4 lines above. But that's too late, because:
>
>
>> Chain DOCKER (4 references)
>> pkts bytes target prot opt in out source
>> destination
>> 1 60 ACCEPT tcp -- !br-02873a83aa96 br-02873a83aa96
>> 0.0.0.0/0 172.20.0.2 tcp dpt:9050
>> 0 0 ACCEPT tcp -- !br-02873a83aa96 br-02873a83aa96
>> 0.0.0.0/0 172.20.0.2 tcp dpt:9040
>> 20 1200 ACCEPT tcp -- !br-02873a83aa96 br-02873a83aa96
>> 0.0.0.0/0 172.20.0.2 tcp dpt:9030
>> 1639 99999 ACCEPT tcp -- !br-02873a83aa96 br-02873a83aa96
>> 0.0.0.0/0 172.20.0.2 tcp dpt:9001
>> 3 180 ACCEPT tcp -- !br-2cba0ae1535f br-2cba0ae1535f
>> 0.0.0.0/0 172.21.0.2 tcp dpt:3334
>
>
> So as you can see, if I had wanted to put some kind of firewall rule in
> place limiting the source of WHO can access those ports opened by
> docker, I could not. Worse, if I wanted to do something as simple as
> limiting connections to specific ports to coming from specific
> interfaces (eg. eno4 vs bond0), I could not.
>
> The only viable solution I can think of would be to allow for specifying
> chain names (eg. rather than inserting shorewall rules into the FORWARD
> chain, it would insert into the ${FORWARD_CHAIN} chain from
> shorewall.conf).
>
> This could be a docker-specific thing, namely that, if DOCKER=Yes, then
> the interface-base forwarding rules (and possibly the
> broadcast/multicast drop rules) would be added to the DOCKER-USER chain.
> Though it could be more generic such that you could specify all chains
> in the config:
> FILTER_INPUT_CHAIN=INPUT
> FILTER_OUTPUT_CHAIN=OUTPUT
> FILTER_FORWARD_CHAIN=FORWARD
>
> And then I could modify the FILTER_FORWARD_CHAIN to be DOCKER-USER -
> though the all/all policy rules would have to not go in the
> FILTER_FORWARD_CHAIN, or the docker rules would never be reached.
>
> This is a rather large security hole if you use DOCKER=Yes (and docker)
> and shorewall.
>
> I tried NOT using DOCKER=yes and just using the docker-proxy stuff (ie.
> host binding ports), but docker-proxy has issues and will often enough
> not be able to establish a connection correctly. So using docker's
> iptables support (which creates the appropriate NAT rules to redirect to
> the correct ports) is a much better way to go.
>
An other way to this would be to use Shorewall provided that you use
the '--iptables=false' (1) option.
1)
https://docs.docker.com/engine/reference/commandline/dockerd/#run-multiple-daemons
--
Matt Darfeuille <[email protected]>
Shorewall Project Committee, one of four core members
https://sourceforge.net/p/shorewall/mailman/message/36596609/
https://shorewall.org
_______________________________________________
Shorewall-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/shorewall-users