Vault secrets valdymas: slaptažodžių saugojimas

Kodėl slaptažodžiai bloknotuose – tai katastrofa laukianti savo valandos

Kiekvienas IT specialistas bent kartą gyvenime yra matęs (arba pats nusikaltęs) slaptažodį, įrašytą paprastame teksto faile, Excel lentelėje ar, dar blogiau, priklijuotą ant monitoriaus. Tai primena situaciją, kai namų raktus paliekame po kilimėliu – visi žino, kur ieškoti, bet vis tiek taip darome, nes „patogu”.

Šiuolaikinėje infrastruktūroje slaptažodžių ir API raktų kiekis auga eksponentiškai. Viena aplikacija gali turėti prieigą prie kelių duomenų bazių, išorinių servisų, debesų platformų. Kiekviena iš šių prieigų reikalauja autentifikacijos. Ir štai čia prasideda tikrasis galvos skausmas.

HashiCorp Vault atsirado kaip atsakas į šį chaosą. Tai ne tiesiog „slaptažodžių saugykla” – tai visa ekosistema, skirta saugiai valdyti bet kokius jautrius duomenis, nuo paprastų slaptažodžių iki šifravimo raktų ir sertifikatų. Bet prieš gilinantis į techninius dalykus, verta suprasti, kodėl tradiciniai metodai nebetinka.

Kai slaptažodžiai gyvena konfigūraciniuose failuose, jie keliauja per Git repozitorijas, backup’us, logus. Kartą patekę į sistemą, jie ten lieka amžinai. Net jei vėliau ištrinsite tokį failą, jis išliks Git istorijoje. O jei kas nors nukopijavo tą failą į savo kompiuterį? Jūs net nežinosite, kiek kopijų egzistuoja.

Kaip Vault iš esmės keičia žaidimo taisykles

Vault veikia pagal fundamentaliai kitokį principą nei tradicinės slaptažodžių valdymo sistemos. Vietoj to, kad tiesiog saugotų slaptažodžius šifruotame konteineryje, jis juos generuoja dinamiškai ir automatiškai anuliuoja.

Įsivaizduokite situaciją: jūsų aplikacija kreipiasi į Vault ir sako „man reikia prieigos prie PostgreSQL duomenų bazės”. Vault tuo momentu sukuria naują duomenų bazės vartotoją su laikinu slaptažodžiu, grąžina jį aplikacijai ir nustato, kad po 24 valandų šis vartotojas bus automatiškai ištrintas. Jei kas nors pavogs šį slaptažodį, jis bus naudingas tik ribotą laiką.

Tai vadinamieji dynamic secrets – vienas galingiausių Vault funkcionalumų. Bet sistema palaiko ir tradicinius statinius slaptažodžius, kai dinaminis generavimas nėra įmanomas.

Kitas svarbus aspektas – centralizuota audito sistema. Kiekvienas kreipimasis į Vault, kiekvienas slaptažodžio nuskaitymas fiksuojamas. Jei įvyksta saugumo incidentas, galite tiksliai pamatyti, kas, kada ir kokius slaptažodžius pasiekė. Su slaptažodžiais konfigūraciniuose failuose tai neįmanoma – niekada nežinosite, kas juos matė.

Vault architektūra be painiavos

Vault susideda iš kelių pagrindinių komponentų, kuriuos svarbu suprasti prieš pradedant diegimą.

Storage backend – tai vieta, kur fiziškai saugomi šifruoti duomenys. Vault palaiko daugybę variantų: nuo paprasto failo sistemos iki Consul, etcd ar debesų sprendimų kaip AWS S3. Svarbu suprasti, kad net jei kas nors gautų tiesioginę prieigą prie šio saugyklos, duomenys yra šifruoti ir nenaudojami be Vault.

Seal/Unseal mechanizmas – tai Vault saugumo šerdis. Kai Vault paleidžiamas, jis yra „sealed” būsenoje ir negali atlikti jokių operacijų. Kad jį „atplėšti” (unseal), reikia kelių raktų dalių. Pagal nutylėjimą Vault naudoja Shamir’s Secret Sharing algoritmą – pagrindinis raktas padalinamas į 5 dalis, ir reikia bent 3 iš jų, kad sistema pradėtų veikti.

