Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Secrets Management - tajne depesze Imperium

Plik

.env
poznałeś w module wprowadzającym i lokalnie sprawdza się doskonale. Na produkcji jest inaczej - i warto wiedzieć dlaczego, zanim ktoś przekona Cię, że wystarczy dopisać go do
.gitignore
.

Cztery powody naraz. Ryzyko przypadkowego commita - jedno

git add -f
albo nowy programista bez skonfigurowanego
.gitignore
i hasło jest w historii repozytorium na zawsze. Brak rotacji - zmiana hasła do bazy oznacza ręczną edycję pliku na każdym serwerze. Brak audytu - nikt nie wie, kto i kiedy odczytał sekret. I plain text - hasła leżą jawnym tekstem na dysku, dostępne dla każdego procesu z uprawnieniami do odczytu.

Zauważ, czego wśród tych powodów nie ma: pliki

.env
nie są wolne, nie zajmują pamięci i działają w Node.js bez zarzutu. Problem nie jest techniczny, tylko organizacyjny.

Rzym nie wysyłał tajnych depesz zwykłym posłańcem. Trafiały do kancelarii, która wydawała je za pokwitowaniem i wiedziała, kto co odebrał.

Kolejność plików .env

Zanim przejdziemy do kancelarii, jedna rzecz o samych plikach.

ConfigModule
przyjmuje ich listę:

1ConfigModule.forRoot({
2  isGlobal: true,
3  envFilePath: ['.env.local', '.env.development', '.env'],
4});

Kolejność rejestracji jest stała:

ConfigModule
,
.forRoot({
, opcje z
isGlobal: true,
, na końcu
})
.

A teraz rzecz, która myli najczęściej: wygrywa plik podany jako pierwszy. Gdy ta sama zmienna występuje w kilku plikach, wartość bierzemy z

.env.local
- nie z
.env
, mimo że to on wygląda na „główny".

Logika jest taka: od najbardziej szczegółowego do najbardziej ogólnego.

.env.local
to Twoje osobiste nadpisania, których nikt inny nie ma;
.env.development
to ustawienia całego środowiska;
.env
to wartości domyślne. Bardziej szczegółowy przesłania ogólniejszy.

Kancelaria - HashiCorp Vault

HashiCorp Vault to narzędzie do centralnego zarządzania sekretami z audytem i rotacją. Nie jest frameworkiem frontendowym, systemem testów ani narzędziem do kompresji obrazów - to skarbiec, który wydaje wartości na żądanie i zapisuje każde wydanie.

Rozwiązuje dokładnie te braki, które wypisaliśmy: sekret nie leży w pliku (nie ma czego zacommitować), można go zmienić w jednym miejscu (rotacja), a każdy odczyt zostaje odnotowany (audyt).

Aplikacja pobiera sekrety przy starcie, zanim cokolwiek ich potrzebuje:

1export async function loadSecrets(): Promise<Record<string, string>> {
2  const vault = new VaultClient({
3    endpoint: process.env.VAULT_ADDR,
4    token: process.env.VAULT_TOKEN,
5  });
6
7  const { data } = await vault.read('secret/data/legion-api');
8
9  return {
10    DATABASE: data.data.DATABASE,
11    JWT_SECRET: data.data.JWT_SECRET,
12    REDIS_PASSWORD: data.data.REDIS_PASSWORD,
13  };
14}

Zwróć uwagę na paradoks, który tu widać: żeby dostać się do skarbca, też potrzebujesz klucza - adresu i tokenu, które nadal przychodzą ze zmiennych środowiskowych. Vault nie usuwa tego problemu, tylko sprowadza go do jednego sekretu zamiast dwudziestu. Ten jeden bywa dodatkowo wydawany krótkoterminowo przez mechanizm samej infrastruktury.

Priorytet źródeł

W dojrzałej konfiguracji sekrety mogą pochodzić z kilku miejsc naraz. Kolejność od najwyższego priorytetu wygląda tak:

  1. HashiCorp Vault - produkcja; ma audyt i rotację.
  2. AWS Secrets Manager - chmura; to samo, ale wbudowane w platformę dostawcy.
  3. Zmienne środowiskowe - fallback; działają zawsze, także lokalnie i w testach.

Reguła jest prosta: im wyżej, tym więcej kontroli. Fallback na zmienne środowiskowe zostaje na dole nie dlatego, że jest zły, tylko dlatego, że nie daje ani audytu, ani rotacji - ale za to działa bez żadnej infrastruktury, więc lokalne uruchomienie nie wymaga stawiania Vaulta.

Odczyt w aplikacji

Niezależnie od źródła, w kodzie sięgasz po sekret tak samo:

1const dbUrl = this.configService.get<string>('DATABASE');

Kolejność członów jest stała:

this
,
.configService
,
.get<string>
,
('DATABASE')
. To ta sama metoda, którą poznałeś przy konfiguracji - i o to chodzi. Serwis nie wie, czy wartość przyszła z Vaulta, z chmury, czy ze zmiennej środowiskowej; zmiana źródła nie dotyka ani jednej linii logiki.

Sekrety w Kubernetes

W klastrze pojawia się dodatkowy kłopot: konfigurację opisujemy plikami YAML, które trzymamy w repozytorium - a sekret w takim pliku to znowu hasło w Gicie.

Sealed Secrets umożliwiają bezpieczne przechowywanie zaszyfrowanych sekretów w repozytorium Git. Szyfrujesz wartość kluczem publicznym kontrolera działającego w klastrze; powstały plik możesz spokojnie zacommitować, bo odszyfrować go potrafi wyłącznie ten kontroler, kluczem prywatnym, którego nigdzie nie wysyłasz.

To rozwiązuje sprzeczność między „wszystko w repozytorium" a „sekretów w repozytorium nie trzymamy". Sealed Secrets nie skalują podów, nie kompresują obrazów i nie monitorują zużycia - szyfrują sekrety tak, by mogły podróżować z kodem.

Podsumowanie

Kancelaria wydaje depesze za pokwitowaniem:

  • .env
    na produkcji jest niebezpieczny z czterech powodów naraz: ryzyko przypadkowego commita, brak rotacji, brak audytu, plain text - nie z powodu wydajności czy pamięci,
  • w
    envFilePath
    wygrywa plik podany jako pierwszy:
    .env.local
    przesłania
    .env.development
    , a ten
    .env
    ,
  • rejestracja w kolejności:
    ConfigModule
    ,
    .forRoot({
    ,
    isGlobal: true,
    ,
    })
    ,
  • HashiCorp Vault to narzędzie do centralnego zarządzania sekretami z audytem i rotacją,
  • dostęp do Vaulta też wymaga sekretu - ale jednego zamiast dwudziestu,
  • priorytet źródeł od najwyższego: Vault (produkcja) → AWS Secrets Manager (chmura) → zmienne środowiskowe (fallback),
  • odczyt zawsze tak samo:
    this
    +
    .configService
    +
    .get<string>
    +
    ('DATABASE')
    - logika nie wie, skąd wartość pochodzi,
  • Sealed Secrets umożliwiają bezpieczne przechowywanie zaszyfrowanych sekretów w repozytorium Git - odszyfruje je tylko kontroler w klastrze.

W następnej lekcji wrócimy do pipeline'u - tym razem z macierzami budowania, artefaktami i cache'em. A na razie zapamiętaj: plik

.env
mówi, co jest hasłem; kancelaria mówi dodatkowo, kto je odebrał i kiedy je zmieniono.

Przejdź do CodeWorlds