AWS Aurora Key Store
Tayra.KeyStore.AwsAurora stores encryption keys in Amazon Aurora PostgreSQL authenticated with AWS IAM database authentication - no static password is ever stored in config or transmitted in plaintext. Tokens are short-lived (15 min) and refreshed automatically by the underlying NpgsqlDataSource on a 13-minute cadence.
Under the hood this is the same PostgreSqlKeyStore you already know - Aurora's wire protocol is PostgreSQL - wrapped with an NpgsqlDataSource whose password provider is the AWS SDK's RDSAuthTokenGenerator. You get the existing Postgres keystore's transactional semantics, schema/table customisation, retry behaviour, and crypto-shred guarantees.
When to use
- Your keystore must run on AWS Aurora (compliance, infra mandate, or because Aurora is your operational standard).
- You can't or don't want to manage a static database password.
- You want IAM-driven access control (rotate access by changing IAM policies, not database credentials).
- You want the same keystore to scale up to millions of subjects in envelope encryption mode.
If you're happy with password authentication on a vanilla PostgreSQL connection, use UsePostgreSqlKeyStore(...) - Aurora is wire-compatible and works fine with the existing Postgres keystore. Reach for the Aurora package only when you specifically want IAM auth.
Prerequisites in your AWS account
Aurora IAM authentication is not enabled by default. You need:
- IAM auth enabled on the cluster. Set
EnableIAMDatabaseAuthentication = true(Console: Modify cluster → Database authentication → Password and IAM database authentication). - A database user with the
rds_iamrole. Run once in the cluster:sqlCREATE USER tayra_app; GRANT rds_iam TO tayra_app; GRANT CONNECT ON DATABASE tayra TO tayra_app; GRANT USAGE, CREATE ON SCHEMA public TO tayra_app; - An IAM policy allowing your app's IAM principal to connect:json
{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": "rds-db:connect", "Resource": "arn:aws:rds-db:us-east-1:123456789012:dbuser:cluster-resource-id/tayra_app" }] } - SSL is mandatory. Aurora rejects IAM connections without SSL; the keystore defaults to
VerifyFull.
Configuration
using Tayra.KeyStore.AwsAurora;
builder.Services
.AddTayra(o => o.LicenseKey = config["Tayra:License"]!)
.UseAwsAuroraKeyStore(opts =>
{
opts.ClusterEndpoint = "my-cluster.cluster-xyz.us-east-1.rds.amazonaws.com";
opts.Database = "tayra";
opts.IamUser = "tayra_app";
opts.Region = "us-east-1";
// Optional - sensible defaults shown.
opts.Port = 5432;
opts.SslMode = AwsAuroraSslMode.VerifyFull;
opts.TokenRefreshInterval = TimeSpan.FromMinutes(13); // tokens valid 15 min
opts.TokenFailureRetryInterval = TimeSpan.FromSeconds(30);
opts.MaxPoolSize = 100;
// Schema/table customisation (inherited from PostgreSqlKeyStoreOptions)
opts.Schema = "public";
opts.TableName = "tayra_encryption_keys";
opts.AutoMigrate = true;
});The AWS credentials chain (env vars, profile, IRSA / IAM role for service account, EC2 instance role, etc.) is what signs the auth token - Tayra does not handle credentials directly.
As envelope-encryption master
Aurora pairs cleanly with the envelope encryption model - IAM auth ensures no static credentials touch the master keystore:
builder.Services
.AddTayra(o => o.LicenseKey = config["Tayra:License"]!)
// Data role: cheap Postgres for 15M wrapped DEKs
.UsePostgreSqlKeyStore(config.GetConnectionString("DataKeyStore")!)
// Master role: hardened Aurora with IAM auth, 1 master per org
.WithAwsAuroraMasterKey(opts =>
{
opts.ClusterEndpoint = "master.cluster-xyz.us-east-1.rds.amazonaws.com";
opts.Database = "tayra_master";
opts.IamUser = "tayra_master_app";
opts.Region = "us-east-1";
opts.KeyIdTemplate = "tayra:master:{tenantId}";
opts.CacheDuration = TimeSpan.FromMinutes(30);
});The default {tenantId} template covers per-tenant masters. To key by org id, region, environment, or any composite - set opts.MasterKeyIdResolver = sp => ... (a function with access to DI) or register your own IMasterKeyIdResolver. See Customizing the master key id.
How the IAM token is refreshed
The keystore uses NpgsqlDataSourceBuilder.UsePeriodicPasswordProvider. Behaviour:
- On startup, AWS SDK generates the first auth token (signed with your AWS credentials, no STS call).
- Every
TokenRefreshInterval(default 13 min, AWS-issued tokens are valid for 15 min), Npgsql calls the provider again. New connections from the pool use the fresh token; existing pooled connections continue to work. - If token generation fails (transient AWS SDK error), Npgsql retries every
TokenFailureRetryInterval(default 30 s).
You don't need to think about the token lifecycle - it's transparent to the keystore code.
Ports and security groups
Allow inbound TCP/5432 (or your Port) from your application's security group / VPC subnet. Aurora's private network is the default and recommended setup.
Cost notes
- IAM auth itself is free.
- Standard Aurora pricing applies (cluster instances, I/O, storage).
- Per-subject keys at consumer scale (millions of subjects) → use envelope encryption so the Aurora cluster only holds one master per org, not 15M rows. This converts a multi-thousand-dollar Aurora bill into a baseline cluster cost.
Crypto-shredding
Identical to PostgreSQL: DELETE removes the key row from the table immediately. Aurora's storage layer reclaims pages on its normal cadence; the row is gone from any active query plane the moment the transaction commits.
See also
- PostgreSQL Key Store - the underlying implementation
- AWS Secrets Manager - alternative AWS-native master keystore
- Envelope Encryption - pair Aurora with a cheap data store at scale
