Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker stack creates permissive iptables port filtering rules on exposed ports #38259

Open
jmcfadyen-ge opened this issue Nov 23, 2018 · 8 comments

Comments

@jmcfadyen-ge
Copy link

jmcfadyen-ge commented Nov 23, 2018

Description

When a container is brought up with an exposed port via docker stack, an iptables rule is created to grant access to the port. The firewall rule created is an anywhere to anywhere over the exposed port number.

This opens up the same port to every interface that can be routed to through the root namespace.

This issue seems to be the same as #14041, but with docker stack.

Steps to reproduce the issue:

  1. Run docker stack deploy -c docker-compose.yml test with the following compose file:
version: "3.0"
services:
  container:
    image: alpine
    ports:
      - 1234:1234
    entrypoint: 'sleep 1000'
  1. Run iptables -nL and see that the DOCKER-INGRESS chain opens the port from anywhere to anywhere.

Describe the results you received:

# iptables --verbose -nL DOCKER-INGRESS
Chain DOCKER-INGRESS (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:1234
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED tcp spt:1234
   18  1563 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0

Describe the results you expected:
Performing the same function with: docker run --rm -p 1234:1234 alpine sleep 1000
The following rule is created in the more restricted DOCKER chain with a specific destination:

# iptables --verbose -nL DOCKER
Chain DOCKER (7 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     tcp  --  !docker0 docker0  0.0.0.0/0            172.17.0.2           tcp dpt:1234

Output of docker version:

Client:
 Version:           18.09.0-ce
 API version:       1.39
 Go version:        go1.11.2
 Git commit:        4d60db472b
 Built:             Fri Nov  9 00:05:34 2018
 OS/Arch:           linux/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.09.0-ce
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.11.2
  Git commit:       4d60db472b
  Built:            Fri Nov  9 00:05:11 2018
  OS/Arch:          linux/amd64
  Experimental:     false

Output of docker info:

Containers: 23
 Running: 0
 Paused: 0
 Stopped: 23
Images: 66
Server Version: 18.09.0-ce
Storage Driver: overlay2
 Backing Filesystem: extfs
 Supports d_type: true
 Native Overlay Diff: false
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
 Volume: local
 Network: bridge host macvlan null overlay
 Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 9f2e07b1fc1342d1c48fe4d7bbb94cb6d1bf278b.m
runc version: 079817cc26ec5292ac375bb9f47f373d33574949
init version: fec3683
Security Options:
 seccomp
  Profile: default
Kernel Version: 4.19.2-arch1-1-ARCH
Operating System: Arch Linux
OSType: linux
Architecture: x86_64
CPUs: 12
Total Memory: 31.33GiB
Name: 
ID: HJMW:HCW4:O5BQ:3TUD:BR77:M26H:3F4C:LFFY:EBAU:XHOE:MURA:QEZC
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
 127.0.0.0/8
Live Restore Enabled: false
@cpuguy83
Copy link
Member

This is working as expected. In the spec you asked docker to expose the port, so the ingress rule is created.

The "docker run" rule is effectively the same as anywhere to anywhere, the main difference being that there is a special rule to handle traffic originating from the docker0 bridge interface (due to past issues with hairpin nat).

Why do you say this is related to the issue on the forwarding policy (which is no longer an issue)?

@jmcfadyen-ge
Copy link
Author

It seems related because it involves similar problems with iptables.

I understand that the rule allows the desired behaviour, but it is too permissive, which could have security implications.
For example, let's say there's a stack with two services.
One service was a reverse web proxy with an exposed port on 80 forwarding to another container hosting a web application also on port 80. A third container could access the webapp directly through the docker_gwbridge interface when it was not meant to be accessible.

It doesn't seem like desired behaviuor to me as there is an explicit drop of docker_gwbridge to docker_gwbridge traffic at the bottom of the rulebase.

@cpuguy83
Copy link
Member

So your issue is that setting up ingress is not granular enough?

@jmcfadyen-ge
Copy link
Author

Yes

@justinclift
Copy link

Seems like a duplicate of #22054?

@jmcfadyen-ge
Copy link
Author

It looks different to me.

#22054 is the stock execution path, this issue seems specific to docker stack.

From what I gather, the other issue is concerned that exposed ports are accessible on all network interfaces, rather than local interfaces. The concern in this issue is that other targets behind a docker host will be accessible other than the container that sets up the exposed port.

@justinclift
Copy link

No worries. 😄

@BenjamenMeyer
Copy link

@jmcfadyen-ge I think they may be closely related. Solve #22054 and you'll likely solve this one too. That said, I'm not sure the docker folks are really looking to solve the issue since I reported that one back in 2016 and it's been over 3 years with no real resolution. Hopefully this will bring some additional light to the issue and help get a proper solution in.

Yes, there's been a couple solutions offered, but they're not really solutions since they require the user to be intimately familiar with Iptables instead of having docker just do the right thing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants