Secrets Management - Google Cloud Single Source of Truth
Date: 2026-01-29 Status: CRITICAL REQUIREMENT - ZERO TOLERANCE
🚨 CRITICAL RULE: NEVER EDIT .env FILES DIRECTLY
Single Source of Truth: Google Cloud Secret Manager
All environment variables and secrets are managed through Google Cloud Secret Manager.
Workflow (MANDATORY):
1. Update Google Cloud Secrets (single source of truth)
↓
2. Regenerate .env files from GCloud secrets
↓
3. Restart services to pick up new configuration❌ FORBIDDEN:
bash
# NEVER do this
echo "NEW_SECRET=value" >> backend/.env
echo "REACT_APP_CONFIG=value" >> web/.env
vim backend/.env.example # Never edit directly✅ CORRECT:
bash
# 1. Add/update secret in Google Cloud
gcloud secrets create NEW_SECRET --data-file=- <<< "value"
# 2. Regenerate .env from GCloud
./scripts/sync-gcloud-secrets.sh
# 3. Restart services
kubectl rollout restart deployment/bayit-backend-productionWhy Google Cloud Secret Manager?
Advantages:
- ✅ Single Source of Truth - One place for all configuration
- ✅ Version Control - All secret changes tracked and auditable
- ✅ Encryption at Rest - Secrets encrypted by Google Cloud KMS
- ✅ Access Control - IAM-based secret access permissions
- ✅ Audit Logs - Every secret access logged for compliance
- ✅ Rotation - Automated secret rotation capabilities
- ✅ Disaster Recovery - Point-in-time secret recovery
Problems with .env Files:
- ❌ No version control (shouldn't be in git)
- ❌ No encryption at rest
- ❌ No access control
- ❌ No audit trail
- ❌ Easy to have drift between environments
- ❌ Easy to forget to sync after local changes
Secrets Management Scripts
Sync Secrets from GCloud
bash
# Regenerate backend .env
./scripts/sync-gcloud-secrets.sh backend
# Regenerate web .env
./scripts/sync-gcloud-secrets.sh web
# Regenerate all .env files
./scripts/sync-gcloud-secrets.sh allAdd New Secret
bash
# 1. Create secret in GCloud
gcloud secrets create SECRET_NAME \
--data-file=- <<< "secret_value"
# 2. Grant access to service account
gcloud secrets add-iam-policy-binding SECRET_NAME \
--member="serviceAccount:SERVICE_ACCOUNT@PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
# 3. Regenerate .env
./scripts/sync-gcloud-secrets.sh backend
# 4. Restart service
kubectl rollout restart deployment/bayit-backend-productionUpdate Existing Secret
bash
# 1. Add new version to GCloud
gcloud secrets versions add SECRET_NAME \
--data-file=- <<< "new_value"
# 2. Regenerate .env
./scripts/sync-gcloud-secrets.sh backend
# 3. Restart service
kubectl rollout restart deployment/bayit-backend-productionView Secret Value
bash
# View latest version
gcloud secrets versions access latest --secret="SECRET_NAME"
# View specific version
gcloud secrets versions access 5 --secret="SECRET_NAME"
# List all versions
gcloud secrets versions list SECRET_NAMEDelete Secret
bash
# Disable specific version (soft delete)
gcloud secrets versions disable 5 --secret="SECRET_NAME"
# Delete specific version (hard delete)
gcloud secrets versions destroy 5 --secret="SECRET_NAME"
# Delete entire secret
gcloud secrets delete SECRET_NAME.env.example Files
Purpose
.env.example files serve as documentation only:
- ✅ List all required environment variables
- ✅ Show expected format/type
- ✅ Provide example/placeholder values
- ✅ Document what each variable does
Rules
Never contain real secrets
- Use placeholders:
YOUR_API_KEY_HERE,CHANGEME, etc. - Use example values:
http://example.com,user@example.com
- Use placeholders:
Never used by applications
- Applications read from
.env(generated from GCloud) .env.exampleis for developer reference only
- Applications read from
Safe to commit to git
- Contains no sensitive information
- Documents expected configuration
Updated when adding new config
- Add new variables to
.env.examplefor documentation - Include comments explaining what the variable does
- But NEVER add real values
- Add new variables to
Environment-Specific Secrets
Development (Local)
bash
# Use development project
gcloud config set project bayit-plus-dev
# Sync dev secrets
./scripts/sync-gcloud-secrets.sh allStaging
bash
# Use staging project
gcloud config set project bayit-plus-staging
# Sync staging secrets
./scripts/sync-gcloud-secrets.sh allProduction
bash
# Use production project
gcloud config set project bayit-plus-prod
# Sync production secrets
./scripts/sync-gcloud-secrets.sh allSecret Naming Conventions
Backend Secrets (Python/FastAPI)
- Format:
UPPERCASE_SNAKE_CASE - Example:
DATABASE_URL,STRIPE_SECRET_KEY
Frontend Secrets (React/Vite)
- Format:
REACT_APP_UPPERCASEorVITE_UPPERCASE - Example:
REACT_APP_API_URL,VITE_API_BASE_URL - Note: Only secrets prefixed with
REACT_APP_orVITE_are exposed to frontend
Shared Secrets
- Format:
SHARED_UPPERCASE_SNAKE_CASE - Example:
SHARED_JWT_SECRET,SHARED_ENCRYPTION_KEY
Security Best Practices
1. Least Privilege Access
bash
# Grant only necessary access
gcloud secrets add-iam-policy-binding SECRET_NAME \
--member="serviceAccount:backend@PROJECT.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
# Never use "allUsers" or "allAuthenticatedUsers"2. Secret Rotation
bash
# Rotate critical secrets every 90 days
# 1. Generate new secret value
# 2. Add new version to GCloud
# 3. Test with new version
# 4. Deploy to all environments
# 5. Disable old version after 7 days3. Audit Access
bash
# View secret access logs
gcloud logging read \
"resource.type=secretmanager.googleapis.com/Secret" \
--limit 1004. Never Log Secrets
python
# ❌ FORBIDDEN
logger.info(f"API Key: {settings.STRIPE_SECRET_KEY}")
print(f"Database password: {db_password}")
# ✅ CORRECT
logger.info("Connecting to database")
logger.info("Stripe API initialized")Compliance Requirements
SOC 2 / ISO 27001
- ✅ All secrets stored in GCloud Secret Manager (encrypted)
- ✅ All secret access logged and auditable
- ✅ IAM-based access control enforced
- ✅ Secrets never committed to version control
- ✅ Regular secret rotation schedule
- ✅ Incident response plan for secret leaks
GDPR / Data Privacy
- ✅ User data encryption keys managed via Secret Manager
- ✅ Access to user data secrets logged and monitored
- ✅ Data retention policies enforced
Troubleshooting
.env file not updating
bash
# Check GCloud authentication
gcloud auth list
# Check project is correct
gcloud config get-value project
# Verify secret exists
gcloud secrets list | grep SECRET_NAME
# Verify you have access
gcloud secrets versions access latest --secret="SECRET_NAME"
# Force regenerate
rm backend/.env web/.env
./scripts/sync-gcloud-secrets.sh allService can't access secrets
bash
# Check service account permissions
gcloud secrets get-iam-policy SECRET_NAME
# Grant access
gcloud secrets add-iam-policy-binding SECRET_NAME \
--member="serviceAccount:SERVICE_ACCOUNT@PROJECT.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"Secret version mismatch
bash
# List all versions
gcloud secrets versions list SECRET_NAME
# Access specific version
gcloud secrets versions access VERSION_NUMBER --secret="SECRET_NAME"
# Promote older version to latest
gcloud secrets versions enable VERSION_NUMBER --secret="SECRET_NAME"Related Documentation
Emergency Procedures
Secret Leaked / Compromised
bash
# 1. IMMEDIATELY disable compromised version
gcloud secrets versions disable VERSION_NUMBER --secret="SECRET_NAME"
# 2. Generate new secret value
NEW_VALUE=$(openssl rand -base64 32)
# 3. Add new version
gcloud secrets versions add SECRET_NAME --data-file=- <<< "$NEW_VALUE"
# 4. Regenerate .env files
./scripts/sync-gcloud-secrets.sh all
# 5. Rolling restart all services
kubectl rollout restart deployment/bayit-backend-production
kubectl rollout restart deployment/bayit-plus-web
# 6. Verify old version disabled
gcloud secrets versions list SECRET_NAME
# 7. Monitor for unauthorized access
gcloud logging read "resource.type=secretmanager.googleapis.com/Secret"
# 8. Notify security team
./scripts/notify-security-incident.sh "SECRET_NAME compromised"Enforcement
This is a CRITICAL REQUIREMENT with ZERO TOLERANCE.
Any .env file edits that bypass Google Cloud Secret Manager will be:
- ❌ Rejected in code review
- ❌ Blocked by CI/CD pipeline
- ❌ Flagged as security incident
Claude Code is configured to REFUSE any direct .env edits and will:
- Create GCloud secrets documentation instead
- Provide sync scripts for regenerating .env
- Never modify .env files directly