Self-hosted database encryption and TLS
Encrypt the PostgreSQL database with your own KMS key, then require verified TLS on every connection, and check both with commands.
Your organization owns the PostgreSQL database of a self-hosted server. CodeHerder does not create it for you. This guide states two settings you must make and shows how to check each one.
Encrypt the database with your own KMS key
Use a customer-managed KMS key for the database storage. Do not use the AWS-managed aws/rds key. A customer-managed key lets you rotate it, restrict it, and audit every use.
- Create a symmetric customer-managed key in the same region as the database.
- Turn on automatic rotation:
aws kms enable-key-rotation --key-id <key-arn>. - Create the instance with
--storage-encrypted --kms-key-id <key-arn>. - Give the same key to snapshot copies, so that a restore can open them.
You cannot change the key of an existing instance. To move one, copy a snapshot with the new key. Then restore the copy as a new instance. Plan a maintenance window for the switch.
Only key administrators may call kms:ScheduleKeyDeletion or kms:PutKeyPolicy. A deleted key makes the database and every snapshot unreadable. See Self-hosted backup and recovery.
Key policy
The RDS service uses this key, not the CodeHerder server. The server instance role needs no grant on it. Do not add one.
Scope the key to RDS in your account. Name the role that creates the instance. Name your key administrators.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "KeyAdministrators",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::<account-id>:role/<key-admin-role>" },
"Action": [
"kms:Create*", "kms:Describe*", "kms:Enable*", "kms:List*",
"kms:Put*", "kms:Update*", "kms:Revoke*", "kms:Disable*",
"kms:Get*", "kms:ScheduleKeyDeletion", "kms:CancelKeyDeletion"
],
"Resource": "*"
},
{
"Sid": "UseOnlyThroughRdsInThisAccount",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::<account-id>:role/<database-provisioning-role>" },
"Action": [
"kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*",
"kms:GenerateDataKey*", "kms:CreateGrant",
"kms:ListGrants", "kms:DescribeKey"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:ViaService": "rds.<region>.amazonaws.com",
"kms:CallerAccount": "<account-id>"
}
}
}
]
}
Verify the key
Run these four commands. Check each result.
aws rds describe-db-instances --db-instance-identifier <id> \
--query 'DBInstances[0].[StorageEncrypted,KmsKeyId]'
aws kms describe-key --key-id <key-arn> \
--query 'KeyMetadata.[KeyManager,KeyState,Origin]'
aws kms get-key-rotation-status --key-id <key-arn>
aws kms get-key-policy --key-id <key-arn> --policy-name default
- The first command must print
trueand your key ARN. - The second must print
CUSTOMER,EnabledandAWS_KMS. - The third must show
KeyRotationEnabledastrue. Thedescribe-keycommand does not show rotation. - The fourth must show only the principals and conditions you chose.
Require verified TLS on every connection
Encryption on the wire is not enough. The client must also check who it talks to. Use sslmode=verify-full in the --db value.
./codeherder --db "postgres://user:pass@<db-host-name>:5432/codeherder?sslmode=verify-full&sslrootcert=/etc/codeherder/rds-bundle.pem"
- Use the DNS name of the database in the DSN. Never use an IP address, because the name check would fail.
- Download the regional CA bundle from
https://truststore.pki.rds.amazonaws.com/<region>/<region>-bundle.pem. Pointsslrootcertat the file. - Download the bundle again when AWS rotates the CA. Restart the server after you replace the file.
- Set
rds.force_sslto1in a custom parameter group. Attach the group to the instance. Do not rely on the engine default. - Set
CH_ENV=productionon the server. The server then refuses to start with a DSN that lackssslmode=verify-fullfor a database that is not on the same host. - Keep the DSN in an environment file with mode
0600. It holds the database password.
Under CH_ENV=production, the server and its maintenance commands refuse a non-local DSN without verify-full. This includes sslmode=require and verify-ca.
Verify TLS
aws rds describe-db-parameters --db-parameter-group-name <group> \
--query "Parameters[?ParameterName=='rds.force_ssl'].[ParameterValue,Source]"
psql "postgres://user@<db-host-name>:5432/codeherder?sslmode=disable"
psql "postgres://user@<db-host-name>:5432/codeherder?sslmode=verify-full&sslrootcert=/etc/codeherder/rds-bundle.pem" \
-c "select ssl, version from pg_stat_ssl where pid = pg_backend_pid()"
- The first command must print
1anduser. - The second command must fail with a message about encryption being required.
- The third command must print
tand a TLS version.
Related guides
- Self-hosting CodeHerder — start the server and set the
--dbvalue - Self-hosted backup and recovery — snapshots need the same key
- Self-hosted infrastructure — the AWS permissions the server needs
Last updated