W Imperium Rzymskim najważniejsze informacje — plany bitew, lokalizacje skarbców, hasła legionów — były przekazywane zaszyfrowanymi depeszami. Tylko uprawnieni dowódcy mogli je odczytać. W aplikacjach produkcyjnych, zarządzanie sekretami (secrets management) pełni identyczną rolę — chroni hasła, klucze API i certyfikaty przed nieautoryzowanym dostępem.
Wielu programistów przechowuje sekrety w plikach
.env — na development to akceptowalne, ale na produkcji to poważne zagrożenie:1// Problemy z plikami .env na produkcji
2const envProblems = {
3 wyciek: 'Plik .env przypadkowo commitowany do repozytorium',
4 brakRotacji: 'Ręczna zmiana haseł na każdym serwerze',
5 brakAudytu: 'Nie wiadomo, kto i kiedy miał dostęp do sekretów',
6 brakSzyfrowania: 'Sekrety zapisane jako plain text na dysku',
7 skalowanie: 'Kopiowanie .env na każdy nowy serwer',
8};
9
10// GitHub Leaked Secrets (2023):
11// > 12 milionów sekretów wyciekło z publicznych repozytoriówHashiCorp Vault to najpopularniejsze narzędzie do zarządzania sekretami. Działa jak cesarskie skarbce (aerarium) — centralnie przechowuje i kontroluje dostęp do sekretów:
1// Jak Vault zarządza sekretami
2interface VaultConcepts {
3 engine: 'Secret Engine — backend przechowywania (KV, Database, PKI)';
4 policy: 'Policy — reguły dostępu (kto co może odczytać)';
5 token: 'Token — klucz autoryzacji (jak pieczęć cesarza)';
6 lease: 'Lease — czas życia sekretu (automatyczna rotacja)';
7 audit: 'Audit Log — pełny rejestr dostępów';
8}
9
10// Przykład użycia Vault API
11// vault kv put secret/roman-api DATABASE=mongodb://... JWT_SECRET=...
12// vault kv get secret/roman-api1// 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 — ładowanie asynchroniczne
20@Module({
21 imports: [
22 ConfigModule.forRoot({
23 isGlobal: true,
24 load: [vaultConfig],
25 }),
26 ],
27})
28export class AppModule {}Dla aplikacji hostowanych na AWS, Secrets Manager zapewnia natywną integrację:
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// Użycie w NestJS
19export default registerAs('aws', async () => {
20 const secrets = await getSecrets();
21 return {
22 database: secrets.DATABASE,
23 jwtSecret: secrets.JWT_SECRET,
24 };
25});W Kubernetes sekrety przechowujemy jako obiekty Secret, zakodowane w 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 pozwalają bezpiecznie przechowywać sekrety w repozytorium Git — są zaszyfrowane kluczem publicznym klastra:
1# Instalacja kubeseal
2brew install kubeseal
3
4# Szyfrowanie sekretów
5kubeseal --format yaml < secret.yaml > sealed-secret.yaml
6
7# Tylko klaster może odszyfrować sealed-secret.yaml
8# Można bezpiecznie commitować do repozytoriumRegularna zmiana sekretów minimalizuje ryzyko wycieku:
1// Strategia rotacji sekretów
2interface RotationStrategy {
3 secret: string;
4 interval: string;
5 method: string;
6}
7
8const rotationPolicies: RotationStrategy[] = [
9 {
10 secret: 'DATABASE_PASSWORD',
11 interval: 'Co 90 dni',
12 method: 'Vault dynamic secrets — automatyczna rotacja',
13 },
14 {
15 secret: 'JWT_SECRET',
16 interval: 'Co 30 dni',
17 method: 'Vault KV v2 — wersjonowanie + graceful rotation',
18 },
19 {
20 secret: 'API_KEYS',
21 interval: 'Co 60 dni',
22 method: 'AWS Secrets Manager — automatyczna Lambda rotacji',
23 },
24];1// config/secrets.provider.ts
2import { ConfigModuleOptions } from '@nestjs/config';
3
4export const secretsConfig: ConfigModuleOptions = {
5 isGlobal: true,
6 load: [
7 // Priorytet 1: Vault (produkcja)
8 async () => {
9 if (process.env.VAULT_ADDR) {
10 return loadFromVault();
11 }
12 return {};
13 },
14 // Priorytet 2: AWS Secrets Manager
15 async () => {
16 if (process.env.AWS_REGION) {
17 return loadFromAWS();
18 }
19 return {};
20 },
21 // Priorytet 3: Zmienne środowiskowe (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 {}Zarządzanie sekretami to sztuka ochrony tajnych depesz Imperium. Nigdy nie zostawiaj sekretów w plikach .env na produkcji — używaj dedykowanych narzędzi: Vault, AWS Secrets Manager lub Kubernetes Secrets.