rootless-containers / slirp4netns

User-mode networking for unprivileged network namespaces
GNU General Public License v2.0
729 stars 82 forks source link

Question: Expose container port only to localhost #261

Closed MarkErik closed 3 years ago

MarkErik commented 3 years ago

I am using rootless Docker with 1.1.9 slirp4netns and I have a container (Traefik) that has a service running at port 8080 which I would like to expose and limit access only to the host machine, ideally at 127.0.0.1:8080.

I am writing a script to access the service, and the best option is to have a known address where I can access it (hence 127.0.0.1:8080).

I understand that with rootless Docker how the network operates is different, so if it is not possible to link a port to the localhost, then the next option is to access the container by its docker bridge network IP address (which also hasn't worked for me).

Here are different options I have tried: In my docker-compose:

    ports:
      - "80:80"
      - "443:443"
      - "127.0.0.1:8080:8080"

Which results in:

docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED       STATUS       PORTS                                                                NAMES
ff900519d32b   traefik:latest   "/entrypoint.sh trae…"   2 hours ago   Up 2 hours   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 127.0.0.1:8080->8080/tcp   traefik

Trying to curl 127.0.0.1:8080 results in curl: (52) Empty reply from server

I have also tried not explicitly linking the port, with perhaps the wrong expectation that I would be able to access the container from its IP:

    ports:
      - "80:80"
      - "443:443"

However, when I try the following, the command hangs.

curl -vL 172.19.0.2:8080
*   Trying 172.19.0.2:8080...
* TCP_NODELAY set

To note, if I run docker-compose exec traefik sh I can access the service at 8080 from within the container. The service is also reachable if I run curl from another container on the same docker bridge network and use the Traefik container IP. I also tried mapping port "8080:8080" in the docker compose file, which allowed me to access the service from localhost, however it also exposed the port outside the host machine (not ideal).

I am running: Ubuntu 20.04.2 Rootless Docker 20.10.5 Slirp4netns 1.1.9

Thank you for any advice!

AkihiroSuda commented 3 years ago

Seems some configuration issue on traefik side?

nginx seems to work as expected

$ export DOCKER_HOST=unix:///run/user/1001/docker.sock
$ docker run -d --name nginx -p 127.0.0.1:8080:80 nginx
$ curl 127.0.0.1:8080
...
<title>Welcome to nginx!</title>
...
MarkErik commented 3 years ago

@AkihiroSuda Thank you for responding - I spun up a fresh VPS to test out your Nginx example, however when I execute the curl command, the response is:

curl 127.0.0.1:8080
curl: (52) Empty reply from server

(which was the same response when I try to curl the traefik container on my other VPS)

Hopefully there is something in the info below that might help diagnose the issue I am experiencing.

mark@vultr:~$  echo $DOCKER_HOST
unix:///run/user/1000/docker.sock
mark@vultr:~$ slirp4netns -v
slirp4netns version 1.1.9
commit: 4e37ea557562e0d7a64dc636eff156f64927335e
libslirp: 4.4.0
SLIRP_CONFIG_VERSION_MAX: 3
libseccomp: 2.3.3
mark@vultr:~$ docker run -d --name nginx -p 127.0.0.1:8080:80 nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
6f28985ad184: Pull complete 
29f7ebf60efd: Pull complete 
879a7c160ac6: Pull complete 
de58cd48a671: Pull complete 
be704f37b5f4: Pull complete 
158aac73782c: Pull complete 
Digest: sha256:d2925188effb4ddca9f14f162d6fba9b5fab232028aa07ae5c1dab764dca8f9f
Status: Downloaded newer image for nginx:latest
b9573de8a3ac383cff08428a4a1b48109868ca3499b6b082e637f460192af260
mark@vultr:~$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                    NAMES
b9573de8a3ac   nginx     "/docker-entrypoint.…"   5 seconds ago   Up 5 seconds   127.0.0.1:8080->80/tcp   nginx
mark@vultr:~$ curl 127.0.0.1:8080
curl: (52) Empty reply from server
mark@vultr:~$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
2ac57adddcda   bridge    bridge    local
00504293f676   host      host      local
e99a7abb25be   none      null      local
mark@vultr:~$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "2ac57adddcda4753925da15123da75365e2e944416f68b49a21a7f1d7f795994",
        "Created": "2021-03-26T01:20:21.883884004+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "b9573de8a3ac383cff08428a4a1b48109868ca3499b6b082e637f460192af260": {
                "Name": "nginx",
                "EndpointID": "0a600aad67474272a7185691fd7dad4e6e9a7cb4df0cb31d80dfa7e197aadd58",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]
