Skip to content

Secrets & Vault Paths

Reference for secrets management and Vault path organization.

Vault Path Structure

secret/
└── fzymgc-house/
    ├── cluster/           # Kubernetes service secrets
    │   ├── authentik      # Authentik admin credentials
    │   ├── argocd/        # ArgoCD configuration
    │   ├── grafana        # Grafana admin/OIDC
    │   ├── vault/         # Vault configuration
    │   ├── windmill       # Windmill tokens
    │   ├── mealie         # Mealie secrets
    │   ├── github         # GitHub tokens
    │   ├── cloudflared/   # Tunnel credentials
    │   └── postgres/      # Database credentials
    │       └── users/     # Per-app DB users
    ├── infrastructure/    # Infrastructure credentials
    │   ├── cloudflare/    # Cloudflare API tokens
    │   ├── hcp/           # HCP Terraform credentials
    │   └── bmc/           # BMC credentials per node
    └── applications/      # Application-specific secrets

Cluster Secrets

Path Purpose Keys
secret/fzymgc-house/cluster/authentik Authentik admin terraform_token, secret_key, bootstrap_password, email_host, email_port, email_username, email_password, email_use_tls, email_from
secret/fzymgc-house/cluster/argocd/* ArgoCD config admin_password, github_token, oidc_secret
secret/fzymgc-house/cluster/grafana Grafana secrets admin_password, oidc_client_id, oidc_client_secret
secret/fzymgc-house/cluster/vault/* Vault config oidc_client_id, oidc_client_secret
secret/fzymgc-house/cluster/windmill Windmill secrets github_token, webhook_secret
secret/fzymgc-house/cluster/mealie Mealie config oidc_client_id, oidc_client_secret
secret/fzymgc-house/cluster/github GitHub integration app_id, app_private_key, webhook_secret
secret/fzymgc-house/cluster/cloudflared/* Tunnel creds tunnel_token, tunnel_id
secret/fzymgc-house/cluster/postgres/users/* DB users username, password

Infrastructure Secrets

Path Purpose Keys
secret/fzymgc-house/infrastructure/cloudflare/bootstrap-token Terraform token token
secret/fzymgc-house/infrastructure/cloudflare/discord-webhook Notifications url
secret/fzymgc-house/infrastructure/cloudflare/hcp-terraform-hmac Webhook auth secret
secret/fzymgc-house/infrastructure/cloudflare/hcp-terraform-worker Worker auth token
secret/fzymgc-house/infrastructure/hcp HCP credentials client_id, client_secret
secret/fzymgc-house/infrastructure/bmc/* Node BMC creds username, password

Vault Policies

Policy to Secret Path Mapping

Policy Paths Consumers
external-secrets-operator secret/data/* (read) External Secrets Operator
fzymgc-cluster-secret-reader secret/data/* (read) General cluster read access
arc-runners secret/data/fzymgc-house/cluster/github GitHub Actions runners
cert-manager pki/fzymgc-house/* cert-manager PKI
mealie secret/data/fzymgc-house/cluster/mealie, secret/data/fzymgc-house/cluster/postgres/users/main-mealie Mealie app
github-actions secret/data/fzymgc-house/cluster/windmill, secret/data/fzymgc-house/cluster/github CI/CD workflows
hcp-terraform Multiple cluster paths Terraform workspaces
infrastructure-developer secret/data/fzymgc-house/infrastructure/*, secret/data/fzymgc-house/* (read) Human operators
admin secret/* (full) Administrators

Auth Methods

Method Mount Purpose
Kubernetes kubernetes/ Service account auth
OIDC oidc/ Human SSO via Authentik
AppRole approle/ Automation/CI
JWT jwt/ HCP Terraform

Kubernetes Integration

ClusterSecretStore

apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
  name: vault
spec:
  provider:
    vault:
      server: "https://vault-internal.vault:8200"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "external-secrets"

ExternalSecret Example

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: grafana-admin
  namespace: grafana
spec:
  secretStoreRef:
    name: vault
    kind: ClusterSecretStore
  target:
    name: grafana-admin-secret
  data:
    - secretKey: admin-password
      remoteRef:
        key: fzymgc-house/cluster/grafana
        property: admin_password

Adding New Secrets

1. Create Secret in Vault

# Login to Vault
export VAULT_ADDR=https://vault.fzymgc.house
vault login -method=oidc

# Create secret
vault kv put secret/fzymgc-house/cluster/new-service \
  api_key="value" \
  secret_key="value"

# Verify
vault kv get secret/fzymgc-house/cluster/new-service

2. Create ExternalSecret

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: new-service-secrets
  namespace: new-service
spec:
  secretStoreRef:
    name: vault
    kind: ClusterSecretStore
  target:
    name: new-service-secrets
  data:
    - secretKey: API_KEY
      remoteRef:
        key: fzymgc-house/cluster/new-service
        property: api_key
    - secretKey: SECRET_KEY
      remoteRef:
        key: fzymgc-house/cluster/new-service
        property: secret_key

3. Update Policy (if new path)

If using a new path pattern, update the relevant policy in tf/vault/:

resource "vault_policy" "new-service" {
  name   = "new-service"
  policy = <<EOT
path "secret/data/fzymgc-house/cluster/new-service" {
  capabilities = ["read"]
}
path "secret/metadata/fzymgc-house/cluster/new-service" {
  capabilities = ["read", "list"]
}
EOT
}

Secret Naming Conventions

Type Pattern Example
Service secrets secret/fzymgc-house/cluster/<service> secret/fzymgc-house/cluster/grafana
Sub-service secrets secret/fzymgc-house/cluster/<service>/<component> secret/fzymgc-house/cluster/argocd/github
Database users secret/fzymgc-house/cluster/postgres/users/<db>-<user> secret/fzymgc-house/cluster/postgres/users/main-mealie
Infrastructure secret/fzymgc-house/infrastructure/<provider> secret/fzymgc-house/infrastructure/cloudflare
Per-node secrets secret/fzymgc-house/infrastructure/<type>/<node> secret/fzymgc-house/infrastructure/bmc/tpi-alpha-1

Rotation Procedures

Manual Rotation

# Update secret value
vault kv put secret/fzymgc-house/cluster/service key=new-value

# Restart dependent pods
kubectl rollout restart deployment/service -n namespace

Automated Rotation

ExternalSecrets refreshes secrets based on refreshInterval:

spec:
  refreshInterval: 1h  # Check for updates hourly

Troubleshooting

Verify Secret Access

# Check Vault path exists
vault kv get secret/fzymgc-house/cluster/service

# Check ExternalSecret status
kubectl get externalsecret -n namespace
kubectl describe externalsecret name -n namespace

# Check synced Kubernetes secret
kubectl get secret name -n namespace -o yaml

Common Issues

Issue Cause Solution
SecretSyncedError Wrong path or missing permissions Verify Vault path and policy
Secret not updating refreshInterval not elapsed Wait or trigger manual refresh
Permission denied Missing policy binding Update Kubernetes auth role

See Vault Operations for detailed operational procedures.