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 <m...@shorewall.org>
Shorewall Project Committee, one of four core members
https://sourceforge.net/p/shorewall/mailman/message/36596609/
https://shorewall.org


_______________________________________________
Shorewall-users mailing list
Shorewall-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/shorewall-users

Reply via email to