Tai reiškia, kad net jei vienas administratorius tampa piktavalis arba praranda savo rakto dalį, sistema išlieka saugi. Tai kaip banko seifas, kuriam reikia trijų skirtingų raktų vienu metu.

Authentication methods – būdai, kaip klientai įrodo savo tapatybę. Vault palaiko daugybę metodų: nuo paprastų token’ų iki Kubernetes service accounts, AWS IAM roles, LDAP, GitHub autentifikacijos. Galite rinktis tai, kas geriausiai dera su jūsų infrastruktūra.

Secrets engines – tai pluginai, kurie valdo skirtingų tipų slaptažodžius. Yra engine’ų duomenų bazėms (PostgreSQL, MySQL, MongoDB), debesų platformoms (AWS, Azure, GCP), PKI sertifikatams, SSH raktams ir dar daugybei kitų dalykų.

Praktinis Vault diegimas žingsnis po žingsnio

Pradėkime nuo paprasčiausio scenarijaus – lokalaus Vault serverio paleidimo development tikslais. Gamybinė aplinka bus sudėtingesnė, bet principai tie patys.

Pirma, parsisiųskite Vault binary iš oficialios HashiCorp svetainės. Tai vienas executable failas, kuris veikia Linux, macOS ir Windows sistemose:


wget https://releases.hashicorp.com/vault/1.15.0/vault_1.15.0_linux_amd64.zip
unzip vault_1.15.0_linux_amd64.zip
sudo mv vault /usr/local/bin/

Development režime galite paleisti Vault be jokios konfigūracijos:


vault server -dev

Tai paleis Vault su visais nustatymais atmintyje, be jokio persistentinio saugojimo. Puikiai tinka eksperimentams, bet NIEKADA nenaudokite production’e.

Gamybiniam diegimui reikia konfigūracijos failo. Sukurkite config.hcl:


storage "file" {
path = "/opt/vault/data"
}

listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = 0
tls_cert_file = "/opt/vault/tls/vault.crt"
tls_key_file = "/opt/vault/tls/vault.key"
}

api_addr = "https://vault.example.com:8200"
cluster_addr = "https://vault.example.com:8201"
ui = true

Keletas svarbių pastabų apie šią konfigūraciją. NIEKADA nenaudokite tls_disable = 1 gamyboje. Vault perduoda labai jautrius duomenis, ir bet kokia komunikacija turi būti šifruota. Taip, tai reiškia papildomo darbo su sertifikatais, bet tai būtina.

Storage backend’ui pasirinkau paprastą failų sistemą demonstracijos tikslais. Gamyboje geriau naudoti Consul arba debesų sprendimus, nes jie palaiko high availability.

Paleiskite Vault:


vault server -config=config.hcl

Dabar prasideda inicializacija. Tai daroma tik vieną kartą per visą Vault gyvavimo ciklą:


vault operator init

Sistema sugeneruos 5 unseal raktus ir vieną root token. IŠSAUGOKITE JUOS SAUGIAI. Jei prarasite unseal raktus, niekada nebegalėsite pasiekti duomenų. Jei prarasite root token, turėsite naudoti unseal raktus jo atkūrimui.

Vault dabar yra sealed būsenoje. Unseal’inkite jį naudodami 3 iš 5 raktų:


vault operator unseal [KEY1]
vault operator unseal [KEY2]
vault operator unseal [KEY3]

Po trečio rakto Vault tampa aktyvus ir pasiruošęs naudojimui.

Secrets engine’ų konfigūravimas realiems scenarijams

Tuščias Vault yra kaip tuščias šaldytuvas – teoriškai naudingas, bet praktiškai nenaudingas. Reikia sukonfigūruoti secrets engine’us.

Pradėkime nuo paprasčiausio – KV (Key-Value) secrets engine. Tai tinka saugoti bet kokius statinius slaptažodžius:


