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 serviceaccount

+108 -27
+1 -1
Makefile
··· 3 3 # To re-generate a bundle for another specific version without changing the standard setup, you can: 4 4 # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) 5 5 # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) 6 - VERSION ?= 0.6.0 6 + VERSION ?= 0.6.1 7 7 8 8 # CHANNELS define the bundle channels used in the bundle. 9 9 # Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable")
+2 -2
helm/hsm-secrets-operator/Chart.yaml
··· 2 2 name: hsm-secrets-operator 3 3 description: A Kubernetes operator that bridges Pico HSM binary data storage with Kubernetes Secrets 4 4 type: application 5 - version: 0.6.0 6 - appVersion: v0.6.0 5 + version: 0.6.1 6 + appVersion: v0.6.1 7 7 icon: https://raw.githubusercontent.com/cncf/artwork/master/projects/kubernetes/icon/color/kubernetes-icon-color.svg 8 8 home: https://github.com/evanjarrett/hsm-secrets-operator 9 9 sources:
+62
internal/config/service_account.go
··· 1 + /* 2 + Copyright 2025. 3 + 4 + Licensed under the Apache License, Version 2.0 (the "License"); 5 + you may not use this file except in compliance with the License. 6 + You may obtain a copy of the License at 7 + 8 + http://www.apache.org/licenses/LICENSE-2.0 9 + 10 + Unless required by applicable law or agreed to in writing, software 11 + distributed under the License is distributed on an "AS IS" BASIS, 12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 + See the License for the specific language governing permissions and 14 + limitations under the License. 15 + */ 16 + 17 + package config 18 + 19 + import ( 20 + "context" 21 + "fmt" 22 + "os" 23 + 24 + corev1 "k8s.io/api/core/v1" 25 + "k8s.io/apimachinery/pkg/types" 26 + "sigs.k8s.io/controller-runtime/pkg/client" 27 + ) 28 + 29 + // GetCurrentServiceAccount returns the service account name the current pod is running under. 30 + // It first gets the current namespace and pod name, then fetches the pod spec from the Kubernetes API 31 + // to get the ServiceAccountName. Returns an error if it cannot be determined. 32 + func GetCurrentServiceAccount(ctx context.Context, k8sClient client.Client) (string, error) { 33 + // Get current namespace 34 + namespace, err := GetCurrentNamespace() 35 + if err != nil { 36 + return "", fmt.Errorf("unable to get current namespace: %w", err) 37 + } 38 + 39 + // Get pod name from hostname 40 + podName, err := os.Hostname() 41 + if err != nil { 42 + return "", fmt.Errorf("unable to get pod name from hostname: %w", err) 43 + } 44 + 45 + // Fetch the pod spec from Kubernetes API 46 + pod := &corev1.Pod{} 47 + podKey := types.NamespacedName{ 48 + Name: podName, 49 + Namespace: namespace, 50 + } 51 + 52 + if err := k8sClient.Get(ctx, podKey, pod); err != nil { 53 + return "", fmt.Errorf("unable to get pod %s/%s: %w", namespace, podName, err) 54 + } 55 + 56 + // Get the service account name from pod spec 57 + if pod.Spec.ServiceAccountName == "" { 58 + return "", fmt.Errorf("pod %s/%s has no service account specified in its spec", namespace, podName) 59 + } 60 + 61 + return pod.Spec.ServiceAccountName, nil 62 + }
+22 -8
internal/modes/manager/manager.go
··· 17 17 package manager 18 18 19 19 import ( 20 + "context" 20 21 "crypto/tls" 21 22 "flag" 22 23 "os" 23 24 "path/filepath" 25 + "time" 24 26 25 27 // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) 26 28 // to ensure that exec-entrypoint and run can make use of them. ··· 243 245 } 244 246 245 247 // setupBaseControllers sets up controllers that don't depend on the agent manager 246 - func setupBaseControllers(mgr ctrl.Manager, cfg *managerConfig) error { 248 + func setupBaseControllers(mgr ctrl.Manager, cfg *managerConfig, serviceAccountName string) error { 247 249 // Create image resolver 248 250 imageResolver := config.NewImageResolver(mgr.GetClient()) 249 251 ··· 258 260 259 261 // Set up discovery DaemonSet controller (manager-owned) 260 262 if err := (&controller.DiscoveryDaemonSetReconciler{ 261 - Client: mgr.GetClient(), 262 - Scheme: mgr.GetScheme(), 263 - ImageResolver: imageResolver, 264 - DiscoveryImage: cfg.discoveryImage, 263 + Client: mgr.GetClient(), 264 + Scheme: mgr.GetScheme(), 265 + ImageResolver: imageResolver, 266 + DiscoveryImage: cfg.discoveryImage, 267 + ServiceAccountName: serviceAccountName, 265 268 }).SetupWithManager(mgr); err != nil { 266 269 setupLog.Error(err, "unable to create controller", "controller", "DiscoveryDaemonSet") 267 270 return err ··· 326 329 return err 327 330 } 328 331 332 + // Get the service account name that this pod is running under 333 + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 334 + defer cancel() 335 + 336 + serviceAccountName, err := config.GetCurrentServiceAccount(ctx, mgr.GetClient()) 337 + if err != nil { 338 + setupLog.Error(err, "unable to get current service account - agent and discovery pods will fail without proper RBAC") 339 + return err 340 + } 341 + setupLog.Info("Detected service account", "serviceAccount", serviceAccountName) 342 + 329 343 // Create agent manager runnable that will create the agent manager after TLS is ready 330 - agentManagerRunnable := NewAgentManagerRunnable(mgr.GetClient(), cfg.agentImage, operatorNamespace, setupLog) 344 + agentManagerRunnable := NewAgentManagerRunnable(mgr.GetClient(), cfg.agentImage, operatorNamespace, serviceAccountName, setupLog) 331 345 332 346 // Add agent manager as a runnable to start after TLS is ready 333 347 setupLog.Info("Adding agent manager to manager") ··· 337 351 } 338 352 339 353 // Setup controllers that don't need the agent manager immediately 340 - if err := setupBaseControllers(mgr, cfg); err != nil { 354 + if err := setupBaseControllers(mgr, cfg, serviceAccountName); err != nil { 341 355 return err 342 356 } 343 357 344 358 // Create a runnable to setup agent-dependent controllers after agent manager is ready 345 - agentControllerSetup := NewAgentControllerSetupRunnable(agentManagerRunnable, mgr, operatorNamespace, operatorName, setupLog) 359 + agentControllerSetup := NewAgentControllerSetupRunnable(agentManagerRunnable, mgr, operatorNamespace, operatorName, serviceAccountName, setupLog) 346 360 setupLog.Info("Adding agent controller setup to manager") 347 361 if err := mgr.Add(agentControllerSetup); err != nil { 348 362 setupLog.Error(err, "unable to add agent controller setup to manager")
+21 -16
internal/modes/manager/runnable.go
··· 37 37 38 38 // AgentManagerRunnable wraps an agent manager to create it after TLS is ready 39 39 type AgentManagerRunnable struct { 40 - k8sClient client.Client 41 - agentImage string 42 - operatorNS string 43 - logger logr.Logger 44 - agentManager *agent.Manager 45 - mu sync.RWMutex 46 - ready bool 47 - readyCh chan struct{} 48 - readyOnce sync.Once 40 + k8sClient client.Client 41 + agentImage string 42 + operatorNS string 43 + serviceAccountName string 44 + logger logr.Logger 45 + agentManager *agent.Manager 46 + mu sync.RWMutex 47 + ready bool 48 + readyCh chan struct{} 49 + readyOnce sync.Once 49 50 } 50 51 51 52 // NewAgentManagerRunnable creates a new agent manager runnable 52 - func NewAgentManagerRunnable(k8sClient client.Client, agentImage string, operatorNS string, logger logr.Logger) *AgentManagerRunnable { 53 + func NewAgentManagerRunnable(k8sClient client.Client, agentImage string, operatorNS, serviceAccountName string, logger logr.Logger) *AgentManagerRunnable { 53 54 return &AgentManagerRunnable{ 54 - k8sClient: k8sClient, 55 - agentImage: agentImage, 56 - operatorNS: operatorNS, 57 - logger: logger.WithName("agent-manager-runnable"), 58 - readyCh: make(chan struct{}), 55 + k8sClient: k8sClient, 56 + agentImage: agentImage, 57 + operatorNS: operatorNS, 58 + serviceAccountName: serviceAccountName, 59 + logger: logger.WithName("agent-manager-runnable"), 60 + readyCh: make(chan struct{}), 59 61 } 60 62 } 61 63 ··· 256 258 mgr manager.Manager 257 259 operatorNamespace string 258 260 operatorName string 261 + serviceAccountName string 259 262 logger logr.Logger 260 263 } 261 264 262 265 // NewAgentControllerSetupRunnable creates a new agent controller setup runnable 263 - func NewAgentControllerSetupRunnable(agentManagerRunnable *AgentManagerRunnable, mgr manager.Manager, operatorNamespace, operatorName string, logger logr.Logger) *AgentControllerSetupRunnable { 266 + func NewAgentControllerSetupRunnable(agentManagerRunnable *AgentManagerRunnable, mgr manager.Manager, operatorNamespace, operatorName, serviceAccountName string, logger logr.Logger) *AgentControllerSetupRunnable { 264 267 return &AgentControllerSetupRunnable{ 265 268 agentManagerRunnable: agentManagerRunnable, 266 269 mgr: mgr, 267 270 operatorNamespace: operatorNamespace, 268 271 operatorName: operatorName, 272 + serviceAccountName: serviceAccountName, 269 273 logger: logger.WithName("agent-controller-setup"), 270 274 } 271 275 } ··· 292 296 AgentManager: agentManager, 293 297 ImageResolver: imageResolver, 294 298 DeviceAbsenceTimeout: 10 * time.Minute, // Default: cleanup agents after 10 minutes of device absence 299 + ServiceAccountName: acsr.serviceAccountName, 295 300 }).SetupWithManager(acsr.mgr); err != nil { 296 301 return fmt.Errorf("unable to create controller HSMPoolAgent: %w", err) 297 302 }