Podman is a major container platform, used by many developers in place of Docker. Podman v4.0 has extensive new support for the IPv6 address format. IPv6 networks with Network Address Translation (NAT) and port forwarding are now fully tested and supported in this latest version of the platform. You can also assign static IPv6 addresses to containers in these networks.

Podman v4.0 is supported in versions 8.6 and 9 of Red Hat Enterprise Linux. This article shows you how to enable the new IPv6 support.

Changes to Podman

The reason you need to take special steps to enable IPv6 is that Podman went through a major architectural change in version 4.0. The new network stack, which has been rewritten from scratch in Rust, is composed of two tools:

  • Netavark: A network setup tool that configures network bridges, firewall rules, and system settings to give containers access to external networks
  • Aardvark: An authoritative DNS server for A and AAAA container records, enabling containers to resolve connections to other containers by their names or aliases.

Both these tools work together to enable container networking with IPv6, but they are not enabled by default.

For more information about the new network stack, please refer to the article Podman 4.0's new network stack: What you need to know.

Note: Podman v4.0 performs several schema migrations in the Podman database during the first run. These schema migrations prevent Podman v3.x and earlier from reading some network configuration information from the database. Therefore, downgrading from Podman v4.0 to an earlier version will cause containers to lose their static IP, MAC address, and port bindings.

Package installation and configuration

If you're using Red Hat Enterprise Linux 8.6, you'll need to take a few additional steps to install the Netavark backend; in Red Hat Enterprise Linux 9, Netavark is installed along with the Podman package.

Install Podman 4.0 on Red Hat Enterprise Linux 8.6

Before upgrading the Podman package from v3.x to 4.x on RHEL 8.6, you should remove all non-default defined networks. You can recreate them after the package upgrade.

On RHEL 8.6, by default Podman will use the Container Network Interface (CNI) backend. In order to use Netavark instead, you must explicitly mention it during installation, as follows:

[root@atomic-test ~]# yum install podman netavark -y

The Aardvark package will be installed as a dependency of the Netavark package, so you don't have to mention it in the command.

Then copy the containers.conf file to /etc/containers/:

[root@atomic-test ~]# cp /usr/share/containers/containers.conf /etc/containers/containers.conf

Edit /etc/containers/containers.conf and find the following line under the [network] section:

network_backend = "cni"

Change that line to:

network_backend = "netavark"

You can issue the following commands to verify that Podman will use the Netavark backend:

[root@atomic-test ~]# podman info |grep -i networkbackend
networkBackend: netavark
[root@atomic-test ~]# cat /var/lib/containers/storage/defaultNetworkBackend
netavark

Install Podman 4.0 on Red Hat Enterprise Linux 9

If you're using RHEL 9, the process is much simpler. The Netavark backend is installed along with Podman with the following command:


[root@atomic-test ~]# yum install podman -y

The Netavark and Aardvark packages are installed as dependencies of Podman, so you don't have to mention them in the command. And Podman uses Netavark as the network backend by default in RHEL 9, so no further configuration is required.

Create an interface that supports the dual IPv4/IPv6 stack

The default bridge, podman0, supports only the IPv4 stack, and DNS is disabled. A look at the default stack shows that IPv6 and DNS are disabled:

# podman network inspect podman
[
    {
         "name": "podman",
         "id": "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9",
         "driver": "bridge",
         "network_interface": "podman0",
         "created": "2022-06-24T18:49:34.800035098+05:30",
         "subnets": [
              {
                   "subnet": "10.88.0.0/16",
                   "gateway": "10.88.0.1"
              }
         ],
         "ipv6_enabled": false,
         "internal": false,
         "dns_enabled": false,
         "ipam_options": {
              "driver": "host-local"
         }
    }
]

You must therefore create a new bridge that supports IPv6 addresses and DNS. Here, we'll name this bridge podman1:

[root@atomic-test ~]# podman network create --ipv6  podman1

Examine the new bridge by issuing this command:

[root@atomic-test ~]# podman network inspect podman1

This snippet from the output shows the use of IPv6:

              {
                   "subnet": "fd96:7c2e:b8d2:bf65::/64",
                   "gateway": "fd96:7c2e:b8d2:bf65::1"
              }
         ],
         "ipv6_enabled": true,

Now create a network interface by specifying the network range for both IPv4 and IPv6:

[root@atomic-test ~]# podman network create --ipv6 --gateway fd00::1:8:1 --subnet fd00::1:8:0/112 --gateway 10.90.0.1 --subnet 10.90.0.0/16 podman1

Attach a pod to the Podman network interface

Podman is now configured to handle pods using IPv6. Run a sample pod as follows:

[root@atomic-test ~]# podman run --network podman1 -d -p 8080:80 docker.io/fedora/apache

Expose the pod with a static IPv6 address, using the new --ip6 option:

[root@atomic-test ~]# podman run --network podman1 -d --ip6 fd00::1:8:9 -p 8080:80 docker.io/fedora/apache

Port mapping

To make the container service reachable from an outside network, Netavark creates appropriate masquerade rules in iptables. For the static pod created in the previous section, let's take a quick look at the associated DNAT rules using the following iptables command:


iptables -t nat -L -n -v

The output should look like this:

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
NETAVARK-HOSTPORT-DNAT  all      ::/0                 ::/0                 ADDRTYPE match dst-type LOCAL