vault secrets enable -path=secret kv-v2

Kodėl kv-v2? Nes antroji versija palaiko versijų valdymą. Jei per klaidą perrašysite slaptažodį, galėsite atkurti ankstesnę versiją.

Įrašykime pirmą slaptažodį:


vault kv put secret/myapp/database username="dbuser" password="super-secret-password"

Nuskaitykime jį:


vault kv get secret/myapp/database

Dabar įdomesnė dalis – dynamic secrets PostgreSQL duomenų bazei. Pirma, įjunkite database secrets engine:


vault secrets enable database

Sukonfiguruokite PostgreSQL connection:


vault write database/config/mypostgres \
plugin_name=postgresql-database-plugin \
allowed_roles="readonly" \
connection_url="postgresql://{{username}}:{{password}}@postgres.example.com:5432/mydb" \
username="vault" \
password="vault-password"

Čia vault vartotojas turi turėti teises kurti naujus vartotojus duomenų bazėje. Sukurkite role, kuri apibrėžia, kokias teises turės dinamiškai sukurti vartotojai:


vault write database/roles/readonly \
db_name=mypostgres \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"

Dabar bet kas su tinkamomis teisėmis gali gauti laikinę prieigą:


vault read database/creds/readonly

Vault sukurs naują PostgreSQL vartotoją, grąžins kredencialus, ir po 1 valandos automatiškai jį ištrins. Jokio rankinio valymo, jokių pasenusių vartotojų.

Policy sistema ir prieigos kontrolė

Vault be policy yra kaip durys be spynos – techniškai veikia, bet nesaugi. Policy apibrėžia, kas gali pasiekti kokius slaptažodžius.

Policy rašomos HCL (HashiCorp Configuration Language) formatu. Štai pavyzdys:


path "secret/data/myapp/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}

path "secret/data/otherapp/*" {
capabilities = ["read", "list"]
}

path "database/creds/readonly" {
capabilities = ["read"]
}

Ši policy leidžia visas operacijas su myapp slaptažodžiais, tik skaitymo prieigą prie otherapp, ir galimybę gauti dinaminius database kredencialus.

Sukurkite policy:


vault policy write myapp-policy myapp-policy.hcl

Dabar sukurkite token su šia policy:


vault token create -policy=myapp-policy

Grąžintas token turės tiksliai tas teises, kurias apibrėžėte policy. Jokių daugiau, jokių mažiau.

Realiose sistemose dažniausiai nenaudosite token’ų tiesiogiai. Vietoj to, naudosite authentication methods kaip Kubernetes arba AWS IAM, kurie automatiškai priskiria policy pagal aplikacijos identitetą.

Pavyzdžiui, Kubernetes autentifikacija:


vault auth enable kubernetes

vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc:443" \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
token_reviewer_jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token

Sukurkite role, kuri susieja Kubernetes service account su Vault policy:


vault write auth/kubernetes/role/myapp \
bound_service_account_names=myapp \
bound_service_account_namespaces=production \
policies=myapp-policy \
ttl=1h

Dabar jūsų aplikacija Kubernetes pod’e gali autentifikuotis naudodama savo service account token ir gauti prieigą prie slaptažodžių be jokių hardcoded kredencialų.

Integracija su aplikacijomis ir CI/CD

Vault vertė atsiskleidžia tik tada, kai jūsų aplikacijos pradeda jį naudoti. Yra keletas būdų tai padaryti.

Tiesioginis API naudojimas – Vault turi REST API, kurį galite kviesti iš bet kokios programavimo kalbos. Pavyzdys Python:


import hvac

client = hvac.Client(url='https://vault.example.com:8200')
client.token = 'your-token'

secret = client.secrets.kv.v2.read_secret_version(path='myapp/database')
username = secret['data']['data']['username']
password = secret['data']['data']['password']

Vault Agent – tai sidecar procesas, kuris automatiškai autentifikuojasi, atnaujina token’us ir gali net automatiškai įterpti slaptažodžius į konfigūracinius failus. Labai patogus Kubernetes aplinkose.

