Claude-skill-registry-data managing-sops-secrets
Manages SOPS-encrypted Kubernetes secrets for Flux GitOps deployments using age encryption
git clone https://github.com/majiayu000/claude-skill-registry-data
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry-data "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/managing-sops-secrets" ~/.claude/skills/majiayu000-claude-skill-registry-data-managing-sops-secrets && rm -rf "$T"
data/managing-sops-secrets/SKILL.mdManaging SOPS Secrets
This skill handles creation, editing, and troubleshooting of SOPS-encrypted secrets in the Superbloom Flux repository.
Capabilities
- Encrypt new secrets with SOPS
- Decrypt and edit existing secrets
- Troubleshoot encryption/decryption issues
- Manage .sops.yaml configuration
Configuration
SOPS config location:
flux/.sops.yaml
creation_rules: - path_regex: 'clusters/superbloom/.*/secrets.yaml' encrypted_regex: '^(data|stringData)$' age: ['<your-age-public-key>']
This encrypts
data and stringData fields in any secrets.yaml under clusters/superbloom/.
Operations
View Decrypted Secret
cd flux sops -d clusters/superbloom/apps/<app>/secrets.yaml
Edit Secret (Interactive)
cd flux sops clusters/superbloom/apps/<app>/secrets.yaml # Opens in $EDITOR, auto-encrypts on save
Edit Secret (Scripted)
cd flux # Decrypt to temp file sops -d clusters/superbloom/apps/<app>/secrets.yaml > /tmp/secrets.yaml # Edit vim /tmp/secrets.yaml # or: sed -i 's/old/new/' /tmp/secrets.yaml # Copy back and encrypt cp /tmp/secrets.yaml clusters/superbloom/apps/<app>/secrets.yaml sops --encrypt --in-place clusters/superbloom/apps/<app>/secrets.yaml # Clean up rm /tmp/secrets.yaml
Create New Encrypted Secret
cd flux # Create plaintext cat > clusters/superbloom/apps/<app>/secrets.yaml << 'EOF' apiVersion: v1 kind: Secret metadata: name: my-secret namespace: my-app type: Opaque stringData: API_KEY: example-api-key-replace-me DATABASE_URL: postgres://user:example@host/db EOF # Encrypt in place sops --encrypt --in-place clusters/superbloom/apps/<app>/secrets.yaml
Secret Structures
Simple Key-Value Secret
apiVersion: v1 kind: Secret metadata: name: app-env namespace: app type: Opaque stringData: API_KEY: REPLACE_WITH_REAL_VALUE DB_PASSWORD: REPLACE_WITH_REAL_VALUE
HelmRelease Values Secret (bjw-s app-template)
apiVersion: v1 kind: Secret metadata: name: app-secrets namespace: app type: Opaque stringData: values.yaml: | controllers: main: serviceAccount: name: app containers: main: image: repository: ghcr.io/saavy1/app tag: latest env: SECRET_KEY: "REPLACE_WITH_REAL_VALUE" service: main: controller: main ports: http: port: 3000
Multiple Documents
apiVersion: v1 kind: Secret metadata: name: app-helm-values namespace: app type: Opaque stringData: values.yaml: | # helm values --- apiVersion: v1 kind: Secret metadata: name: app-env namespace: app type: Opaque stringData: API_KEY: REPLACE_WITH_REAL_VALUE
Encrypted File Format
After encryption, files look like:
stringData: API_KEY: ENC[AES256_GCM,data:...,iv:...,tag:...,type:str] sops: age: - recipient: <your-age-public-key> enc: | -----BEGIN AGE ENCRYPTED FILE----- <encrypted-key-data> -----END AGE ENCRYPTED FILE----- lastmodified: "2025-12-14T01:00:00Z" mac: ENC[AES256_GCM,...] encrypted_regex: ^(data|stringData)$ version: 3.11.0
Troubleshooting
"no matching creation rules found"
Cause: Running sops from wrong directory or path doesn't match regex
Fix: Run from
flux/ directory:
cd flux sops --encrypt --in-place clusters/superbloom/apps/<app>/secrets.yaml
Or update
.sops.yaml path_regex to match your file path.
"could not decrypt" / Key not found
Cause: Age private key not available
Fix: Ensure key exists:
cat ~/.config/sops/age/keys.txt # or export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt
Flux not decrypting secrets in cluster
Cause: Cluster missing sops-age secret
Check:
kubectl get secret sops-age -n flux-system
Fix: The sops-age secret must contain the private key for cluster-side decryption.
Changes not reflected after push
Cause: Flux hasn't reconciled yet
Fix:
flux reconcile kustomization flux-system --with-source
"expected string, got &value.valueUnstructured{Value:...}"
Cause: SOPS preserved a numeric type. Look for
type:int in the encrypted value:
DISCORD_CLIENT_ID: ENC[AES256_GCM,data:...,type:int] # ← problem
When decrypted, this becomes a number, but
stringData requires strings.
Fix: Re-encrypt as string:
cd flux sops clusters/superbloom/apps/<app>/secrets.yaml # In editor, ensure the value is quoted: "1234567890" # Save - SOPS will re-encrypt with type:str
Or decrypt, fix, re-encrypt:
sops -d clusters/superbloom/apps/<app>/secrets.yaml > /tmp/secrets.yaml # Edit /tmp/secrets.yaml - quote numeric values: "1234567890" cp /tmp/secrets.yaml clusters/superbloom/apps/<app>/secrets.yaml sops --encrypt --in-place clusters/superbloom/apps/<app>/secrets.yaml rm /tmp/secrets.yaml
Prevention: Always quote values that look like numbers:
stringData: DISCORD_CLIENT_ID: "1325902250497020000" # ← quoted = string PORT: "3000" # ← quoted = string
Security Notes
- Never commit plaintext secrets
- The age public key (in
) is safe to share.sops.yaml - The age private key must be kept secure
- Flux cluster has its own copy of private key
- Clean up temp files:
rm /tmp/secrets.yaml
Best Practices
- Always work from
directoryflux/ - Verify encryption: look for
patternsENC[AES256_GCM,...] - Test decryption before committing
- Use temp files for complex edits
- Clean up plaintext temp files immediately