We use cookies to enhance your experience on the site
CodeWorlds

Secrets Management — Secret Dispatches of the Empire

In the Roman Empire, the most important information — battle plans, treasury locations, legion passwords — was transmitted via encrypted dispatches. Only authorized commanders could read them. In production applications, secrets management serves the same role — it protects passwords, API keys, and certificates from unauthorized access.

Why NOT .env in Production?

Many developers store secrets in

.env
files — for development this is acceptable, but in production it is a serious risk:

1// Problems with .env files in production
2const envProblems = {
3  leak: '.env file accidentally committed to repository',
4  noRotation: 'Manual password changes on every server',
5  noAudit: 'No record of who accessed secrets and when',
6  noEncryption: 'Secrets stored as plain text on disk',
7  scaling: 'Copying .env to every new server',
8};
9
10// GitHub Leaked Secrets (2023):
11// > 12 million secrets leaked from public repositories

HashiCorp Vault — Treasury of the Empire

HashiCorp Vault is the most popular secrets management tool. It works like the imperial treasury (aerarium) — centrally storing and controlling access to secrets:

1// How Vault manages secrets
2interface VaultConcepts {
3  engine: 'Secret Engine — storage backend (KV, Database, PKI)';
4  policy: 'Policy — access rules (who can read what)';
5  token: 'Token — authorization key (like the emperor\'s seal)';
6  lease: 'Lease — secret lifetime (automatic rotation)';
7  audit: 'Audit Log — complete access log';
8}
9
10// Example Vault API usage
11// vault kv put secret/roman-api DATABASE=mongodb://... JWT_SECRET=...
12// vault kv get secret/roman-api

Vault Integration with NestJS

1// config/vault.config.ts
2import { registerAs } from '@nestjs/config';
3import Vault from 'node-vault';
4
5const vault = Vault({
6  endpoint: process.env.VAULT_ADDR || 'http://127.0.0.1:8200',
7  token: process.env.VAULT_TOKEN,
8});
9
10export default registerAs('secrets', async () => {
11  const result = await vault.read('secret/data/roman-api');
12  return {
13    database: result.data.data.DATABASE,
14    jwtSecret: result.data.data.JWT_SECRET,
15    redisPassword: result.data.data.REDIS_PASSWORD,
16  };
17});
18
19// app.module.ts — asynchronous loading
20@Module({
21  imports: [
22    ConfigModule.forRoot({
23      isGlobal: true,
24      load: [vaultConfig],
25    }),
26  ],
27})
28export class AppModule {}

AWS Secrets Manager

For applications hosted on AWS, Secrets Manager provides native integration:

1// config/aws-secrets.ts
2import {
3  SecretsManagerClient,
4  GetSecretValueCommand,
5} from '@aws-sdk/client-secrets-manager';
6
7const client = new SecretsManagerClient({ region: 'eu-central-1' });
8
9export async function getSecrets(): Promise<Record<string, string>> {
10  const command = new GetSecretValueCommand({
11    SecretId: 'roman-api/production',
12  });
13
14  const response = await client.send(command);
15  return JSON.parse(response.SecretString);
16}
17
18// Usage in NestJS
19export default registerAs('aws', async () => {
20  const secrets = await getSecrets();
21  return {
22    database: secrets.DATABASE,
23    jwtSecret: secrets.JWT_SECRET,
24  };
25});

Kubernetes Secrets

In Kubernetes, we store secrets as Secret objects, encoded in base64:

1# k8s/secret.yaml
2apiVersion: v1
3kind: Secret
4metadata:
5  name: roman-api-secrets
6  namespace: production
7type: Opaque
8data:
9  DATABASE: bW9uZ29kYjovL2ltcGVyYXRvcjpyb21hQG1vbmdvZGI6MjcwMTcvaW1wZXJpdW0=
10  JWT_SECRET: c3VwZXItc2VjcmV0LWtleS1taW4tMTYtY2hhcnM=

Sealed Secrets — Safe Committing

Sealed Secrets allow you to safely store secrets in a Git repository — they are encrypted with the cluster's public key:

1# Install kubeseal
2brew install kubeseal
3
4# Encrypt secrets
5kubeseal --format yaml < secret.yaml > sealed-secret.yaml
6
7# Only the cluster can decrypt sealed-secret.yaml
8# It is safe to commit to the repository

Secret Rotation

Regular secret rotation minimizes the risk of leaks:

1// Secret rotation strategy
2interface RotationStrategy {
3  secret: string;
4  interval: string;
5  method: string;
6}
7
8const rotationPolicies: RotationStrategy[] = [
9  {
10    secret: 'DATABASE_PASSWORD',
11    interval: 'Every 90 days',
12    method: 'Vault dynamic secrets — automatic rotation',
13  },
14  {
15    secret: 'JWT_SECRET',
16    interval: 'Every 30 days',
17    method: 'Vault KV v2 — versioning + graceful rotation',
18  },
19  {
20    secret: 'API_KEYS',
21    interval: 'Every 60 days',
22    method: 'AWS Secrets Manager — automatic Lambda rotation',
23  },
24];

NestJS ConfigModule with External Provider

1// config/secrets.provider.ts
2import { ConfigModuleOptions } from '@nestjs/config';
3
4export const secretsConfig: ConfigModuleOptions = {
5  isGlobal: true,
6  load: [
7    // Priority 1: Vault (production)
8    async () => {
9      if (process.env.VAULT_ADDR) {
10        return loadFromVault();
11      }
12      return {};
13    },
14    // Priority 2: AWS Secrets Manager
15    async () => {
16      if (process.env.AWS_REGION) {
17        return loadFromAWS();
18      }
19      return {};
20    },
21    // Priority 3: Environment variables (fallback)
22    () => ({
23      database: process.env.DATABASE,
24      jwtSecret: process.env.JWT_SECRET,
25    }),
26  ],
27};
28
29// app.module.ts
30@Module({
31  imports: [ConfigModule.forRoot(secretsConfig)],
32})
33export class AppModule {}

Secrets management is the art of protecting the Empire's secret dispatches. Never leave secrets in .env files in production — use dedicated tools: Vault, AWS Secrets Manager, or Kubernetes Secrets.

Go to CodeWorlds