A Kubernetes operator that bridges Hardware Security Module (HSM) data storage with Kubernetes Secrets, providing true secret portability th
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

remove hsmPath and just use the name

+119 -51
+111 -3
CLAUDE.md
··· 310 310 apiVersion: hsm.j5t.io/v1alpha1 311 311 kind: HSMSecret 312 312 metadata: 313 - name: appname-secret 313 + name: appname-secret # HSM path automatically set to this name 314 314 namespace: appnamespace 315 315 spec: 316 - hsmPath: "secrets/appnamespace/appname-secret" # Path on Pico HSM 317 316 secretName: "appname-secret" # Target K8s Secret name (optional) 318 317 autoSync: true # Enable bidirectional sync (default: true) 319 318 secretType: "Opaque" # Kubernetes Secret type (default: Opaque) ··· 658 657 - Implement secure key storage and retrieval 659 658 - Support HSM-specific error handling 660 659 660 + ### PKCS#11 Library Compatibility 661 + 662 + **⚠️ Important: Use OpenSC Library for Pico HSM** 663 + 664 + The Pico HSM requires the **OpenSC PKCS#11 library** (`/usr/lib/opensc-pkcs11.so`) instead of the default CardContact library for proper data object support. 665 + 666 + **Library Configuration:** 667 + ```yaml 668 + # HSMDevice CRD configuration 669 + spec: 670 + pkcs11: 671 + libraryPath: "/usr/lib/opensc-pkcs11.so" # Use OpenSC instead of libsc-hsm-pkcs11.so 672 + slotId: 0 673 + pinSecret: 674 + name: "hsm-pin" 675 + key: "pin" 676 + ``` 677 + 678 + **Key Differences:** 679 + - **CardContact Library** (`libsc-hsm-pkcs11.so`): Cryptographic operations only, no data object support 680 + - **OpenSC Library** (`opensc-pkcs11.so`): Full PKCS#11 compliance with data object support 681 + 682 + ### Manual HSM Access with pkcs11-tool 683 + 684 + **⚠️ Authentication Required**: HSM secrets created by the operator are marked as **private objects** and require PIN authentication to view. 685 + 686 + **Common Operations:** 687 + 688 + ```bash 689 + # List all secrets (requires PIN authentication) 690 + pkcs11-tool --module="/usr/lib/opensc-pkcs11.so" \ 691 + --login --pin="YOUR_PIN" \ 692 + --list-objects --type=data 693 + 694 + # List public objects only (no PIN required) 695 + pkcs11-tool --module="/usr/lib/opensc-pkcs11.so" \ 696 + --list-objects --type=data 697 + 698 + # Read a specific secret component 699 + pkcs11-tool --module="/usr/lib/opensc-pkcs11.so" \ 700 + --login --pin="YOUR_PIN" \ 701 + --read-object --type=data --label="secret-name/api_key" 702 + 703 + # Get HSM info 704 + pkcs11-tool --module="/usr/lib/opensc-pkcs11.so" -I 705 + 706 + # List all object types 707 + pkcs11-tool --module="/usr/lib/opensc-pkcs11.so" \ 708 + --login --pin="YOUR_PIN" \ 709 + --list-objects 710 + ``` 711 + 712 + **Secret Storage Structure:** 713 + - Each API secret becomes **multiple PKCS#11 data objects** 714 + - Object naming: `secret-name/key-name` (e.g., `user-credentials/api_key`) 715 + - Private objects: `flags: modifiable private` (require authentication) 716 + - Public objects: `flags: modifiable` (visible without authentication) 717 + 718 + **Emergency Access without Kubernetes:** 719 + ```bash 720 + # Backup all secrets from pod 721 + kubectl exec HSM_AGENT_POD -- sh -c ' 722 + pkcs11-tool --module="/usr/lib/opensc-pkcs11.so" \ 723 + --login --pin="$PKCS11_PIN" \ 724 + --list-objects --type=data > /tmp/hsm-backup.txt 725 + ' 726 + 727 + # Copy backup file 728 + kubectl cp HSM_AGENT_POD:/tmp/hsm-backup.txt ./hsm-secrets-backup.txt 729 + ``` 730 + 661 731 ### Kubernetes Integration 662 732 - Standard Secret object management 663 733 - RBAC for Secret read/write operations ··· 778 848 kubectl get hsmpool pico-hsm-discovery-pool -o jsonpath='{.status.reportingPods[*].podName}' 779 849 ``` 780 850 781 - This operator design provides a secure, hardware-backed secret management solution that integrates seamlessly with Kubernetes while maintaining the security benefits of HSM-based storage. 851 + This operator design provides a secure, hardware-backed secret management solution that integrates seamlessly with Kubernetes while maintaining the security benefits of HSM-based storage. 852 + 853 + ## Quick Reference: Pico HSM + OpenSC 854 + 855 + ### API Testing (Recommended) 856 + ```bash 857 + # Test API functionality with working HSM 858 + cd /home/data/hsm-secrets-operator/examples/api 859 + 860 + # Create a secret (HSM path = secret name) 861 + ./create-secret.sh my-test-secret 862 + 863 + # List all secrets 864 + ./list-secrets.sh 865 + 866 + # Get specific secret via curl 867 + curl http://localhost:8090/api/v1/hsm/secrets/my-test-secret | jq '.' 868 + ``` 869 + 870 + ### Direct PKCS#11 Access 871 + ```bash 872 + # Get agent pod name 873 + AGENT_POD=$(kubectl get pods -l app.kubernetes.io/name=hsm-agent -o jsonpath='{.items[0].metadata.name}') 874 + 875 + # List all secrets (requires authentication) 876 + kubectl exec $AGENT_POD -- sh -c 'pkcs11-tool --module="/usr/lib/opensc-pkcs11.so" --login --pin="$PKCS11_PIN" --list-objects --type=data' 877 + 878 + # Read specific secret component 879 + kubectl exec $AGENT_POD -- sh -c 'pkcs11-tool --module="/usr/lib/opensc-pkcs11.so" --login --pin="$PKCS11_PIN" --read-object --type=data --label="my-test-secret/api_key"' 880 + 881 + # HSM device info 882 + kubectl exec $AGENT_POD -- pkcs11-tool --module="/usr/lib/opensc-pkcs11.so" -I 883 + ``` 884 + 885 + ### Troubleshooting 886 + - **API works, pkcs11-tool doesn't see objects**: Use `--login --pin` for private objects 887 + - **`CKR_DEVICE_REMOVED` errors**: Restart agent pod to reset PKCS#11 session 888 + - **`CKR_TEMPLATE_INCONSISTENT` errors**: Switch from CardContact to OpenSC library 889 + - **Agent crash loop**: Check library path and PIN secret configuration
-5
api/v1alpha1/hsmsecret_types.go
··· 26 26 27 27 // HSMSecretSpec defines the desired state of HSMSecret. 28 28 type HSMSecretSpec struct { 29 - // HSMPath is the path on the Pico HSM where the secret data is stored 30 - // Example: "secrets/appnamespace/appname-secret" 31 - HSMPath string `json:"hsmPath"` 32 - 33 29 // SecretName is the name of the Kubernetes Secret object to create/update 34 30 // Defaults to the HSMSecret name if not specified 35 31 // +optional ··· 100 96 // +kubebuilder:object:root=true 101 97 // +kubebuilder:subresource:status 102 98 // +kubebuilder:resource:shortName=hsmsec 103 - // +kubebuilder:printcolumn:name="HSM Path",type=string,JSONPath=`.spec.hsmPath` 104 99 // +kubebuilder:printcolumn:name="Secret Name",type=string,JSONPath=`.spec.secretName` 105 100 // +kubebuilder:printcolumn:name="Sync Status",type=string,JSONPath=`.status.syncStatus` 106 101 // +kubebuilder:printcolumn:name="Last Sync",type=date,JSONPath=`.status.lastSyncTime`
-10
config/crd/bases/hsm.j5t.io_hsmsecrets.yaml
··· 17 17 scope: Namespaced 18 18 versions: 19 19 - additionalPrinterColumns: 20 - - jsonPath: .spec.hsmPath 21 - name: HSM Path 22 - type: string 23 20 - jsonPath: .spec.secretName 24 21 name: Secret Name 25 22 type: string ··· 62 59 description: AutoSync enables bidirectional synchronization between 63 60 HSM and Kubernetes Secret 64 61 type: boolean 65 - hsmPath: 66 - description: |- 67 - HSMPath is the path on the Pico HSM where the secret data is stored 68 - Example: "secrets/appnamespace/appname-secret" 69 - type: string 70 62 secretName: 71 63 description: |- 72 64 SecretName is the name of the Kubernetes Secret object to create/update ··· 84 76 Only applies when AutoSync is true 85 77 format: int32 86 78 type: integer 87 - required: 88 - - hsmPath 89 79 type: object 90 80 status: 91 81 description: HSMSecretStatus defines the observed state of HSMSecret.
+1 -1
config/rbac/role.yaml
··· 2 2 apiVersion: rbac.authorization.k8s.io/v1 3 3 kind: ClusterRole 4 4 metadata: 5 - name: hsm-secrets-operator-manager-role 5 + name: manager-role 6 6 rules: 7 7 - apiGroups: 8 8 - ""
+1 -2
config/samples/hsm_v1alpha1_hsmsecret.yaml
··· 7 7 name: hsmsecret-sample 8 8 namespace: default 9 9 spec: 10 - # HSM path where the secret is stored 11 - hsmPath: "secrets/default/test-secret" 10 + # HSM path is automatically set to the metadata.name (hsmsecret-sample) 12 11 13 12 # Name of the Kubernetes Secret to create (defaults to HSMSecret name if not specified) 14 13 secretName: "database-credentials"
-4
examples/advanced/multi-environment.yaml
··· 21 21 app: myapp 22 22 environment: development 23 23 spec: 24 - hsmPath: "secrets/development/database-credentials" 25 24 secretName: "database-credentials" 26 25 autoSync: true 27 26 syncInterval: 300 # 5 minutes - frequent sync for dev ··· 46 45 app: myapp 47 46 environment: staging 48 47 spec: 49 - hsmPath: "secrets/staging/database-credentials" 50 48 secretName: "database-credentials" 51 49 autoSync: true 52 50 syncInterval: 600 # 10 minutes ··· 75 73 annotations: 76 74 hsm.j5t.io/description: "Production database credentials - HIGH SECURITY" 77 75 spec: 78 - hsmPath: "secrets/production/database-credentials" 79 76 secretName: "database-credentials" 80 77 autoSync: true 81 78 syncInterval: 1800 # 30 minutes - less frequent for stability ··· 101 98 type: tls 102 99 scope: global 103 100 spec: 104 - hsmPath: "secrets/shared/wildcard-example-com-tls" 105 101 secretName: "wildcard-tls" 106 102 autoSync: true 107 103 syncInterval: 86400 # Daily sync for certificates
-1
examples/agent-deployment/agent-example.yaml
··· 64 64 tier: database 65 65 spec: 66 66 # Path on HSM where secret is stored 67 - hsmPath: "secrets/production/database/credentials" 68 67 69 68 # Name of Kubernetes Secret to create/sync 70 69 secretName: "database-credentials"
-1
examples/basic/api-keys.yaml
··· 10 10 hsm.j5t.io/description: "API keys for external services (Stripe, AWS, etc.)" 11 11 spec: 12 12 # Path on the HSM where API keys are stored 13 - hsmPath: "secrets/api-keys/external-services" 14 13 15 14 # Name of the Secret containing API keys 16 15 secretName: "external-api-keys"
-1
examples/basic/database-secret.yaml
··· 11 11 hsm.j5t.io/description: "PostgreSQL database credentials for production" 12 12 spec: 13 13 # Path on the HSM where the secret is stored 14 - hsmPath: "secrets/production/database-credentials" 15 14 16 15 # Name of the Kubernetes Secret to create/maintain 17 16 secretName: "database-credentials"
-1
examples/basic/tls-certificate.yaml
··· 10 10 hsm.j5t.io/description: "TLS certificate and key for webapp.example.com" 11 11 spec: 12 12 # Path on the HSM where the TLS cert/key is stored 13 - hsmPath: "secrets/tls/webapp-example-com" 14 13 15 14 # Name of the TLS Secret to create 16 15 secretName: "webapp-tls"
-2
examples/deployment/complete-setup.yaml
··· 48 48 type: database 49 49 criticality: high 50 50 spec: 51 - hsmPath: "secrets/production/webapp-database" 52 51 secretName: "webapp-database-credentials" 53 52 autoSync: true 54 53 syncInterval: 600 # 10 minutes ··· 65 64 app: webapp 66 65 type: tls 67 66 spec: 68 - hsmPath: "secrets/tls/webapp-example-com" 69 67 secretName: "webapp-tls-cert" 70 68 autoSync: true 71 69 syncInterval: 3600 # 1 hour
-1
examples/high-availability/mirrored-hsm-device.yaml
··· 121 121 annotations: 122 122 hsm.j5t.io/description: "HA database credentials with mirroring support" 123 123 spec: 124 - hsmPath: "secrets/ha/database-credentials" 125 124 secretName: "ha-database-credentials" 126 125 127 126 # Enable auto-sync for HA
-10
helm/hsm-secrets-operator/crds/hsm.j5t.io_hsmsecrets.yaml
··· 17 17 scope: Namespaced 18 18 versions: 19 19 - additionalPrinterColumns: 20 - - jsonPath: .spec.hsmPath 21 - name: HSM Path 22 - type: string 23 20 - jsonPath: .spec.secretName 24 21 name: Secret Name 25 22 type: string ··· 62 59 description: AutoSync enables bidirectional synchronization between 63 60 HSM and Kubernetes Secret 64 61 type: boolean 65 - hsmPath: 66 - description: |- 67 - HSMPath is the path on the Pico HSM where the secret data is stored 68 - Example: "secrets/appnamespace/appname-secret" 69 - type: string 70 62 secretName: 71 63 description: |- 72 64 SecretName is the name of the Kubernetes Secret object to create/update ··· 84 76 Only applies when AutoSync is true 85 77 format: int32 86 78 type: integer 87 - required: 88 - - hsmPath 89 79 type: object 90 80 status: 91 81 description: HSMSecretStatus defines the observed state of HSMSecret.
-1
helm/hsm-secrets-operator/templates/resources.yaml
··· 10 10 {{- include "hsm-secrets-operator.labels" $ | nindent 4 }} 11 11 app.kubernetes.io/component: hsmsecret 12 12 spec: 13 - hsmPath: {{ .hsmPath }} 14 13 {{- with .secretName }} 15 14 secretName: {{ . }} 16 15 {{- end }}
-1
helm/hsm-secrets-operator/templates/tests/test-crds.yaml
··· 44 44 name: test-secret 45 45 namespace: {{ .Release.Namespace }} 46 46 spec: 47 - hsmPath: "secrets/test/test-secret" 48 47 secretName: "test-secret" 49 48 autoSync: true 50 49 syncInterval: 300
-1
helm/hsm-secrets-operator/values.yaml
··· 251 251 secrets: 252 252 - name: "example-secret" 253 253 namespace: "default" 254 - hsmPath: "secrets/examples/example-secret" 255 254 syncInterval: 300 256 255 257 256 # Additional configuration
+6 -6
internal/controller/hsmsecret_controller.go
··· 167 167 // Read secret from HSM with readonly fallback support 168 168 hsmData, err := r.readSecretWithFallback(ctx, hsmSecret, hsmClient) 169 169 if err != nil { 170 - logger.Error(err, "Failed to read secret from HSM and mirrors", "path", hsmSecret.Spec.HSMPath) 170 + logger.Error(err, "Failed to read secret from HSM and mirrors", "path", hsmSecret.Name) 171 171 return ctrl.Result{RequeueAfter: time.Minute * 2}, err 172 172 } 173 173 ··· 289 289 Namespace: hsmSecret.Namespace, 290 290 Labels: map[string]string{ 291 291 "managed-by": "hsm-secrets-operator", 292 - "hsm-path": strings.ReplaceAll(hsmSecret.Spec.HSMPath, "/", "_"), 292 + "hsm-path": strings.ReplaceAll(hsmSecret.Name, "/", "_"), 293 293 }, 294 294 }, 295 295 Type: secretType, ··· 361 361 362 362 // Try to read from primary HSM first (via agent) 363 363 if hsmClient != nil && hsmClient.IsConnected() { 364 - data, err := hsmClient.ReadSecret(ctx, hsmSecret.Spec.HSMPath) 364 + data, err := hsmClient.ReadSecret(ctx, hsmSecret.Name) 365 365 if err == nil { 366 - logger.V(1).Info("Successfully read secret from primary HSM", "path", hsmSecret.Spec.HSMPath) 366 + logger.V(1).Info("Successfully read secret from primary HSM", "path", hsmSecret.Name) 367 367 return data, nil 368 368 } 369 369 logger.V(1).Info("Failed to read from primary HSM, attempting fallback", "error", err) ··· 376 376 if err != nil { 377 377 logger.Error(err, "Failed to find HSM device for readonly fallback") 378 378 } else if hsmDevice != nil { 379 - data, err := r.MirroringManager.GetReadOnlyAccess(ctx, hsmSecret.Spec.HSMPath, hsmDevice) 379 + data, err := r.MirroringManager.GetReadOnlyAccess(ctx, hsmSecret.Name, hsmDevice) 380 380 if err == nil { 381 - logger.Info("Successfully read secret from readonly mirror", "path", hsmSecret.Spec.HSMPath) 381 + logger.Info("Successfully read secret from readonly mirror", "path", hsmSecret.Name) 382 382 return data, nil 383 383 } 384 384 logger.V(1).Info("Failed to read from mirrors", "error", err)