I'm assuming you're using the slirp4netns
network driver, since that's what the documentation implicitly suggests.
Docker rootless runs slirp4netns
with the --disable-host-loopback
option, which prohibits connecting to 127.0.0.1:*
on the host namespace, presumably for security reasons.
If the host service is listening on 0.0.0.0
you can try connecting to the local network IP of the host, for instance 192.168.1.100
.
If you have to access a service listening on the host's 127.0.0.1
or don't want to change your configuration (e.g. because it's shared with others that don't use docker rootless), you could use socat
to forward traffic between namespaces using a socket.
Suppose you want to access 127.0.0.1:8080
running on the host from inside a container. You would need 2 socat
instances for that:
An instance listening on a unix socket that forwards traffic to 127.0.0.1:8080
:
socat UNIX-LISTEN:/tmp/forward-docker.socket,fork TCP:127.0.0.1:8080
An instance running on the docker namespace that forwards traffic to the socket created by the previous command:
nsenter -U --preserve-credentials -n -t $(cat "$XDG_RUNTIME_DIR/docker.pid") -- socat TCP4-LISTEN:8080,reuseaddr,fork /tmp/forward-docker.socket
The trick here is that the networks are isolated between namespaces, but the filesystem is shared, so we use an unix socket to bridge the gap. Note that these commands run on the host. After that you can access that host port from inside any container.
If the service you're trying to access is running in another container and forwarding a port on the host, you can skip the first instance and only run the one in the namespace, but you would need to change the port, as it would have already been used by docker in the namespace, and forward to 127.0.0.1
. For instance, let's say you are running a service like this:
docker network create another-net
docker run --rm -p 8080:8080 --net another-net python python -m http.server 8080
You only need command #2 but with a different port, for example 8081
:
nsenter -U --preserve-credentials -n -t $(cat "$XDG_RUNTIME_DIR/docker.pid") -- socat TCP4-LISTEN:8081,reuseaddr,fork TCP:127.0.0.1:8080
And then from any other container you would access the new port.
An example bash script to do all that in a single easy command, with the option of binding different ports:
#!/bin/bash
set -e
PORTS=($(echo "$1" | grep -oP '^\d+(:\d+)?$' | sed -e 's/:/ /g'))
if [ -z $PORTS ]; then
cat <<EOF
Usage:
$(basename "$0") SRC[:DEST]
SRC: will be the port accessible inside the container
DEST:
the connection will be redirected to this port on the host.
if not specified, the same port as SRC will be used
EOF
exit 1
fi
SOURCE=${PORTS[0]}
DEST=${PORTS[1]-${PORTS[0]}}
SOCKFILE="$XDG_RUNTIME_DIR/forward-docker2host-${SOURCE}_$DEST.sock"
socat UNIX-LISTEN:"$SOCKFILE",fork TCP:127.0.0.1:$DEST &
nsenter -U --preserve-credentials -n -t $(cat "$XDG_RUNTIME_DIR/docker.pid") -- socat TCP4-LISTEN:$SOURCE,reuseaddr,fork "$SOCKFILE" &
echo forwarding $SOURCE:$DEST... use ctrl+c to quit
sleep 365d