Try   HackMD

Share Host's Network Stack with Container - Podman for example

tags: Container Podman

Normal Case

Have an HTTP server which listens on:

  • IP: 0.0.0.0 -> any network interface
  • Port: TCP 8080
$ cat ./http.js var http = require('http'); http.createServer(function (req, res) { console.log ("Client: " + req.socket.remoteAddress + ":" + req.socket.remotePort); res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World!'); }).listen({host: '0.0.0.0', port: 8080});

Make the HTTP server run in a container:

$ podman run -dt --rm -v ./http.js:/tmp/http.js -p 8080:8080 docker.io/library/node:alpine node /tmp/http.js fb43918d92791448c79ebaa90e0024d4230cf3fb434546ab7208d0bc9e2db346 $ podman ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fb43918d9279 docker.io/library/node:alpine node /tmp/http.js 10 seconds ago Up 10 seconds ago 0.0.0.0:8080->8080/tcp angry_robinson

In this model, it is port forwarding from host's TCP 8080 port to the contianer's TCP 8080 port.
The host can query the HTTP server locating in the container and get the response easily by:

$ curl http://localhost:8080 Hello World!

The Server only Listens on the Loopback Interface

There are some cases that the server only listens on the loopback interface, which means localhost.

However, if the server runs in a container, then the server is listening on the container's loopback interface, instead of the host's. Since they are different loopback interfaces, an user on host will be failed to query the server listening on the container's loopback interface.

Here is an example that the HTTP server listens on 127.0.0.1:8080:

$ cat http.js var http = require('http'); http.createServer(function (req, res) { console.log ("Client: " + req.socket.remoteAddress + ":" + req.socket.remotePort); res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World!'); }).listen({host: '127.0.0.1', port: 8080});

Have the HTTP Server Run in a Container like Normal Case

$ podman run -dt --rm -v ./http.js:/tmp/http.js -p 8080:8080 docker.io/library/node:alpine node /tmp/http.js 636d8b6dfe2b07c487a5019682bfaf9b64b10adb9e2bfdc5be3d1cd1ddcea9d0 $ podman ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 636d8b6dfe2b docker.io/library/node:alpine node /tmp/http.js 1 second ago Up 2 seconds ago 0.0.0.0:8080->8080/tcp gallant_chatelet

Try to query the HTTP server locating in the container from host:

$ curl http://localhost:8080 curl: (56) Recv failure: Connection reset by peer

The query is failed to have a connection. Because, it tried to query an HTTP server listening on host's loopback interface, which does not exist.

Have the HTTP Server Run in a Container Sharing Host's Network Stack

There is the option --network=mode mentioned in Podman's document:

--network=mode, --net
...
  * host: Do not create a network namespace, the container will use the host’s network. Note: The host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
...

Since the option --network=host can force the container share the same network stack with the host, the container shares the same loopback interface with the host as well.

Have the HTTP server run in a container with --network=host:

$ podman run -dt --rm -v ./http.js:/tmp/http.js --network=host docker.io/library/node:alpine node /tmp/http.js a810e197824ac04a47ce829b234767274a27eeb786e6c18094606b61f10bc6fd $ podman ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a810e197824a docker.io/library/node:alpine node /tmp/http.js 2 seconds ago Up 3 seconds ago gracious_vaughan

Try to query the HTTP server locating in the container from host again:

$ curl http://localhost:8080 Hello World!

Now, it works!