/DB

HashiCorp Vault

Optional Socigy.OpenSource.DB.HashiCorp package: field encryption keyed from Vault and rotating PostgreSQL credentials from Vault's Database secrets engine, wired with one DI call each.

updated 6 Jun 20263 min readv0.3.2View as Markdown

The optional Socigy.OpenSource.DB.HashiCorp package integrates Socigy with HashiCorp Vault for two things:

  1. Field encryption. It supplies the IFieldEncryptor for [Encrypted] columns, keyed from Vault.
  2. Rotating DB credentials. It supplies an IDbCredentialsProvider that leases short-lived PostgreSQL credentials from Vault's Database secrets engine, which the generated connection factory consumes automatically.
dotnet add package Socigy.OpenSource.DB.HashiCorp
NOTE
This package depends on the main Socigy.OpenSource.DB package and VaultSharp. The two Vault features are independent: register either or both.

Field encryption

The data-encryption key is read from a Vault KV-v2 secret at startup and used for fast, local AES-256-CBC + HMAC encryption. Per-field crypto stays synchronous with no Vault round-trip per row. Store a Base64-encoded 32-byte key in Vault:

vault kv put secret/socigy/db-encryption-key key="$(openssl rand -base64 32)"

Then register it:

builder.Services.AddSocigyVaultEncryption(o =>
{
    o.Address = "https://vault.example.com:8200";
    o.Token = builder.Configuration["Vault:Token"];     // or AppRoleId + AppRoleSecretId
    o.KvMountPoint = "secret";
    o.KeySecretPath = "socigy/db-encryption-key";
    o.KeyField = "key";
});

At host start the package reads the key and installs the ambient SocigyFieldEncryption encryptor, so every [Encrypted] column just works.

NOTE
Key material is fetched into process memory for local crypto. Rotating the key means updating the KV secret and re-encrypting existing rows. (A Transit data-key envelope mode that keeps old rows readable across rotations is a planned enhancement.)

Rotating database credentials

Configure Vault's Database secrets engine with a role per database, then map each Socigy database name to its Vault role:

builder.Services.AddSocigyVaultCredentials(o =>
{
    o.Address = "https://vault.example.com:8200";
    o.AppRoleId = builder.Configuration["Vault:RoleId"];
    o.AppRoleSecretId = builder.Configuration["Vault:SecretId"];
    o.DatabaseMountPoint = "database";
    o.BaseConnectionString = "Host=db.internal;Port=5432;Pooling=true";   // no user/pass
    o.DatabaseRoles["AuthDb"] = "auth-db-role";
    o.DatabaseRoles["UserDb"] = "user-db-role";
    o.RefreshInterval = TimeSpan.FromMinutes(30);   // fallback only; renewal normally tracks the lease TTL
});

builder.AddAuthDb();   // the generated factory picks up IDbCredentialsProvider automatically
builder.AddUserDb();

How it fits together:

  • At startup the package leases credentials for each configured database and composes a connection string (BaseConnectionString + the leased Username/Password), cached in memory. The string is built with DbConnectionStringBuilder, so leased passwords containing ;, =, quotes or spaces are escaped correctly.
  • The generated connection factory calls the provider synchronously for the current connection string each time it opens a connection. See Connections & DI.
  • A background timer renews credentials at ~2/3 of the actual lease TTL (LeaseDurationSeconds returned by Vault), so they are refreshed before they expire even when the role issues short leases. RefreshInterval is only a fallback used when the lease TTL is unknown. When credentials rotate, new connections use the new string and Npgsql's old pool drains naturally.

Authentication

Both Add… calls accept either a token (Token) or AppRole (AppRoleId + AppRoleSecretId). AppRole is recommended for production workloads.

The Vault auth token is kept alive automatically by a background service: it renews the token (renew-self) before it expires, and when renewal can no longer extend it (max TTL) and AppRole credentials are configured, it re-logs-in for a fresh token. A static, non-renewable token cannot be kept alive — for long-running services use AppRole, a periodic token, or a renewable token. The library logs a clear error if it detects a token it cannot keep alive.

WARNING
Use an https:// Vault Address in production. If the address is plaintext http:// to a non-loopback host, the library logs a warning — tokens, keys, and leased credentials would otherwise travel unencrypted. Plain http:// to loopback is fine for local development.

Diagnostics

All background actions are observable so admins can track what the library does:

  • OpenTelemetry spans under the existing Socigy.OpenSource.DB ActivitySource: vault.encryption.key.fetch, vault.credentials.lease (with db.name, vault.database.role, and lease-duration tags), and vault.token.renew / vault.token.relogin for background auth-token upkeep. Subscribe with AddSource("Socigy.OpenSource.DB") (see Diagnostics & OpenTelemetry).
  • ILogger messages under categories Socigy.OpenSource.DB.Vault.Encryption and Socigy.OpenSource.DB.Vault.Credentials: key load, each credential lease (database, role, user, lease seconds), renewal ticks, and failures. The connection factory also logs when it refreshes rotating credentials, and SocigyFieldEncryption.Configure logs when an encryptor is installed.