Chain NETAVARK-HOSTPORT-DNAT (2 references)
target     prot opt source               destination
NETAVARK-DN-F11DC6A6D09CF  tcp      ::/0                 ::/0                 tcp dpt:8080 /* dnat name: podman1 id: 8de28bebed939bb6449fe3d97cce5ae2e4a785462bb9ffa8d1417143f809bff0 */

Chain NETAVARK-DN-F11DC6A6D09CF (1 references)
target     prot opt source               destination
NETAVARK-HOSTPORT-SETMARK  tcp      fd00::1:8:0/112      ::/0                 tcp dpt:8080
NETAVARK-HOSTPORT-SETMARK  tcp      ::1                  ::/0                 tcp dpt:8080
DNAT       tcp      ::/0                 ::/0                 tcp dpt:8080 to:[fd00::1:8:9]:80

When a packet destined for container port 8080 enters the host network, the packet is first processed by the iptables PREROUTING chain. Packets are then further processed by the NETAVARK-HOSTPORT-DNAT chain (the only custom chain defined in the PREROUTING chain). Depending on the port used, packets are further processed by the port-specific DNAT chain NETAVARK-DN-F11DC6A6D09CF defined in NETAVARK-DN-F11DC6A6D09CF. Packets are then sent to the appropriate container.

Aardvark DNS server

Aardvark keeps track of the containers and their assigned IP addresses based on the network interface to which the pod has been attached. Containers attached to the same network are resolvable using their names.

The following example uses the podman1 bridge we previously created:

[root@atomic-test ~]# ls /run/containers/networks/aardvark-dns/
aardvark.pid podman

Like DNS zone records, Aardvark keeps track of what is similar to DNS A/AAA records. The mapping of container names to IP addresses is stored in this file. If a container is not reachable using its container name, you can refer to this file to see if a record has been created for that container.

The /run/containers/networks/aardvark-dns/podman1 file shows DNS records for podman1:

fd00::1:8:1
0f6d61bb7f6e8dcb4b8586ebc90d699b0e988406240419e8e48e1082ffd00451  fd00::1:8:2 goofy_cannon,0f6d61bb7f6e
0f6d61bb7f6e8dcb4b8586ebc90d699b0e988406240419e8e48e1082ffd00451 10.90.0.2  goofy_cannon,0f6d61bb7f6e
75647734d979ab6ded5636741ccd794fd1845fe0d5ef7161071fe9bd93d7f1fa  fd00::1:8:9 test1,75647734d979
75647734d979ab6ded5636741ccd794fd1845fe0d5ef7161071fe9bd93d7f1fa 10.90.0.3  test1,75647734d979
e394edc2e5bda1e790785d07eb32f8ec72e010f0dac5978e2152df472291d715  fd00::1:8:10 test2,e394edc2e5bd
e394edc2e5bda1e790785d07eb32f8ec72e010f0dac5978e2152df472291d715 10.90.0.4  test2,e394edc2e5bd

Whenever a pod is deleted, Aardvark removes the entry from its database.

Rootless containers

Rootless containers continue to use the slirp4netns service for communication. To expose the container service externally, slirp4netns listens on the host network according to the port mapping configuration. We want rootless containers to be reachable using IPV6 addresses as well.

By default, when a Podman container is started, it does not get an IP address. Containers within the host should be reachable using the mapped port in the format host_ip:port.

For rootless containers, you can define custom Podman network interfaces as the rootless user and then attach the pod to them. Containers should be reachable within the host using the container IP address.

To enable IPv6 addresses with a custom network, enter:

[awx@atomic-test ~]$ podman network create --ipv6 redhat  

Inspect the newly created redhat interface as follows:

[awx@atomic-test ~]$ podman network inspect redhat
[
    {
         "name": "redhat",
         "id": "262e49487783d58774dfa5f581e07849583558c059416d014774317ffc4190a7",
         "driver": "bridge",
         "network_interface": "podman1",
         "created": "2022-06-29T05:30:24.968361698-04:00",
         "subnets": [
              {
                   "subnet": "10.89.0.0/24",
                   "gateway": "10.89.0.1"
              },
              {
                   "subnet": "fd25:1552:57a6:f8ee::/64",
                   "gateway": "fd25:1552:57a6:f8ee::1"
              }
         ],
         "ipv6_enabled": true,
         "internal": false,
         "dns_enabled": true,
         "ipam_options": {
              "driver": "host-local"
         }
    }
]

Attach a rootless container to the custom network with a static IPv6 address as follows:

[awx@atomic-test ~]$ podman run --network redhat  -d --ip6 fd25:1552:57a6:f8ee::10 -p 8081:80 docker.io/fedora/apache

Rootless containers within the host should be reachable by their own IP address or by a static IP address. External communication for such containers will continue to be handled by slirp4netns.

Aardvark DNS is enabled only for custom network interfaces, created using podman network create. Aardvark takes care of resolution for rootless containers using their respective container names attached to the same custom network interface.

More in Podman 4.0

Along with IPv6 support, the new network stack included with Podman v4.0 features improved support for containers in multiple networks and improved performance. Podman also continues to support the CNI stack. There are several other features which are not discussed here, such as the podman network connect command, which allows you to connect a running container to another network. So explore the shiny new network stack, and leave a comment on this article if you have thoughts about the improvements in IPv6 support.

Comments