Secrets Management¶
Before You Read¶
This guide explains how to manage secrets operationally. For the architecture behind this, see Security Model.
Architecture Summary¶
GCP Secret Manager (source of truth)
↓ (synced by ESO every N minutes)
Kubernetes Secret (namespace-scoped)
↓ (mounted as env vars or volume)
Pod
Critical rule: Never commit secret values to Git. Only ExternalSecret CRDs (which reference secret names, not values) are in Git.
Adding a New Secret¶
Step 1: Create the Secret in GCP Secret Manager¶
Use Terraform (preferred) or gcloud CLI.
Via Terraform (add to infrastructure-management/projects/orofi-{env}/secrets.tf):
module "my_new_secret" {
source = "../../modules/secretmanager"
project_id = local.project_id
secret_id = "${local.env}-my-new-secret"
}
Apply:
Via gcloud CLI (quick for ad-hoc):
# Create the secret (empty)
gcloud secrets create dev-my-new-secret \
--project=orofi-dev-cloud \
--replication-policy=automatic
# Add a version (the actual value)
echo -n "my-secret-value" | gcloud secrets versions add dev-my-new-secret \
--data-file=- \
--project=orofi-dev-cloud
Step 2: Grant Access to the Service Account¶
The microservice's GCP service account must have roles/secretmanager.secretAccessor on the new secret.
Via Terraform (add to modules/service-accounts call for that service):
module "microservice_identity_sa" {
source = "../../modules/service-accounts"
...
secret_ids = [
"existing-secret-1",
"existing-secret-2",
"dev-my-new-secret", # ← add this
]
}
Step 3: Create an ExternalSecret Resource¶
Add an ExternalSecret to the service's Kubernetes manifests (in infrastructure-configuration):
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: my-new-secret
namespace: microservice-identity
spec:
refreshInterval: "1h"
secretStoreRef:
name: gcp-secret-manager # cluster-wide SecretStore, references {env}-ext-secrets-manager SA
kind: ClusterSecretStore
target:
name: my-new-secret # name of the K8s Secret to create
creationPolicy: Owner
data:
- secretKey: MY_KEY # key in the K8s Secret
remoteRef:
key: dev-my-new-secret # name in GCP Secret Manager
Step 4: Mount the Secret in the Pod¶
In the Helm chart values.yaml for the service, add:
Or as a volume mount for files (e.g., certificates):
volumes:
- name: my-cert
secret:
secretName: my-new-secret
volumeMounts:
- name: my-cert
mountPath: /etc/certs
readOnly: true
Updating a Secret Value¶
Rotate the Value in GCP¶
# Add a new version (old version remains accessible until disabled)
echo -n "new-secret-value" | gcloud secrets versions add {env}-my-secret \
--data-file=- \
--project=orofi-{env}-cloud
Wait for ESO to Sync¶
The External Secrets Operator syncs on the refreshInterval defined in the ExternalSecret (typically 1h). To force immediate sync:
# Annotate the ExternalSecret to trigger a resync
kubectl annotate externalsecret my-new-secret \
-n microservice-identity \
force-sync=$(date +%s) \
--overwrite
Restart Pods to Pick Up New Secret¶
If the secret is mounted as an environment variable (not a volume), pods must restart to pick up the new value:
kubectl rollout restart deployment/microservice-identity -n microservice-identity
kubectl rollout status deployment/microservice-identity -n microservice-identity
Volume mounts vs env vars
Secrets mounted as volumes are updated automatically without a pod restart (Kubernetes refreshes volume content). Secrets injected as environment variables require a pod restart.
Secret Rotation for Database Passwords¶
Database passwords require extra steps because both the GCP secret and the Cloud SQL user password must be updated atomically.
# 1. Generate a new password
NEW_PASSWORD=$(openssl rand -base64 32)
# 2. Update Cloud SQL user password
gcloud sql users set-password {username} \
--host=% \
--instance=orofi-{env}-cloud-{env}-oro-mysql-instance \
--password="$NEW_PASSWORD" \
--project=orofi-{env}-cloud
# 3. Update the secret in GCP Secret Manager
echo -n "$NEW_PASSWORD" | gcloud secrets versions add {env}-microservice-{name}-db-connection \
--data-file=- \
--project=orofi-{env}-cloud
# 4. Force ESO resync
kubectl annotate externalsecret microservice-{name}-db-connection \
-n microservice-{name} \
force-sync=$(date +%s) --overwrite
# 5. Restart the service to use new credentials
kubectl rollout restart deployment/microservice-{name} -n microservice-{name}
Secret Inventory by Service¶
Shared Secrets (all services receive these)¶
| Secret Name | Purpose |
|---|---|
{env}-shared-microservice-secrets |
Shared configuration values |
{env}-redis-auth-password |
Redis AUTH password |
Per-Service Secrets¶
| Service | Secret Name |
|---|---|
api-gateway-public |
{env}-api-gateway-public-secret |
api-gateway-account |
{env}-api-gateway-account-secret |
api-gateway-oro |
{env}-api-gateway-oro-secret |
api-gateway-admin-dashboard |
{env}-api-gateway-admin-dashboard-secret |
microservice-communication |
{env}-microservice-communication-secret |
microservice-identity |
{env}-microservice-identity-secret |
microservice-monolith |
{env}-microservice-monolith-secret |
microservice-analytics |
{env}-microservice-analytics-secret |
Database Connection Secrets¶
| Secret Name | Used By |
|---|---|
{env}-microservice-communication-db-connection |
microservice-communication |
{env}-microservice-identity-db-connection |
microservice-identity |
{env}-microservice-monolith-db-connection |
microservice-monolith |
{env}-microservice-analytics-db-connection |
microservice-analytics |
{env}-flyway-admin-db-connection |
Migration runner |
Platform Secrets¶
| Secret Name | Used By |
|---|---|
{env}-mongodb-connection |
Mongo Express, MongoDB Operator |
{env}-kafka-secrets |
Kafka SASL credentials |
{env}-oauth2-secrets |
OAuth2 Proxy (Kafka UI, Mongo Express) |
{env}-argocd-secrets |
ArgoCD configuration |
{env}-grafana-secrets |
Grafana |
{env}-slack-webhooks |
Alerting |
{env}-firebase-secret |
Firebase Admin SDK |
Authentication Secrets¶
| Secret Name | Used By |
|---|---|
microservice-identity-jwt-private-key-secret |
JWT signing |
microservice-identity-apikey-private-key-secret |
API key signing |
microservice-identity-encryption-search-hash-pepper-key-secret |
HMAC pepper |
{env}-admin-dashboard-gateway-apikey |
Admin gateway authentication |
{env}-oro-gateway-apikey |
Oro gateway authentication |
{env}-public-gateway-apikey |
Public gateway authentication |
{env}-account-gateway-apikey |
Account gateway authentication |
Mobile Secrets¶
| Secret Name | Used By |
|---|---|
{env}-appstore-api-key |
iOS App Store releases |
{env}-appstore-cert |
iOS signing certificate |
{env}-appstore-profile |
iOS provisioning profile |
See Also¶
- Security Model — ESO and IAM architecture
- Environment Variables — using secrets as env vars
- Certificate Rotation Runbook — TLS cert rotation