Exploiting IPv6 using podman

In this post I’ll show how you can use podman to provide native IPv6 connectivity to containers. This allows direct access to and from the Internet without the need for such gimmicks as NAT and port forwarding.

Install podman

By default, podman will place a CNI configuration file in

At the time of this writing the file 87-podman-bridge.conflist does not provide IPv6 information. We will fix this now.

Create a new file:

88-podmanv6-bridge.conflist 

which we will modify to use IPv6 so that changes made as a result of these instructions do not touch the podman installed files. We will be keeping the installed podman files as they are.

The CNI networks to use are supplied on the command line, allowing you to choose which network configuration to use. Having an additional configuration file will expose you to this feature. By using a separate configuration file, you can start over if you need to. Once you are comfortable with your changes, you can setup your config files however you like.

Add this file to /etc/cni/net.d/

The IPv6 subnet to use (“subnet”:”2600:3c03:e000:0391::/64″ n the json above) is what your service provider assigns to you. Your provider already knows how to get packets from the network to you and how to get packets from you out to their destination on the network. I’ll talk more about this subnet in section “About that IPv6 Subnet” later.

To use this configuration, just supply the name of the CNI network (not filename) on the podman command. In this example the CNI network name is

podman6, which corresponds to the second like of json (“name”: “podmanv6”) in 88-podmanv6-bridge.conflist file above.

To use this network with podman run:

Here an httpd container is started using the image dperson/nginx and is given the name “http-X”. The name “http-X” is given to the container and it runs on the CNI network named “podman6” which we configured above.

You can find the IPv6 address assigned to the container via:

sudo podman inspect http-X | grep GlobalIPv6Address

In this example the address assigned via CNI host-local plugin is

"GlobalIPv6Address": ": "2600:3c03:e000:391::2" 

Now from another place on the IPv6 network point your browser to this IPv6 address. Using cURL:

You are connecting from your browser directly to the container via IPv6. There is no need to NAT, or to map port 80 on the host to port 80 on the container.

About That IPv6 Subnet

A subnet, to simplify, gives you a range of IP addresses, typically shown as a prefix and a mask (i.e. prefix/mask.) For example, the IPv4 prefix 10.88.0.0/16 lets you use 64k address all starting with 10.88. A prefix is how I refer to the subnet as a whole, and how the network routes packets to you. The network only cares about the prefix when deciding how to get the packet to its destination. Once at the destination, how that prefix is used is up to you. More on this shortly.

Using IPv4 one is typically allocated just one routable IP address. You probably have one assigned via e.g. DHCP from your ISP for your home or one that is assigned to your Virtual Machine (VM) when using a cloud provider. Even then it’s often the case that the IPv4 address assigned to your VM is routable, but you don’t see the routable address inside your VM. A private, non-routable, RFC1918 IPv4 address (e.g. 192.168.0.1) is used. The cloud provider will use NAT to translate the routable address to an RFC1918 non-routable address inside your VM. Last I checked, the big cloud providers such as AWS, Azure, Google and IBM did this. Due to this limitation, I stopped using them. Another limitation I found was a lack of IPv6 support of any kind, or just providing one IPv6 address.

Smaller providers such as Linode and Digital Ocean (and I’m sure others) do provide your VM with a routable IPv4 address. Because of the ability to get a routable IPv4 address, I use them exclusively. For IPv6, they also provide one routable IPv6 address as well. What’s even better, they will provide multiple IPv6 addresses (i.e. a subnet) of various sizes. With Linode, you can get an entire /64. The ability to have an IPv6 subnet is key to this entire article. Getting an IPv6 subnet is the norm, not the exception. See RFC 7934 for more information.

With only one IPv6 address, hacks like NAT and/or “port forwarding” that, sadly, are common when IPv4 addressing is used would need to also be used with IPv6. These games were played due to the limited number of IPv4 addresses available. IPv6 was invented (over 20 years ago!) to solve this addressing problem. Given the number of IPv6 address available, there isn’t any reason not to leverage them. Some ISP’s will assign an IPv6 Address to a subscriber for the link which connects the subscriber to the ISP. The ISP typically will also supply a “delegated prefix“, of /48, /56 or /64 for use on networks internal to the subscriber via DHCPv6, or some other means. e.g. Linode provides my IPv6 subnets via their web interface.

Another common option for obtaining an IPv6 subnet is to use the Hurricane Electric (HE) tunnel broker. HE will tunnel IPv6 inside IPv4 so that a site that cannot obtain IPv6 from their provider can still access IPv6 networks by tunneling IPv6 packets inside IPv4 packets. HE will provide you with an IPv6 subnet for use at your site and a tunnel endpoint. See Hurricane Electric for specifics.

In the podman IPv6 example above, I used the IPv6 subnet 2600:3c03:e000:0391::/64 provided to me. The network provider will get any packets with a destination IPv6 address matching the first 64 bits of 2600:3c03:e000:0391 (in hexadecimal) to podman, most likely via the interface which connects to your provider, what I’ll refer to as an “uplink”. In some cases yet another IPv6 address is assigned to the uplink, in other cases, just one subnet is provided. In the podman example above, a seperate IPv6 address (not shown) exists on the uplink. I simply had to put the provided subnet in the CNI configuration. The provider takes case of IP routine etc. Things are more interesting when only one subnet is supplied for your entire system, regardless of how many interfaces, physical (eth0, eth1 etc.) or virtual (e.g. podman0, podman1, docker0 etc.)

When only a single subnet is at your disposal all you need to do is subnet the subnet. In the IPv4 case, say your node is given 10.89.0.0/16, and the uplink (eth0) is assigned the address 192.168.0.1. Your provider knows how to get traffic to and from any packet with 192.168.x.x as the prefix. (Note: If this is your home IPv4 router connected to your ISP, you would be using NAT to translate 192.168.x.x address to the routable IPv4 address on the uplink to your ISP.) Once a packet arrives on your node, you can do what you want with the packet. You could assign a network for podman to use 192.168.1.0/24, another, 192.168.2.0/24 for local LAN devices connected via eth1 etc.

With IPv6 the same thing is possible, and there is no need for NAT! To simplify, if given 2001:DB8:1::/48 (HE will provide you a /48!), you can split this up into 64k different /64 subnets, 2001:DB8:1:0::/64 through 2001:DB8:1:FFFF::/64, more than enough for your home, enterprise or VM to use for podman networks.

This entry was posted in IPv6, Networking, Technical Difficulties. Bookmark the permalink.