Storing User Keys in OpenBAO
The user keys must be stored outside of the PostgreSQL server. The recommended way is to use the OpenBao KMS.
Warning
This document is intended for demos and training. DO NOT USE THIS IN PRODUCTION.
OpenBao or Hashicorp Vault ?
Info
Hashicorp Vault is now proprietary software. OpenBao is an open source fork of Hashicorp Vault maintained by the Cloud Native Computing Foundation.
We strongly recommend using OpenBao over Hashicorp Vault.
That being said the documentation below should work nicely with
Hashicorp Vault. All you have to do is replace the bao command
line by vault
or if you are lazy:
Requirements
The starting point of this how-to is to have a OpenBao installed on the same server as Postgres.
- A PostgreSQL instance up and running
- The Cryptic Elephant is installed
- OpenBao is installed (not running)
For simplicity, we will run OpenBao and Postgres on the same host. In practice, they should be located on 2 different hosts.
You can check the requirements are met with:
$ psql [...] -c `\dx` |grep tce
tce | 0.9.0-dev | 0.9.0-dev | public | The Cryptic Elephant (TCE)
$ bao -version
OpenBao v2.5.5
Please refer to the OpenBao Install Guide if you need assistance.
Start OpenBao
On the OpenBao host, launch the service
The output will contain a root token such as b.blIKqmKNXr5T4HGevc6gvTRH
Let the server run and open a new terminal.
You now access the web interface at
For this demo, we will use the CLI commands but every bao
command below can be achieved via the web interface too.
Configure the environment variables to use the CLI
Info
The variable names differ from OpenBao and Hashicorp Vault
You can check the server status at anytime with:
$ bao status
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 1
Threshold 1
Version 2.5.5
Build Date 2026-06-17T11:18:48Z
Storage Type inmem
Cluster Name vault-cluster-5d589040
Cluster ID 0f4e54e2-5b73-7ffb-405d-ab736a8e4fe9
HA Enabled false
Info
The OpenBao server runs in dev mode which means its
storage is not persistent. In other words, if you stop the
server, you will have to replay the entire how-to from here.
Principles
The TCE extension within Postgres will connect to OpenBao using a credentials file and will use the key assigned to each role.
flowchart
subgraph OpenBao
token
key
end
subgraph Postgres
role --> key
credentials --> token
end
Storing keys in OpenBAO
On the OpenBao host, the transit secret engine must be enabled to encrypt/decrypt using keys:
Now, authenticated as an OpenBAO role that will be used by Postgres, create the user keys in the vault:
The user keys are now available.
Add a Credential file
On the Postgres host:
First transfer the Openbao certificate located at /tmp/kms-tls/vault-ca.pem
on the OpenBao to the PostgreSQL instance in the
/var/lib/postgresql/.config/openbao folder
export TCE_CONFIG_DIR=/var/lib/postgresql/.config/openbao/
mkdir -p $TCE_CONFIG_DIR
# Adapt this line as needed
rsync /tmp/kms-tls/vault-ca.pem $TCE_CONFIG_DIR
Then to configure the openbao server's address and the token to use to interact
with OpenBAO, a configuration file must be created. By default this file is named
/var/lib/postgresql/.config/openbao/credentials.ini:
server_url=https://127.0.0.1:8200
server_cert_file=/var/lib/postgresql/.config/openbao/vault-ca.pem
# the root token that was provided by the `bao server` command earlier
token=b.blIKqmKNXr5T4HGevc6gvTRH
Configure the TCE extension
Connect to the Postgres instance, and set the tce.kms.type to openbao
alongside the location of the credentials file. It is advised to set this setting
database wide:
CREATE DATABASE demo;
ALTER DATABASE demo SET session_preload_libraries TO 'tce';
ALTER DATABASE demo SET tce.kms.type TO 'openbao';
Create a Role
Reconnect to the Postgres instance, to apply the changes and defines the roles:
\connect demo
CREATE EXTENSION tce;
CREATE ROLE sue SUPERUSER LOGIN;
SET ROLE sue;
SECURITY LABEL FOR tce_user_key ON ROLE sue
IS 'HOSTED ON KMS USING sue_key';
CREATE ROLE alice LOGIN;
ALTER DATABASE demo OWNER TO alice;
SECURITY LABEL FOR tce_user_key ON ROLE alice
IS 'HOSTED ON KMS USING alice_key';
Enjoy
Reconnect to the Postgres instance as Alice, to apply the changes and create the demo objects:
\connect demo alice
CREATE TABLE person (
id SERIAL,
firstname VARCHAR(100),
credit_card TCETEXT
);
INSERT INTO person VALUES
(1,'Bernard','1234567890'),
(2,'Bob','1234567890');
SELECT *, raw(credit_card) as raw_value from person;
id | firstname | credit_card | raw_value
----+-----------+-------------+---------------------------------------------
1 | Bernard | 1234567890 | TCE:229:132:89:15:181:57:201:...
2 | Bob | 1234567890 | TCE:53:68:180:197:138:99:200:...
Enhanced security
For better protection, each Postgres role may use its own OpenBao token and policy to access its own user key, this can be controlled using profiles:
flowchart
subgraph OpenBao
policy --> key
policy --> token
end
subgraph Postgres
credentials --> profile
profile --> token
profile --> role
key --> role
end
Create a Policy for each role
Define a strict policy to access only the encrypt/decrypt capabilities:
On the OpenBao host:
Create a policy file named /tmp/kms-tls/postgres-tce-transit.hcl
# Policy For Alice
path "transit/encrypt/alice_key" {
capabilities = [ "update" ]
}
path "transit/decrypt/alice_key" {
capabilities = [ "update" ]
}
# Policy For Sue
path "transit/encrypt/sue_key" {
capabilities = [ "update" ]
}
path "transit/decrypt/sue_key" {
capabilities = [ "update" ]
}
Activate the policy:
Create a personal token for this policy
Each role may use its own OpenBao token access to access their own keys, this can be controlled using profile
On the OpenBao host:
$ bao token create \
-policy="postgres-tce-transit" \
-ttl="30d" \
-display-name="alice-token"
$ bao token create \
-policy="postgres-tce-transit" \
-ttl="30d" \
-display-name="sue-token"
The output will contain a token such as s.hhiGEwpn5XIFMAof74f87LpX
The -ttl option defines how long the token will live. In this case,
the demo will stop working after 30 days.
Create a profile with each personal token
On the Postgres host:
Add the tokens for Alice and Sue in the credentials file where sections are role profiles used mainly to associate tokens to database roles.
By default the credentials file is named /var/lib/postgresql/.config/openbao/credentials.ini:
server_url=https://127.0.0.1:8200
server_cert_file=/var/lib/postgresql/.config/openbao/vault-ca.pem
[alice_profile]
token=s.hhiGEwpn5XIFMAof74f87LpX
[sue_profile]
token=s.koplhspskfemsf5f3c1s95dd
Create a token for this policy
And finally associate the profile to the role
\connect demo sue
ALTER ROLE alice SET tce.kms.openbao.profile TO 'alice_profile';
ALTER ROLE sue SET tce.kms.openbao.profile TO 'sue_profile';
Now Alice gets access to her user key via a dedicated policy
\connect demo alice
SELECT *, raw(credit_card) as raw_value from person;
id | firstname | credit_card | raw_value
----+-----------+-------------+---------------------------------------------
1 | Bernard | 1234567890 | TCE:229:132:89:15:181:57:201:...
2 | Bob | 1234567890 | TCE:53:68:180:197:138:99:200:...
Rotating keys
It is advised to change keys on a regular basis. Since the vault transit keys are stored in a keyring, old keys remain active even though new keys exist. This means that when a new key is created in OpenBAO key ring, updating the security label will automatically unlock the DEK using the old key and create a new envelope using the latest key.
This is the same thing when rotating envelope key, updating the security label will create a new envelope key.
Clean up
Once the demo done, you can return to the initial state with this procedure:
On the Postgres host:
Delete the credentials:
Drop the demo objects:
On the OpenBao host:
Just stop the OpenBao server, all the object were create in memory and the config files will be deleted.