Security is a big topic today and in the news almost every day. As the database usually holds sensitive data this data must be well protected. In most cases this is done by encrypting critical data inside the database and decrypt only when requested. But this is not all: When a client reads the data it is decrypted inside the database and then send back over the network unencrypted. What do you win with such a setup? The only risk it protects you from is a theft of either your disks or the whole server. Even more important is that all the connections to your database are encrypted so the traffic from and to your database can not be read be someone else. In this post we’ll look at how you can do this with PostgreSQL.

Obviously, for securing the connections to the database by using SSL we’ll need a server certificate. As I am on Linux this can be generated very easy by using openssl to create a self signed certificate. Be aware that your PostgreSQL binaries need to be compiled with “–with-openssl” for the following to work. You can check this by using using pg_config:

postgres@pgbox:/u01/app/postgres/local/dmk/ [PG960] pg_config | grep CONFIGURE
CONFIGURE = '--prefix=/u01/app/postgres/product/96/db_0' '--exec-prefix=/u01/app/postgres/product/96/db_0' '--bindir=/u01/app/postgres/product/96/db_0/bin' '--libdir=/u01/app/postgres/product/96/db_0/lib' '--sysconfdir=/u01/app/postgres/product/96/db_0/etc' '--includedir=/u01/app/postgres/product/96/db_0/include' '--datarootdir=/u01/app/postgres/product/96/db_0/share' '--datadir=/u01/app/postgres/product/96/db_0/share' '--with-pgport=5432' '--with-perl' '--with-python' '--with-tcl' '--with-openssl' '--with-pam' '--with-ldap' '--with-libxml' '--with-libxslt' '--with-segsize=2' '--with-blocksize=8' '--with-wal-segsize=16' '--with-extra-version= dbi services build'

To create a self signed certificate with openssl simple execute the following command:

postgres@pgbox:/home/postgres/ [PG960] openssl req -new -text -out server.req

This creates a new certificate request based on the information you provide. The only important point here (for the scope of this post) is that the “Common Name” must match the server name where your PostgreSQL is running on, e.g.:

