Description
Rootless Docker cannot access a private registry when the access needs to occur over a port-forwarded SSH connection. The only discovered workaround involves manually entering the rootless namespace (via nsenter), creating the SSH tunnel, then running docker push or docker pull.
Reproduce
Basic
- On a remote machine
docker run -d -p 5000:5000 --name registry registry:2
- Install rootless docker on the host machine
docker pull hello-world
docker tag hello-world localhost:5000/hello-world
ssh -L 5000:localhost:5000 user@remote
docker push localhost:5000/hello-world
Result:
Using default tag: latest
The push refers to repository [localhost:5001/hello-world]
Get "http://localhost:5000/v2/": dial tcp [::1]:5000: connect: connection refused
Two things may be influencing this: the fact that rootless docker is running inside of a namespace network, and the fact that by default, Docker is invoking rootlesskit with the --disable-host-loopback flag.
CLI Issue
However, even when running rootlesskit with the arguments needed to enable general network access to localhost:5000, docker push still does not work.
On the host with rootless docker installed (note the absence of --disable-host-loopback) and using the gateway address of the slirp4netns network the following WORKS
rootlesskit --net=slirp4netns --mtu=65520 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --port-driver=slirp4netns --copy-up=/etc --copy-up=/run --propagation=rslave curl http://10.0.2.2:5000/v2/_catalog
Result:
However, running docker push still does not work
rootlesskit --net=slirp4netns --mtu=65520 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --port-driver=slirp4netns --copy-up=/etc --copy-up=/run --propagation=rslave docker push 10.0.2.2:5000/hello-world
The push refers to repository [10.0.2.2:5000/hello-world]
Get "http://10.0.2.2:5000/v2/": dial tcp 10.0.2.2:5000: connect: network is unreachable
[rootlesskit:child ] error: command [docker push 10.0.2.2:5000/hello-world] exited: exit status 1
[rootlesskit:parent] error: child exited: exit status 1
This is a different error than when running docker push directly
Expected behavior
One of the above mechanisms allows pushing/pulling to the private registry
docker version
Client: Docker Engine - Community
Version: 24.0.6
API version: 1.43
Go version: go1.20.7
Git commit: ed223bc
Built: Mon Sep 4 12:32:17 2023
OS/Arch: linux/amd64
Context: rootless
Server: Docker Engine - Community
Engine:
Version: 24.0.6
API version: 1.43 (minimum version 1.12)
Go version: go1.20.7
Git commit: 1a79695
Built: Mon Sep 4 12:32:17 2023
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.24
GitCommit: 61f9fd88f79f081d64d6fa3bb1a0dc71ec870523
runc:
Version: 1.1.9
GitCommit: v1.1.9-0-gccaecfc
docker-init:
Version: 0.19.0
GitCommit: de40ad0
rootlesskit:
Version: 1.1.1
ApiVersion: 1.1.1
NetworkDriver: slirp4netns
PortDriver: builtin
StateDir: /tmp/rootlesskit702691539
slirp4netns:
Version: 1.1.8
GitCommit: unknown
docker info
Client: Docker Engine - Community
Version: 24.0.6
Context: rootless
Debug Mode: false
Plugins:
buildx: Docker Buildx (Docker Inc.)
Version: v0.11.2
Path: /usr/libexec/docker/cli-plugins/docker-buildx
compose: Docker Compose (Docker Inc.)
Version: v2.20.3
Path: /home/<user>/.docker/cli-plugins/docker-compose
Server:
Containers: 5
Running: 0
Paused: 0
Stopped: 5
Images: 8
Server Version: 24.0.6
Storage Driver: fuse-overlayfs
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.runc.v2 runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 61f9fd88f79f081d64d6fa3bb1a0dc71ec870523
runc version: v1.1.9-0-gccaecfc
init version: de40ad0
Security Options:
seccomp
Profile: builtin
rootless
Kernel Version: 4.19.0-22-amd64
Operating System: Debian GNU/Linux 10 (buster)
OSType: linux
Architecture: x86_64
CPUs: 16
Total Memory: 31.43GiB
Name: ...
ID: c5d7058e-12b3-4363-8931-67f274a08282
Docker Root Dir: /home/<user>/.local/share/docker
Debug Mode: false
Experimental: false
Insecure Registries:
localhost:5000
localhost:5001
10.0.2.2:5000
10.0.2.2:5001
Live Restore Enabled: false
Additional Info
The only sequence that does work is to use enterns to create the SSH tunnel, background it, then run docker push/pull. There are multiple issues with this approach, one of which is that tunnel will not be closed when the nsenter parent that created it dies.
nsenter -U --preserve-credentials -n -m -t $(cat $XDG_RUNTIME_DIR/docker.pid)
ssh -fN -L 5000:localhost:5000 user@remote
docker push localhost:5000/hello-world
I do not believe this is fundamentally an issue with rootlesskit, as Podman also uses rootlesskit and with rooless podman, pushing/pulling over an SSH tunnel works without any additional configuration.
Other things I've tried
- Using various port bindings on rootless kit (both via running the command directly and via EVs set in the rootless docker's systemd configuration
- These either had no effect or resulted in an error about being unable to bind port 5000 (presumably because it was already being forwarded to SSH)
error: failed to expose port {tcp 127.0.0.1 5000 5000 10.0.2.100}: reply.Error: map[desc:bad request: add_hostfwd: slirp_add_hostfwd failed]
@AkihiroSuda Do you know of any workarounds to allow pushing/pulling to an SSH-tunneled registry in docker rootless?
Description
Rootless Docker cannot access a private registry when the access needs to occur over a port-forwarded SSH connection. The only discovered workaround involves manually entering the rootless namespace (via
nsenter), creating the SSH tunnel, then runningdocker pushordocker pull.Reproduce
Basic
docker run -d -p 5000:5000 --name registry registry:2docker pull hello-worlddocker tag hello-world localhost:5000/hello-worldssh -L 5000:localhost:5000 user@remotedocker push localhost:5000/hello-worldResult:
Two things may be influencing this: the fact that rootless docker is running inside of a namespace network, and the fact that by default, Docker is invoking
rootlesskitwith the--disable-host-loopbackflag.CLI Issue
However, even when running
rootlesskitwith the arguments needed to enable general network access tolocalhost:5000,docker pushstill does not work.On the host with rootless docker installed (note the absence of --disable-host-loopback) and using the gateway address of the
slirp4netnsnetwork the following WORKSResult:
However, running
docker pushstill does not workThis is a different error than when running
docker pushdirectlyExpected behavior
One of the above mechanisms allows pushing/pulling to the private registry
docker version
docker info
Additional Info
The only sequence that does work is to use
enternsto create the SSH tunnel, background it, then run docker push/pull. There are multiple issues with this approach, one of which is that tunnel will not be closed when thensenterparent that created it dies.I do not believe this is fundamentally an issue with
rootlesskit, as Podman also usesrootlesskitand with rooless podman, pushing/pulling over an SSH tunnel works without any additional configuration.Other things I've tried
@AkihiroSuda Do you know of any workarounds to allow pushing/pulling to an SSH-tunneled registry in docker rootless?