On 8/4/2020 5:24 AM, 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=FORWAR>
> 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 doesn't work. it would result in NO traffic reaching the Docker
chains, since Shorewall lets nothing fall off the end of the FORWARD
chain. And packets only reach the last rule in the FORWARD chain if
there is a configuration error.

> 
> 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.
> 

There was never an intention that Shorewall-generated rules be able to
override those configured by Docker. There was a desire that either
Shorewall or Docker should be able to be restarted without having to
restart the other. Since restarting Docker always results in Docker's
filter rules being first, starting/restarting Shorewall while Docker is
running produces that same result.

I have said a number of times that if I were to run Shorewall and Docker
on one physical system, I would run at least one of them in a virtual
machine. Note that doing so requires that the Shorewall-generated
ruleset make the decision about what traffic to send to Docker, but it
keeps the two products out of each other's hair and insures an
understandable security framework.

Also, if I had it to do over again, I would not implement any Docker
support in Shorewall. I think doing so was a mistake.

-Tom
-- 
Tom Eastep        \ Q: What do you get when you cross a mobster
Shoreline,         \    with an international standard?
Washington, USA     \ A: Someone who makes you an offer you
http://shorewall.org \    can't understand
                      \________________________________________



Attachment: signature.asc
Description: OpenPGP digital signature

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

Reply via email to