Generating a 2048 bit RSA private key
............................................................................................................................................+++
..................................................................................................+++
writing new private key to 'privkey.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CH
State or Province Name (full name) []:BS
Locality Name (eg, city) [Default City]:Basel
Organization Name (eg, company) [Default Company Ltd]:dbi services
Organizational Unit Name (eg, section) []:dba
Common Name (eg, your name or your server's hostname) []:pgbox
Email Address []:xx@xx@com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

This created two files in the directory where you executed the command (the first one is the certificate request and the second one is the private key):

-rw-r--r--. 1 postgres postgres  3519 Sep  9 13:24 server.req
-rw-r--r--. 1 postgres postgres  1821 Sep  9 13:24 privkey.pem

If you want your PostgreSQL instance to start automatically you should remove the pass phrase from the generated private key:

postgres@pgbox:/home/postgres/ [PG960] openssl rsa -in privkey.pem -out server.key
Enter pass phrase for privkey.pem:
writing RSA key
postgres@pgbox:/home/postgres/ [PG960] rm privkey.pem

The password which is asked for is the one you provided when you generated the certificate request above. The new key is now in “server.key”. Now you can create your certificate:

postgres@pgbox:/home/postgres/ [PG960] openssl req -x509 -in server.req -text -key server.key -out server.crt

If everything went well your brand new certificate should be available:

postgres@pgbox:/home/postgres/ [PG960] ls -l server.crt 
-rw-r--r--. 1 postgres postgres 4473 Sep  9 13:32 server.crt
postgres@pgbox:/home/postgres/ [PG960] cat server.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 12528845138836301488 (0xaddf6645ea37a6b0)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=CH, ST=BS, L=Basel, O=dbi services, OU=dba, CN=pgbox/emailAddress=xx@xx@com
        Validity
            Not Before: Sep  9 11:32:42 2016 GMT
            Not After : Oct  9 11:32:42 2016 GMT
        Subject: C=CH, ST=BS, L=Basel, O=dbi services, OU=dba, CN=pgbox/emailAddress=xx@xx@com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:cb:4f:d1:b7:81:c4:83:22:2f:fb:9f:4b:fa:6a:
                    16:77:fd:62:37:91:f1:09:cc:c4:e1:04:e1:de:f2:
                    3f:77:35:ec:e5:8f:5a:03:1d:7b:53:8e:5a:72:76:
                    42:2a:cb:95:9a:35:4a:98:1d:78:3c:21:85:3d:7c:
                    59:f6:e8:7b:20:d0:73:db:42:ff:38:ca:0c:13:f6:
                    cc:3e:bc:b0:8f:41:29:f1:c7:33:45:79:c7:04:33:
                    51:47:0b:23:f8:d6:58:68:2d:95:83:c9:ad:40:7c:
                    95:9a:0c:ff:92:bd:d6:4f:b2:96:6c:41:45:0d:eb:
                    19:57:b3:9a:fc:1c:82:01:9c:2d:e5:2e:1b:0f:47:
                    ab:84:fa:65:ed:80:e7:19:da:ab:89:09:ed:6a:2c:
                    3a:aa:fe:dc:ba:53:e5:52:3f:1c:db:47:4c:4a:d6:
                    e5:0f:76:12:df:f4:6c:fd:5a:fb:a5:70:b4:7b:06:
                    c3:0c:b1:4d:cf:04:8e:5c:b0:05:cb:f2:ac:78:a6:
                    12:44:55:07:f9:88:55:59:23:11:0f:dd:53:14:6a:
                    e8:c4:bb:6a:94:af:1e:54:e8:7d:4f:10:8a:e5:7e:
                    31:3b:cf:28:28:80:37:62:eb:5e:49:26:9d:10:17:
                    33:bc:a7:3f:2a:06:a4:f0:37:a5:b3:07:6d:ce:6a:
                    b7:17
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                EA:63:B1:7F:07:DF:31:3F:55:28:77:CC:FB:F2:1F:3A:D6:45:3F:55
            X509v3 Authority Key Identifier: 
                keyid:EA:63:B1:7F:07:DF:31:3F:55:28:77:CC:FB:F2:1F:3A:D6:45:3F:55

            X509v3 Basic Constraints: 
                CA:TRUE
    Signature Algorithm: sha256WithRSAEncryption
         18:2b:96:b6:01:d8:3e:7f:bb:35:0c:4b:53:c2:9c:02:22:41:
         25:82:d3:b6:a9:88:6e:0e:5d:5b:d3:ac:00:43:0a:04:f4:12:
         6e:22:fd:3f:77:63:0e:42:28:e3:09:6b:16:67:5f:b7:08:08:
         74:a3:55:1f:49:09:69:96:e8:f6:2e:9c:8a:d6:a0:e2:f7:d8:
         30:62:06:f0:5e:1a:85:fe:ff:2d:39:64:f7:f1:e9:ce:21:02:
         f3:86:5f:3b:f6:12:1d:61:cd:a8:bf:36:e2:98:d4:99:b6:95:
         5e:05:87:8d:ab:2f:30:38:b2:fe:68:ac:50:8d:98:fd:aa:4d:
         79:e2:f5:71:92:d6:e5:1d:59:42:02:49:7a:2e:e0:f3:ba:41:
         4d:f4:15:33:44:36:14:43:3b:7a:41:1b:61:6c:ff:78:fb:13:
         4a:a4:e0:96:6c:45:80:0e:30:e3:63:9d:dc:f1:77:16:22:9c:
         7a:c9:92:96:53:3b:62:87:ca:cb:e8:4a:a4:4f:69:a6:a0:5a:
         a9:eb:be:58:7f:c1:da:d4:d7:41:d4:54:06:fb:5b:8b:ea:46:
         68:f5:e6:1e:2b:6a:0b:65:f9:66:5a:a2:14:ec:eb:05:2f:99:
         46:bc:bb:d8:11:f6:3f:2e:6e:15:48:ac:70:1f:18:2d:e2:78:
         4b:a3:cb:ef
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIJAK3fZkXqN6awMA0GCSqGSIb3DQEBCwUAMHkxCzAJBgNV
BAYTAkNIMQswCQYDVQQIDAJCUzEOMAwGA1UEBwwFQmFzZWwxFTATBgNVBAoMDGRi
aSBzZXJ2aWNlczEMMAoGA1UECwwDZGJhMQ4wDAYDVQQDDAVwZ2JveDEYMBYGCSqG
SIb3DQEJARYJeHhAeHhAY29tMB4XDTE2MDkwOTExMzI0MloXDTE2MTAwOTExMzI0
MloweTELMAkGA1UEBhMCQ0gxCzAJBgNVBAgMAkJTMQ4wDAYDVQQHDAVCYXNlbDEV
MBMGA1UECgwMZGJpIHNlcnZpY2VzMQwwCgYDVQQLDANkYmExDjAMBgNVBAMMBXBn
Ym94MRgwFgYJKoZIhvcNAQkBFgl4eEB4eEBjb20wggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQDLT9G3gcSDIi/7n0v6ahZ3/WI3kfEJzMThBOHe8j93Nezl
j1oDHXtTjlpydkIqy5WaNUqYHXg8IYU9fFn26Hsg0HPbQv84ygwT9sw+vLCPQSnx
xzNFeccEM1FHCyP41lhoLZWDya1AfJWaDP+SvdZPspZsQUUN6xlXs5r8HIIBnC3l
LhsPR6uE+mXtgOcZ2quJCe1qLDqq/ty6U+VSPxzbR0xK1uUPdhLf9Gz9WvulcLR7
BsMMsU3PBI5csAXL8qx4phJEVQf5iFVZIxEP3VMUaujEu2qUrx5U6H1PEIrlfjE7
zygogDdi615JJp0QFzO8pz8qBqTwN6WzB23OarcXAgMBAAGjUDBOMB0GA1UdDgQW
BBTqY7F/B98xP1Uod8z78h861kU/VTAfBgNVHSMEGDAWgBTqY7F/B98xP1Uod8z7
8h861kU/VTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAYK5a2Adg+
f7s1DEtTwpwCIkElgtO2qYhuDl1b06wAQwoE9BJuIv0/d2MOQijjCWsWZ1+3CAh0
o1UfSQlpluj2LpyK1qDi99gwYgbwXhqF/v8tOWT38enOIQLzhl879hIdYc2ovzbi
mNSZtpVeBYeNqy8wOLL+aKxQjZj9qk154vVxktblHVlCAkl6LuDzukFN9BUzRDYU
Qzt6QRthbP94+xNKpOCWbEWADjDjY53c8XcWIpx6yZKWUztih8rL6EqkT2mmoFqp
675Yf8Ha1NdB1FQG+1uL6kZo9eYeK2oLZflmWqIU7OsFL5lGvLvYEfY/Lm4VSKxw
Hxgt4nhLo8vv
-----END CERTIFICATE-----

For PostgreSQL to accept the key when it starts up you’ll need to modify the permissions:

postgres@pgbox:/home/postgres/ [PG960] chmod 600 server.key
postgres@pgbox:/home/postgres/ [PG960] ls -l server.key
-rw-------. 1 postgres postgres 1675 Sep  9 13:30 server.key

Both files (server.key and server.crt) need to be copied to your data directory (you can adjust this by using the ssl_cert_file and ssl_key_file configuration parameters):

postgres@pgbox:/home/postgres/ [PG960] mv server.key server.crt $PGDATA/

Now you can turn on ssl…

(postgres@[local]:5432) [postgres] > alter system set ssl='on';
ALTER SYSTEM
Time: 5.427 ms

… and restart your instance:

postgres@pgbox:/home/postgres/ [PG960] pg_ctl -D $PGDATA restart -m fast

How can you test if SSL connections do work? Add the following line to pg_hba.conf for your instance:

hostssl  all             all             127.0.0.1/32            md5

Reload your server and then create a new connection:

postgres@pgbox:/u02/pgdata/PG1/ [PG960] psql -h localhost -p 5432 postgres
psql (9.6rc1 dbi services build)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

Works as expected. For everything which is not for testing you’ll need a real certificate, of course. Just in case you expected to configure another port: PostgreSQL listens for normal and SSL connections on the same port. When the client supports SSL then SSL connections will be established, otherwise normal connections. When you want to force the use of SSL connections you can do it by adjusting your pg_hba.conf (deny all connections which are not SSL).