Secrets Management¶
Ziel: Sichere Verwaltung von API-Keys, Credentials und Tokens für Cloud Functions, Jobs und CI/CD.
1. Google Secret Manager¶
Google Secret Manager ist der zentrale Speicher für alle sensiblen Credentials in Production.
1.1 Verwendungszweck¶
Secrets werden verwendet für: - Job Credentials (z.B. ERP-API-Keys für Connectors) - SMTP-Credentials (Strato App-Passwort für E-Mail-Versand) - Externe API-Keys (Google Maps, Payment-Provider, etc.) - OAuth Client Secrets - Verschlüsselungs-Keys
NICHT in Secret Manager:
- Firebase Service Account (wird via ADC bereitgestellt)
- Öffentliche API-Keys (z.B. Firebase Web API Key)
- Non-sensitive Konfiguration (gehört nach Firestore system_settings)
1.2 Secret anlegen¶
Via gcloud CLI:
# Neues Secret anlegen
echo '{"apiKey":"abc123","secret":"xyz789"}' | \
gcloud secrets create job-myJobId-credentials \
--data-file=- \
--replication-policy=automatic \
--project=${PROJECT_ID}
# Version aktualisieren (Secret bleibt bestehen, neue Version wird hinzugefügt)
echo '{"apiKey":"new123","secret":"new789"}' | \
gcloud secrets versions add job-myJobId-credentials \
--data-file=- \
--project=${PROJECT_ID}
# Secret-Wert abrufen (für Debugging)
gcloud secrets versions access latest \
--secret=job-myJobId-credentials \
--project=${PROJECT_ID}
Via Google Cloud Console:
- Secret Manager öffnen
- Create Secret klicken
- Name:
job-myJobId-credentials(Pattern:job-{jobId}-credentialsfür System-Jobs) - Secret value: JSON-String mit allen Feldern eingeben
- Replication:
Automatic(multi-region) - Create klicken
1.3 IAM-Berechtigung¶
Cloud Functions benötigen die Rolle roles/secretmanager.secretAccessor:
# Service Account ermitteln
PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format='value(projectNumber)')
SA="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
# Zugriff auf spezifisches Secret gewähren
gcloud secrets add-iam-policy-binding job-myJobId-credentials \
--member="serviceAccount:${SA}" \
--role="roles/secretmanager.secretAccessor" \
--project=${PROJECT_ID}
# ODER: Zugriff auf alle Secrets gewähren (weniger granular, einfacher)
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="serviceAccount:${SA}" \
--role="roles/secretmanager.secretAccessor"
1.4 Secret in Job verwenden¶
Firestore-Konfiguration (system_settings/jobs/entries/{jobId}):
{
id: 'myJobId',
// ... andere Felder ...
secretName: 'projects/${PROJECT_ID}/secrets/job-myJobId-credentials',
// NIEMALS credentials direkt in parameters speichern!
parameters: {
timeout: 300, // OK: non-sensitive config
// apiKey: "..." ❌ VERBOTEN
}
}
Job-Handler (core/functions/src/jobs/instances/job_myJobId.js):
exports.execute = async (job, credentials, logger) => {
// credentials ist bereits geparste JSON aus Secret Manager
const apiKey = credentials?.apiKey;
const secret = credentials?.secret;
if (!apiKey) {
throw new Error('API-Key fehlt in Secret Manager');
}
// ... Job-Logik mit apiKey ...
};
Credential-Workflow:
1. Secret wird beim Job-Start vom Executor aus Secret Manager geladen
2. Automatisches Parsing als JSON
3. Falls Secret nicht existiert oder IAM fehlt → credentials = null
4. Handler muss Null-Check durchführen
1.5 Secret-Rotation¶
# Neue Version hinzufügen (alte bleibt verfügbar)
echo '{"apiKey":"rotated456"}' | \
gcloud secrets versions add job-myJobId-credentials --data-file=-
# Alte Version deaktivieren (nicht löschen — für Audit-Trail)
gcloud secrets versions disable <version-number> \
--secret=job-myJobId-credentials
# Secret komplett löschen (nur wenn Job gelöscht wird)
gcloud secrets delete job-myJobId-credentials
Best Practice: Rotation alle 90 Tage für kritische Credentials (ERP-API-Keys, SMTP-Passwörter).
1.6 Lokale Entwicklung (Emulator)¶
Secrets werden im Emulator NICHT aus Secret Manager geladen (um keine Prod-Credentials zu verwenden).
Lokale Test-Credentials (nur für Entwicklung):
# Umgebungsvariable setzen
export EASYSALE_TEST_CREDENTIALS='{"apiKey":"test123"}'
# Im Code prüfen
if (process.env.FUNCTIONS_EMULATOR === 'true') {
credentials = JSON.parse(process.env.EASYSALE_TEST_CREDENTIALS || '{}');
}
2. GitHub Secrets (CI/CD)¶
GitHub Secrets werden für automatische Deployments in GitHub Actions verwendet.
2.1 Verwendungszweck¶
Secrets werden verwendet für:
- Android Keystores (Base64-kodiert)
- iOS Distribution Certificates (Base64-kodiert)
- iOS Provisioning Profiles (Base64-kodiert)
- App Store Connect API Key
- Firebase Service Account Key (für firebase deploy)
- Google Cloud Service Account (für GCP-Operationen)
2.2 Secrets generieren¶
Android Keystores:
cd /Users/michaelmodlmair/Development/easySale
chmod +x scripts/generate_android_keystores.sh
./scripts/generate_android_keystores.sh
Ergebnis: ~/.easysale/keystores/github-secrets.txt mit:
- ANDROID_KEYSTORE_ERP_BASE64
- ANDROID_KEYSTORE_ERP_PASSWORD
- ANDROID_KEY_ALIAS_ERP
- ANDROID_KEY_PASSWORD_ERP
- ANDROID_KEYSTORE_SHOP_BASE64
- ANDROID_KEYSTORE_SHOP_PASSWORD
- ANDROID_KEY_ALIAS_SHOP
- ANDROID_KEY_PASSWORD_SHOP
iOS Zertifikate & Profile:
Ergebnis: ~/.easysale/ios-secrets/github-secrets.txt mit:
- IOS_CERTIFICATE_BASE64
- IOS_CERTIFICATE_PASSWORD
- IOS_PROVISIONING_PROFILE_ERP_BASE64
- IOS_PROVISIONING_PROFILE_SHOP_BASE64
- APP_STORE_CONNECT_API_KEY_ID
- APP_STORE_CONNECT_ISSUER_ID
- APP_STORE_CONNECT_API_KEY_BASE64
Vollständige Anleitung: siehe github-actions-setup.md
2.3 Secrets in GitHub Repository hinterlegen¶
Via GitHub UI:
- Repository Settings öffnen
- Settings → Secrets and variables → Actions
- New repository secret klicken
- Name + Value aus den generierten Dateien einfügen
- Add secret klicken
- Für jedes Secret wiederholen
Via GitHub CLI:
# Alle Android-Secrets auf einmal setzen
cd ~/.easysale/keystores
while IFS='=' read -r key value; do
gh secret set "$key" --body "$value" --repo Tech-Schuppen/easySale
done < github-secrets.txt
# Alle iOS-Secrets auf einmal setzen
cd ~/.easysale/ios-secrets
while IFS='=' read -r key value; do
gh secret set "$key" --body "$value" --repo Tech-Schuppen/easySale
done < github-secrets.txt
2.4 Secrets in GitHub Actions verwenden¶
Workflow-Datei (.github/workflows/client-release.yml):
jobs:
build-android:
runs-on: ubuntu-latest
steps:
- name: Decode Keystore
run: |
echo "${{ secrets.ANDROID_KEYSTORE_ERP_BASE64 }}" | base64 -d > keystore.jks
- name: Build APK
env:
KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_ERP_PASSWORD }}
KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS_ERP }}
KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD_ERP }}
run: |
flutter build apk --release \
--dart-define=KEYSTORE_PATH=../keystore.jks
2.5 Secret-Rotation¶
Android Keystores: - Keystores sollten nie rotiert werden (Apps können nicht mehr aktualisiert werden) - Nur bei Kompromittierung: neuen Keystore generieren + neue App-Version mit neuem Package-Name
iOS Certificates:
- Distribution Certificates sind 1 Jahr gültig
- Vor Ablauf: neues Zertifikat in Apple Developer Portal erstellen
- Provisioning Profiles: ebenfalls 1 Jahr gültig, können aber jederzeit neu erstellt werden
- Skript erneut ausführen: ./scripts/generate_ios_secrets.sh
- GitHub Secrets aktualisieren
App Store Connect API Key: - Kann jederzeit revoziert und neu erstellt werden - Bei Verdacht auf Kompromittierung: Key in App Store Connect widerrufen - Neuen Key generieren und GitHub Secret aktualisieren
2.6 Secrets lokal testen (NICHT empfohlen)¶
# GitHub Secrets lokal exportieren (nur für Debugging)
export ANDROID_KEYSTORE_ERP_BASE64="..."
export ANDROID_KEYSTORE_ERP_PASSWORD="..."
# Workflow lokal simulieren (mit act CLI)
act -s ANDROID_KEYSTORE_ERP_BASE64="..." -s ANDROID_KEYSTORE_ERP_PASSWORD="..."
⚠️ Warnung: Niemals echte Secrets in Shell History oder lokalen Files speichern. Nutze HISTIGNORE oder temporäre Dateien mit chmod 600.
3. Credential-Hygiene-Regeln¶
3.1 Harte Regeln (IMMER einhalten)¶
❌ VERBOTEN:
- Credentials als Parameter im Firestore-Dokument system_settings/jobs/entries/{jobId}.parameters
- Credentials in Cloud Function Environment Variables (über Firebase Console sichtbar)
- Credentials in Git (auch nicht in .env-Dateien oder Kommentaren)
- Credentials in Cloud Logging (auch nicht verschleiert oder base64-kodiert)
- Credentials über Cloud Functions Callables setzen/lesen/aktualisieren
✅ ERLAUBT: - Secret Manager für Production (Google Cloud Secret Manager) - GitHub Secrets für CI/CD (GitHub Actions) - Lokale Umgebungsvariablen für Entwicklung (nur auf Dev-Machine, nie committen) - ADC (Application Default Credentials) für Service-Account-Auth
3.2 Credential-Arten¶
| Art | Speicher Production | Speicher CI/CD | Rotation | TTL |
|---|---|---|---|---|
| ERP-API-Keys (Connectors) | Secret Manager | — | 90 Tage | unbegrenzt |
| SMTP App-Passwort | Secret Manager | — | 90 Tage | unbegrenzt |
| Firebase Service Account | ADC (implizit) | GitHub Secret (JSON) | — | unbegrenzt |
| Android Keystores | — | GitHub Secret (Base64) | nie | unbegrenzt |
| iOS Certificates | — | GitHub Secret (Base64) | 1 Jahr | 1 Jahr |
| App Store Connect API | — | GitHub Secret (Base64) | bei Bedarf | unbegrenzt |
| OAuth Client Secrets | Secret Manager | — | bei Bedarf | unbegrenzt |
3.3 Audit & Detection¶
ESLint-Regel: no-pii-logging (Custom Rule)
- Verhindert console.log(password), logger.info({ apiKey })
- Enforcement via Pre-Commit-Hook
Secret Scanning: - GitHub Advanced Security: automatische Detection von leaked Credentials - GitGuardian: scannt alle Commits + Pull Requests
Manueller Check:
# Lokaler Scan nach potenziellen Secrets in Git-History
git log -p | grep -iE "(password|secret|apikey|token|credential)" | head -50
4. Pre-Go-Live-Check¶
| Check | Verantwortlich | Status |
|---|---|---|
| Secret Manager in Projekt aktiviert | DevOps | ☐ |
| Job-Credentials als Secrets angelegt (nicht in Firestore) | DevOps | ☐ |
IAM roles/secretmanager.secretAccessor für Compute SA |
DevOps | ☐ |
| GitHub Secrets für Android + iOS hinterlegt | DevOps | ☐ |
| Keine Credentials in Environment Variables (Firebase Console) | DevOps | ☐ |
ESLint no-pii-logging aktiv |
DevOps | ☐ |
| Secret Scanning aktiviert (GitHub Advanced Security) | DevOps | ☐ |
Lokale .env-Dateien in .gitignore |
DevOps | ☐ |