I tried:
services:
susebox:
build: .
entrypoint:
- python3
- -m
- http.server
ports:
- 8000:8000
extra_hosts:
- "host.docker.internal:host-gateway"
but I can't do curl to host.docker.internal. ping is working on host.docker.internal but I still can't access the service running on localhost:
# inside of the container
$ curl http://host.docker.internal:8080/
curl: (7) Failed to connect to host.docker.internal port 8080 after 0 ms: Connection refused
Also tried:
# network_mode: "host"
$ curl http://localhost:8080/ # this runs ok outside of container
curl: (7) Failed to connect to localhost port 8080 after 0 ms: Connection refused
but I still can't reach localhost from inside of docker container. I'm using rootless docker containers.
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
I ended up creating a Virtual Ethernet pair for this. The idea is to have one end of the pair in the host's network namespace, the other end in the container's network namespace. With the script below, you can access any port of the host from the container by referring to the IP 192.168.100.1:
#!/bin/bash
# This script creates a bridge between a ROOTLESS Docker container's and the host's network namespace.
# This should allow a rootless Caddy container to (reverse) proxy requests to apps running in the host's network namespace.
# Specify the user running the rootless Docker daemon
DOCKER_USER="<YOUR USER HERE THAT IS RUNNING THE DOCKER DAEMON>"
# Create the VETH pair
ip link add caddyhost type veth peer name caddycontainer
# Assign IP addresses to each end of the VETH pair
ip addr add 192.168.100.1/24 dev caddyhost
ip link set caddyhost up
# Use 'sudo -u' to run 'docker inspect' as the Docker user
pid=$(sudo -u $DOCKER_USER docker inspect --format '{{.State.Pid}}' caddy)
ip link set caddycontainer netns $pid
nsenter -t $pid -n ip addr add 192.168.100.2/24 dev caddycontainer
nsenter -t $pid -n ip link set caddycontainer up
This script needs to be run as root on the host. If the Docker container is recreated, it needs to be run again as that operation breaks the VETH pair.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With