mark@vultr:~$ docker info
Client:
 Context:    default
 Debug Mode: false

Server:
 Containers: 1
  Running: 1
  Paused: 0
  Stopped: 0
 Images: 3
 Server Version: 20.10.5
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Native Overlay Diff: false
 Logging Driver: json-file
 Cgroup Driver: none
 Cgroup Version: 1
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: io.containerd.runtime.v1.linux runc io.containerd.runc.v2
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 269548fa27e0089a8b8278fc4fc781d7f65a939b
 runc version: 12644e614e25b05da6fd08a38ffa0cfe1903fdec
 init version: de40ad0
 Security Options:
  seccomp
   Profile: default
  rootless
 Kernel Version: 5.4.0-65-generic
 Operating System: Ubuntu 20.04.2 LTS
 OSType: linux
 Architecture: x86_64
 CPUs: 1
 Total Memory: 1.941GiB
 Name: vultr.guest
 ID: SY3Q:3PJK:V7WX:WZVJ:XWGC:QGVT:LZHT:CKZ6:MXR3:YA6F:QGM2:X2PC
 Docker Root Dir: /home/mark/.local/share/docker
 Debug Mode: false
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false
 Product License: Community Engine

WARNING: Running in rootless-mode without cgroups. To enable cgroups in rootless-mode, you need to boot the system in cgroup v2 mode.
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled

mark@vultr:~/.config/systemd/user$ cat docker.service 
[Unit]
Description=Docker Application Container Engine (Rootless)
Documentation=https://docs.docker.com/engine/security/rootless/

[Service]
Environment=PATH=/home/mark/bin:/sbin:/usr/sbin:/home/mark/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
Environment=DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=slirp4netns
ExecStart=/home/mark/bin/dockerd-rootless.sh 
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
StartLimitBurst=3
StartLimitInterval=60s
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
Delegate=yes
Type=simple
KillMode=mixed

[Install]
WantedBy=default.target
AkihiroSuda commented 3 years ago

DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=slirp4netns

Does "builtin" driver work?

MarkErik commented 3 years ago

@AkihiroSuda Yes, when I set the driver to "builtin" it works:

mark@vultr:~$ curl 127.0.0.1:8080
<!DOCTYPE html>

And when I set the driver to "slirp4netns" it stops working:

mark@vultr:~$ curl 127.0.0.1:8080
curl: (52) Empty reply from server
MarkErik commented 3 years ago

@AkihiroSuda Unfortunately, the "builtin" driver isn't ideal as it doesn't pass through the IP address of a request, because for Traefik I need to log the IP of the request so that I can create specific rules.

AkihiroSuda commented 3 years ago

Seems a bug of rootlesskit-docker-proxy

AkihiroSuda commented 3 years ago

Workaround:

$ docker --context=rootless run -it --rm -p 8080:80 nginx:alpine
$ rootlessctl --socket /tmp/rootlesskit395039187/api.sock  list-ports --json
{"id":17,"spec":{"proto":"tcp4","parentIP":"0.0.0.0","parentPort":8080,"childPort":8080,"childIP":"127.0.0.1"}}
$ rootlessctl --socket /tmp/rootlesskit395039187/api.sock  remove-ports 17
17
$ rootlessctl --socket /tmp/rootlesskit395039187/api.sock  add-ports 127.0.0.1:8080:10.0.2.100:8080/tcp
18
$ curl localhost:8080
...
<title>Welcome to nginx!</title>
...
AkihiroSuda commented 3 years ago

Fix is coming: https://github.com/rootless-containers/rootlesskit/pull/245 Will be included in Docker v20.10.6, or maybe v20.10.7.