### Initialize and Setup CertificateSigningRequestReconciler Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/types.md Provides an example of initializing the CertificateSigningRequestReconciler with Kubernetes clients, scheme, and configuration, followed by its setup with a manager. This is useful for setting up the controller in a Kubernetes environment. ```go reconciler := &controller.CertificateSigningRequestReconciler{ ClientSet: clientset.NewForConfigOrDie(k8sConfig), Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Config: controller.Config{ RegexStr: "^node-.*\\.example\\.com$", IPPrefixesStr: "10.0.0.0/8", LogLevel: 0, }, } if err := reconciler.SetupWithManager(mgr); err != nil { log.Fatal(err) } ``` -------------------------------- ### HostResolver Usage Examples Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/types.md Shows how to assign standard library resolvers or a custom resolver with a timeout to the DNSResolver field. Includes an example of performing a DNS lookup. ```go // Using standard library resolver config.DNSResolver = net.DefaultResolver // Using custom resolver with timeout customResolver := &net.Resolver{ PreferGo: true, Dial: func(ctx context.Context, network, address string) (net.Conn, error) { return (&net.Dialer{Timeout: 500 * time.Millisecond}).DialContext(ctx, network, address) }, } config.DNSResolver = customResolver // Resolution call ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) def cancel() ips, err := config.DNSResolver.LookupHost(ctx, "example.com") if err != nil { log.Error(err, "DNS resolution failed") } ``` -------------------------------- ### Start kubelet-csr-approver Controller Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/README.md This Go code snippet shows the main entry point for starting the kubelet-csr-approver controller. It calls the `Run` function from the `cmd` package and exits with the returned code. ```go package main import ( "os" "github.com/postfinance/kubelet-csr-approver/internal/cmd" ) func main() { code := cmd.Run() os.Exit(code) } ``` -------------------------------- ### Reconcile Method Example Usage Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/controller.md An example demonstrating how the Reconcile method is called by controller-runtime and how to check its results. This method is not intended for direct user invocation. ```go // This method is called automatically by controller-runtime // when a CertificateSigningRequest is created or modified. // No direct invocation needed in user code. // Example of checking reconciliation results: result, err := reconciler.Reconcile( context.Background(), ctrl.Request{ NamespacedName: types.NamespacedName{ Name: "csr-name", }, }, ) if err != nil { log.Error(err, "reconciliation failed") } if result.Requeue { log.Info("CSR will be requeued") } ``` -------------------------------- ### Helm Install Command for kubelet-csr-approver Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/README.md Installs the kubelet-csr-approver using Helm, allowing customization of provider regex, IP prefixes, and expiration seconds. Ensure the namespace is set to 'kube-system'. ```bash helm repo add kubelet-csr-approver https://postfinance.github.io/kubelet-csr-approver helm install kubelet-csr-approver kubelet-csr-approver/kubelet-csr-approver -n kube-system \ --set providerRegex='^node-\\w*\\.int\\.company\\.ch$' \ --set providerIpPrefixes='192.168.8.0/22' \ --set maxExpirationSeconds='86400' \ --set bypassDnsResolution='false' ``` -------------------------------- ### Install Kubelet CSR Approver with Helm Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/charts/kubelet-csr-approver/README.md Installs the Kubelet CSR Approver using Helm, allowing customization of provider-specific checks and maximum expiration times. ```bash helm repo add kubelet-csr-approver https://postfinance.github.io/kubelet-csr-approver helm install kubelet-csr-approver kubelet-csr-approver/kubelet-csr-approver -n kube-system \ --set providerRegex='^node-\w*\.int\.company\.ch$' \ --set providerIpPrefixes='192.168.8.0/22' \ --set maxExpirationSeconds='86400' --set bypassDnsResolution='false' ``` -------------------------------- ### Controller Manager Initialization Example Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/types.md Demonstrates how to initialize the controller manager with a custom configuration. Ensure all required fields like RegexStr and IPPrefixesStr are set. ```go config := &controller.Config{ RegexStr: "^node-[a-z0-9]+\.example\.com$", IPPrefixesStr: "10.0.0.0/8,172.16.0.0/12", MaxExpirationSeconds: 86400, // 1 day LogLevel: 0, MetricsAddr: ":8080", ProbeAddr: ":8081", DNSResolver: net.DefaultResolver, } logger := controller.InitLogger(config) _, mgr, code := cmd.CreateControllerManager(config, logger) if code != 0 { os.Exit(code) } ``` -------------------------------- ### Configuration Flow Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/architecture.md Details the process of preparing configuration, starting from command-line arguments or environment variables to running the controller manager. ```text Command-line Arguments / Environment Variables ↓ prepareCmdlineConfig() ├─ Parse flags and env vars ├─ Validate numeric ranges └─ Create Config struct ↓ CreateControllerManager() ├─ Compile regex pattern → Config.ProviderRegexp ├─ Parse IP prefixes → Config.ProviderIPSet ├─ Set up controller-runtime manager ├─ Create CertificateSigningRequestReconciler └─ Register with manager ↓ Run() └─ Start manager (blocks until shutdown signal) ``` -------------------------------- ### Run kubelet-csr-approver from CLI Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/README.md Example of how to run the kubelet-csr-approver controller from the command line with specific configuration flags for provider regex and IP prefixes. ```bash ./kubelet-csr-approver \ --provider-regex='^node-[a-z0-9]+\.example\.com$' \ --provider-ip-prefixes='10.0.0.0/8' ``` -------------------------------- ### Run Controller with Default Settings Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/cmd.md Starts the controller using default settings parsed from command-line arguments and environment variables. Returns an integer exit code: 0 on success, non-zero on failure. ```Go package main import ( os "os" "github.com/postfinance/kubelet-csr-approver/internal/cmd" ) func main() { code := cmd.Run() os.Exit(code) } ``` -------------------------------- ### Example: Additional Security Checks Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/provider-checks.md Implement ProviderChecks to enforce stricter security policies on CSRs. This example verifies the number of DNS names and IP addresses, checks the DNS name structure, and disallows email addresses. ```go // ProviderChecks validates CSR against additional security policies func ProviderChecks( csr *certificatesv1.CertificateSigningRequest, x509cr *x509.CertificateRequest, ) (valid bool, reason string) { // Check 1: Ensure exactly one DNS name (even stricter than config) if len(x509cr.DNSNames) != 1 { return false, "Exactly one DNS name required" } // Check 2: Ensure exactly one IP address if len(x509cr.IPAddresses) != 1 { return false, "Exactly one IP address required" } // Check 3: Verify DNS name structure dnsName := x509cr.DNSNames[0] parts := strings.Split(dnsName, ".") if len(parts) < 3 { return false, "DNS name must have at least 3 components" } // Check 4: Verify no email addresses (kubelet certs shouldn't have these) if len(x509cr.EmailAddresses) > 0 { return false, "Email addresses not allowed in kubelet certificates" } return true, "" ``` -------------------------------- ### Register Controller with Manager Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/controller.md Sets up the CertificateSigningRequestReconciler with the provided controller-runtime manager. This is essential for the controller to start watching and processing CSR resources. ```go mgr, _ := ctrl.NewManager(config, ctrl.Options{}) reconciler := &controller.CertificateSigningRequestReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), } if err := reconciler.SetupWithManager(mgr); err != nil { log.Fatal("unable to set up controller", err) } ``` -------------------------------- ### Example: Node Selector Labels Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/provider-checks.md Implement ProviderChecks to validate CSRs based on node selector labels. This example checks if the node name, extracted from the CSR username, has a 'prod-' or 'staging-' prefix. ```go import "strings" // ProviderChecks validates based on node selector labels func ProviderChecks( csr *certificatesv1.CertificateSigningRequest, x509cr *x509.CertificateRequest, ) (valid bool, reason string) { // Extract node name nodeName := strings.TrimPrefix(csr.Spec.Username, "system:node:") // In a real implementation, look up the node object via the API // For this example, we'll check node naming conventions if !strings.HasPrefix(nodeName, "prod-") && !strings.HasPrefix(nodeName, "staging-") { return false, "Node must have prod- or staging- prefix" } return true, "" ``` -------------------------------- ### Regex Validation Example Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/errors.md Demonstrates how a DNS name is checked against a provider-specific regex pattern. CSRs with DNS names not matching the regex are denied. ```text Regex: ^node-.*\.example\.com$ DNS name: "test.other.com" Result: Denied (does not match regex) DNS name: "node-123.example.com" Result: Pass (matches regex) ``` -------------------------------- ### Example: Conditional Approval Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/provider-checks.md Implement ProviderChecks for time-based validation, approving CSRs only during specific hours. This example restricts approvals to business hours (6 AM to 6 PM). ```go // ProviderChecks implements time-based validation func ProviderChecks( csr *certificatesv1.CertificateSigningRequest, x509cr *x509.CertificateRequest, ) (valid bool, reason string) { // Only approve CSRs created during business hours (example) hour := time.Now().Hour() if hour < 6 || hour > 18 { return false, "CSR approvals only allowed during business hours" } return true, "" ``` -------------------------------- ### DNS Resolution Failure Example Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/errors.md Illustrates a scenario where a DNS name in the SAN cannot be resolved. This check is performed unless DNS resolution is bypassed. ```go // DNS resolution times out after 1 second // Can be bypassed with --bypass-dns-resolution=true // Indicates controller cannot reach DNS servers or hostname is not resolvable ``` -------------------------------- ### ProviderChecks Function Example Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/provider-checks.md Demonstrates how to call the `ProviderChecks` function and check its validity. This is typically used within a testing context to verify CSR validation logic. ```go valid, reason := ProviderChecks(csr, x509cr) if !valid { t.Errorf("Expected valid=true, got reason: %s", reason) } ``` -------------------------------- ### SAN IP Out of Provider Range Example Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/errors.md Details an error where an IP address in the x509 CSR's SAN is not within the allowed provider IP prefixes or subnets. The CSR is denied. ```go // One of the SAN IP addresses, {ip}, is not part of the allowed IP Prefixes/Subnets, denying the CSR. ``` -------------------------------- ### Example: Inventory Validation Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/provider-checks.md Implement ProviderChecks to validate CSRs against an external node inventory system. This ensures that only known nodes can request certificates. It extracts the hostname from the CSR's username and checks its existence in the inventory. ```go package controller import ( "crypto/x509" "fmt" "strings" certificatesv1 "k8s.io/api/certificates/v1" ) // InventoryClient represents a node inventory system type InventoryClient interface { IsKnownNode(hostname string) (bool, error) } var inventoryClient InventoryClient // ProviderChecks validates CSR against an external node inventory func ProviderChecks( csr *certificatesv1.CertificateSigningRequest, _ *x509.CertificateRequest, ) (valid bool, reason string) { // Extract hostname from username (e.g., "system:node:mynode" → "mynode") hostname := strings.TrimPrefix(csr.Spec.Username, "system:node:") if hostname == csr.Spec.Username { // Not a system:node: CSR, shouldn't reach here but check anyway return false, "Invalid username format" } // Check against inventory system known, err := inventoryClient.IsKnownNode(hostname) if err != nil { // Return error by logging and denying return false, fmt.Sprintf("Inventory check failed: %v", err) } if !known { return false, fmt.Sprintf("Node %s is not in the inventory", hostname) } return true, "" } ``` -------------------------------- ### Resolved IP Out of Range Example Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/errors.md Shows a case where an IP address resolved from a DNS name is not present in the provider-specified set of whitelisted IPs. The CSR is denied in this situation. ```go // One of the resolved IP addresses, {ip}, isn't part of the provider-specified set of whitelisted IP. denying the certificate ``` -------------------------------- ### SAN IP Not in Resolved Set Example Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/errors.md Highlights an error where an IP address listed in the x509 CSR's SAN is not found within the set of IP addresses resolved from the DNS name. This prevents IP spoofing. ```go // One of the SAN IP addresses, {ip}, is not contained in the set of resolved IP addresses, denying the CSR. ``` -------------------------------- ### Expiration Exceeds Maximum Example Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/errors.md Illustrates the condition where the requested certificate expiration (`Spec.ExpirationSeconds`) is longer than the maximum allowed duration (`MaxExpirationSeconds`). ```go // CSR spec.expirationSeconds is longer than the maximum allowed expiration second ``` -------------------------------- ### Provider-Specific Check Failure Example Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/errors.md Shows a scenario where a custom `ProviderChecks` function returns `valid=false`, leading to the denial of the CSR. This allows for external identity validation. ```go // Default implementation always returns (true, "") // Can be customized by modifying the ProviderChecks function // Useful for external identity validation (LDAP, inventory systems) ``` -------------------------------- ### Build kubelet-csr-approver with Debug Tags Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/README.md Build the kubelet-csr-approver locally with the 'debug' tag to include all authentication providers. This is necessary for running on clusters with specific authentication configurations like oidc. ```bash go build -tags debug ./cmd/kubelet-csr-approver/ ``` -------------------------------- ### SetupWithManager Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/controller.md Registers the CSR reconciler with the controller-runtime manager, enabling it to watch and reconcile CertificateSigningRequest resources. ```APIDOC ## SetupWithManager ### Description Registers the CSR reconciler with the controller-runtime manager, enabling it to watch and reconcile CertificateSigningRequest resources. ### Method (Called during controller initialization) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go mgr, _ := ctrl.NewManager(config, ctrl.Options{}) reconciler := &controller.CertificateSigningRequestReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), } if err := reconciler.SetupWithManager(mgr); err != nil { log.Fatal("unable to set up controller", err) } ``` ### Response #### Success Response - **error** (`error`) - Returns nil on successful registration, or an error if the controller cannot be registered. #### Response Example (See Go example for return values) ``` -------------------------------- ### Enable Debug Logging Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/architecture.md Run the kubelet-csr-approver with the --level=5 flag to enable verbose debug logging for detailed output during operation. ```bash ./kubelet-csr-approver --level=5 ``` -------------------------------- ### Create and Configure Controller Manager Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/cmd.md Creates and configures a controller manager for CSR reconciliation. It validates configuration, compiles regex, parses IP prefixes, sets up the manager, and registers the CSR controller. Returns the configured CSR reconciler, the controller-runtime manager, and an exit code. ```Go package main import ( "github.com/postfinance/kubelet-csr-approver/internal/cmd" "github.com/postfinance/kubelet-csr-approver/internal/controller" ctrl "sigs.k8s.io/controller-runtime" ) func main() { config := &controller.Config{ RegexStr: "^node-.*\\.example\\.com$", IPPrefixesStr: "10.0.0.0/8,172.16.0.0/12", LogLevel: 0, } logger := ctrl.Log.WithName("setup") csrController, mgr, code := cmd.CreateControllerManager(config, logger) if code != 0 { os.Exit(code) } // Start the manager if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { logger.Error(err, "problem running manager") os.Exit(1) } } ``` -------------------------------- ### Create Custom Controller Manager Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/README.md This Go code demonstrates how to create a custom controller manager for kubelet-csr-approver. It initializes configuration and logger, then creates the reconciler and manager. ```go import ( "github.com/postfinance/kubelet-csr-approver/internal/cmd" "github.com/postfinance/kubelet-csr-approver/internal/controller" ) config := &controller.Config{ RegexStr: "^node-.*\\.example\\.com$", IPPrefixesStr: "10.0.0.0/8", LogLevel: 0, } logger := controller.InitLogger(config) reconciler, mgr, code := cmd.CreateControllerManager(config, logger) if code == 0 { mgr.Start(context.Background()) } ``` -------------------------------- ### Initialize Structured Logger Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/utils.md Initializes a structured logger with a specified verbosity level. It maps the provided LogLevel to the appropriate logr.Logger severity and sets it as the global controller-runtime logger. Panics if the log level is outside the supported range of [-5, 10]. ```go func InitLogger(config *Config) logr.Logger ``` ```go config := &controller.Config{ LogLevel: 0, // Info level } logger := InitLogger(config) logger.V(0).Info("Application started") logger.V(1).Info("Detailed information") logger.V(-1).Info("Warning level") ``` -------------------------------- ### Kubelet CSR Approver File Structure Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/README.md This snippet outlines the directory and file structure of the kubelet-csr-approver project. It shows the entry point, internal package organization, and module definition. ```text kubelet-csr-approver/ ├── cmd/kubelet-csr-approver/ │ └── main.go # Entry point ├── internal/ │ ├── cmd/ │ │ ├── cmd.go # CLI setup (Run, CreateControllerManager) │ │ └── k8s-auth.go # Auth provider selection │ └── controller/ │ ├── csr_controller.go # Main reconciler │ ├── provider_specific_checks.go # Extension point │ ├── regex_ip_checks.go # Validation methods │ └── utils.go # Utility functions ├── go.mod # Go module definition └── README.md # Project README ``` -------------------------------- ### Valid and Invalid IP Prefix Formats Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/errors.md Illustrates the correct and incorrect formats for specifying IP prefixes using the --provider-ip-prefixes flag or PROVIDER_IP_PREFIXES environment variable. Incorrect formats will result in parsing errors. ```bash # Valid formats --provider-ip-prefixes='10.0.0.0/8' --provider-ip-prefixes='10.0.0.0/8,172.16.0.0/12' --provider-ip-prefixes='10.0.0.0/8,fc00::/7' --provider-ip-prefixes='192.168.1.0/24' # Invalid formats --provider-ip-prefixes='10.0.0.0/33' # INVALID: /33 exceeds /32 --provider-ip-prefixes='10.0.0.1' # INVALID: missing /prefix --provider-ip-prefixes='10.0.0.0/8 10.1.0.0/16' # INVALID: space-separated ``` -------------------------------- ### Rebuilding Kubelet CSR Approver Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/provider-checks.md Rebuild the Kubelet CSR Approver binary after making changes to the provider checks. Ensure you are in the correct directory to execute the build command. ```bash go build ./cmd/kubelet-csr-approver/ ``` -------------------------------- ### Test IP Prefix Parsing Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/errors.md A Go code snippet demonstrating how to parse and validate an IP prefix using the netip package. This can be used for testing prefix validity before passing them to the controller. ```go import "net/netip" // Test a prefix prefix, err := netip.ParsePrefix("10.0.0.0/8") if err != nil { log.Fatal("Invalid prefix:", err) } log.Println("Valid prefix:", prefix) ``` -------------------------------- ### Flag and Environment Variable Precedence Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/configuration.md Demonstrates the precedence of configuration settings, where command-line flags override environment variables, which in turn override built-in defaults. ```bash # These are equivalent: export PROVIDER_REGEX="^node-.*\.example\.com$" kubelet-csr-approver ``` ```bash # This overrides the environment variable: kubelet-csr-approver --provider-regex="^other-.*\.example\.com$" ``` -------------------------------- ### InitLogger Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/utils.md Initializes a structured logger with a specified verbosity level, suitable for application-wide logging. ```APIDOC ## InitLogger ### Description Initializes a structured logger with specified verbosity level. ### Signature ```go func InitLogger(config *Config) logr.Logger ``` ### Parameters #### Path Parameters - None #### Query Parameters - None #### Request Body - None ### Parameters - **config** (`*Config`) - Required - Configuration object containing `LogLevel` field. ### Returns - **logr.Logger** - Initialized logger instance. ### Log Level Mapping The `LogLevel` in the config is inverted for compatibility between zap and logr: - `-5` maps to Fatal (most severe) - `0` maps to Info - `10` maps to Verbose (least severe) ### Behavior 1. Creates a new flash logger instance. 2. Validates log level is between -5 and 10 (inclusive). 3. Inverts the log level for zap compatibility. 4. Creates a zapr logger wrapping the zap logger. 5. Sets the logger as the global controller-runtime logger. 6. Returns the configured logr.Logger. ### Panics - Fatal error if log level is outside [-5, 10] range. ### Example ```go config := &controller.Config{ LogLevel: 0, // Info level } logger := InitLogger(config) logger.V(0).Info("Application started") logger.V(1).Info("Detailed information") logger.V(-1).Info("Warning level") ``` ``` -------------------------------- ### Provide Valid Provider Regex Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/errors.md When the --provider-regex flag or PROVIDER_REGEX environment variable is not specified or empty, the controller exits with code 10. Ensure a valid regex pattern is provided. ```bash # Provide a valid regex --provider-regex='^node-.*\.example\.com$' # Or via environment variable export PROVIDER_REGEX='^node-.*\.example\.com$' ``` -------------------------------- ### Custom DNS Resolver Configuration Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/architecture.md Provide a custom HostResolver implementation to override the default DNS resolution behavior. This is configured via the Config.DNSResolver field. ```go config.DNSResolver = &CustomResolver{...} ``` -------------------------------- ### Test ProviderChecks Function Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/provider-checks.md Provides a basic test case for the ProviderChecks function using a sample CSR and x509 CertificateRequest. This demonstrates how to set up test data for validating custom provider checks. ```go func TestProviderChecks(t *testing.T) { csr := &certificatesv1.CertificateSigningRequest{ Spec: certificatesv1.CertificateSigningRequestSpec{ Username: "system:node:testnode", }, } x509cr := &x509.CertificateRequest{ Subject: pkix.Name{ CommonName: "system:node:testnode", }, DNSNames: []string{"testnode.example.com"}, IPAddresses: []net.IP{net.ParseIP("10.0.0.1")}, } ``` -------------------------------- ### Set Health Probe Bind Address Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/configuration.md Configure the address and port for health probe endpoints like /healthz and /readyz. Defaults to ':8081'. ```bash # Default: listen on all interfaces, port 8081 --health-probe-bind-address=:8081 ``` ```bash # Specific interface --health-probe-bind-address=127.0.0.1:8081 ``` ```bash # Using environment variable HEALTH_PROBE_BIND_ADDRESS=:9091 ``` -------------------------------- ### Configure Skipping Deny Step Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/configuration.md Control whether to add a `CertificateDenied` condition to invalid CSRs. When `true`, invalid CSRs are left untouched; when `false` (default), they are denied. ```bash # Default: deny invalid CSRs --skip-deny-step=false ``` ```bash # Conservative: only approve valid, leave others untouched --skip-deny-step=true ``` ```bash # Using environment variable SKIP_DENY_STEP=false ``` -------------------------------- ### Set Metrics Bind Address Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/configuration.md Configure the address and port for the Prometheus metrics endpoint. Defaults to ':8080'. ```bash # Default: listen on all interfaces, port 8080 --metrics-bind-address=:8080 ``` ```bash # Specific interface --metrics-bind-address=127.0.0.1:8080 ``` ```bash # Different port --metrics-bind-address=:9090 ``` ```bash # Using environment variable METRICS_BIND_ADDRESS=0.0.0.0:9090 ``` -------------------------------- ### Valid and Invalid Regex Patterns Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/errors.md Demonstrates valid and invalid regex patterns that can be used with the --provider-regex flag. Invalid patterns may cause silent failures during regex compilation. ```go // Valid patterns regexp.MustCompile(`^node-[a-z0-9]+\.example\.com$`) // Works regexp.MustCompile(`^[^-]+-node-\w+$`) // Works regexp.MustCompile(`.*`) // Works (allows all) // Invalid patterns regexp.MustCompile(`^node-.*\.example\.com$`) // INVALID: . not escaped regexp.MustCompile(`^node-(test)`) // INVALID: unclosed group ``` -------------------------------- ### Config Struct Definition Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/types.md Defines the configuration parameters for the CSR approver controller, including logging level, network addresses, leader election settings, and validation rules. ```go type Config struct { LogLevel int8 MetricsAddr string ProbeAddr string LeaderElection bool RegexStr string ProviderRegexp func(string) bool IPPrefixesStr string ProviderIPSet *netipx.IPSet MaxExpirationSeconds int32 K8sConfig *rest.Config DNSResolver HostResolver BypassDNSResolution bool IgnoreNonSystemNodeCsr bool SkipDenyCSR bool AllowedDNSNames int BypassHostnameCheck bool } ``` -------------------------------- ### Kubelet-CSR-Approver Architecture Diagram Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/architecture.md Visual representation of the kubelet-csr-approver's interaction with the Kubernetes API Server and the controller-runtime manager, detailing the reconciliation process and validation steps. ```text ┌─────────────────────────────────────────────┐ │ Kubernetes API Server │ │ └─ CertificateSigningRequest resources │ └──────────────────┬──────────────────────────┘ │ Watch & List │ ┌──────────────────▼──────────────────────────┐ │ Controller-Runtime Manager │ │ ├─ Event Queue │ │ ├─ Leader Election (optional) │ │ └─ Metrics & Health Endpoints │ └──────────────────┬──────────────────────────┘ │ Reconciliation Request │ ┌──────────────────▼──────────────────────────┐ │ CertificateSigningRequestReconciler │ │ ├─ Baseline Checks │ │ │ ├─ Signer name validation │ │ │ ├─ Status checks │ │ │ └─ Certificate existence check │ │ ├─ X.509 Validation │ │ │ ├─ PEM parsing │ │ │ ├─ Username format check │ │ │ ├─ Common name verification │ │ │ └─ SAN content validation │ │ ├─ DNS Validation │ │ │ ├─ Prefix check │ │ │ ├─ Regex matching │ │ │ ├─ DNS resolution │ │ │ └─ IP range validation │ │ ├─ IP Validation │ │ │ └─ IP range checks │ │ ├─ Duration Validation │ │ │ └─ Max expiration check │ │ └─ Provider Checks │ │ └─ Custom validation hook │ └──────────────────┬──────────────────────────┘ │ Approval/Denial Decision │ ┌──────────────────▼──────────────────────────┐ │ Kubernetes API Server │ │ └─ Update CSR Status with Condition │ └─────────────────────────────────────────────┘ ``` -------------------------------- ### Accessing Embedded Config Fields Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/types.md Demonstrates direct access to configuration fields like MaxExpirationSeconds and BypassDNSResolution from an embedded Config struct within the CertificateSigningRequestReconciler. ```go var reconciler *CertificateSigningRequestReconciler // Access config fields directly: maxExpiration := reconciler.MaxExpirationSeconds bypassDNS := reconciler.BypassDNSResolution ``` -------------------------------- ### Implement Custom Provider Checks Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/README.md Allows for custom validation logic specific to your environment or provider. Implement the `ProviderChecks` function to add your own validation rules before a CSR is approved or denied. ```go func ProviderChecks( csr *certificatesv1.CertificateSigningRequest, x509cr *x509.CertificateRequest, ) (valid bool, reason string) { // Add custom validation logic if shouldAllow { return true, "" } return false, "Custom validation failed: reason here" ``` -------------------------------- ### Kubelet CSR Approver Deployment Configuration Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/architecture.md A typical Kubernetes Deployment configuration for the kubelet-csr-approver. It specifies two replicas for high availability with leader election, the container image, and command-line arguments for provider regex, IP prefixes, and leader election enablement. It also exposes metrics and health check endpoints. ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: kubelet-csr-approver namespace: kube-system spec: replicas: 2 # With leader election selector: matchLabels: app: kubelet-csr-approver template: metadata: labels: app: kubelet-csr-approver spec: serviceAccountName: kubelet-csr-approver containers: - name: approver image: ghcr.io/postfinance/kubelet-csr-approver:v1.x.x args: - --provider-regex=^node-.*\.example\.com$ - --provider-ip-prefixes=10.0.0.0/8 - --leader-election=true ports: - name: metrics containerPort: 8080 - name: health containerPort: 8081 livenessProbe: httpGet: path: /healthz port: health initialDelaySeconds: 15 periodSeconds: 20 ``` -------------------------------- ### Configure Hostname Check Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/configuration.md Control whether DNS names in a CSR must be prefixed by the node's hostname. Use `true` to allow any DNS name matching the provider regex, or `false` for strict hostname prefix checking. ```bash # Strict: DNS name must start with node hostname --bypass-hostname-check=false ``` ```bash # Relaxed: DNS name must match regex only --bypass-hostname-check=true ``` ```bash # Using environment variable BYPASS_HOSTNAME_CHECK=true ``` -------------------------------- ### CSR Approval Data Flow Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/architecture.md Illustrates the sequence of events from a kubelet requesting a certificate to the final signed certificate being issued and used. ```text 1. Kubelet requests certificate ↓ 2. Kubelet creates CertificateSigningRequest object ↓ 3. Kubernetes API notifies controller of new CSR ↓ 4. Controller-runtime queues reconciliation request ↓ 5. CertificateSigningRequestReconciler.Reconcile() is called ├─ Read CSR from API ├─ Baseline checks ├─ Parse x509 CSR ├─ Validate username format ├─ Validate SAN content ├─ DNS validation (hostname, regex, resolution) ├─ IP range validation ├─ Expiration validation ├─ Provider-specific checks └─ Decision: Approve or Deny ↓ 6. Update CSR status with condition ├─ Approved: CertificateApproved condition └─ Denied: CertificateDenied condition ↓ 7. Kubernetes certificate controller picks up approval ↓ 8. Kubernetes signs the certificate ↓ 9. Kubelet retrieves signed certificate ↓ 10. Kubelet uses certificate for API communication ``` -------------------------------- ### HostResolver Interface Definition Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/types.md Defines the interface for DNS resolution, allowing for custom implementations. The standard library's net.DefaultResolver and net.Resolver satisfy this interface. ```go type HostResolver interface { LookupHost(ctx context.Context, host string) ([]string, error) } ``` -------------------------------- ### Configure Provider Regex for CSR DNS Names Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/configuration.md Use this to specify a Go regular expression that each SAN DNS name in the certificate request must match. This is crucial for validating node hostnames and acts as a primary security boundary. The regex is matched against the full DNS name in the CSR SAN. If no DNS names are present, this check is skipped. Invalid regex patterns will cause the controller to exit with code 10. ```bash --provider-regex='^node-[a-z0-9]+\.example\.com$' ``` ```bash --provider-regex='^[a-z0-9]+\.example\.com$' ``` ```bash --provider-regex='node-\d+$' ``` ```bash PROVIDER_REGEX="^k8s-node-[a-z0-9]+\.prod\.internal$" ``` -------------------------------- ### Configure IP Prefixes for CSR IP Addresses Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/configuration.md Restrict allowed IP addresses in CSR SANs by providing a comma-separated list of IP address prefixes in CIDR notation. All CSR IP addresses must fall within at least one of these prefixes. Both IPv4 and IPv6 are supported. Invalid prefix formats will cause the controller to exit with code 10. This check applies to both explicit IP addresses and IPs resolved from DNS names. ```bash --provider-ip-prefixes='10.0.0.0/8,172.16.0.0/12,192.168.0.0/16' ``` ```bash --provider-ip-prefixes='10.0.0.0/8,fc00::/7' ``` ```bash --provider-ip-prefixes='192.168.1.0/24' ``` ```bash PROVIDER_IP_PREFIXES="10.0.0.0/8,172.16.0.0/12" ``` -------------------------------- ### Enable Leader Election Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/configuration.md Enable leader election for multi-replica deployments to ensure only one controller instance processes CSRs at a time. This is required for high availability. ```bash # Single replica: no leader election needed --leader-election=false ``` ```bash # Multiple replicas: enable leader election --leader-election=true ``` ```bash # Using environment variable LEADER_ELECTION=true ``` -------------------------------- ### WhitelistedIPCheck Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/controller.md Verifies that all IP addresses listed in the Subject Alternative Names (SANs) of a Certificate Signing Request (CSR) are within the provider-configured IP ranges. ```APIDOC ## WhitelistedIPCheck ### Description Verifies that all IP addresses listed in the Subject Alternative Names (SANs) of a Certificate Signing Request (CSR) are within the provider-configured IP ranges. ### Method (Implicitly called within reconciliation logic) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go valid, reason, err := reconciler.WhitelistedIPCheck( &csr, parsedX509CSR, ) if !valid { log.Info("IP whitelist check failed", "reason", reason) } ``` ### Response #### Success Response - **valid** (`bool`) - True if all IP addresses are within the provider-specified ranges. - **reason** (`string`) - Human-readable reason for failure (empty on success). - **err** (`error`) - Parsing errors for malformed IP addresses. #### Response Example (See Go example for return values) ``` -------------------------------- ### Validation Flow Diagram Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/README.md Illustrates the step-by-step process the controller follows to validate a CSR. This flow includes checks for signer, status, x509 parsing, username, SAN content, common name, DNS, IP, expiration, and provider-specific rules. ```text CSR Created/Modified ↓ Signer check ↓ (must be kubelet-serving) Status check ↓ (must not be already approved/denied) Parse x509 CSR ↓ (must be valid PEM PKCS#10) Username check ↓ (must be system:node:*) SAN content check ↓ (must have DNS name or IP) Common name check ↓ (must match username) DNS validation ├─ Count check (max allowed) ├─ Hostname prefix check ├─ Regex validation ├─ DNS resolution ├─ Resolved IP validation └─ SAN IP consistency check ↓ IP validation ↓ Expiration check ↓ Provider checks (custom) ↓ Approve or Deny with condition ``` -------------------------------- ### GetCertApprovalCondition Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/utils.md Inspects a CSR status to determine if it has already been approved or denied by checking for specific conditions. ```APIDOC ## GetCertApprovalCondition ### Description Inspects a CSR status to determine if it has already been approved or denied. ### Signature ```go func GetCertApprovalCondition(status *capiv1.CertificateSigningRequestStatus) (approved, denied bool) ``` ### Parameters #### Path Parameters - None #### Query Parameters - None #### Request Body - None ### Parameters - **status** (`*capiv1.CertificateSigningRequestStatus`) - Required - CSR status object to inspect. ### Returns - **approved** (`bool`) - True if the CSR has a `CertificateApproved` condition. - **denied** (`bool`) - True if the CSR has a `CertificateDenied` condition. ### Behavior - Iterates through all conditions in the CSR status. - Returns true for `approved` if a condition of type `CertificateApproved` is found. - Returns true for `denied` if a condition of type `CertificateDenied` is found. - Both can be true, neither, or only one. ### Example ```go var csr certificatesv1.CertificateSigningRequest // ... populate csr from API ... approved, denied := GetCertApprovalCondition(&csr.Status) if approved { log.Info("CSR is already approved") } else if denied { log.Info("CSR is already denied") } ``` ``` -------------------------------- ### Perform Whitelisted IP Check Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/controller.md Verifies that all IP addresses listed in the CSR's Subject Alternative Names (SANs) are present within the configured provider IP set. Use this to ensure only authorized IPs are used. ```go valid, reason, err := reconciler.WhitelistedIPCheck( &csr, parsedX509CSR, ) if !valid { log.Info("IP whitelist check failed", "reason", reason) } ``` -------------------------------- ### Configure Allowed DNS Names Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/configuration.md Set the maximum number of DNS names allowed in a single CSR. This helps prevent certificates from covering too many hostnames. The default is 1. ```bash # Allow only one DNS name (default) --allowed-dns-names=1 ``` ```bash # Allow up to 5 DNS names --allowed-dns-names=5 ``` ```bash # Using environment variable ALLOWED_DNS_NAMES=3 ``` -------------------------------- ### Perform DNS Validation Check Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/controller.md Executes a series of DNS checks on a CSR, including count, prefix, regex, and resolution checks against provider-specific IP sets. Use this to validate DNS names in a CSR. ```go ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) def cancel() valid, reason, err := reconciler.DNSCheck( ctx, &csr, parsedX509CSR, ) if !valid { log.Info("DNS check failed", "reason", reason) } if err != nil { log.Error(err, "DNS check error") } ``` -------------------------------- ### Set Log Verbosity Level Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/configuration.md Control the log verbosity using an integer level, ranging from -5 (Fatal) to 10 (Verbose). Defaults to 0 (Info). ```bash # Info level (default) --level=0 ``` ```bash # Warning level --level=-3 ``` ```bash # Verbose debug --level=5 ``` ```bash # Using environment variable LEVEL=-4 # Error level only ``` -------------------------------- ### Set Maximum Certificate Expiration Duration Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/configuration.md Define the maximum allowed certificate expiration duration in seconds. CSRs requesting longer expirations will be denied. The default of 367 days is a Kubernetes convention for kubelet certificates. Values outside the range [0, 31708800] will cause exit code 2. If `ExpirationSeconds` is not specified in the CSR, this check is skipped. ```bash --max-expiration-sec=86400 ``` ```bash --max-expiration-sec=31536000 ``` ```bash MAX_EXPIRATION_SEC=604800 ``` -------------------------------- ### RBAC ClusterRole for Kubelet CSR Approver Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/architecture.md This ClusterRole grants the necessary permissions for the kubelet-csr-approver service account to manage certificate signing requests, including reading, watching, listing, updating approval status, and approving specific signer types. ```yaml kind: ClusterRole metadata: name: kubelet-csr-approver rules: - apiGroups: ["certificates.k8s.io"] resources: ["certificatesigningrequests"] verbs: ["get", "watch", "list"] - apiGroups: ["certificates.k8s.io"] resources: ["certificatesigningrequests/approval"] verbs: ["update"] - apiGroups: ["certificates.k8s.io"] resources: ["signers"] resourceNames: ["kubernetes.io/kubelet-serving"] verbs: ["approve"] ``` -------------------------------- ### ParseCSR Source: https://github.com/postfinance/kubelet-csr-approver/blob/main/_autodocs/api-reference/utils.md Decodes a PEM-encoded PKCS#10 certificate signing request and parses it into a structured object. ```APIDOC ## ParseCSR ### Description Decodes a PEM-encoded PKCS#10 certificate signing request and parses it. ### Signature ```go func ParseCSR(pemBytes []byte) (*x509.CertificateRequest, error) ``` ### Parameters #### Path Parameters - None #### Query Parameters - None #### Request Body - None ### Parameters - **pemBytes** (`[]byte`) - Required - PEM-encoded PKCS#10 CSR bytes. ### Returns - **`*x509.CertificateRequest`** - Parsed certificate request object. Returns `nil` on error. - **error** - Error if PEM decoding or parsing fails. ### Error Cases - Returns error if `pemBytes` do not contain a valid PEM block. - Returns error if PEM block type is not `"CERTIFICATE REQUEST"`. - Returns error if the certificate request is malformed. ### Behavior 1. Decodes the PEM block from the input bytes. 2. Validates that the block type is `"CERTIFICATE REQUEST"`. 3. Parses the DER-encoded certificate request. 4. Returns the parsed `x509.CertificateRequest` object. ### Example ```go var csr certificatesv1.CertificateSigningRequest // ... populate csr from API ... x509csr, err := ParseCSR(csr.Spec.Request) if err != nil { log.Error(err, "failed to parse CSR") return } log.Info("Parsed CSR", "commonName", x509csr.Subject.CommonName, "dnsNames", x509csr.DNSNames, "ipAddresses", x509csr.IPAddresses, ) ``` ```