skip to Main Content

This is my first time asking something on stackoverflow. For years I’ve been lurking but now I decided to finally register myself. Hence, I apologize if my question/information is not formatted nicely.

Current situation:

I’m slowly getting more and more familiar with Podman and I’m in the process of moving some of my containers over from docker (rootful) to podman (rootless). I’m using Podman 4.3.1 on Debian 11. I’ve managed to get some containers working and was able to externally connect to them. However, the container shows client/source ip ‘127.0.0.1’ instead of my real client’s IPv4. I was wondering whether something like the following is possible?

Ideal situation:

Assigning a specific IPv4 to the container (rootless). Using nftables/iptables to forward packets from the host’s network to the containers ipv4 (e.g. 192.168.1.12). Being able to see the real client’s IPv4 in the container to still be able use fail2ban etc.

As you may notice, I’m still very much in the process of learning how containerization works and specifically for networking. I don’t want to use the hosts network for my container for security reasons. If something is unclear tell me and I’ll try to better explain myself.

Thanks for taking your time to read this 🙂

2

Answers


  1. When you’re running Podman as a non-root user, the virtual tap device that represents the container’s eth0 interface can’t be attached directly to a bridge device. This means it’s not possible to use netfilter rules to direct traffic into the container; instead, Podman relies on a proxy process.

    There are some notes on this configuration here.

    By default, Podman uses the rootlessport proxy, which replaces the source ip of the connection with an internal ip from the container namespace. You can, however, explicitly request Podman to use slirp4netns as the port handler, which will preserve the source address at the expense of some performance.

    For example, if I start a container like this:

    podman run --name darkhttpd --rm -p 8080:8080 docker.io/alpinelinux/darkhttpd
    

    And then connect to this from somewhere:

    curl 192.168.1.200:8080
    

    I will see in the access log:

    10.0.2.100 - - [12/Feb/2023:15:30:54 +0000] "GET / HTTP/1.1" 200 354 "" "curl/7.85.0"
    

    Where 10.0.2.100 is in fact the address of the container:

    $ podman exec darkhttpd ip a show tap0
    2: tap0: <BROADCAST,UP,LOWER_UP> mtu 65520 qdisc fq_codel state UNKNOWN qlen 1000
        link/ether 26:77:5b:e8:f4:6e brd ff:ff:ff:ff:ff:ff
        inet 10.0.2.100/24 brd 10.0.2.255 scope global tap0
           valid_lft forever preferred_lft forever
        inet6 fd00::2477:5bff:fee8:f46e/64 scope global dynamic flags 100
           valid_lft 86391sec preferred_lft 14391sec
        inet6 fe80::2477:5bff:fee8:f46e/64 scope link
           valid_lft forever preferred_lft forever
    

    But if I explicitly request slirp4nets as the port handler:

    podman run --name darkhttpd --rm -p 8080:8080 --network slirp4nets:port_handler=slirp4netns docker.io/alpinelinux/darkhttpd
    

    Then in the access log I will see the actual source ip of the request:

    192.168.1.97 - - [12/Feb/2023:15:32:17 +0000] "GET / HTTP/1.1" 200 354 "" "curl/7.74.0"
    

    In most cases, you don’t want to rely on the source ip address for authentication/authorization purposes, so the default behavior makes sense.

    If you need the remote ip for logging purposes, the option presented here will work, or you can also look into running a front-end proxy in the global namespace that places the client ip into the X-Forwarded-For header and use that for your logs.

    Login or Signup to reply.
  2. Here is an alternative solution not mentioned in the nice
    answer by @larsks.

    Socket activation

    When using socket activation of containers, the source IP is available to the container.

    Support for socket activation is not yet wide-spread but for instance the container image docker.io/library/mariadb supports socket activation. The container image docker.io/library/nginx also supports socket activation (although in a non-standard way, as nginx uses its own environment variable instead of using the standard systemd environment variable LISTEN_FDS)

    I wrote a minimal demo of how to use run docker.io/library/nginx with Podman and socket activation:

    https://github.com/eriksjolund/podman-nginx-socket-activation

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search