diff --git a/docs/tutorials/ci-cd-integration.md b/docs/tutorials/ci-cd-integration.md new file mode 100644 index 0000000..4bcbf15 --- /dev/null +++ b/docs/tutorials/ci-cd-integration.md @@ -0,0 +1,466 @@ +# CI/CD Integration Tutorial + +Learn how to automate sealed secret creation in your CI/CD pipelines. + +## Overview + +This tutorial shows you how to: +- Create sealed secrets in CI/CD pipelines +- Download sealing certificates for offline encryption +- Use `kubeseal` CLI with plugin-exported certificates +- Integrate with GitHub Actions, GitLab CI, and Jenkins + +## Prerequisites + +- Headlamp Sealed Secrets plugin installed +- Sealed Secrets controller running in your cluster +- Access to download sealing certificates +- CI/CD system (GitHub Actions, GitLab CI, or Jenkins) + +## Step 1: Download the Sealing Certificate + +The sealing certificate is the public key used to encrypt secrets. You can download it from Headlamp: + +### Using Headlamp UI + +1. Navigate to **Sealed Secrets → Sealing Keys** +2. Find the active certificate (no expiry warning) +3. Click **Download** +4. Save as `sealed-secrets-cert.pem` + +### Using kubectl + +Alternatively, fetch it directly from the controller: + +```bash +kubectl get secret -n kube-system \ + -l sealedsecrets.bitnami.com/sealed-secrets-key=active \ + -o jsonpath='{.items[0].data.tls\.crt}' | base64 -d > sealed-secrets-cert.pem +``` + +Or use the controller's certificate endpoint: + +```bash +curl http://sealed-secrets-controller.kube-system:8080/v1/cert.pem > sealed-secrets-cert.pem +``` + +## Step 2: Install kubeseal CLI + +Install the `kubeseal` command-line tool: + +**macOS (Homebrew):** +```bash +brew install kubeseal +``` + +**Linux:** +```bash +KUBESEAL_VERSION='0.24.0' +wget "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz" +tar -xvzf kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz kubeseal +sudo install -m 755 kubeseal /usr/local/bin/kubeseal +``` + +**Windows (Chocolatey):** +```powershell +choco install kubeseal +``` + +**Verify installation:** +```bash +kubeseal --version +# Output: kubeseal version: v0.24.0 +``` + +## Step 3: Create Sealed Secrets in CI/CD + +### GitHub Actions Example + +Create `.github/workflows/sealed-secrets.yml`: + +```yaml +name: Create Sealed Secrets + +on: + push: + paths: + - 'secrets/**' + workflow_dispatch: + +jobs: + seal-secrets: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install kubeseal + run: | + KUBESEAL_VERSION='0.24.0' + wget "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz" + tar -xvzf kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz kubeseal + sudo install -m 755 kubeseal /usr/local/bin/kubeseal + + - name: Download sealing certificate + run: | + # Option 1: From repository secret + echo "${{ secrets.SEALED_SECRETS_CERT }}" > sealed-secrets-cert.pem + + # Option 2: From cluster (requires kubectl access) + # kubectl get secret -n kube-system \ + # -l sealedsecrets.bitnami.com/sealed-secrets-key=active \ + # -o jsonpath='{.items[0].data.tls\.crt}' | base64 -d > sealed-secrets-cert.pem + + - name: Create sealed secret + run: | + # Create a plain Kubernetes secret + kubectl create secret generic my-app-secret \ + --from-literal=database-password=${{ secrets.DB_PASSWORD }} \ + --from-literal=api-key=${{ secrets.API_KEY }} \ + --dry-run=client \ + -o yaml > secret.yaml + + # Seal the secret + kubeseal --cert sealed-secrets-cert.pem \ + --format=yaml < secret.yaml > sealed-secret.yaml + + # Commit and push (optional) + git add sealed-secret.yaml + git commit -m "chore: update sealed secret" + git push + + - name: Apply to cluster + run: | + kubectl apply -f sealed-secret.yaml +``` + +**Store the certificate as a GitHub Secret:** +1. Go to repository Settings → Secrets and variables → Actions +2. Click "New repository secret" +3. Name: `SEALED_SECRETS_CERT` +4. Value: Paste contents of `sealed-secrets-cert.pem` + +### GitLab CI Example + +Create `.gitlab-ci.yml`: + +```yaml +stages: + - seal + - deploy + +variables: + KUBESEAL_VERSION: "0.24.0" + +seal-secrets: + stage: seal + image: alpine:latest + before_script: + - apk add --no-cache curl tar + - curl -LO "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz" + - tar -xvzf kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz kubeseal + - mv kubeseal /usr/local/bin/ + - chmod +x /usr/local/bin/kubeseal + script: + # Get certificate from GitLab CI variable + - echo "$SEALED_SECRETS_CERT" > sealed-secrets-cert.pem + + # Create and seal secret + - | + cat < secret.yaml + apiVersion: v1 + kind: Secret + metadata: + name: my-app-secret + namespace: production + stringData: + database-password: "${DB_PASSWORD}" + api-key: "${API_KEY}" + EOF + + - kubeseal --cert sealed-secrets-cert.pem --format=yaml < secret.yaml > sealed-secret.yaml + artifacts: + paths: + - sealed-secret.yaml + only: + - main + +deploy-sealed-secret: + stage: deploy + image: bitnami/kubectl:latest + script: + - kubectl apply -f sealed-secret.yaml + dependencies: + - seal-secrets + only: + - main +``` + +**Set GitLab CI Variables:** +1. Go to Settings → CI/CD → Variables +2. Add `SEALED_SECRETS_CERT` (type: File) +3. Add `DB_PASSWORD` and `API_KEY` (type: Masked) + +### Jenkins Pipeline Example + +Create `Jenkinsfile`: + +```groovy +pipeline { + agent any + + environment { + KUBESEAL_VERSION = '0.24.0' + NAMESPACE = 'production' + } + + stages { + stage('Install kubeseal') { + steps { + sh ''' + if ! command -v kubeseal &> /dev/null; then + wget "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz" + tar -xvzf kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz kubeseal + sudo install -m 755 kubeseal /usr/local/bin/kubeseal + fi + ''' + } + } + + stage('Download Certificate') { + steps { + withCredentials([file(credentialsId: 'sealed-secrets-cert', variable: 'CERT_FILE')]) { + sh 'cp $CERT_FILE sealed-secrets-cert.pem' + } + } + } + + stage('Create Sealed Secret') { + steps { + withCredentials([ + string(credentialsId: 'db-password', variable: 'DB_PASSWORD'), + string(credentialsId: 'api-key', variable: 'API_KEY') + ]) { + sh ''' + # Create secret manifest + kubectl create secret generic my-app-secret \ + --namespace=${NAMESPACE} \ + --from-literal=database-password=${DB_PASSWORD} \ + --from-literal=api-key=${API_KEY} \ + --dry-run=client \ + -o yaml > secret.yaml + + # Seal it + kubeseal --cert sealed-secrets-cert.pem \ + --format=yaml < secret.yaml > sealed-secret.yaml + + # Show sealed secret (safe to log) + cat sealed-secret.yaml + ''' + } + } + } + + stage('Deploy') { + steps { + sh 'kubectl apply -f sealed-secret.yaml' + } + } + } + + post { + always { + cleanWs() + } + } +} +``` + +## Step 4: Verify Sealed Secret + +After creating the sealed secret, verify it was created and unsealed: + +```bash +# Check sealed secret exists +kubectl get sealedsecret my-app-secret -n production + +# Check the unsealed secret was created +kubectl get secret my-app-secret -n production + +# Verify secret contains correct keys +kubectl get secret my-app-secret -n production -o jsonpath='{.data}' | jq 'keys' +# Output: ["api-key", "database-password"] +``` + +## Advanced Patterns + +### Pattern 1: Different Secrets per Environment + +Create environment-specific sealed secrets: + +```bash +# Development +kubectl create secret generic my-app-secret \ + --namespace=dev \ + --from-literal=api-url=https://dev-api.example.com \ + --dry-run=client -o yaml | \ +kubeseal --cert sealed-secrets-cert.pem --format=yaml > dev-sealed-secret.yaml + +# Production +kubectl create secret generic my-app-secret \ + --namespace=production \ + --from-literal=api-url=https://api.example.com \ + --dry-run=client -o yaml | \ +kubeseal --cert sealed-secrets-cert.pem --format=yaml > prod-sealed-secret.yaml +``` + +### Pattern 2: Bulk Secret Creation + +Create multiple secrets from a directory: + +```bash +#!/bin/bash +# seal-all-secrets.sh + +CERT="sealed-secrets-cert.pem" +SECRETS_DIR="plain-secrets" +OUTPUT_DIR="sealed-secrets" + +mkdir -p "$OUTPUT_DIR" + +for secret_file in "$SECRETS_DIR"/*.yaml; do + filename=$(basename "$secret_file") + kubeseal --cert "$CERT" --format=yaml < "$secret_file" > "$OUTPUT_DIR/$filename" + echo "Sealed: $filename" +done +``` + +### Pattern 3: Update Existing Sealed Secret + +To update a sealed secret, re-seal it completely: + +```bash +# Get current secret +kubectl get sealedsecret my-app-secret -o yaml > current-sealed.yaml + +# Extract metadata +# ... modify as needed ... + +# Create new version +kubectl create secret generic my-app-secret \ + --from-literal=database-password=NEW_PASSWORD \ + --from-literal=api-key=NEW_KEY \ + --dry-run=client -o yaml | \ +kubeseal --cert sealed-secrets-cert.pem --format=yaml > updated-sealed-secret.yaml + +# Apply +kubectl apply -f updated-sealed-secret.yaml +``` + +## Best Practices + +### 1. Certificate Management + +✅ **DO:** +- Store certificate in CI/CD secrets (encrypted at rest) +- Download fresh certificate periodically (before expiry) +- Use certificate from same cluster where secrets will be deployed + +❌ **DON'T:** +- Commit certificate to Git (it's public, but still clutters repo) +- Use expired certificates +- Mix certificates from different clusters + +### 2. Secret Rotation + +```bash +# Check certificate expiry in Headlamp +# Sealed Secrets → Sealing Keys → Check "Valid Until" + +# Download new certificate before expiry +# Re-seal all secrets with new certificate +# Deploy new sealed secrets +``` + +### 3. Scope Selection + +- **Use strict scope** for production secrets: + ```bash + kubeseal --cert cert.pem --scope strict < secret.yaml + ``` + +- **Use namespace-wide** for shared secrets: + ```bash + kubeseal --cert cert.pem --scope namespace-wide < secret.yaml + ``` + +- **Use cluster-wide** only for truly global secrets: + ```bash + kubeseal --cert cert.pem --scope cluster-wide < secret.yaml + ``` + +### 4. GitOps Integration + +Store sealed secrets in Git alongside manifests: + +``` +my-app/ +├── deployment.yaml +├── service.yaml +└── sealed-secret.yaml # Safe to commit! +``` + +Apply with: +```bash +kubectl apply -f my-app/ +``` + +## Troubleshooting + +### "no key could decrypt secret" + +**Cause**: Certificate mismatch or secret was sealed for different namespace/name. + +**Solution**: +```bash +# Verify you're using the correct certificate +kubeseal --cert sealed-secrets-cert.pem --validate < sealed-secret.yaml + +# Re-seal with correct scope and metadata +kubectl create secret generic EXACT_NAME \ + --namespace EXACT_NAMESPACE \ + --from-literal=key=value \ + --dry-run=client -o yaml | \ +kubeseal --cert sealed-secrets-cert.pem --scope strict --format=yaml > sealed-secret.yaml +``` + +### "certificate has expired" + +**Solution**: Download fresh certificate from Headlamp: +```bash +# Check expiry +openssl x509 -in sealed-secrets-cert.pem -noout -enddate + +# Download new one from Headlamp UI or kubectl +``` + +### CI/CD pipeline fails to seal + +**Check:** +1. `kubeseal` is installed: `kubeseal --version` +2. Certificate file exists and is valid +3. Input secret YAML is well-formed +4. Namespace exists in cluster + +## Next Steps + +- **[Multi-Cluster Setup](multi-cluster-setup.md)** - Manage secrets across multiple clusters +- **[Secret Rotation](secret-rotation.md)** - Rotate secrets and certificates +- **[RBAC Permissions](../user-guide/rbac-permissions.md)** - Configure access control + +## Resources + +- [kubeseal CLI Documentation](https://github.com/bitnami-labs/sealed-secrets#usage) +- [GitHub Actions Secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets) +- [GitLab CI Variables](https://docs.gitlab.com/ee/ci/variables/) +- [Jenkins Credentials](https://www.jenkins.io/doc/book/using/using-credentials/) diff --git a/docs/user-guide/rbac-permissions.md b/docs/user-guide/rbac-permissions.md new file mode 100644 index 0000000..90dd86c --- /dev/null +++ b/docs/user-guide/rbac-permissions.md @@ -0,0 +1,475 @@ +# RBAC Permissions Guide + +Configure Role-Based Access Control for Sealed Secrets operations. + +## Overview + +The Headlamp Sealed Secrets plugin integrates with Kubernetes RBAC to control: +- Who can **view** SealedSecrets +- Who can **create** SealedSecrets +- Who can **delete** SealedSecrets +- Who can **view unsealed Secrets** +- Who can **download sealing certificates** + +The plugin automatically checks permissions and hides/disables UI elements based on your RBAC roles. + +## Required Permissions + +### Minimum Read-Only Access + +To **view** sealed secrets in Headlamp: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: sealed-secrets-viewer +rules: +# View SealedSecrets +- apiGroups: ["bitnami.com"] + resources: ["sealedsecrets"] + verbs: ["get", "list"] + +# View namespaces (for filtering) +- apiGroups: [""] + resources: ["namespaces"] + verbs: ["list"] +``` + +**What you can do:** +- ✅ List all sealed secrets +- ✅ View sealed secret details +- ✅ See encrypted data (safe to view) +- ❌ Create new sealed secrets +- ❌ Delete sealed secrets +- ❌ View unsealed secret values + +### Creator Access + +To **create** sealed secrets: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: sealed-secrets-creator +rules: +# Create and view SealedSecrets +- apiGroups: ["bitnami.com"] + resources: ["sealedsecrets"] + verbs: ["get", "list", "create"] + +# Download sealing certificates +- apiGroups: [""] + resources: ["services/proxy"] + verbs: ["get"] + resourceNames: ["sealed-secrets-controller"] + +# Or direct service access +- apiGroups: [""] + resources: ["services"] + verbs: ["get"] + resourceNames: ["sealed-secrets-controller"] + +# View namespaces +- apiGroups: [""] + resources: ["namespaces"] + verbs: ["list"] +``` + +**What you can do:** +- ✅ Everything from viewer role +- ✅ Create new sealed secrets +- ✅ Download sealing certificates +- ❌ Delete sealed secrets +- ❌ View unsealed secret values + +### Full Access + +To have **complete control** over sealed secrets: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: sealed-secrets-admin +rules: +# Full SealedSecret access +- apiGroups: ["bitnami.com"] + resources: ["sealedsecrets"] + verbs: ["get", "list", "create", "update", "patch", "delete"] + +# View unsealed Secrets +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] + +# Access controller +- apiGroups: [""] + resources: ["services", "services/proxy"] + verbs: ["get"] + +# View namespaces +- apiGroups: [""] + resources: ["namespaces"] + verbs: ["list"] +``` + +**What you can do:** +- ✅ Everything from creator role +- ✅ Delete sealed secrets +- ✅ View unsealed secret values (decrypt) +- ✅ Update sealed secrets + +## Example RBAC Configurations + +### Example 1: Development Team (Namespace-Scoped) + +Allow developers to manage sealed secrets in their namespace: + +```yaml +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: dev-team-sealed-secrets + namespace: development +rules: +- apiGroups: ["bitnami.com"] + resources: ["sealedsecrets"] + verbs: ["get", "list", "create", "delete"] + +- apiGroups: [""] + resources: ["services"] + verbs: ["get"] + resourceNames: ["sealed-secrets-controller"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: dev-team-sealed-secrets + namespace: development +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: dev-team-sealed-secrets +subjects: +- kind: Group + name: developers + apiGroup: rbac.authorization.k8s.io +``` + +### Example 2: Platform Team (Cluster-Wide) + +Allow platform team to manage all sealed secrets: + +```yaml +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: platform-sealed-secrets +rules: +- apiGroups: ["bitnami.com"] + resources: ["sealedsecrets"] + verbs: ["*"] + +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] + +- apiGroups: [""] + resources: ["services", "services/proxy"] + verbs: ["get"] + +- apiGroups: [""] + resources: ["namespaces"] + verbs: ["list"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: platform-sealed-secrets +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: platform-sealed-secrets +subjects: +- kind: Group + name: platform-team + apiGroup: rbac.authorization.k8s.io +``` + +### Example 3: Read-Only Auditor + +Allow security team to audit sealed secrets without modification: + +```yaml +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: sealed-secrets-auditor +rules: +- apiGroups: ["bitnami.com"] + resources: ["sealedsecrets"] + verbs: ["get", "list"] + +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] + +- apiGroups: [""] + resources: ["namespaces"] + verbs: ["list"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: security-team-auditor +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: sealed-secrets-auditor +subjects: +- kind: Group + name: security-auditors + apiGroup: rbac.authorization.k8s.io +``` + +### Example 4: CI/CD Service Account + +Allow automated systems to create sealed secrets: + +```yaml +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: ci-cd-sealed-secrets + namespace: ci-cd + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: ci-cd-sealed-secrets-creator +rules: +- apiGroups: ["bitnami.com"] + resources: ["sealedsecrets"] + verbs: ["create", "get", "list"] + +- apiGroups: [""] + resources: ["services"] + verbs: ["get"] + resourceNames: ["sealed-secrets-controller"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: ci-cd-sealed-secrets +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: ci-cd-sealed-secrets-creator +subjects: +- kind: ServiceAccount + name: ci-cd-sealed-secrets + namespace: ci-cd +``` + +## Plugin UI Behavior + +The plugin automatically adjusts the UI based on permissions: + +### With Full Permissions +- ✅ "Create Sealed Secret" button visible +- ✅ "Delete" button visible on details page +- ✅ "Decrypt" button visible (if Secret access granted) +- ✅ Download sealing certificates + +### With Read-Only Permissions +- ❌ "Create Sealed Secret" button hidden +- ❌ "Delete" button hidden/disabled +- ❌ "Decrypt" button hidden +- ❌ Download button hidden/disabled + +### Testing Permissions + +The plugin uses Kubernetes `SelfSubjectAccessReview` to check permissions in real-time. + +You can test manually: + +```bash +# Check if you can create SealedSecrets +kubectl auth can-i create sealedsecrets.bitnami.com + +# Check if you can list SealedSecrets +kubectl auth can-i list sealedsecrets.bitnami.com + +# Check if you can view Secrets (for decryption) +kubectl auth can-i get secrets + +# Check in specific namespace +kubectl auth can-i create sealedsecrets.bitnami.com -n production +``` + +## Common Permission Issues + +### Issue 1: "Create Sealed Secret" Button Not Showing + +**Cause**: Missing `create` permission for SealedSecrets. + +**Solution**: +```yaml +rules: +- apiGroups: ["bitnami.com"] + resources: ["sealedsecrets"] + verbs: ["create"] # Add this verb +``` + +### Issue 2: Cannot Download Sealing Certificates + +**Cause**: Missing service access permission. + +**Solution**: +```yaml +rules: +- apiGroups: [""] + resources: ["services"] + verbs: ["get"] + resourceNames: ["sealed-secrets-controller"] +``` + +Or for proxy access: +```yaml +rules: +- apiGroups: [""] + resources: ["services/proxy"] + verbs: ["get", "create"] +``` + +### Issue 3: "Decrypt" Button Not Showing + +**Cause**: Missing `get` permission for Secrets. + +**Solution**: +```yaml +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] # Required for decryption +``` + +### Issue 4: Cannot See SealedSecrets in Other Namespaces + +**Cause**: Using `Role` instead of `ClusterRole`, or missing namespace list permission. + +**Solution**: Use `ClusterRole` and `ClusterRoleBinding`: +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole # Not Role +metadata: + name: sealed-secrets-viewer +rules: +- apiGroups: ["bitnami.com"] + resources: ["sealedsecrets"] + verbs: ["get", "list"] +- apiGroups: [""] + resources: ["namespaces"] + verbs: ["list"] # Required +``` + +## Security Best Practices + +### 1. Principle of Least Privilege + +Only grant the minimum permissions needed: + +```yaml +# Good: Namespace-scoped for dev team +kind: Role +metadata: + namespace: dev-team-namespace + +# Risky: Cluster-wide for dev team +kind: ClusterRole # Only if truly needed +``` + +### 2. Separate Secret Access + +Don't grant Secret access unless absolutely necessary: + +```yaml +# ✅ Good: Can create SealedSecrets but not view Secrets +- apiGroups: ["bitnami.com"] + resources: ["sealedsecrets"] + verbs: ["create"] + +# ❌ Risky: Can view unsealed Secrets +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] # Only for authorized users +``` + +### 3. Use Groups, Not Individual Users + +```yaml +# ✅ Good: Use groups +subjects: +- kind: Group + name: developers + +# ❌ Harder to maintain: Individual users +subjects: +- kind: User + name: alice +- kind: User + name: bob +``` + +### 4. Audit RBAC Changes + +```bash +# List all ClusterRoleBindings for SealedSecrets +kubectl get clusterrolebindings -o json | \ + jq '.items[] | select(.roleRef.name | contains("sealed-secrets"))' + +# List all RoleBindings in a namespace +kubectl get rolebindings -n production -o yaml +``` + +## Troubleshooting Commands + +```bash +# 1. Check your current permissions +kubectl auth can-i --list + +# 2. Check specific permission +kubectl auth can-i create sealedsecrets.bitnami.com -n production + +# 3. Check as another user (requires admin) +kubectl auth can-i create sealedsecrets --as alice --as-group developers + +# 4. View your roles +kubectl get rolebindings,clusterrolebindings --all-namespaces -o wide | grep $(kubectl auth whoami) + +# 5. Describe a role +kubectl describe clusterrole sealed-secrets-creator +``` + +## Next Steps + +- **[Creating Secrets](creating-secrets.md)** - Create your first sealed secret +- **[Scopes Explained](scopes-explained.md)** - Understand security scopes +- **[Security Hardening](../deployment/security-hardening.md)** - Production security guide + +## Resources + +- [Kubernetes RBAC Documentation](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) +- [SelfSubjectAccessReview API](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#checking-api-access) +- [Best Practices for RBAC](https://kubernetes.io/docs/concepts/security/rbac-good-practices/) diff --git a/docs/user-guide/scopes-explained.md b/docs/user-guide/scopes-explained.md new file mode 100644 index 0000000..5eb328d --- /dev/null +++ b/docs/user-guide/scopes-explained.md @@ -0,0 +1,396 @@ +# 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 + +### Strict Scope (Recommended) + +**Binding**: Namespace + Name + Key + +The sealed secret can ONLY be unsealed if all three match: +- ✅ Same namespace +- ✅ Same secret name +- ✅ Same key name + +**Example:** +```yaml +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:** +```bash +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:** +```yaml +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:** +```bash +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:** +```yaml +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:** +```bash +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) + +```yaml +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) + +```yaml +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) + +```yaml +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**: + ```bash + kubectl delete sealedsecret old-secret -n production + ``` + +2. **Re-encrypt with new scope**: + ```bash + 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**: + ```bash + 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:** +```bash +# Explicit strict scope +kubeseal --scope strict +``` + +❌ **Risky:** +```bash +# 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: + +```bash +kubectl get sealedsecrets --all-namespaces -o json | \ + jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name): \(.spec.template.type // "strict")"' +``` + +### 3. Separate Sensitive from Non-Sensitive + +```bash +# 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: + +```yaml +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 + +```bash +# 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 + +```bash +# 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 + +```bash +# 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 + +- **[RBAC Permissions](rbac-permissions.md)** - Control who can create/view sealed secrets +- **[Creating Secrets](creating-secrets.md)** - Complete guide to secret creation +- **[Secret Rotation](../tutorials/secret-rotation.md)** - Rotate secrets and keys + +## Resources + +- [Sealed Secrets Scopes Documentation](https://github.com/bitnami-labs/sealed-secrets#scopes) +- [Security Best Practices](../deployment/security-hardening.md)