Vault Agent konfigūracijos pavyzdys:


pid_file = "./pidfile"

vault {
address = "https://vault.example.com:8200"
}

auto_auth {
method "kubernetes" {
mount_path = "auth/kubernetes"
config = {
role = "myapp"
}
}

sink "file" {
config = {
path = "/home/vault/.vault-token"
}
}
}

template {
source = "/etc/myapp/config.tpl"
destination = "/etc/myapp/config.yaml"
}

Template failas config.tpl gali atrodyti taip:


{{ with secret "secret/data/myapp/database" }}
database:
username: {{ .Data.data.username }}
password: {{ .Data.data.password }}
{{ end }}

Vault Agent automatiškai užpildys šabloną tikrais slaptažodžiais ir perkraus aplikaciją, kai slaptažodžiai pasikeičia.

CI/CD integracija – GitLab CI, GitHub Actions, Jenkins – visi gali integruotis su Vault. Pavyzdys GitLab CI:


deploy:
image: vault:latest
script:
- export VAULT_ADDR=https://vault.example.com:8200
- export VAULT_TOKEN=$(vault write -field=token auth/jwt/login role=gitlab-ci jwt=$CI_JOB_JWT)
- export DB_PASSWORD=$(vault kv get -field=password secret/myapp/database)
- ./deploy.sh

Čia naudojamas JWT autentifikacijos metodas, kuris leidžia GitLab CI job’ui autentifikuotis naudojant GitLab sugeneruotą JWT token. Jokių slaptažodžių GitLab variables!

Aukštas prieinamumas ir disaster recovery

Vault gamyboje turi būti high availability. Jei Vault nukrenta, visos aplikacijos, kurios nuo jo priklauso, taip pat nustos veikti.

Vault palaiko HA režimą su active-standby architektūra. Vienas Vault node’as yra active ir aptarnauja visus užklausimus, kiti yra standby ir pasiruošę perimti valdymą, jei active node’as nukrenta.

Kad tai veiktų, reikia storage backend’o, kuris palaiko HA. Populiariausi pasirinkimai:

Consul – HashiCorp produktas, specialiai sukurtas distributed sistemoms
Raft – integruotas Vault storage, nereikalaujantis išorinio Consul
– Debesų sprendimai: AWS DynamoDB, Google Cloud Storage, Azure Storage

Raft yra paprasčiausias variantas šiuolaikinėms Vault versijoms. Konfigūracija:


storage "raft" {
path = "/opt/vault/data"
node_id = "vault-1"
}

listener "tcp" {
address = "0.0.0.0:8200"
cluster_address = "0.0.0.0:8201"
tls_cert_file = "/opt/vault/tls/vault.crt"
tls_key_file = "/opt/vault/tls/vault.key"
}

api_addr = "https://vault-1.example.com:8200"
cluster_addr = "https://vault-1.example.com:8201"

Po inicializacijos prijunkite kitus node’us:


vault operator raft join https://vault-1.example.com:8200

Backup strategija yra kritiškai svarbi. Net jei naudojate HA, reikia backup’ų katastrofos atveju.

Vault palaiko snapshot’us:


vault operator raft snapshot save backup.snap

Šis failas yra šifruotas ir gali būti saugomas bet kur. Atkūrimas:


vault operator raft snapshot restore backup.snap

Automatizuokite snapshot’us cron job’u arba Kubernetes CronJob:


0 2 * * * vault operator raft snapshot save /backups/vault-$(date +\%Y\%m\%d).snap

Disaster recovery procedūra turėtų būti dokumentuota ir periodiškai testuojama. Pagrindiniai žingsniai:

1. Atkurkite Vault serverius naujoje infrastruktūroje
2. Restore paskutinį snapshot
3. Unseal Vault naudojant išsaugotus unseal raktus
4. Patikrinkite, ar visi slaptažodžiai prieinami
5. Atnaujinkite DNS įrašus, kad aplikacijos kreiптųsi į naują Vault

