How to setup PostgreSQL on an IPv6 enabled network

Enterprise PostgreSQL Solutions

Comments are off

How to setup PostgreSQL on an IPv6 enabled network

1. Overview

PostgreSQL is a great open source database, not only because it supports lot of database features, but also because it supports different network setup. For example, you can set it up on an IPv6 enabled network in just a few steps. This blog will demonstrate how to setup PostgreSQL on an IPv6 network in Linux.

Before we dive into the detail, let’s discuss a little bit IPv6. IPv6 was developed by the Internet Engineering Task Force (IETF) in late 1998 and was intended to replace IPv4. With the IPv4 address exhaustion issue, after about two decades, IPv6 now is finally coming into the real picture. Below is the state of IPv6 Deployment in 2018:

* Over 25% of all Internet-connected networks advertise IPv6 connectivity.
* Google reports 49 countries deliver more than 5% of traffic over IPv6, with new countries joining all the time.
* Google reports 24 countries whose IPv6 traffic exceeds 15%.

If you check your home internet modem/router and most likely you will find the IPv6 is already enabled. There are many documents and RFCs explain IPv6 in much more detail. For example, IP Version 6 Addressing Architecture defined in RFC 4291. In this blog, I will just explain some simple concepts which is required in this demo.

2. Setup IPv6 network

Link-local address

Not like IPv4, all the interface of an IPv6 enabled host require a link-local address. The link-local address will always start with the prefix fe80:: and it is generated during TCP/IP stack boot up on that interface. The interesting part is that link-local address doesn’t request a DHCP server or any manual configuration. The link-local can be set to derive from the MAC address of the interface, in this case, if you know the MAC of the interface then you can create the link-local address by simply copy and paste the MAC to a link-local calculator.

Global address

However, there is a limitation as the name indicated, it only works between the hosts which are directly connected. To allow the communication cross the internet or multiple routers, the host needs to have a global address. There are many different ways to setup a global IPv6 address. Here, we introduce three typical ways: Manually, DHCPv6 and SLAAC.

  • To manually setup an IPv6 global address, you can use either ip or ifconfig. For example,
sudo ip -6 addr add 2020:1:0:0::db1/64 dev eth0 

sudo ifconfig eth0 inet6 add 2020:1:0:0::db1/64 

Since IPv6 allows the HEX characters, you can make your own customized IPv6 address for fun. For example, configure a PostgreSQL server with IPv6 address like, :db:feed, :da7a:ba5e, :db1, :db2 etc,.

  • Stateless address autoconfiguration (SLAAC) requires to have a router which broadcast the router advertisement periodically. The router should also response to router solicitation request from any host machine. Once the host receive the router advertisement, it will use the prefix to generate the global IPv6 address automatically. Below is an example,
    Install the Router Advertisement Daemon (radvd) on PostgreSQL server side,
sudo apt-get install radvd

then configure the radvd conf file, for example,

interface eno1
{
   AdvSendAdvert on;
   AdvManagedFlag off;
   AdvOtherConfigFlag on;
   prefix 2020:1:0:0::/64
   {
        AdvAutonomous on;
   };
};

Configure a new IPv6 address on Postgres server for radvd daemon to use

sudo ip -6 addr add 2020:1:0:0::db1/64 dev eno1

Then start the radvd daemon, sudo radvd -l /etc/radvd.conf
Now, if you check the ip address on the client machine side, you should see something like below,

enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.20.14.27  netmask 255.255.255.0  broadcast 172.20.14.255
        inet6 2020:1::8dcf:b8be:dbcc:26c6  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::8005:8b22:cd7d:ee39  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:29:ab:c9  txqueuelen 1000  (Ethernet)
        RX packets 118  bytes 38615 (38.6 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 181  bytes 26919 (26.9 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

where, 2020:1::8dcf:b8be:dbcc:26c6 is the global address generated after receive the router advertisement.

  • Stateful IPv6 address is done via a DHCPv6 server. The setup is similar to the IPv4 DHCP server setup. Below is an example on an Ubuntu machine,
sudo apt-get install isc-dhcp-server

After the DHCP Server has been installed, edit the configuration file for IPv6 address assignment.

sudo vim /etc/dhcp/dhcpd6.conf

and add below information,

ddns-update-style none;
default-lease-time 7200; # 12 hours
max-lease-time 86400; # 12 hours
authoritative;

### Subnet
subnet6 2020:2:0:0::/64 {
    range6
        2020:2:0:0::1001  2020:2:0:0::1005;
}

Configure a new IPv6 address on Postgres server for DHCPv6 server to use

sudo ip -6 addr add 2020:2:0:0::db1/64 dev eno1
sudo dhcpd -6 -d -cf /etc/dhcp/dhcpd6.conf eno1

On the client machine side, run a dhcp request for IPv6 manually. sudo dhclient -6 enp0s3, and then perform an ip address check.

enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.20.14.27  netmask 255.255.255.0  broadcast 172.20.14.255
        inet6 2020:2::1004  prefixlen 128  scopeid 0x0<global>
        inet6 2020:1::8dcf:b8be:dbcc:26c6  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::8005:8b22:cd7d:ee39  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:29:ab:c9  txqueuelen 1000  (Ethernet)
        RX packets 1118  bytes 253355 (253.3 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 495  bytes 76673 (76.6 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

where, 2020:2::1004 is the global IPv6 address assigned by the DHCPv6 server.

3. Connetivity test using ping6

  • PostgreSQL server
    • Network interface: eno1
    • link-local: fe80::1c79:293f:1b6e:c826
    • IPv6 global address(SLAAC): 2020:1::db1
    • IPv6 global address(DHCPv6): 2020:2::db1
  • psql client IPs
    • Network interface: enp0s3
    • link-local: fe80::8005:8b22:cd7d:ee39
    • IPv6 global address generated(SLAAC): 2020:1::8dcf:b8be:dbcc:26c6
    • IPv6 global address assigned(DHCPv6): 2020:2::1004
ping6 test from server to client
$ ping6 fe80::8005:8b22:cd7d:ee39%eno1
PING fe80::8005:8b22:cd7d:ee39%eno1(fe80::8005:8b22:cd7d:ee39%eno1) 56 data bytes
64 bytes from fe80::8005:8b22:cd7d:ee39%eno1: icmp_seq=1 ttl=64 time=0.582 ms
64 bytes from fe80::8005:8b22:cd7d:ee39%eno1: icmp_seq=2 ttl=64 time=0.572 ms

$ ping6 2020:1::8dcf:b8be:dbcc:26c6
PING 2020:1::8dcf:b8be:dbcc:26c6(2020:1::8dcf:b8be:dbcc:26c6) 56 data bytes
64 bytes from 2020:1::8dcf:b8be:dbcc:26c6: icmp_seq=1 ttl=64 time=0.576 ms
64 bytes from 2020:1::8dcf:b8be:dbcc:26c6: icmp_seq=2 ttl=64 time=0.601 ms

$ ping6 2020:2::1004
PING 2020:2::1004(2020:2::1004) 56 data bytes
64 bytes from 2020:2::1004: icmp_seq=1 ttl=64 time=0.896 ms
64 bytes from 2020:2::1004: icmp_seq=2 ttl=64 time=0.631 ms
ping6 test from client to server
$ ping6 fe80::1c79:293f:1b6e:c826%enp0s3
PING fe80::1c79:293f:1b6e:c826%enp0s3(fe80::1c79:293f:1b6e:c826%enp0s3) 56 data bytes
64 bytes from fe80::1c79:293f:1b6e:c826%enp0s3: icmp_seq=1 ttl=64 time=0.144 ms
64 bytes from fe80::1c79:293f:1b6e:c826%enp0s3: icmp_seq=2 ttl=64 time=0.220 ms

$ ping6 2020:1::db1
PING 2020:1::db1(2020:1::db1) 56 data bytes
64 bytes from 2020:1::db1: icmp_seq=1 ttl=64 time=0.825 ms
64 bytes from 2020:1::db1: icmp_seq=2 ttl=64 time=0.520 ms

$ ping6 2020:2::db1
PING 2020:2::db1(2020:2::db1) 56 data bytes
64 bytes from 2020:2::db1: icmp_seq=1 ttl=64 time=0.508 ms
64 bytes from 2020:2::db1: icmp_seq=2 ttl=64 time=0.486 ms

4. Configure PostgreSQL for IPv6

Edit the postgresql.conf file to allow Postgres listen on different interfaces

listen_addresses = '*'   

Edit the host-based authentication file to allow client machine to connect with different source IPs.

# IPv6 local connections:
host    all             all             fe80::8005:8b22:cd7d:ee39/128           trust
host    all             all             2020:1::8dcf:b8be:dbcc:26c6/128         trust
host    all             all             2020:2::1004/128                        trust
psql client connect to Postgres server using link-local address with interface name
$ psql -d postgres -U david -h fe80::1c79:293f:1b6e:c826%enp0s3
psql (14devel)
Type "help" for help.

postgres=# SELECT datname,pid, usename, client_addr FROM pg_stat_activity where usename='david';
 datname  |  pid  | usename |        client_addr        
----------+-------+---------+---------------------------
          | 24170 | david   | 
 postgres | 24244 | david   | fe80::8005:8b22:cd7d:ee39
(2 rows)
psql client connect to Postgres server using global address(Stateless address)
$ psql -d postgres -U david -h 2020:1::db1
psql (14devel)
Type "help" for help.

postgres=# SELECT datname,pid, usename, client_addr FROM pg_stat_activity where usename='david';
 datname  |  pid  | usename |         client_addr         
----------+-------+---------+-----------------------------
          | 24131 | david   | 
 postgres | 24149 | david   | 2020:1::8dcf:b8be:dbcc:26c6
psql client connect to Postgres server using global address(Stateful address)
$ psql -d postgres -U david -h 2020:2::db1
psql (14devel)
Type "help" for help.

postgres=# SELECT datname,pid, usename, client_addr FROM pg_stat_activity where usename='david';
 datname  |  pid  | usename | client_addr  
----------+-------+---------+--------------
          | 24170 | david   | 
 postgres | 24235 | david   | 2020:2::1004

5. Typical errors

Using link-local to connect Postgres without the interface name
$ psql -d postgres -U david -h fe80::1c79:293f:1b6e:c826
psql: error: could not connect to server: could not connect to server: Invalid argument
    Is the server running on host "fe80::1c79:293f:1b6e:c826" and accepting
    TCP/IP connections on port 5432?
psql client using a wrong global address as source address
$ psql -d postgres -h 2020:2::db1
psql: error: could not connect to server: FATAL:  no pg_hba.conf entry for host "2020:1::8dcf:b8be:dbcc:26c6", user "dbtest", database "postgres"

This is due to multiple IPv6 global addresses available on the same interface. In this case, the application, i.e. psql should have an option to select the preferred IPv6, otherwise, the kernel will pick up the IPv6 global address based on predefined policy and rules. Please check Source Address Selection for details. A simple solution is the remove other global IPv6 addresses, and disable the corresponding service i.e. radvd or DHCPv6 server.

6. Summary

We demonstrated how to setup a simple IPv6 network with one Postgres server and one psql client. To enable the IPv6 configuration is pretty simple on PostgreSQL side, but some the basic IPv6 knowledge is required.

Ref: Configuring IPv6 addresses