{"id":50718017,"url":"https://github.com/cobaltcore-dev/metal-credential-sync","last_synced_at":"2026-06-09T20:31:52.145Z","repository":{"id":363515826,"uuid":"1260201011","full_name":"cobaltcore-dev/metal-credential-sync","owner":"cobaltcore-dev","description":"A Kubernetes Operator that synchronizes BMC (Baseboard Management Controller) credentials from the metal-operator’s BMCSecret resources to external secret backends like HashiCorp Vault or OpenBao.","archived":false,"fork":false,"pushed_at":"2026-06-09T07:26:10.000Z","size":144,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-09T09:23:23.062Z","etag":null,"topics":["go","kubernetes","operator"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cobaltcore-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-06-05T08:49:29.000Z","updated_at":"2026-06-09T07:26:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cobaltcore-dev/metal-credential-sync","commit_stats":null,"previous_names":["cobaltcore-dev/metal-credential-sync"],"tags_count":null,"template":false,"template_full_name":"cobaltcore-dev/repository-template","purl":"pkg:github/cobaltcore-dev/metal-credential-sync","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cobaltcore-dev%2Fmetal-credential-sync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cobaltcore-dev%2Fmetal-credential-sync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cobaltcore-dev%2Fmetal-credential-sync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cobaltcore-dev%2Fmetal-credential-sync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cobaltcore-dev","download_url":"https://codeload.github.com/cobaltcore-dev/metal-credential-sync/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cobaltcore-dev%2Fmetal-credential-sync/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34125332,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"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":["go","kubernetes","operator"],"created_at":"2026-06-09T20:31:51.217Z","updated_at":"2026-06-09T20:31:52.140Z","avatar_url":"https://github.com/cobaltcore-dev.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![REUSE status](https://api.reuse.software/badge/github.com/cobaltcore-dev/metal-credential-sync)](https://api.reuse.software/info/github.com/cobaltcore-dev/metal-credential-sync)\n\n# metal-credential-sync\n\n## About this project\n\nA Kubernetes Operator that synchronizes BMC (Baseboard Management Controller) credentials from the metal-operator’s BMCSecret resources to external secret backends like HashiCorp Vault or OpenBao.\n\n## Overview\n\nMetal Credential Sync watches BMCSecret resources from the [metal-operator](https://github.com/ironcore-dev/metal-operator), discovers associated BMC infrastructure, and maintains synchronized copies in configurable backend systems using logical hierarchical paths (region/hostname/username).\n\n## Features\n\n- **Automatic Synchronization**: Watches BMCSecret resources and syncs credentials to external backends\n- **Selective Sync**: Optional label-based filtering to control which BMCSecrets are synced\n- **Multi-BMC Support**: Creates separate backend entries for each BMC that shares credentials\n- **Flexible Path Construction**: Configurable path templates using region, hostname, and username\n- **Pluggable Backend Architecture**: Interface-based design supporting multiple backends\n- **HashiCorp Vault Support**: Full support for Vault KV v1 and v2 engines\n- **Multiple Auth Methods**: Kubernetes service account auth, token auth, and AppRole (future)\n- **Automatic Cleanup**: Removes backend secrets when BMCSecrets are deleted\n- **Configuration Options**: CRD-based or environment variable configuration\n- **Runtime Config Reload**: Automatically detects and applies SecretBackendConfig changes\n- **Sync Status Tracking**: Dedicated CRD tracks synchronization state per BMCSecret\n\n## Architecture\n\n```\nBMCSecret (metal-operator)\n    └─\u003e Metal Credential Sync watches\n        └─\u003e Discovers BMC resources\n            └─\u003e Extracts region, hostname, username\n                └─\u003e Builds Vault path: bmc/\u003cregion\u003e/\u003chostname\u003e/\u003cusername\u003e\n                    └─\u003e Syncs credentials to Vault\n```\n\n## Installation\n\n### Prerequisites\n\n- Kubernetes cluster (v1.30+)\n- [metal-operator](https://github.com/ironcore-dev/metal-operator) v0.3.0+ installed\n- HashiCorp Vault server (v1.12.0+) with KV secrets engine enabled\n- Go 1.25.6+ (for building from source)\n\n### Install CRDs\n\n```bash\nmake install\n```\n\n### Deploy Operator\n\n```bash\n# Build and push image\nmake docker-build docker-push IMG=\u003cyour-registry\u003e/metal-credential-sync:latest\n\n# Deploy to cluster\nmake deploy IMG=\u003cyour-registry\u003e/metal-credential-sync:latest\n```\n\n## Configuration\n\n### Option 1: SecretBackendConfig CRD (Recommended)\n\nCreate a `SecretBackendConfig` resource:\n\n```yaml\napiVersion: config.metal.ironcore.dev/v1alpha1\nkind: SecretBackendConfig\nmetadata:\n  name: default-backend-config\nspec:\n  backend: vault\n  vaultConfig:\n    address: \"https://vault.example.com:8200\"\n    authMethod: kubernetes\n    kubernetesAuth:\n      role: metal-credential-sync\n      path: kubernetes\n    mountPath: secret\n    tlsConfig:\n      skipVerify: false\n  pathTemplate: \"bmc/{{.Region}}/{{.Hostname}}/{{.Username}}\"\n  regionLabelKey: \"region\"\n  # Optional: Only sync BMCSecrets with this label\n  syncLabel: \"metal-credential-sync.metal.ironcore.dev/sync\"\n```\n\nApply the configuration:\n\n```bash\nkubectl apply -f config/samples/config_v1alpha1_secretbackendconfig.yaml\n```\n\n**Runtime Configuration Changes**: The operator watches the `SecretBackendConfig` resource and automatically detects changes. When you update the configuration (e.g., change `regionLabelKey` or `pathTemplate`), the operator invalidates its cache and applies the new configuration on the next reconciliation cycle (within 5 minutes). See [MIGRATION.md](./MIGRATION.md) for details on handling configuration changes and migrating secrets.\n\n### Selective Sync with Labels\n\nIf you configure a `syncLabel`, only BMCSecrets with that label will be synced:\n\n```yaml\nspec:\n  syncLabel: \"metal-credential-sync.metal.ironcore.dev/sync\"\n```\n\nThen label BMCSecrets you want to sync:\n\n```yaml\napiVersion: metal.ironcore.dev/v1alpha1\nkind: BMCSecret\nmetadata:\n  name: admin-creds\n  labels:\n    metal-credential-sync.metal.ironcore.dev/sync: \"true\"\ndata:\n  username: YWRtaW4=\n  password: c2VjcmV0MTIz\n```\n\nIf `syncLabel` is not configured or empty, all BMCSecrets will be synced.\n\n### Option 2: Environment Variables (Fallback)\n\nIf no `SecretBackendConfig` is found, the operator falls back to environment variables:\n\n```yaml\nenv:\n- name: SECRET_BACKEND_TYPE\n  value: vault\n- name: VAULT_ADDR\n  value: https://vault.example.com:8200\n- name: VAULT_AUTH_METHOD\n  value: kubernetes\n- name: VAULT_ROLE\n  value: metal-credential-sync\n- name: VAULT_MOUNT_PATH\n  value: secret\n- name: PATH_TEMPLATE\n  value: \"bmc/{{.Region}}/{{.Hostname}}/{{.Username}}\"\n- name: REGION_LABEL_KEY\n  value: region\n- name: SYNC_LABEL\n  value: \"metal-credential-sync.metal.ironcore.dev/sync\"\n```\n\n## Vault Setup\n\n### Enable KV v2 Engine\n\n```bash\nvault secrets enable -version=2 -path=secret kv\n```\n\n### Create Policy\n\n```bash\nvault policy write bmc-operator - \u003c\u003cEOF\npath \"secret/data/bmc/*\" {\n  capabilities = [\"create\", \"read\", \"update\", \"delete\"]\n}\npath \"secret/metadata/bmc/*\" {\n  capabilities = [\"list\", \"read\", \"delete\"]\n}\nEOF\n```\n\n### Configure Kubernetes Auth\n\n```bash\n# Enable Kubernetes auth\nvault auth enable kubernetes\n\n# Configure auth method\nvault write auth/kubernetes/config \\\n    kubernetes_host=\"https://kubernetes.default.svc:443\"\n\n# Create role\nvault write auth/kubernetes/role/metal-credential-sync \\\n    bound_service_account_names=metal-credential-sync-controller-manager \\\n    bound_service_account_namespaces=metal-credential-sync-system \\\n    policies=bmc-operator \\\n    ttl=1h\n```\n\n## Usage\n\n### Example: Syncing BMC Credentials\n\n1. Create a BMCSecret (from metal-operator):\n\n```yaml\napiVersion: metal.ironcore.dev/v1alpha1\nkind: BMCSecret\nmetadata:\n  name: admin-creds\ndata:\n  username: YWRtaW4=  # base64: admin\n  password: c2VjcmV0MTIz  # base64: secret123\n```\n\n2. Create BMC resources that reference the secret:\n\n```yaml\napiVersion: metal.ironcore.dev/v1alpha1\nkind: BMC\nmetadata:\n  name: bmc-us-east-1-server1\n  labels:\n    region: us-east-1\nspec:\n  bmcSecretRef:\n    name: admin-creds\n  hostname: bmc-server1.east.example.com\n  protocol: Redfish\n```\n\n3. The operator will automatically:\n   - Discover the BMC resources referencing `admin-creds`\n   - Extract region from labels (`us-east-1`)\n   - Extract hostname from spec (`bmc-server1.east.example.com`)\n   - Extract username from secret (`admin`)\n   - Build Vault path: `bmc/us-east-1/bmc-server1.east.example.com/admin`\n   - Sync credentials to Vault\n\n4. Verify in Vault:\n\n```bash\nvault kv get secret/bmc/us-east-1/bmc-server1.east.example.com/admin\n```\n\n### Monitoring Sync Status\n\nThe operator automatically creates a `BMCSecretSyncStatus` resource for each BMCSecret to track synchronization state.\n\nView all sync statuses:\n\n```bash\nkubectl get bmcsecretsyncstatuses\n```\n\nExample output:\n```\nNAME                           BMCSECRET      TOTAL   SUCCESSFUL   FAILED   LAST SYNC\nadmin-creds-sync-status        admin-creds    2       2            0        2026-02-23T10:30:00Z\n```\n\nView detailed status for a specific secret:\n\n```bash\nkubectl get bmcsecretsyncstatus admin-creds-sync-status -o yaml\n```\n\nExample detailed output:\n\n```yaml\napiVersion: config.metal.ironcore.dev/v1alpha1\nkind: BMCSecretSyncStatus\nmetadata:\n  name: admin-creds-sync-status\nspec:\n  bmcSecretRef: admin-creds\nstatus:\n  totalPaths: 2\n  successfulPaths: 2\n  failedPaths: 0\n  lastSyncAttempt: \"2026-02-23T10:30:00Z\"\n  backendPaths:\n  - path: bmc/us-east-1/bmc-server1.east.example.com/admin\n    bmcName: bmc-us-east-1-server1\n    region: us-east-1\n    hostname: bmc-server1.east.example.com\n    username: admin\n    lastSyncTime: \"2026-02-23T10:30:00Z\"\n    syncStatus: Success\n  - path: bmc/us-west-1/bmc-server5.west.example.com/admin\n    bmcName: bmc-us-west-1-server5\n    region: us-west-1\n    hostname: bmc-server5.west.example.com\n    username: admin\n    lastSyncTime: \"2026-02-23T10:30:00Z\"\n    syncStatus: Success\n  conditions:\n  - type: Synced\n    status: \"True\"\n    observedGeneration: 1\n    lastTransitionTime: \"2026-02-23T10:30:00Z\"\n    reason: AllPathsSynced\n    message: Successfully synced to 2 backend paths\n```\n\n**Status Fields**:\n- `totalPaths`: Number of backend paths that should be synced\n- `successfulPaths`: Number of paths successfully synced\n- `failedPaths`: Number of paths that failed to sync\n- `lastSyncAttempt`: Timestamp of the last reconciliation\n- `backendPaths[]`: Detailed information for each backend path\n  - `path`: Full path in the backend\n  - `bmcName`: Name of the BMC resource\n  - `region`, `hostname`, `username`: Path components\n  - `lastSyncTime`: When this specific path was last synced\n  - `syncStatus`: \"Success\" or \"Failed\"\n  - `errorMessage`: Error details if sync failed\n- `conditions[]`: Kubernetes standard conditions\n\nWatch sync status in real-time:\n\n```bash\nkubectl get bmcsecretsyncstatuses -w\n```\n\n### Path Template Variables\n\nThe operator supports the following variables in path templates:\n\n- `{{.Region}}`: Extracted from BMC labels using `regionLabelKey` (default: \"region\")\n- `{{.Hostname}}`: Extracted from BMC `spec.hostname` field, falls back to BMC name\n- `{{.Username}}`: Extracted from BMCSecret data\n\nDefault template: `bmc/{{.Region}}/{{.Hostname}}/{{.Username}}`\n\nCustom template example:\n```yaml\npathTemplate: \"infrastructure/bmc/{{.Region}}/{{.Hostname}}\"\n```\n\n## Authentication Methods\n\n### Kubernetes Auth (Recommended)\n\nUses the pod’s service account token:\n\n```yaml\nvaultConfig:\n  authMethod: kubernetes\n  kubernetesAuth:\n    role: metal-credential-sync\n    path: kubernetes\n```\n\n### Token Auth\n\nUses a pre-configured token from a Kubernetes secret:\n\n```yaml\nvaultConfig:\n  authMethod: token\n  tokenAuth:\n    secretRef:\n      name: vault-token\n      namespace: metal-credential-sync-system\n      key: token\n```\n\nCreate the token secret:\n\n```bash\nkubectl create secret generic vault-token \\\n  --from-literal=token=hvs.CAESI... \\\n  -n metal-credential-sync-system\n```\n\n## Monitoring\n\nThe operator emits Kubernetes events:\n\n- `Normal/Synced`: Successfully synced to backend\n- `Warning/PartialSync`: Some secrets failed to sync\n- `Warning/SyncFailed`: Failed to sync specific path\n- `Warning/MissingCredentials`: Username or password not found\n- `Warning/BackendUnavailable`: Cannot connect to backend\n- `Normal/NoBMCReference`: No BMCs reference this secret\n\nView events:\n\n```bash\nkubectl get events --field-selector involvedObject.kind=BMCSecret\n```\n\nCheck operator logs:\n\n```bash\nkubectl logs -n metal-credential-sync-system \\\n  deployment/metal-credential-sync-controller-manager\n```\n\n## Development\n\n### Prerequisites\n\n- Go 1.25.6+\n- Kubebuilder v4.11+\n- Docker (for building images)\n- kubectl\n- Access to a Kubernetes cluster\n\n### Build\n\n```bash\nmake build\n```\n\n### Run Locally\n\n```bash\n# Set environment variables\nexport VAULT_ADDR=https://vault.example.com:8200\nexport VAULT_TOKEN=hvs.CAESI...\n\n# Run operator\nmake run\n```\n\n### Run Tests\n\n```bash\nmake test\n```\n\n### Generate Manifests\n\n```bash\nmake manifests\n```\n\n## Project Structure\n\n```\nmetal-credential-sync/\n├── api/\n│   └── v1alpha1/\n│       ├── secretbackendconfig_types.go  # Backend configuration CRD\n│       └── groupversion_info.go\n├── cmd/\n│   └── main.go                           # Operator entry point\n├── internal/\n│   ├── controller/\n│   │   ├── bmcsecret_controller.go       # Main reconciliation logic\n│   │   └── bmcresolver/\n│   │       ├── resolver.go               # BMC discovery utilities\n│   │       └── credentials.go            # Credential extraction\n│   └── secretbackend/\n│       ├── interface.go                  # Backend interface\n│       ├── factory.go                    # Backend factory\n│       ├── config.go                     # Configuration structures\n│       ├── pathbuilder.go                # Path template builder\n│       ├── vault/\n│       │   ├── vault.go                  # Vault implementation\n│       │   └── auth.go                   # Vault authentication\n│       └── openbao/\n│           └── openbao.go                # OpenBao stub (future)\n├── config/\n│   ├── crd/                              # CRD manifests\n│   ├── rbac/                             # RBAC configuration\n│   ├── manager/                          # Manager deployment\n│   └── samples/                          # Example configurations\n├── Makefile\n├── Dockerfile\n└── README.md\n```\n\n## Security Considerations\n\n- **Never log passwords or tokens** - The operator sanitizes logs to prevent credential leakage\n- **TLS by default** - Always use TLS for Vault communication\n- **Minimal RBAC** - Operator has read-only access to BMCSecrets\n- **Path-restricted policies** - Vault policies limit access to specific paths\n- **Audit trail** - Vault audit logs track all secret operations\n\n## Troubleshooting\n\n### Operator won’t start\n\nCheck if metal-operator CRDs are installed:\n\n```bash\nkubectl get crd bmcsecrets.metal.ironcore.dev\nkubectl get crd bmcs.metal.ironcore.dev\n```\n\n### Authentication failures\n\nVerify Vault role and service account binding:\n\n```bash\nvault read auth/kubernetes/role/metal-credential-sync\n```\n\nCheck operator service account:\n\n```bash\nkubectl get sa -n metal-credential-sync-system metal-credential-sync-controller-manager\n```\n\n### Secrets not syncing\n\nCheck operator logs for errors:\n\n```bash\nkubectl logs -n metal-credential-sync-system \\\n  deployment/metal-credential-sync-controller-manager --tail=100\n```\n\nVerify BMC resources reference the secret:\n\n```bash\nkubectl get bmc -o yaml | grep -A2 bmcSecretRef\n```\n\nVerify credentials in BMCSecret:\n\n```bash\nkubectl get bmcsecret \u003cname\u003e -o yaml\n```\n\n### Vault connection issues\n\nTest connectivity from operator pod:\n\n```bash\nkubectl exec -n metal-credential-sync-system \\\n  deployment/metal-credential-sync-controller-manager -- \\\n  curl -k https://vault.example.com:8200/v1/sys/health\n```\n\n## Roadmap\n\n- [ ] OpenBao backend implementation\n- [ ] AppRole authentication method\n- [ ] Status conditions on BMCSecret\n- [ ] Metrics and Prometheus integration\n- [ ] Webhook validation for SecretBackendConfig\n- [ ] Password hash comparison (instead of plaintext)\n- [ ] Token renewal for long-running operations\n- [ ] Integration tests with testcontainers\n- [ ] E2E tests with real Vault instance\n\n## Support, Feedback, Contributing\n\nThis project is open to feature requests/suggestions, bug reports etc. via [GitHub issues](https://github.com/cobaltcore-dev/metal-credential-sync/issues). Contribution and feedback are encouraged and always welcome. For more information about how to contribute, the project structure, as well as additional contribution information, see our [Contribution Guidelines](CONTRIBUTING.md).\n\n## Related Projects\n\n- [metal-operator](https://github.com/ironcore-dev/metal-operator) - Kubernetes operator for bare metal management\n- [HashiCorp Vault](https://www.vaultproject.io/) - Secrets management solution\n- [OpenBao](https://openbao.org/) - Open source Vault fork\n\n## Security / Disclosure\nIf you find any bug that may be a security problem, please follow our instructions at [in our security policy](https://github.com/cobaltcore-dev/metal-credential-sync/security/policy) on how to report it. Please do not create GitHub issues for security-related doubts or problems.\n\n## Code of Conduct\n\nWe as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone. By participating in this project, you agree to abide by its [Code of Conduct](https://github.com/SAP/.github/blob/main/CODE_OF_CONDUCT.md) at all times.\n\n## Licensing\n\nCopyright 2026 SAP SE or an SAP affiliate company and metal-credential-sync contributors. Please see our [LICENSE](LICENSE) for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available [via the REUSE tool](https://api.reuse.software/info/github.com/cobaltcore-dev/metal-credential-sync).\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"Bundesministerium für Wirtschaft und Energie (BMWE)-EU funding logo\" src=\"https://apeirora.eu/assets/img/BMWK-EU.png\" width=\"400\"/\u003e\n\u003c/p\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcobaltcore-dev%2Fmetal-credential-sync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcobaltcore-dev%2Fmetal-credential-sync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcobaltcore-dev%2Fmetal-credential-sync/lists"}