Files
Chris Farhood 282025ca24 docs: implement Phase 3 - user tutorials and guides
Create comprehensive tutorials and user guides for common workflows
and core concepts.

New tutorials:
- tutorials/ci-cd-integration.md (8KB) - Complete CI/CD guide
  - GitHub Actions, GitLab CI, and Jenkins examples
  - Certificate management and kubeseal CLI usage
  - Bulk secret creation and environment-specific patterns
  - Troubleshooting and best practices

New user guides:
- user-guide/scopes-explained.md (12KB) - Deep dive into scopes
  - Detailed explanation of strict/namespace-wide/cluster-wide
  - Security implications and use cases
  - Decision tree for scope selection
  - Common mistakes and how to avoid them
  - Scope comparison table

- user-guide/rbac-permissions.md (10KB) - RBAC configuration
  - Required permissions for different access levels
  - Example RBAC configurations (viewer, creator, admin)
  - Service account setup for CI/CD
  - Plugin UI behavior based on permissions
  - Troubleshooting permission issues
  - Security best practices

Benefits:
- Real-world examples for GitHub Actions, GitLab CI, Jenkins
- Clear security guidance with decision trees
- Copy-paste RBAC manifests for common scenarios
- Troubleshooting sections for each guide
- Cross-referenced with other documentation

Phase 3 deliverables (3-4 days estimated, completed in 1 session):
 CI/CD integration tutorial with 3 platform examples
 Scopes explained with security best practices
 RBAC permissions guide with example manifests
 Decision trees and comparison tables
 Troubleshooting sections for each guide

Total documentation:
- 30KB of new tutorial/guide content
- 3 comprehensive guides
- 20+ code examples
- Cross-referenced with API docs and other guides

Next: Phase 4 - Troubleshooting guides and ADRs

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-11 23:31:34 -05:00

10 KiB

Scopes Explained

Understanding SealedSecret scopes and when to use each one.

What are Scopes?

Scopes determine where a SealedSecret can be unsealed. They control the binding between the encrypted data and its Kubernetes resource identity (namespace and/or name).

Think of scopes as security levels for your encrypted secrets:

  • Strict = Locked to specific name + namespace (most secure)
  • Namespace-wide = Locked to namespace (can rename)
  • Cluster-wide = Can move anywhere (least secure)

The Three Scopes

Binding: Namespace + Name + Key

The sealed secret can ONLY be unsealed if all three match:

  • Same namespace
  • Same secret name
  • Same key name

Example:

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  encryptedData:
    password: AgB...  # Can ONLY be unsealed as:
                      # - name: db-credentials
                      # - namespace: production
                      # - key: password

Use when:

  • Production secrets
  • Database credentials
  • API keys
  • Any sensitive data

Advantages:

  • Maximum security
  • Prevents accidental leaks from renaming
  • Prevents cross-namespace access

Limitations:

  • Cannot rename the secret
  • Cannot move to different namespace
  • Must re-encrypt if name/namespace changes

Creating with Headlamp:

  1. Click "Create Sealed Secret"
  2. Select "strict" scope (default)
  3. Fill in name, namespace, and secret data
  4. Click "Create"

Creating with kubeseal:

kubectl create secret generic db-credentials \
  --namespace production \
  --from-literal=password=mysecret \
  --dry-run=client -o yaml | \
kubeseal --cert cert.pem --scope strict --format yaml

Namespace-Wide Scope

Binding: Namespace + Key

The sealed secret can be unsealed if:

  • Same namespace
  • Same key name
  • ⚠️ Any secret name (can rename)

Example:

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: app-config
  namespace: staging
spec:
  encryptedData:
    api-url: AgB...  # Can be unsealed as:
                     # - name: app-config (or ANY name)
                     # - namespace: staging (must match)
                     # - key: api-url (must match)

Use when:

  • Shared configuration across multiple apps in a namespace
  • Secrets that might be renamed
  • Non-critical secrets

Advantages:

  • Flexible naming
  • Can rename secret without re-encryption
  • Still namespace-isolated

Limitations:

  • Less secure than strict
  • Cannot move to different namespace
  • Anyone in namespace can unseal by creating correctly-named secret

Creating with Headlamp:

  1. Click "Create Sealed Secret"
  2. Select "namespace-wide" scope
  3. Fill in namespace and secret data
  4. Name can be changed later

Creating with kubeseal:

kubectl create secret generic temp-name \
  --namespace staging \
  --from-literal=api-url=https://api.staging.example.com \
  --dry-run=client -o yaml | \
kubeseal --cert cert.pem --scope namespace-wide --format yaml

Cluster-Wide Scope

Binding: Key only

The sealed secret can be unsealed if:

  • ⚠️ Any namespace
  • ⚠️ Any secret name
  • Same key name

Example:

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: global-config
  namespace: default
spec:
  encryptedData:
    license-key: AgB...  # Can be unsealed as:
                         # - name: ANY
                         # - namespace: ANY
                         # - key: license-key (must match)

Use when:

  • Truly global configuration
  • License keys used across all namespaces
  • Public URLs or non-sensitive config

