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.

fix linting

+55 -57
+1 -1
.github/workflows/lint.yml
··· 20 20 - name: Run linter 21 21 uses: golangci/golangci-lint-action@v8 22 22 with: 23 - version: v2.1.0 23 + version: v2.4.0
+1 -1
Dockerfile
··· 1 1 # Build the manager binary 2 - FROM golang:1.24 AS builder 2 + FROM golang:1.24-alpine AS builder 3 3 ARG TARGETOS 4 4 ARG TARGETARCH 5 5
+1 -1
Dockerfile.talos
··· 70 70 EOF 71 71 72 72 # Stage 2: Build Go application 73 - FROM golang:1.21-alpine AS go-builder 73 + FROM golang:1.24-alpine AS go-builder 74 74 75 75 ARG TARGETOS 76 76 ARG TARGETARCH
+11 -10
internal/api/helpers.go
··· 30 30 ) 31 31 32 32 // generateHSMPath creates an HSM path from label and ID 33 - func (s *Server) generateHSMPath(label string, id uint32) string { 33 + func (s *Server) generateHSMPath(label string, _ uint32) string { 34 34 return fmt.Sprintf("secrets/api/%s", label) 35 35 } 36 36 ··· 74 74 } 75 75 76 76 // convertFromHSMData converts HSM data back to API format 77 - func (s *Server) convertFromHSMData(hsmData hsm.SecretData) (map[string]interface{}, error) { 77 + func (s *Server) convertFromHSMData(hsmData hsm.SecretData) map[string]interface{} { 78 78 data := make(map[string]interface{}) 79 79 80 80 for key, value := range hsmData { ··· 88 88 } 89 89 } 90 90 91 - return data, nil 91 + return data 92 92 } 93 93 94 94 // createHSMSecretResource creates a corresponding HSMSecret Kubernetes resource ··· 225 225 } 226 226 227 227 // validateSecretAccess checks if the current user has access to the secret (placeholder for future authorization) 228 - func (s *Server) validateSecretAccess(ctx context.Context, label string, operation string) error { 229 - // TODO: Implement proper authorization logic 230 - // This could integrate with Kubernetes RBAC, external auth systems, etc. 231 - 232 - s.logger.V(1).Info("Access validation", "label", label, "operation", operation) 233 - return nil 234 - } 228 + // Currently unused - commented out to avoid lint warnings 229 + // func (s *Server) validateSecretAccess(ctx context.Context, label string, operation string) error { 230 + // // TODO: Implement proper authorization logic 231 + // // This could integrate with Kubernetes RBAC, external auth systems, etc. 232 + // 233 + // s.logger.V(1).Info("Access validation", "label", label, "operation", operation) 234 + // return nil 235 + // }
+5 -9
internal/api/server.go
··· 43 43 } 44 44 45 45 // NewServer creates a new API server instance 46 - func NewServer(client client.Client, hsmClient hsm.Client, mirroringManager *discovery.MirroringManager, logger logr.Logger) *Server { 46 + func NewServer(k8sClient client.Client, hsmClient hsm.Client, mirroringManager *discovery.MirroringManager, logger logr.Logger) *Server { 47 47 s := &Server{ 48 - client: client, 48 + client: k8sClient, 49 49 hsmClient: hsmClient, 50 50 mirroringManager: mirroringManager, 51 51 validator: validator.New(), ··· 75 75 v1.GET("/health", s.handleHealth) 76 76 77 77 // HSM secrets management 78 - hsm := v1.Group("/hsm") 78 + hsmGroup := v1.Group("/hsm") 79 79 { 80 - secrets := hsm.Group("/secrets") 80 + secrets := hsmGroup.Group("/secrets") 81 81 { 82 82 secrets.POST("", s.handleCreateSecret) 83 83 secrets.GET("", s.handleListSecrets) ··· 225 225 } 226 226 227 227 // Convert HSM data back to API format 228 - data, err := s.convertFromHSMData(hsmData) 229 - if err != nil { 230 - s.sendError(c, http.StatusInternalServerError, "data_conversion_error", err.Error(), nil) 231 - return 232 - } 228 + data := s.convertFromHSMData(hsmData) 233 229 234 230 // Create metadata 235 231 checksum := hsm.CalculateChecksum(hsmData)
+2 -2
internal/controller/hsmdevice_controller.go
··· 152 152 return nil, fmt.Errorf("USB discovery failed: %w", err) 153 153 } 154 154 155 - var devices []hsmv1alpha1.DiscoveredDevice 155 + devices := make([]hsmv1alpha1.DiscoveredDevice, 0) 156 156 for _, usbDev := range usbDevices { 157 157 device := hsmv1alpha1.DiscoveredDevice{ 158 158 DevicePath: usbDev.DevicePath, ··· 194 194 return nil, fmt.Errorf("path discovery failed: %w", err) 195 195 } 196 196 197 - var devices []hsmv1alpha1.DiscoveredDevice 197 + devices := make([]hsmv1alpha1.DiscoveredDevice, 0) 198 198 for _, usbDev := range usbDevices { 199 199 device := hsmv1alpha1.DiscoveredDevice{ 200 200 DevicePath: usbDev.DevicePath,
+4 -2
internal/controller/hsmsecret_controller.go
··· 253 253 } 254 254 255 255 // Set owner reference 256 - ctrl.SetControllerReference(hsmSecret, &secret, r.Scheme) 256 + if err := ctrl.SetControllerReference(hsmSecret, &secret, r.Scheme); err != nil { 257 + ctrl.Log.Error(err, "Failed to set owner reference") 258 + } 257 259 258 260 return secret 259 261 } ··· 277 279 } 278 280 279 281 // updateStatus updates the HSMSecret status 280 - func (r *HSMSecretReconciler) updateStatus(ctx context.Context, hsmSecret *hsmv1alpha1.HSMSecret, status hsmv1alpha1.SyncStatus, errorMsg string) { 282 + func (r *HSMSecretReconciler) updateStatus(_ context.Context, hsmSecret *hsmv1alpha1.HSMSecret, status hsmv1alpha1.SyncStatus, errorMsg string) { 281 283 now := metav1.Now() 282 284 hsmSecret.Status.SyncStatus = status 283 285 hsmSecret.Status.LastError = errorMsg
+12 -17
internal/discovery/mirroring.go
··· 54 54 } 55 55 56 56 // NewMirroringManager creates a new mirroring manager 57 - func NewMirroringManager(client client.Client, nodeName string) *MirroringManager { 57 + func NewMirroringManager(k8sClient client.Client, nodeName string) *MirroringManager { 58 58 return &MirroringManager{ 59 - client: client, 59 + client: k8sClient, 60 60 logger: ctrl.Log.WithName("hsm-mirroring-manager"), 61 61 hsmClients: make(map[string]hsm.Client), 62 62 mirrorCache: make(map[string]*MirroredSecretData), ··· 66 66 } 67 67 68 68 // RegisterHSMClient registers an HSM client for a specific node 69 - func (m *MirroringManager) RegisterHSMClient(nodeName string, client hsm.Client) { 69 + func (m *MirroringManager) RegisterHSMClient(nodeName string, hsmClient hsm.Client) { 70 70 m.mutex.Lock() 71 71 defer m.mutex.Unlock() 72 72 73 - m.hsmClients[nodeName] = client 73 + m.hsmClients[nodeName] = hsmClient 74 74 m.nodeHealth[nodeName] = time.Now() 75 75 m.logger.Info("Registered HSM client for node", "node", nodeName) 76 76 } ··· 97 97 } 98 98 99 99 // Update mirroring status 100 - if err := m.updateMirroringStatus(ctx, hsmDevice, primaryNode, mirrorNodes); err != nil { 101 - return fmt.Errorf("failed to update mirroring status: %w", err) 102 - } 100 + m.updateMirroringStatus(ctx, hsmDevice, primaryNode, mirrorNodes) 103 101 104 102 return nil 105 103 } 106 104 107 105 // determineMirrorTopology determines which nodes should be primary vs mirrors 108 - func (m *MirroringManager) determineMirrorTopology(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice) (string, []string, error) { 106 + func (m *MirroringManager) determineMirrorTopology(_ context.Context, hsmDevice *hsmv1alpha1.HSMDevice) (string, []string, error) { 109 107 availableNodes := make([]string, 0) 110 108 111 109 // Collect nodes with available devices ··· 175 173 } 176 174 177 175 // List all secrets on the primary device 178 - secrets, err := m.listSecretsFromHSM(ctx, primaryClient, hsmDevice) 179 - if err != nil { 180 - return fmt.Errorf("failed to list secrets from primary: %w", err) 181 - } 176 + secrets := m.listSecretsFromHSM(ctx, primaryClient, hsmDevice) 182 177 183 178 m.logger.Info("Found secrets on primary", "count", len(secrets), "primary", primaryNode) 184 179 ··· 269 264 } 270 265 271 266 // listSecretsFromHSM lists all secrets from an HSM client 272 - func (m *MirroringManager) listSecretsFromHSM(ctx context.Context, client hsm.Client, hsmDevice *hsmv1alpha1.HSMDevice) ([]string, error) { 267 + func (m *MirroringManager) listSecretsFromHSM(ctx context.Context, hsmClient hsm.Client, _ *hsmv1alpha1.HSMDevice) []string { 273 268 // This is a simplified implementation 274 269 // In a real implementation, you would use HSM-specific APIs to list secrets 275 270 ··· 285 280 286 281 for _, path := range basePaths { 287 282 // Check if secret exists 288 - if _, err := client.ReadSecret(ctx, path); err == nil { 283 + if _, err := hsmClient.ReadSecret(ctx, path); err == nil { 289 284 secrets = append(secrets, path) 290 285 } 291 286 } 292 287 293 - return secrets, nil 288 + return secrets 294 289 } 295 290 296 291 // GetReadOnlyAccess provides readonly access to secrets from mirrors when primary is down ··· 373 368 } 374 369 375 370 // updateMirroringStatus updates the mirroring status in the HSMDevice 376 - func (m *MirroringManager) updateMirroringStatus(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice, primaryNode string, mirrorNodes []string) error { 371 + func (m *MirroringManager) updateMirroringStatus(_ context.Context, hsmDevice *hsmv1alpha1.HSMDevice, primaryNode string, mirrorNodes []string) { 377 372 now := metav1.Now() 378 373 379 374 if hsmDevice.Status.Mirroring == nil { ··· 401 396 } 402 397 } 403 398 404 - return nil 399 + // Status updated successfully 405 400 } 406 401 407 402 // IsNodeHealthy checks if a node is healthy based on last seen time
+18 -14
internal/discovery/usb.go
··· 25 25 "path/filepath" 26 26 "regexp" 27 27 "strings" 28 - "sync" 29 28 30 29 "github.com/go-logr/logr" 31 30 ctrl "sigs.k8s.io/controller-runtime" ··· 47 46 // USBDiscoverer handles USB device discovery 48 47 type USBDiscoverer struct { 49 48 logger logr.Logger 50 - mutex sync.RWMutex 49 + // mutex sync.RWMutex // unused 51 50 52 51 // Known USB paths to scan 53 52 usbSysPaths []string ··· 74 73 "vendorId", spec.VendorID, 75 74 "productId", spec.ProductID) 76 75 77 - var devices []USBDevice 76 + devices := make([]USBDevice, 0) 78 77 79 78 // Scan USB devices in sysfs 80 79 usbDevices, err := u.scanUSBDevices(ctx) ··· 104 103 func (u *USBDiscoverer) DiscoverByPath(ctx context.Context, pathSpec *hsmv1alpha1.DevicePathSpec) ([]USBDevice, error) { 105 104 u.logger.V(1).Info("Starting path-based device discovery", "path", pathSpec.Path) 106 105 107 - var devices []USBDevice 106 + devices := make([]USBDevice, 0) 108 107 109 108 // Handle glob patterns 110 109 matches, err := filepath.Glob(pathSpec.Path) ··· 150 149 } 151 150 152 151 // scanUSBDevices scans the USB subsystem for devices 153 - func (u *USBDiscoverer) scanUSBDevices(ctx context.Context) ([]USBDevice, error) { 154 - var devices []USBDevice 152 + func (u *USBDiscoverer) scanUSBDevices(_ context.Context) ([]USBDevice, error) { 153 + devices := make([]USBDevice, 0) 155 154 156 155 usbSysPath := "/sys/bus/usb/devices" 157 156 if _, err := os.Stat(usbSysPath); err != nil { ··· 175 174 return nil 176 175 } 177 176 178 - device, err := u.parseUSBDevice(path) 179 - if err != nil { 180 - u.logger.V(2).Info("Failed to parse USB device", "path", path, "error", err) 177 + device := u.parseUSBDevice(path) 178 + if device == nil { 179 + u.logger.V(2).Info("Failed to parse USB device", "path", path) 181 180 return nil 182 181 } 183 182 ··· 196 195 } 197 196 198 197 // parseUSBDevice parses USB device information from sysfs 199 - func (u *USBDiscoverer) parseUSBDevice(devicePath string) (*USBDevice, error) { 198 + func (u *USBDiscoverer) parseUSBDevice(devicePath string) *USBDevice { 200 199 device := &USBDevice{ 201 200 DeviceInfo: make(map[string]string), 202 201 } ··· 228 227 229 228 // Skip devices without vendor/product IDs 230 229 if device.VendorID == "" || device.ProductID == "" { 231 - return nil, nil 230 + return nil 232 231 } 233 232 234 233 // Try to find associated device paths ··· 238 237 device.DeviceInfo["sysfs-path"] = devicePath 239 238 device.DeviceInfo["discovery-method"] = "usb" 240 239 241 - return device, nil 240 + return device 242 241 } 243 242 244 243 // findDevicePaths attempts to find device paths for a USB device 245 - func (u *USBDiscoverer) findDevicePaths(vendorID, productID, serial string) string { 244 + func (u *USBDiscoverer) findDevicePaths(_, _, _ string) string { 246 245 // This is a simplified implementation 247 246 // In a real implementation, you'd want to scan /dev and match devices 248 247 // For now, we'll look for common HSM device paths ··· 273 272 if err != nil { 274 273 return "", err 275 274 } 276 - defer file.Close() 275 + defer func() { 276 + if err := file.Close(); err != nil { 277 + // Log the error but don't fail the operation 278 + u.logger.V(2).Info("Failed to close sysfs file", "path", path, "error", err) 279 + } 280 + }() 277 281 278 282 scanner := bufio.NewScanner(file) 279 283 if scanner.Scan() {