{"id":30893064,"url":"https://github.com/fredericrous/vault-transit-unseal-operator","last_synced_at":"2026-01-06T01:13:18.866Z","repository":{"id":313490067,"uuid":"1051465946","full_name":"fredericrous/vault-transit-unseal-operator","owner":"fredericrous","description":null,"archived":false,"fork":false,"pushed_at":"2025-09-07T13:15:15.000Z","size":128,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-07T15:09:58.448Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fredericrous.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-06T03:53:30.000Z","updated_at":"2025-09-07T13:15:05.000Z","dependencies_parsed_at":"2025-09-07T15:10:02.761Z","dependency_job_id":null,"html_url":"https://github.com/fredericrous/vault-transit-unseal-operator","commit_stats":null,"previous_names":["fredericrous/vault-operator","fredericrous/vault-transit-unseal-operator"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/fredericrous/vault-transit-unseal-operator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredericrous%2Fvault-transit-unseal-operator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredericrous%2Fvault-transit-unseal-operator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredericrous%2Fvault-transit-unseal-operator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredericrous%2Fvault-transit-unseal-operator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fredericrous","download_url":"https://codeload.github.com/fredericrous/vault-transit-unseal-operator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredericrous%2Fvault-transit-unseal-operator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274231515,"owners_count":25245600,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-09-08T20:04:12.674Z","updated_at":"2026-01-06T01:13:18.854Z","avatar_url":"https://github.com/fredericrous.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Vault Transit Unseal Operator\n\n**Never manually unseal Vault again!** This Kubernetes operator automatically initializes and unseals HashiCorp Vault instances using transit unseal, making Vault operations truly hands-free.\n\n## Features\n\n- 🚀 **Automatic Initialization \u0026 Unsealing** - No manual intervention required\n- 🔧 **Flexible Configuration** - Direct values, ConfigMaps, Secrets with YAML path extraction\n- 🔐 **Security First** - Recovery keys only in logs, never stored by default\n- ⚡ **Post-Unseal Setup** - Auto-configures KV engine and External Secrets Operator\n- 🔄 **Integration Ready** - Works with Reflector and Reloader out of the box\n- 🔑 **Automatic Token Recovery** - Backs up and recovers admin tokens for disaster recovery (v1.6.0+)\n- 🚨 **Self-Healing** - Automatically generates new tokens when missing using recovery keys\n\n## Prerequisites\n\n- Kubernetes 1.24+\n- Transit Vault instance (can be external)\n- Transit token with appropriate permissions\n- Vault configured with transit seal (see [Vault Configuration](#vault-configuration))\n\n## Vault Configuration\n\n**Important:** This operator does NOT replace Vault's seal configuration. Your Vault instance must be configured to use transit unsealing in its configuration file.\n\nAdd the following to your Vault configuration (`vault.hcl`):\n\n```hcl\n# Transit auto-unseal configuration\nseal \"transit\" {\n  address         = \"http://your-transit-vault:8200\"  # Replace with your transit vault address\n  disable_renewal = \"false\"\n  key_name        = \"autounseal\"\n  mount_path      = \"transit\"\n  tls_skip_verify = \"true\"  # Only for development\n  \n  # The token can be injected via environment variable\n  token = \"your-transit-token\"  # or use VAULT_SEAL_TRANSIT_TOKEN env var\n}\n```\n\nThe operator automates the unsealing process but requires Vault to be configured for transit unsealing. Without this configuration, Vault won't know how to decrypt its master key.\n\n## Service Discovery\n\nThe operator automatically discovers Vault services in your cluster. You have two options:\n\n### Automatic Service Discovery (Default)\nThe operator will find the appropriate service based on your pod selector:\n\n```yaml\nspec:\n  vaultPod:\n    namespace: vault\n    selector:\n      app.kubernetes.io/name: vault\n    # Service is auto-discovered - no configuration needed\n```\n\n### Explicit Service Configuration\nFor precise control, specify the service name and port:\n\n```yaml\nspec:\n  vaultPod:\n    namespace: vault\n    selector:\n      app.kubernetes.io/name: vault\n    serviceName: vault-http     # Your Vault service name\n    servicePort: 8200           # Service port (not target port!)\n```\n\n**Important**: The `servicePort` is the port exposed by the Service (e.g., 8200), not the container's target port (e.g., 8300 for HTTP-in-mesh).\n\n## Installation\n\n### Using Helm (Recommended)\n\n```bash\nhelm repo add fredericrous https://fredericrous.github.io/charts\nhelm repo update\nhelm install vault-transit-unseal-operator fredericrous/vault-transit-unseal-operator \\\n  --namespace vault-transit-unseal-system \\\n  --create-namespace\n```\n\n### Configure Transit Token\n\nThe operator needs a token to authenticate with your transit Vault:\n\n```bash\nkubectl create secret generic vault-transit-token \\\n  -n vault \\\n  --from-literal=token=\u003cYOUR_TRANSIT_TOKEN\u003e\n```\n\n### Deploy VaultTransitUnseal Resource\n\n```yaml\napiVersion: vault.homelab.io/v1alpha1\nkind: VaultTransitUnseal\nmetadata:\n  name: vault-main\n  namespace: vault\nspec:\n  vaultPod:\n    selector:\n      app.kubernetes.io/name: vault\n  transitVault:\n    address: http://transit-vault:8200  # Transit vault address\n    secretRef:\n      name: vault-transit-token\n  initialization:\n    # Token recovery configuration (v1.6.0+)\n    tokenRecovery:\n      enabled: true         # Enable automatic recovery\n      backupToTransit: true # Backup tokens to transit vault\n      autoGenerate: true    # Generate new tokens if backup missing\n  postUnsealConfig:\n    enableKV: true\n    enableExternalSecretsOperator: true\n```\n\nThat's it! The operator will now automatically initialize and unseal your Vault pods.\n\n### Complete Example\n\nHere's how the Vault configuration and operator work together:\n\n```yaml\n# 1. Vault ConfigMap with transit seal configuration\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: vault-config\n  namespace: vault\ndata:\n  vault.hcl: |\n    seal \"transit\" {\n      address = \"http://transit-vault:8200\"  # Transit vault endpoint\n      key_name = \"autounseal\"\n      mount_path = \"transit\"\n      token = \"TRANSIT_TOKEN_PLACEHOLDER\"  # Will be replaced by init container\n    }\n    # ... rest of Vault config\n\n---\n# 2. Vault StatefulSet that uses the config\napiVersion: apps/v1\nkind: StatefulSet\nmetadata:\n  name: vault\n  namespace: vault\nspec:\n  template:\n    spec:\n      initContainers:\n      - name: config-templater\n        # Replace token placeholder with actual secret\n        command: [\"sh\", \"-c\", \"sed -i 's/TRANSIT_TOKEN_PLACEHOLDER/'$TOKEN'/g' /vault/config/vault.hcl\"]\n        env:\n        - name: TOKEN\n          valueFrom:\n            secretKeyRef:\n              name: vault-transit-token\n              key: token\n      containers:\n      - name: vault\n        image: hashicorp/vault:1.20.1\n        args: [\"server\", \"-config=/vault/config/vault.hcl\"]\n\n---\n# 3. VaultTransitUnseal CRD that manages unsealing\napiVersion: vault.homelab.io/v1alpha1\nkind: VaultTransitUnseal\nmetadata:\n  name: vault-main\n  namespace: vault\nspec:\n  vaultPod:\n    selector:\n      app.kubernetes.io/name: vault\n  transitVault:\n    address: http://transit-vault:8200  # Same as in vault.hcl\n    secretRef:\n      name: vault-transit-token  # Same secret as StatefulSet\n```\n\nThe operator handles the unsealing lifecycle, but Vault must be configured to use transit sealing.\n\n## Advanced Configuration\n\n### Using ConfigMaps for Dynamic Configuration\n\nStore transit vault addresses in ConfigMaps and even use nested YAML with dot notation:\n\n```yaml\n# ConfigMap with structured YAML\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: vault-config\n  namespace: vault\ndata:\n  config.yaml: |\n    environments:\n      production:\n        transit:\n          address: \"https://vault.prod.example.com:8200\"  # Production vault endpoint\n---\n# Extract nested values\napiVersion: vault.homelab.io/v1alpha1\nkind: VaultTransitUnseal\nmetadata:\n  name: vault-main\n  namespace: vault\nspec:\n  transitVault:\n    addressFrom:\n      configMapKeyRef:\n        name: vault-config\n        key: config.yaml.environments.production.transit.address\n      default: \"http://fallback-vault:8200\"  # Fallback transit vault address\n    secretRef:\n      name: vault-transit-token\n```\n\n### Production-Ready Example\n\n```yaml\napiVersion: vault.homelab.io/v1alpha1\nkind: VaultTransitUnseal\nmetadata:\n  name: vault-production\n  namespace: vault\nspec:\n  vaultPod:\n    selector:\n      app.kubernetes.io/name: vault\n  \n  transitVault:\n    addressFrom:\n      configMapKeyRef:\n        name: vault-config\n        key: transit.address\n      default: \"https://backup-vault:8200\"  # Backup transit vault address\n    secretRef:\n      name: vault-transit-token\n    keyName: autounseal-prod\n    mountPath: transit\n  \n  initialization:\n    recoveryShares: 5\n    recoveryThreshold: 3\n    # Token recovery configuration (v1.6.0+)\n    tokenRecovery:\n      enabled: true         # Enable automatic recovery\n      backupToTransit: true # Backup tokens to transit vault\n      autoGenerate: true    # Generate new tokens if backup missing\n    secretNames:\n      storeRecoveryKeys: false  # Production: keys only in logs\n      adminTokenAnnotations:\n        # Cross-namespace secret sync\n        reflector.v1.k8s.emberstack.com/reflection-allowed: \"true\"\n        reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces: \"external-secrets,argocd\"\n        # Automatic pod restarts on secret change\n        reloader.stakater.com/match: \"true\"\n  \n  postUnsealConfig:\n    enableKV: true\n    enableExternalSecretsOperator: true\n    externalSecretsOperatorConfig:\n      kubernetesAuth:\n        roleName: \"external-secrets\"\n        serviceAccounts:\n        - name: external-secrets\n          namespace: external-secrets\n```\n\n## How It Works\n\n1. **Watches** for Vault pods in your cluster\n2. **Detects** uninitialized or sealed instances\n3. **Initializes** Vault with transit unseal configuration\n4. **Stores** admin tokens in Kubernetes secrets\n5. **Configures** KV engine and authentication post-initialization\n6. **Monitors** continuously and unseals automatically when needed\n\n## Security Best Practices\n\n- **Recovery Keys**: Not stored by default - appear once in operator logs during initialization\n- **Production**: Keep `storeRecoveryKeys: false` and capture keys from logs\n- **Development**: Set `storeRecoveryKeys: true` for convenience\n- **Token Rotation**: Implement regular transit token rotation\n\n### Created Secrets\n\n- `vault-admin-token`: Root token for Vault admin access\n- `vault-keys`: Recovery keys (only if `storeRecoveryKeys: true`)\n\n## Troubleshooting\n\n### Common Issues\n\n#### Operator Not Finding Vault Pods\n\n**Symptom**: Operator logs show no Vault pods found\n\n**Solution**: Check your pod selector matches your Vault deployment labels:\n```bash\n# Check Vault pod labels\nkubectl get pods -n vault --show-labels\n\n# Verify selector in VaultTransitUnseal\nkubectl get vaulttransitunseal -n vault -o yaml\n```\n\n#### Transit Authentication Failures\n\n**Symptom**: \"permission denied\" errors in operator logs\n\n**Solution**: Verify transit token permissions:\n```bash\n# Test token manually\nexport VAULT_ADDR=\u003ctransit-vault-address\u003e\nexport VAULT_TOKEN=\u003cyour-transit-token\u003e\nvault write transit/keys/autounseal type=aes256-gcm96\n```\n\n#### ConfigMap Key Not Found\n\n**Symptom**: \"key not found in ConfigMap\" errors\n\n**Solution**: \n1. Check if using dot notation for nested YAML\n2. Verify the ConfigMap exists and contains the expected structure\n3. Consider adding a default value\n\n### Debugging\n\nView operator logs:\n```bash\nkubectl logs -n vault-transit-unseal-system deployment/vault-transit-unseal-controller-manager -f\n```\n\nEnable debug logging via Helm:\n```yaml\ncontrollerManager:\n  manager:\n    args:\n    - --zap-log-level=debug\n```\n\n## API Reference\n\nFor detailed configuration options:\n```bash\nkubectl explain vaulttransitunseal.spec\n```\n\n## Release Process\n\nNew releases automatically update the Helm chart:\n\n```bash\n# Tag and push a new version\ngit tag v0.2.0\ngit push origin v0.2.0\n# Create a GitHub release → chart updates automatically\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n### Development\n\n#### Running Tests\n\nTo run all tests including controller integration tests:\n```bash\nmake test\n```\n\nThis will automatically download required test binaries (etcd, kube-apiserver) on first run.\n\nFor unit tests only:\n```bash\nmake test-unit\n```\n\n#### Installation from Source\n\nTo install from source:\n```bash\ngit clone https://github.com/fredericrous/vault-transit-unseal-operator.git\ncd vault-transit-unseal-operator\nhelm install vault-transit-unseal-operator ./charts/vault-transit-unseal-operator \\\n  --namespace vault-transit-unseal-system \\\n  --create-namespace\n```\n\nFor complete command-line flags documentation, see `vault-transit-unseal-operator --help`.\n\n\n## License\n\nMIT","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffredericrous%2Fvault-transit-unseal-operator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffredericrous%2Fvault-transit-unseal-operator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffredericrous%2Fvault-transit-unseal-operator/lists"}