Poniedziałek, 6:00 rano. Alert z TruffleHog: wykryto AWS access key w commit history publicznego repo. Developer przez przypadek commitnął plik z credentials tydzień temu, potem usunął - ale git history pamięta. Teraz ten key jest na GitGuardian public leaks database. AWS cost explorer pokazuje nieznane instancje EC2 - crypto mining. Konto skompromitowane.
Przeczytaj także: Phishing w erze AI 2026: Jak rozpoznać i bronić się przed za
Badania GitGuardian pokazują że w 2024 roku wykryto ponad 12 milionów secrets w publicznych repozytoriach GitHub. API keys, database passwords, private keys, tokens. Każdy z nich to potencjalny vector ataku. A to tylko publiczne repo - w prywatnych jest więcej.
CI/CD pipeline jest szczególnie ryzykowny: build servers mają dostęp do wszystkiego (code, secrets, production). Jeden compromised CI = pełen dostęp do infrastruktury. Secrets management w pipeline to nie “nice to have” - to fundament bezpieczeństwa.
Dlaczego secrets w CI/CD są tak problematyczne?
CI/CD potrzebuje secrets żeby działać. Deploy na AWS wymaga credentials. Push do registry wymaga tokena. Test na staging database wymaga password. Secrets muszą być dostępne dla pipeline - pytanie jak je tam dostarczyć bezpiecznie.
Wiele miejsca do pomyłki. Secrets mogą wyciec przez: code commits, log output, environment variables w UI, docker images, artifact repositories, shared runners. Każdy etap pipeline to potencjalny leak.
Persistence of leaks. Secret w git history jest tam “na zawsze” (bez rewrite history). Secret w docker image layer jest tam nawet po “usunięciu” w kolejnym layer. Secret w logs może być archived przez lata.
Sprawling permissions. Pipeline często ma więcej uprawnień niż potrzebuje (“bo tak było łatwiej”). Root access do production, admin na cloud account. Skompromitowany CI = full blast radius.
Shared environments. Self-hosted runners shared między projektami. Jedna vuln w jednym projekcie = potential access do secrets innych projektów.
Developer convenience vs. security. Hardcoding secrets jest “szybsze” niż proper secrets management. Developers pod presją czasową wybierają convenience.
Jakie są najczęstsze błędy w zarządzaniu secrets?
Secrets w code repository. Najbardziej oczywisty i najczęstszy błąd. Plik .env w repo. Config file z plaintext password. SQL script z credentials.
Secrets w docker images. COPY .env /app/ lub ENV DATABASE_PASSWORD=secret w Dockerfile. Image jest pushowany do registry, secrets są extractable.
Secrets w CI logs. echo $SECRET_TOKEN dla debugging. Password pojawia się w stack trace. Verbose mode loguje wszystko.
Environment variables w CI UI. Niektóre CI/CD pozwalają ustawić env vars które są widoczne dla wszystkich z dostępem do projektu. “Secret” który nie jest secret.
Hardcoded secrets w tests. “To tylko test, użyjemy prawdziwego API key.” Test jest commitowany, key wyciekł.
Shared secrets across environments. Jeden API key dla dev, staging i production. Dev kompromituje key = production zagrożone.
No rotation. Secret ustawiony 3 lata temu, nigdy nie rotowany. Jeśli wyciekł - jest nadal valid.
Jak poprawnie przekazywać secrets do CI/CD pipeline?
CI/CD native secrets (GitHub Secrets, GitLab CI Variables, etc.). Secrets przechowywane w CI/CD platform, encrypted at rest, injected jako env variables podczas build. Basic level security, odpowiedni dla mniej krytycznych secrets.
External secrets manager (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager). Secrets przechowywane poza CI/CD, pipeline pobiera je w runtime. Better security, centralized management, audit trail.
Just-in-time credentials. Zamiast static secrets - krótkotrwałe, dynamicznie generowane credentials. Vault dynamic secrets: pipeline żąda credentials, Vault generuje tymczasowe, credentials wygasają po use.
OIDC federation. Pipeline authenticuje się do cloud provider bez static credentials. GitHub Actions OIDC z AWS: GitHub dostarcza token, AWS weryfikuje, przyznaje temporary credentials. Zero static secrets.
Sealed Secrets / SOPS. Encrypted secrets przechowywane w repo, decryptable tylko przez authorized systems. GitOps-friendly approach.
Jak skonfigurować HashiCorp Vault dla CI/CD?
Vault jako central secrets store. Wszystkie secrets w Vault, applications i pipelines pobierają z Vault. Single source of truth.
Authentication methods dla CI/CD:
- AppRole: pipeline ma role_id (static) i secret_id (dynamic), używa do auth
- JWT/OIDC: CI platform dostarcza JWT, Vault weryfikuje i przyznaje token
- Kubernetes auth: dla K8s-based CI, service account auth
- AWS/GCP/Azure auth: pipeline running w cloud authenticuje przez cloud IAM
Policies i access control. Principle of least privilege: pipeline dla project X ma dostęp tylko do secrets project X. Policies definiują co kto może czytać/pisać.
Dynamic secrets. Zamiast static database password - Vault generuje tymczasowe credentials na żądanie. Pipeline żąda DB access → Vault tworzy user/password z TTL 1h → pipeline używa → credentials auto-expire.
Audit logging. Każdy dostęp do secrets jest logowany. Kto, kiedy, co. Forensics capability.
Example: GitHub Actions + Vault:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: hashicorp/vault-action@v2
with:
url: https://vault.example.com
method: jwt
role: github-actions-role
secrets: |
secret/data/myapp/prod db_password | DB_PASSWORD
- run: deploy.sh
env:
DB_PASSWORD: ${{ steps.vault.outputs.DB_PASSWORD }}
Jak używać AWS Secrets Manager / Azure Key Vault / GCP Secret Manager?
AWS Secrets Manager:
- Secrets stored encrypted (KMS)
- Automatic rotation (RDS, DocumentDB, Redshift native)
- IAM-based access control
- CI/CD access przez IAM role (EC2 instance profile, ECS task role, OIDC)
# GitHub Actions z AWS OIDC
jobs:
deploy:
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/GitHubActions
aws-region: eu-central-1
- run: |
SECRET=$(aws secretsmanager get-secret-value --secret-id myapp/prod --query SecretString --output text)
# use secret
Azure Key Vault:
- Secrets, keys, certificates management
- Azure RBAC lub Key Vault access policies
- Managed Identity dla Azure-hosted CI
- Service Principal dla external CI
GCP Secret Manager:
- Secrets versioning
- IAM-based access
- Workload Identity Federation dla external CI
Multi-cloud consideration. Jeśli jesteś multi-cloud, rozważ Vault jako abstraction layer zamiast vendor-specific solutions.
Jak zapobiegać przypadkowemu commitowaniu secrets?
Pre-commit hooks. Narzędzia skanujące kod przed commitem:
- TruffleHog: entropy-based detection + known patterns
- git-secrets: AWS-focused, customizable patterns
- detect-secrets: Yelp’s tool, baseline capability
- Gitleaks: fast, comprehensive
Install globally:
# Example: gitleaks as pre-commit hook
brew install gitleaks
echo 'gitleaks protect --staged' >> .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
Pre-commit framework:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
CI/CD scanning. Backup dla pre-commit - skanowanie w pipeline:
- GitHub Advanced Security (secret scanning)
- GitLab Secret Detection
- Dedicated tools (TruffleHog, Gitleaks) w CI
.gitignore best practices. .env*, *.pem, *.key, credentials.json - always ignore. Template files (.env.example) bez actual values.
Jak obsługiwać secrets w Docker builds?
Problem: secrets w obrazie. Docker layers są cached i inspectable. Nawet RUN rm /secrets nie usuwa secrets z poprzedniego layer.
BuildKit secrets (—secret):
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=api_key \
API_KEY=$(cat /run/secrets/api_key) && \
./configure --api-key=$API_KEY
docker build --secret id=api_key,src=./api_key.txt .
Secret nie jest w image layer, dostępny tylko podczas build.
Multi-stage builds dla compile-time secrets:
FROM golang:1.21 AS builder
COPY --from=secrets /run/secrets/github_token /token
RUN GITHUB_TOKEN=$(cat /token) go build ./...
FROM alpine
COPY --from=builder /app/binary /app/
# Final image has no secrets
Runtime secrets injection. Nie bake secrets do image. Inject at runtime przez:
- Environment variables (docker run -e)
- Docker secrets (Swarm)
- Kubernetes Secrets
- External secrets manager (app fetches at startup)
Jak zarządzać secrets w Kubernetes?
Native Kubernetes Secrets. Base64 encoded (nie encrypted!). Stored in etcd. Basic isolation.
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
type: Opaque
data:
db-password: cGFzc3dvcmQxMjM= # base64
Problem: K8s Secrets are not encrypted at rest by default. Anyone with etcd access or sufficient RBAC can read.
Encryption at rest. Enable encryption provider (aesgcm, kms):
# EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- kms:
name: my-kms
endpoint: unix:///var/run/kmsplugin/socket.sock
External Secrets Operator. Syncs secrets from external stores (Vault, AWS SM, Azure KV) to K8s Secrets:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
spec:
secretStoreRef:
name: vault
target:
name: myapp-secrets
data:
- secretKey: db-password
remoteRef:
key: secret/myapp
property: password
Sealed Secrets (Bitnami). Encrypt secrets z public key, commit encrypted version to repo, controller decrypts in cluster. GitOps-friendly.
Jak rotować secrets bez downtime?
Problem: secret rotation wymaga update wszystkich consumers. Naiwne podejście = downtime.
Dual-secret period. Przez krótki czas oba secrets (stary i nowy) są valid. Steps:
- Generate new secret, add to system (both valid)
- Update all consumers to use new secret
- Remove old secret
Database credential rotation:
- Create new DB user with new password
- Update application config (Vault, env var)
- Deploy application (reads new credentials)
- Delete old DB user
Vault dynamic secrets. No rotation needed - each lease is fresh credentials. Old credentials auto-expire.
AWS Secrets Manager automatic rotation. Lambda function rotates secret per schedule. Multi-user rotation strategy maintains two active users.
Application support. Application should handle credential refresh gracefully. Connection pooling that re-authenticates. Retry on auth failure.
Jak monitorować i audytować dostęp do secrets?
Centralized logging. Wszystkie secrets access → central log (SIEM). Who accessed what, when, from where.
Vault audit log:
{
"time": "2026-01-06T10:00:00Z",
"auth": {"accessor": "token-abc123"},
"request": {"path": "secret/data/myapp/prod"},
"response": {"data": {"keys": ["db_password"]}}
}
Alerting na anomalie:
- Access from unexpected IP/location
- High volume of secret reads
- Access outside business hours
- Failed authentication attempts
Secret sprawl detection. Inventory wszystkich secrets. Which are used? Which are stale? Who has access? Regular review.
Compliance requirements. PCI DSS, SOC 2, HIPAA - wszystkie wymagają audit trail dla sensitive data access. Secrets management musi to wspierać.
Jak testować secrets management bez używania production secrets?
Test secrets vs. production secrets. Separate stores, separate access. Test pipeline nie ma dostępu do production secrets.
Fake/mock secrets dla unit tests. Nie potrzebujesz real AWS credentials do unit testu. Mock the secrets manager.
Integration test secrets. Separate credentials dla test environment. Limited permissions (read-only gdzie możliwe).
Secret detection w test data. Upewnij się że test fixtures nie zawierają real secrets. Skanuj test data.
CI environment isolation. Secrets available tylko dla odpowiedniego environment. Production deploy ma production secrets, staging ma staging secrets.
Tabela: Secrets management maturity model
| Poziom | Charakterystyka | Przykład | Risk Level | Next Step |
|---|---|---|---|---|
| 0 - Chaos | Secrets w code/repo | Hardcoded passwords w .env committed | Critical | Pre-commit hooks, remove from history |
| 1 - Basic | CI native secrets | GitHub Secrets, GitLab CI Variables | High | Centralize w external secrets manager |
| 2 - Centralized | External secrets manager | Vault lub cloud-native (AWS SM) | Medium | Implement rotation, dynamic secrets |
| 3 - Dynamic | Short-lived credentials | Vault dynamic DB creds, OIDC federation | Low | Full audit, zero standing privileges |
| 4 - Zero Trust | Just-in-time, fully audited | Dynamic creds + approval workflow + full audit | Minimal | Continuous improvement, red team testing |
Secrets management w CI/CD to fundament bezpieczeństwa software delivery. Jeden wyciek credentials może compromisować całą infrastrukturę. Dobre praktyki nie są trudne - wymagają świadomości i konsekwencji.
Kluczowe wnioski:
- Never commit secrets - pre-commit hooks są must-have
- CI native secrets to baseline - external secrets manager to better
- OIDC federation eliminuje static credentials dla cloud
- Docker secrets (BuildKit —secret) chroni przed layer leaks
- Rotation musi być planned - dual-secret period, dynamic secrets
- Audit everything - kto, co, kiedy accessed secrets
- Test environment izolacja - test secrets ≠ production secrets
- Kubernetes Secrets nie są encrypted domyślnie - use external solutions
Inwestycja w proper secrets management zwraca się przy pierwszym unikniętym incydencie. Koszt wdrożenia Vault czy AWS Secrets Manager jest ułamkiem kosztu breach response.
ARDURA Consulting dostarcza specjalistów DevSecOps przez body leasing z doświadczeniem w implementacji secrets management dla enterprise CI/CD pipelines. Nasi eksperci pomagają wdrażać HashiCorp Vault, konfigurować cloud-native secrets managers i budować secure delivery pipelines. Porozmawiajmy o zabezpieczeniu twojego CI/CD.