Connecting to a Private or Shield Heroku Postgres Database from an External Resource
Last updated June 04, 2024
Table of Contents
Use Mutual TLS to create a secure and mutually authenticated channel between an external resource and a Heroku Postgres database running in a Private Space or a Shield Private Space. External resources can include any mTLS-enabled application or system running in private data centers or public clouds. To use this feature, you must also allowlist the external IP that will connect to your database.
Overview
This feature is only available for Private and Shield Postgres databases.
Heroku provisions Certificate Authorities (CAs) in the Private Space and Shield Private Space, and generates certificates for the Postgres database server and your client. The server certificate chain, client certificate chain, and client private key are then exposed for configuration of your Postgres client. You can provision additional client certificates if needed.
This process involves three high-level steps:
- Configuring Mutual TLS and allowlisting your external IP
- Setting client-side certificates
- Connecting to your database from an external resource
Heroku prerequisites
The following Heroku resources are required to set up Mutual TLS:
- A Private Space or Shield Private Space
- A Heroku app running in the Space with an attached Private or Shield Heroku Postgres database
Configuring Mutual TLS and allowlisting your IP
Step 1: Install the Mutual TLS CLI plugin
$ heroku plugins:install mtls
Step 2: Enable Mutual TLS
Enable Mutual TLS on your database using:
$ heroku data:mtls:create DATABASE_NAME --app APP_NAME
where DATABASE_NAME
is the name of your Postgres database, and APP_NAME
is the name of your application.
Here’s an example command with accompanying output:
$ heroku data:mtls:create postgresql-rectangle-12345 --app example-app
addon: postgresql-rectangle-12345
app: example-app
status: Provisioning
Enabling MTLS on postgresql-rectangle-12345... done
It typically takes 5–10 minutes to enable Mutual TLS. You can track your progress with heroku data:mtls DATABASE_NAME --app APP_NAME
.
Step 3: Allowlist external IPs
A hard limit of 60 IP blocks can be allowlisted per Postgres database. After Mutual TLS has been enabled, allowlist your IP block to access your Postgres database using the following Heroku CLI command (note the values to substitute below):
$ heroku data:mtls:ip-rules:create DATABASE_NAME --app APP_NAME \
--cidr CIDR_BLOCK \
--description DESCRIPTION
- Replace
DATABASE_NAME
with the name of your Postgres database (for example,postgresql-rectangle-12345
). - Replace
APP_NAME
with your app’s name (for example,example-app
). - Replace
CIDR_BLOCK
with the CIDR block you would like to allowlist. An individual IP address is represented asx.x.x.x/32
where x can be any number between 0 and 255 (inclusive). - Replace
DESCRIPTION
with a readable description of this allowlisted CIDR block (for example,"Office IP"
).
Here’s an example command with accompanying output:
$ heroku data:mtls:ip-rules:create postgresql-rectangle-12345 --app example-app \
--cidr "1.2.3.4/32" \
--description "My Office IP"
Creating IP Rule for database postgresql-rectangle-12345... done
cidr: 1.2.3.4/32
created_at: 2019-07-17 00:05:52 +0000
description: My Office IP
id: 38c466b6-dcfb-4869-b5ac-40420b786fb4
status: Authorizing
updated_at: 2019-07-17 00:05:52 +0000
It usually takes a few minutes to allowlist your external IPs. You can track your progress with the following command:
$ heroku data:mtls:ip-rules:get DATABASE_NAME --id IP_RULE_ID --app APP_NAME
where DATABASE_NAME
and APP_NAME
are as described above, and IP_RULE_ID
is the id
in the output of the create command (for example, the id is 38c466b6-dcfb-4869-b5ac-40420b786fb4
for the IP allowlisted above).
Here’s an example command with accompanying output:
$ heroku data:mtls:ip-rules:get postgresql-rectangle-12345 --id "38c466b6-dcfb-4869-b5ac-40420b786fb4" --app example-app
cidr: 1.2.3.4/32
created_at: 2019-07-17 00:05:52 +0000
description: My Office IP
id: 38c466b6-dcfb-4869-b5ac-40420b786fb4
status: Authorized
updated_at: 2019-07-17 00:05:52 +0000
You can view all the IP addresses allowlisted for this database with heroku data:mtls:ip-rules DATABASE_NAME --app APP_NAME
.
Setting client-side certificates
Step 1: Download client-side certificates
View all client-side certificates for the Postgres database using
$ heroku data:mtls:certificates DATABASE_NAME --app APP_NAME
where DATABASE_NAME
and APP_NAME
are as defined above. Here’s an example command with accompanying output:
$ heroku data:mtls:certificates postgresql-rectangle-12345 --app example-app
id Created At Status
61acf66f-e505-452c-a517-e727689fb54f 2019-07-16 23:39:26 +0000 ready
Download the certificates needed to configure your client to access your Postgres database using the following Heroku CLI command (note the values to substitute below):
$ heroku data:mtls:certificates:download DATABASE_NAME \
--id CERTIFICATE_ID \
--prefix PREFIX \
--dir DIRECTORY \
--app APP_NAME
- Replace
DATABASE_NAME
with the name of your Postgres database (for example,postgresql-rectangle-12345
). - Replace
APP_NAME
with your app’s name (for example,example-app
). - Replace
CERTIFICATE_ID
with theid
of the certificate to download (for example, theid
of the certificate in the example above is61acf66f-e505-452c-a517-e727689fb54f
). - Replace
PREFIX
with a prefix to put in front of the names of the downloaded files (for example,demo_
). - Replace
DIRECTORY
with the directory to download files to (for example,./folder
). This defaults to$HOME/.postgresql
.
Here’s an example command with accompanying output:
$ heroku data:mtls:certificates:download postgresql-rectangle-12345 \
--id "61acf66f-e505-452c-a517-e727689fb54f" \
--prefix "demo_" \
--dir "./folder" \
--app example-app
Downloading certificates for database postgresql-rectangle-12345... done
your certs are now located in ./folder
Open up the folder
directory and you should see three files:
- demo_postgresql.crt (client certificate with chain)
- demo_postgresql.key (client private key)
- demo_root.crt (server certificate chain)
Step 2: Configure environment variables
Set necessary environment variables using the following Heroku CLI command (note the values to substitute below):
$ export DATABASE_URL=`heroku config:get DATABASE_URL -a APP_NAME`
$ export PGSSLCERT=DIRECTORY/PREFIXpostgresql.crt
$ export PGSSLKEY=DIRECTORY/PREFIXpostgresql.key
$ export PGSSLROOTCERT=DIRECTORY/PREFIXroot.crt
- Replace
DATABASE_URL
with the URL of your Postgres database (for example,postgresql-rectangle-12345
). - Replace
APP_NAME
with your app’s name (for example,example-app
). - Replace
DIRECTORY
with the directory you downloaded files to in the previous step. - Replace
PREFIX
with the prefix you put in front of the names of the downloaded files in the previous step.
Set appropriate permissions for your private key. For the key downloaded in the previous step, permissions can be set using:
$ chmod 0600 ./folder/demo_postgresql.key
You don’t need to set the PGSSLCERT, PGSSLKEY, and PGSSLROOTCERT config variables if you downloaded your files to the default directory without setting any prefix.
It isn’t recommended to omit the prefix on your development environment as it causes the certificate to be sent to all databases, which can interfere with normal Heroku CLI operations.
Client certificate renewal
A client certificate has an expiration date of 13 months after its creation date. 75 days before the expiration date, the following occurs:
- A new client certificate is created for you
- An email is sent to you about the pending expiration of your current certificate. The contents of the email includes details about your new certificate and how to begin using it.
After a new certificate has been created, it’s recommended you begin using it before the current certificate expires and no longer works.
Connecting to your database from an external resource
You can now connect to your database using:
$ psql "${DATABASE_URL}?sslmode=verify-ca"
psql (11.1, server 11.4 (Ubuntu 11.4-1.pgdg16.04+1))
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
abcdefghij1234=>
Configuring external applications to connect via mTLS
When configuring other applications to connect to your database via mTLS we recommend referring to the documentation for that application to see if there are any special requirements.
For example applications which use the JDBC PostgreSQL connector require a connection string containing the sslcert
, sslkey
, sslroot
and sslmode
parameters, and require that the key be provided in either the PKCS-12 or PKCS-8 DER format.
The key provided by the data:mtls:certificates:download
command is in a PEM format, which can be converted to DER format using this openssl
command (run from the directory containing the downloaded key file):
$ openssl pkcs8 -topk8 -inform PEM -in postgresql.key -outform DER -out postgresql.pk8 -nocrypt
As described above, you should set appropriate permissions on the new key file:
$ chmod 0600 postgresql.pk8
Further details on constructing the JDBC connection URL are available in the official PostgreSQL documentation.
For any issues or concerns with using this feature, open a support ticket.
Configuring Mutual TLS via the Dashboard
You can enable MTLS and adjust allowed IPs for a Private or Shield Postgres database from the Heroku dashboard. From the dashboard, select a Heroku Postgres database and then go to the Settings
tab. To configure MTLS, click Show Configuration
next to Secure External Database Access
.