Správa tajných klíčů – .env, gitignore, CI secrets a Vault
Jak správně ukládat API klíče a hesla v projektu. Soubor .env, .gitignore, GitHub Actions secrets, HashiCorp Vault a co dělat, když klíč uteče do gitu.
API klíče, hesla k databázi, tokeny k platebním bránám – každý projekt má tajemství, která nesmí skončit v repozitáři. Přesto je uniklý klíč v gitu jedna z nejčastějších bezpečnostních nehod. Tady je praktický postup od malého projektu po produkční infrastrukturu.
Pravidlo číslo jedna: tajemství nepatří do kódu
Tohle je špatně, i když je repozitář „soukromý“:
STRIPE_KEY = "sk_live_51H..."
Soukromý repozitář se může stát veřejným, může ho naklonovat kolega na nezabezpečený notebook nebo ho můžete nahrát jako přílohu někam, kam nemáte. A git si pamatuje všechno – smazání v dalším commitu klíč z historie neodstraní.
Úroveň 1: soubor .env
Tajemství patří do proměnných prostředí. Pro lokální vývoj je nejpohodlnější soubor .env v kořeni projektu:
# .env
DATABASE_URL=postgres://app:heslo@localhost:5432/app
STRIPE_SECRET_KEY=sk_test_...
Načtení v Pythonu (balíček python-dotenv):
from dotenv import load_dotenv
import os
load_dotenv()
db_url = os.environ["DATABASE_URL"] # spadne hned, pokud chybí – to je dobře
V Node.js od verze 20.6 není potřeba balíček dotenv, stačí přepínač:
node --env-file=.env server.js
.gitignore a .env.example
Hned při založení projektu:
echo ".env" >> .gitignore
echo ".env.*" >> .gitignore
echo "!.env.example" >> .gitignore
Do repozitáře naopak patří .env.example se seznamem proměnných, ale bez hodnot:
# .env.example
DATABASE_URL=
STRIPE_SECRET_KEY=
Nový kolega si ho zkopíruje (cp .env.example .env) a hned ví, co vyplnit.
Ověření, že .env opravdu ignorujete:
git check-ignore -v .env
Pokud příkaz nic nevypíše, soubor ignorovaný není.
Úroveň 2: tajemství na serveru
Na produkčním serveru máte několik možností:
- systemd:
EnvironmentFile=/etc/myapp/envv unit souboru, soubor s právychmod 600a vlastníkemroot. - Docker Compose:
env_file: .envnebo lépesecrets:, které se připojí jako soubor do/run/secrets/<název>. - Docker: nikdy nedávejte tajemství do
DockerfilepřesENVneboARG– zůstanou v historii vrstev image (docker history). Pro build použijteRUN --mount=type=secret.
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci
docker build --secret id=npmrc,src=$HOME/.npmrc .
Úroveň 3: CI/CD
V GitHub Actions uložte tajemství v Settings → Secrets and variables → Actions a v workflow je čtěte přes kontext secrets:
- name: Deploy
env:
SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
run: ./deploy.sh
GitHub hodnoty v logu automaticky maskuje, ale jen pokud se objeví v přesném tvaru. Base64 nebo jinak upravená hodnota maskovaná nebude, takže tajemství nevypisujte.
Úroveň 4: správce tajemství (Vault a spol.)
Jakmile máte více služeb, serverů a lidí, .env soubory přestanou stačit. Pomůže centrální správce:
| Nástroj | Kdy se hodí |
|---|---|
| HashiCorp Vault / OpenBao | Vlastní infrastruktura, dynamická hesla k DB, audit přístupu |
| AWS Secrets Manager, GCP Secret Manager, Azure Key Vault | Aplikace běží v daném cloudu |
| SOPS + age | Šifrované soubory s tajemstvími přímo v gitu |
| 1Password CLI, Doppler, Infisical | Týmy, které chtějí pohodlné sdílení |
Příklad s Vaultem (KV engine v2):
vault kv put secret/myapp DATABASE_URL="postgres://..."
vault kv get -field=DATABASE_URL secret/myapp
Výhoda Vaultu je i v tom, že umí generovat dynamická, krátkodobá hesla k databázi. Když unikne heslo platné hodinu, škoda je mnohem menší.
Co dělat, když klíč unikne
- Okamžitě klíč zneplatněte (rotujte) u poskytovatele. To je jediný krok, který skutečně pomáhá.
- Projděte logy poskytovatele, zda nebyl klíč zneužit.
- Teprve potom odstraňte klíč z historie gitu, např. nástrojem
git filter-repo:
git filter-repo --replace-text <(echo 'sk_live_51H...==>REMOVED')
git push --force --all
Přepis historie nestačí sám o sobě: klíč už mohl někdo stáhnout, je v forcích i v cache. Proto vždy nejdřív rotace.
Prevence: skenování tajemství
- GitHub secret scanning a push protection – u veřejných repozitářů zablokuje push se známým formátem klíče.
- gitleaks jako pre-commit hook:
# gitleaks v8.19+ (starší verze používaly "detect" a "protect")
gitleaks git -v # sken celé historie repozitáře
gitleaks git --pre-commit --staged -v # jen změny připravené ke commitu
- trufflehog pro hloubkové skenování repozitářů a historie.
Shrnutí
.env mimo git, .env.example v gitu, tajemství v CI přes secrets, na produkci soubory s omezenými právy nebo správce tajemství. A když klíč unikne: nejdřív rotovat, pak uklízet.