Jei praradote unseal raktus, situacija tampa daug sudėtingesnė. Todėl jie turi būti saugomi atskirai nuo Vault, idealiu atveju – offline arba hardware security module (HSM).

Kaip tai veikia realiame pasaulyje

Teorija yra gražu, bet kaip tai atrodo praktikoje? Pasidalinsiu keliais scenarijais iš realių projektų.

Scenarijus 1: Mikroservisų aplikacija Kubernetes

Turite 20 mikroservisų, kiekvienas reikalauja prieigos prie skirtingų duomenų bazių, Redis, RabbitMQ, išorinių API. Anksčiau visi slaptažodžiai buvo Kubernetes secrets, kurie iš tikrųjų yra tik base64 encoded tekstas.

Su Vault:
– Kiekvienas mikroservisas turi savo Kubernetes service account
– Vault autentifikacija sukonfigūruota kiekvienam service account su specifine policy
– Vault Agent sidecar container’is kiekviename pod’e automatiškai gauna slaptažodžius
– Duomenų bazių kredencialai yra dinaminiai, atnaujinami kas 24 valandas
– Centralizuoti audit logai rodo, kuris servisas kada pasiekė kokius slaptažodžius

Rezultatas: jei vienas mikroservisas būtų kompromituotas, užpuolikas gautų tik to konkretaus serviso slaptažodžius, ir tik 24 valandų laikotarpiui.

Scenarijus 2: CI/CD pipeline su daug aplinkų

Turite development, staging, production aplinkas. Kiekviena turi skirtingus slaptažodžius. GitLab CI pipeline turi deploy’inti į visas aplinkas.

Su Vault:
– Kiekviena aplinka turi savo Vault namespace arba path prefix
– GitLab CI autentifikuojasi naudojant JWT
– Policy leidžia prieigą tik prie tos aplinkos slaptažodžių, į kurią vyksta deployment
– Slaptažodžiai niekada nelieka GitLab variables arba logose

Rezultatas: developeriai gali deploy’inti į staging be prieigos prie production slaptažodžių. Audit logai rodo, kas kada deploy’ino ir kokius slaptažodžius naudojo.

Scenarijus 3: Sertifikatų valdymas

Turite dešimtis servisų, kuriems reikia SSL/TLS sertifikatų. Anksčiau sertifikatai buvo generuojami rankiniu būdu, išplatinami per Ansible, ir dažnai pasibaigdavo galiojimo laikas be perspėjimo.

Su Vault PKI engine:
– Vault veikia kaip intermediate CA
– Servisai automatiškai gauna sertifikatus per Vault API
– Sertifikatai galioja tik 30 dienų, bet automatiškai atnaujinami
– Vault Agent automatiškai perkrauna servisus su naujais sertifikatais

Rezultatas: jokių pasibaigusių sertifikatų, jokio rankinio darbo, automatinis rotation.

Dažniausios klaidos ir kaip jų išvengti

Per kelerius metus dirbant su Vault, mačiau daugybę klaidų. Štai dažniausios:

Klaida #1: Root token naudojimas production’e

Root token turi neribotą prieigą prie visko. Jį reikėtų naudoti tik inicializacijai ir emergency situacijoms. Sukurkite service-specific token’us su ribotomis teisėmis.

Klaida #2: TLS išjungimas „laikinai”

„Laikinai” virsta „amžinai” greičiau nei manote. Vault be TLS yra kaip bankas be durų. Naudokite Let’s Encrypt, jei neturite korporacinės CA.

Klaida #3: Nepakankamas policy granularity

Viena policy visiems servisams – bloga idėja. Kiekvienas servisas turi turėti tik tas teises, kurių jam reikia. Tai vadinamasis „least privilege” principas.

Klaida #4: Unseal raktų saugojimas toje pačioje vietoje kaip Vault

Jei Vault serveris kompromituotas, ir unseal raktai yra tame pačiame serveryje – žaidimas baigtas. Saugokite juos atskirai, idealiu atveju – offline.

Klaida #5: Ignoruojami audit logai