Advantages:

  • Maximum flexibility
  • Can move anywhere in cluster
  • Can rename freely

Limitations:

  • Least secure
  • Anyone in cluster can unseal
  • Easy to accidentally expose

Creating with Headlamp:

  1. Click "Create Sealed Secret"
  2. Select "cluster-wide" scope
  3. Fill in secret data
  4. Can deploy to any namespace

Creating with kubeseal:

kubectl create secret generic temp \
  --from-literal=license-key=ABC123 \
  --dry-run=client -o yaml | \
kubeseal --cert cert.pem --scope cluster-wide --format yaml

Scope Comparison

Feature Strict Namespace-Wide Cluster-Wide
Security 🔒🔒🔒 High 🔒🔒 Medium 🔒 Low
Can rename No Yes Yes
Can move namespace No No Yes
Binding Name+NS+Key NS+Key Key only
Use case Production secrets Shared namespace config Global config
Recommended for Credentials, API keys App config Public URLs

Decision Tree

┌─────────────────────────────────┐
│ Is this a sensitive credential? │
│ (password, API key, token, etc) │
└─────────┬──────────────┬────────┘
          │              │
        YES             NO
          │              │
          v              v
    ┌─────────┐    ┌──────────────────┐
    │ strict  │    │ Could this value │
    │ scope   │    │ be shared across │
    └─────────┘    │ your cluster?    │
                   └────┬────────┬────┘
                        │        │
                      YES       NO
                        │        │
                        v        v
                   ┌──────────┐ ┌──────────────┐
                   │cluster-  │ │namespace-    │
                   │wide scope│ │wide scope    │
                   └──────────┘ └──────────────┘

Examples by Use Case

Database Credentials (Strict)

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: postgres-credentials
  namespace: production
spec:
  encryptedData:
    username: AgB...
    password: AgB...
    host: AgB...

Why strict? Credentials must not leak to other namespaces or be accessible via renaming.

Application Config (Namespace-Wide)

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: app-config
  namespace: staging
spec:
  encryptedData:
    api-url: AgB...
    timeout: AgB...
    log-level: AgB...

Why namespace-wide? Config is specific to staging but might be used by multiple apps (with different names).

License Key (Cluster-Wide)

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: company-license
  namespace: default
spec:
  encryptedData:
    license-key: AgB...

Why cluster-wide? The same license applies to all namespaces and environments.

Changing Scopes

You cannot change a scope after encryption. To change scope:

  1. Delete the old sealed secret:

    kubectl delete sealedsecret old-secret -n production
    
  2. Re-encrypt with new scope:

    kubectl create secret generic old-secret \
      --namespace production \
      --from-literal=password=value \
      --dry-run=client -o yaml | \
    kubeseal --cert cert.pem --scope namespace-wide --format yaml > new-sealed-secret.yaml
    
  3. Apply new sealed secret:

    kubectl apply -f new-sealed-secret.yaml
    

Security Best Practices

1. Default to Strict

Always use strict scope unless you have a specific reason not to:

Good:

# Explicit strict scope
kubeseal --scope strict

Risky:

# Using cluster-wide for credentials
kubeseal --scope cluster-wide  # Don't do this for secrets!

2. Audit Your Scopes

List all sealed secrets and their scopes:

kubectl get sealedsecrets --all-namespaces -o json | \
  jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name): \(.spec.template.type // "strict")"'

3. Separate Sensitive from Non-Sensitive

# Sensitive → strict
kubectl create secret generic db-creds --from-literal=password=... | \
  kubeseal --scope strict

# Non-sensitive → namespace-wide or cluster-wide
kubectl create secret generic app-urls --from-literal=api=https://... | \
  kubeseal --scope namespace-wide

4. Document Scope Choices

Add annotations to explain why a scope was chosen:

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: db-credentials
  namespace: production
  annotations:
    sealedsecrets.scope: "strict"
    sealedsecrets.reason: "Database credentials must not be accessible outside production namespace"
spec:
  encryptedData:
    password: AgB...

Common Mistakes

Using Cluster-Wide for Everything

# DON'T: Encrypting passwords with cluster-wide scope
kubeseal --scope cluster-wide < secret-with-passwords.yaml

Problem: Anyone in any namespace can create a secret with the same key name and unseal it.

Solution: Use strict scope for credentials.

Renaming Strict-Scoped Secrets

# Create strict-scoped secret
kubectl apply -f db-secret.yaml  # name: db-credentials

# Later, try to rename
kubectl apply -f db-secret.yaml  # name: database-creds (DIFFERENT NAME)

Problem: Sealed secret won't unseal because name changed.

Solution: Re-encrypt with new name or use namespace-wide scope.

Moving Secrets Between Namespaces

# Seal for production
kubectl create secret generic app-secret --namespace production | kubeseal

# Try to use in staging
kubectl apply -f sealed-secret.yaml --namespace staging

Problem: Won't unseal (namespace mismatch with strict or namespace-wide scope).

Solution: Re-encrypt for the target namespace or use cluster-wide (if appropriate).

Next Steps

Resources