Vault generuoja detalizuotus audit logus, bet jei jų niekas nežiūri, kokia iš jų nauda? Integruokite su SIEM sistema arba bent jau periodiškai peržiūrėkite anomalijas.

Klaida #6: Statinių slaptažodžių naudojimas, kai galimi dinaminiai

Jei Vault palaiko dynamic secrets jūsų duomenų bazei ar debesų platformai – naudokite juos. Tai drastiškai sumažina riziką.

Klaida #7: Neišbandyta disaster recovery procedūra

Backup’ai be tested recovery yra tik iliuzija saugumo. Periodiškai praktikuokite Vault atkūrimą iš snapshot’o test aplinkoje.

Monitoringas ir troubleshooting

Vault turi būti monitorinamas kaip bet kuri kritinė sistema. Pagrindinės metrikos:

Seal status – ar Vault sealed ar unsealed
HA status – kuris node’as yra active
Token TTL – ar nėra token’ų, kurių galiojimo laikas baigiasi
Secret engine health – ar duomenų bazių connections veikia
Audit log volume – staigus padidėjimas gali reikšti ataką

Vault exportuoja Prometheus metricas:


telemetry {
prometheus_retention_time = "30s"
disable_hostname = true
}

Grafana dashboard’ai Vault monitoringui yra prieinami community.

Dažniausios problemos ir sprendimai:

Problema: „Error: permission denied”
Sprendimas: Patikrinkite policy. Naudokite vault token capabilities komandą pamatyti, kokias teises turi token:


vault token capabilities secret/data/myapp/database

Problema: „Error: connection refused”
Sprendimas: Patikrinkite, ar Vault procesas veikia, ar firewall leidžia 8200 portą, ar TLS sertifikatas galioja.

Problema: Vault sealed po restart
Sprendimas: Tai normalu. Vault visada startuoja sealed būsenoje. Reikia unseal arba naudoti auto-unseal su cloud KMS.

Problema: Dynamic secrets negeneruojami
Sprendimas: Patikrinkite, ar Vault turi connection į duomenų bazę, ar Vault vartotojas turi teises kurti naujus vartotojus, ar creation_statements SQL yra teisingas.

Ateities perspektyvos ir alternatyvos

Vault nėra vienintelis sprendimas secrets valdymui, nors ir vienas populiariausių. Verta žinoti alternatyvas:

AWS Secrets Manager – puikus pasirinkimas, jei visa infrastruktūra AWS. Glaudžiai integruotas su kitais AWS servisais, bet vendor lock-in.

Azure Key Vault – analogiškas Azure ekosistemoje.

Google Secret Manager – GCP variantas.

Kubernetes External Secrets Operator – sinchronizuoja slaptažodžius iš išorinių sistemų (įskaitant Vault) į Kubernetes secrets. Gali būti paprastesnis variantas nei Vault Agent.

SOPS (Secrets OPerationS) – Mozilla sukurtas įrankis slaptažodžiams šifruoti failuose. Paprastesnis nei Vault, bet neturi dynamic secrets ar centralizuoto audito.

Vault ateitis atrodo šviesi. HashiCorp nuolat prideda naujus secrets engine’us, gerina Kubernetes integraciją, tobulina auto-unseal mechanizmus. Versija 1.15 pridėjo Kubernetes secrets engine, leidžiantį Vault generuoti Kubernetes service account token’us dinamiškai.

Tendencijos, kurias matome:

– Vis daugiau organizacijų pereina nuo statinių prie dinaminių slaptažodžių
– Glaudesnė integracija su service mesh technologijomis (Istio, Consul Connect)
– Auto-unseal tampa standartu, o ne išimtimi
– Multi-cloud strategijos skatina naudoti vendor-neutral sprendimus kaip Vault

Praktiniai patarimai pradedantiesiems ir ne tik

Jei tik pradedate su Vault, štai keletas patarimų, kurie sutaupys jums laiko ir nervų:

Pradėkite mažai. Nebandykite iškart migruoti visų slaptažodžių. Pasirinkite

Daugiau

Rspack: Rust-based web bundler