autocertwebhook

package module
v1.2.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 4, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

README

auto-cert-webhook

Go Go Report Card GoDoc codecov

A lightweight framework for building Kubernetes admission webhooks with automatic TLS certificate management.

Features

  • Self-signed CA and serving certificate generation
  • Automatic certificate rotation using openshift/library-go
  • Hot-reload certificates via Secret informer (no file watching)
  • Automatic caBundle synchronization to WebhookConfiguration
  • Leader election for multi-replica deployments
  • Support multiple webhooks in a single server
  • Prometheus metrics for certificate monitoring

Requirements

  • Go: 1.25+
  • Kubernetes: 1.16+ (uses admissionregistration.k8s.io/v1 API)

Installation

go get github.com/jimyag/auto-cert-webhook

Quick Start

package main

import (
    "encoding/json"

    admissionv1 "k8s.io/api/admission/v1"
    corev1 "k8s.io/api/core/v1"

    webhook "github.com/jimyag/auto-cert-webhook"
)

func main() {
    webhook.Run(&myWebhook{})
}

type myWebhook struct{}

func (m *myWebhook) Configure() webhook.Config {
    return webhook.Config{
        Name: "my-webhook",
    }
}

func (m *myWebhook) Webhooks() []webhook.Hook {
    return []webhook.Hook{
        {
            Path:  "/mutate-pods",
            Type:  webhook.Mutating,
            Admit: m.mutatePod,
        },
        {
            Path:  "/validate-pods",
            Type:  webhook.Validating,
            Admit: m.validatePod,
        },
    }
}

func (m *myWebhook) mutatePod(ar admissionv1.AdmissionReview) *admissionv1.AdmissionResponse {
    pod := &corev1.Pod{}
    json.Unmarshal(ar.Request.Object.Raw, pod)

    modified := pod.DeepCopy()
    if modified.Labels == nil {
        modified.Labels = make(map[string]string)
    }
    modified.Labels["mutated"] = "true"

    return webhook.PatchResponse(pod, modified)
}

func (m *myWebhook) validatePod(ar admissionv1.AdmissionReview) *admissionv1.AdmissionResponse {
    // validation logic
    return webhook.Allowed()
}

Configuration

All configuration is done through the Config struct returned by Configure():

func (m *myWebhook) Configure() webhook.Config {
    return webhook.Config{
        // Required
        Name: "my-webhook",

        // Optional - all have sensible defaults
        Namespace:             "webhook-system",     // default: auto-detected
        ServiceName:           "my-webhook-svc",     // default: Name
        Port:                  8443,                 // default: 8443
        MetricsEnabled:        ptr(true),            // default: true
        MetricsPort:           8080,                 // default: 8080
        MetricsPath:           "/metrics",           // default: /metrics
        HealthzPath:           "/healthz",           // default: /healthz
        ReadyzPath:            "/readyz",            // default: /readyz
        CASecretName:          "my-webhook-ca",      // default: <Name>-ca
        CertSecretName:        "my-webhook-cert",    // default: <Name>-cert
        CABundleConfigMapName: "my-webhook-bundle",  // default: <Name>-ca-bundle
        CAValidity:            365 * 24 * time.Hour, // default: 2 days
        CARefresh:             30 * 24 * time.Hour,  // default: 1 day
        CertValidity:          30 * 24 * time.Hour,  // default: 1 day
        CertRefresh:           12 * time.Hour,       // default: 12 hours
        LeaderElection:        ptr(true),            // default: true
        LeaderElectionID:      "my-webhook-leader",  // default: <Name>-leader
        LeaseDuration:         30 * time.Second,     // default: 30s
        RenewDeadline:         10 * time.Second,     // default: 10s
        RetryPeriod:           5 * time.Second,      // default: 5s
    }
}

func ptr[T any](v T) *T { return &v }

Architecture

┌─────────────────────────────────────────────────────────────┐
│                     Webhook Pod                              │
├─────────────────────────────────────────────────────────────┤
│  Leader Only:                                                │
│    - CertManager (certificate rotation)                      │
│    - CABundleSyncer (patch WebhookConfiguration)            │
│                                                              │
│  All Pods:                                                   │
│    - CertProvider (watch Secret, hot-reload)                │
│    - TLS Server (serve admission requests)                  │
│    - Metrics Server (Prometheus metrics)                    │
└─────────────────────────────────────────────────────────────┘

Conventions

The framework uses the following naming and structure conventions:

Resource Naming
Resource Default Name Description
CA Secret <Name>-ca Stores CA certificate and private key
Cert Secret <Name>-cert Stores server certificate and private key
CA Bundle ConfigMap <Name>-ca-bundle Stores CA bundle for webhook clients
Leader Election Lease <Name>-leader Lease resource for leader election
MutatingWebhookConfiguration <Name> Must match Config.Name
ValidatingWebhookConfiguration <Name> Must match Config.Name
Secret and ConfigMap Structure

CA Secret (kubernetes.io/tls):

  • tls.crt: CA certificate (PEM)
  • tls.key: CA private key (PEM)

Cert Secret (kubernetes.io/tls):

  • tls.crt: Server certificate (PEM)
  • tls.key: Server private key (PEM)

CA Bundle ConfigMap:

  • ca-bundle.crt: CA certificate bundle (PEM)
Environment Variables for Pod Identity
Variable Description
POD_NAME Used as leader election identity (falls back to hostname)
POD_NAMESPACE Namespace detection (falls back to ServiceAccount namespace file)

Prerequisites

The framework creates Secrets and ConfigMaps automatically. You need to create the WebhookConfiguration manually or via Helm/Kustomize.

Important: The MutatingWebhookConfiguration and/or ValidatingWebhookConfiguration must have the same name as Config.Name. The framework uses this name to find and patch the caBundle field automatically.

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: my-webhook  # Must match Config.Name
webhooks:
- name: my-webhook.default.svc
  clientConfig:
    service:
      name: my-webhook
      namespace: default
      path: /mutate-pods
      port: 443
    caBundle: ""  # auto-populated by the framework
  rules:
  - operations: ["CREATE"]
    apiGroups: [""]
    apiVersions: ["v1"]
    resources: ["pods"]
  sideEffects: None
  admissionReviewVersions: ["v1"]

Required RBAC

rules:
- apiGroups: [""]
  resources: ["secrets", "configmaps"]
  verbs: ["get", "list", "watch", "create", "update"]
- apiGroups: ["coordination.k8s.io"]
  resources: ["leases"]
  verbs: ["get", "create", "update"]
- apiGroups: ["admissionregistration.k8s.io"]
  resources: ["mutatingwebhookconfigurations", "validatingwebhookconfigurations"]
  verbs: ["get", "update", "patch"]
- apiGroups: [""]
  resources: ["events"]
  verbs: ["create", "patch"]

Environment Variables

All configuration options can be set via environment variables with the ACW_ prefix. Configuration priority: code > environment variables > defaults.

Variable Description Default
ACW_NAME Webhook name (required if not set in code) -
ACW_NAMESPACE Namespace for webhook resources Auto-detected
ACW_SERVICE_NAME Kubernetes service name <Name>
ACW_PORT Webhook server port 8443
ACW_METRICS_ENABLED Enable metrics server true
ACW_METRICS_PORT Metrics server port 8080
ACW_METRICS_PATH Metrics endpoint path /metrics
ACW_HEALTHZ_PATH Health check endpoint path /healthz
ACW_READYZ_PATH Readiness endpoint path /readyz
ACW_CA_SECRET_NAME CA certificate secret name <Name>-ca
ACW_CERT_SECRET_NAME Server certificate secret name <Name>-cert
ACW_CA_BUNDLE_CONFIGMAP_NAME CA bundle configmap name <Name>-ca-bundle
ACW_CA_VALIDITY CA certificate validity (e.g., 48h) 48h
ACW_CA_REFRESH CA certificate refresh interval 24h
ACW_CERT_VALIDITY Server certificate validity 24h
ACW_CERT_REFRESH Server certificate refresh interval 12h
ACW_LEADER_ELECTION Enable leader election true
ACW_LEADER_ELECTION_ID Leader election lease name <Name>-leader
ACW_LEASE_DURATION Leader election lease duration 30s
ACW_RENEW_DEADLINE Leader election renew deadline 10s
ACW_RETRY_PERIOD Leader election retry period 5s
POD_NAMESPACE Namespace (backward compatibility) Auto-detected
POD_NAME Pod identity for leader election hostname

The namespace is automatically detected from /var/run/secrets/kubernetes.io/serviceaccount/namespace (mounted by Kubernetes). You only need to set ACW_NAMESPACE or POD_NAMESPACE if running outside a Kubernetes cluster or without a ServiceAccount.

Metrics

The framework exposes Prometheus metrics on a separate HTTP port (default: 8080).

Metric Type Labels Description
admission_webhook_certificate_expiry_timestamp_seconds Gauge type Certificate expiry timestamp (unix seconds)
admission_webhook_certificate_not_before_timestamp_seconds Gauge type Certificate not-before timestamp (unix seconds)
admission_webhook_certificate_valid_duration_seconds Gauge type Total certificate validity duration (seconds)

Example Prometheus alert:

groups:
- name: webhook-certificates
  rules:
  - alert: WebhookCertificateExpiringSoon
    expr: admission_webhook_certificate_expiry_timestamp_seconds{type="serving"} - time() < 86400 * 7
    for: 1h
    labels:
      severity: warning
    annotations:
      summary: "Webhook certificate expiring in less than 7 days"

Examples

Complete working examples with deployment manifests and test scripts:

Example Type Description
pod-mutating Mutating Webhook Injects labels into pods automatically
pod-validating Validating Webhook Enforces pod policies (labels, image tags, resource limits)

Each example includes:

  • Complete Go implementation
  • Dockerfile for container builds
  • Makefile with docker-build-push, deploy, undeploy, and test targets
  • Kubernetes manifests (namespace, RBAC, deployment, service, webhook configuration)
  • Test script for validation

License

Apache-2.0

Documentation

Overview

Package autocertwebhook provides a lightweight framework for building Kubernetes admission webhooks with automatic TLS certificate management.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Allowed

func Allowed() *admissionv1.AdmissionResponse

Allowed returns an admission response that allows the request.

func AllowedWithMessage

func AllowedWithMessage(message string) *admissionv1.AdmissionResponse

AllowedWithMessage returns an admission response that allows the request with a message.

func Denied

func Denied(message string) *admissionv1.AdmissionResponse

Denied returns an admission response that denies the request.

func DeniedWithReason

func DeniedWithReason(message string, reason metav1.StatusReason, code int32) *admissionv1.AdmissionResponse

DeniedWithReason returns an admission response that denies the request with a specific reason.

func Errored

func Errored(err error) *admissionv1.AdmissionResponse

Errored returns an admission response for an error.

func ErroredWithCode

func ErroredWithCode(err error, code int32) *admissionv1.AdmissionResponse

ErroredWithCode returns an admission response for an error with a specific code.

func PatchResponse

func PatchResponse(original, modified interface{}) *admissionv1.AdmissionResponse

PatchResponse creates a patch response from the original and modified objects.

func PatchResponseFromPatches

func PatchResponseFromPatches(patches []jsonpatch.JsonPatchOperation) *admissionv1.AdmissionResponse

PatchResponseFromPatches creates a patch response from pre-built patches.

func PatchResponseFromRaw

func PatchResponseFromRaw(original, modified []byte) *admissionv1.AdmissionResponse

PatchResponseFromRaw creates a patch response from raw JSON bytes.

func Run

func Run(admission Admission) error

Run starts the webhook server with the given Admission implementation. This is the main entry point for using this library.

func RunWithContext

func RunWithContext(ctx context.Context, admission Admission) error

RunWithContext starts the webhook server with the given context.

Types

type Admission

type Admission interface {
	// Configure returns the server-level configuration.
	Configure() Config

	// Webhooks returns all webhook definitions.
	Webhooks() []Hook
}

Admission is the main interface that users need to implement.

type AdmitFunc

AdmitFunc is the function signature for handling admission requests.

type Config

type Config struct {
	// Name is the webhook name, used for generating certificate resources.
	// This will be used as prefix for Secret, ConfigMap, and Lease names.
	// IMPORTANT: The MutatingWebhookConfiguration and ValidatingWebhookConfiguration
	// must use this same name for automatic caBundle patching to work.
	// Required. Env: ACW_NAME
	Name string `envconfig:"NAME"`

	// Namespace is the namespace where the webhook is deployed.
	// If empty, auto-detected from ServiceAccount or defaults to "default".
	// Env: ACW_NAMESPACE
	Namespace string `envconfig:"NAMESPACE"`

	// ServiceName is the name of the Kubernetes service for the webhook.
	// If empty, defaults to Name.
	// Env: ACW_SERVICE_NAME
	ServiceName string `envconfig:"SERVICE_NAME"`

	// Port is the port the webhook server listens on.
	// Env: ACW_PORT
	Port int `envconfig:"PORT" default:"8443"`

	// MetricsEnabled enables the metrics server.
	// Env: ACW_METRICS_ENABLED
	MetricsEnabled *bool `envconfig:"METRICS_ENABLED" default:"true"`

	// MetricsPort is the port for the metrics server.
	// Env: ACW_METRICS_PORT
	MetricsPort int `envconfig:"METRICS_PORT" default:"8080"`

	// MetricsPath is the path for metrics endpoint.
	// Env: ACW_METRICS_PATH
	MetricsPath string `envconfig:"METRICS_PATH" default:"/metrics"`

	// HealthzPath is the path for health check endpoint.
	// Env: ACW_HEALTHZ_PATH
	HealthzPath string `envconfig:"HEALTHZ_PATH" default:"/healthz"`

	// ReadyzPath is the path for readiness check endpoint.
	// Env: ACW_READYZ_PATH
	ReadyzPath string `envconfig:"READYZ_PATH" default:"/readyz"`

	// CASecretName is the name of the secret containing the CA certificate.
	// If empty, defaults to "<Name>-ca".
	// Env: ACW_CA_SECRET_NAME
	CASecretName string `envconfig:"CA_SECRET_NAME"`

	// CertSecretName is the name of the secret containing the server certificate.
	// If empty, defaults to "<Name>-cert".
	// Env: ACW_CERT_SECRET_NAME
	CertSecretName string `envconfig:"CERT_SECRET_NAME"`

	// CABundleConfigMapName is the name of the configmap containing the CA bundle.
	// If empty, defaults to "<Name>-ca-bundle".
	// Env: ACW_CA_BUNDLE_CONFIGMAP_NAME
	CABundleConfigMapName string `envconfig:"CA_BUNDLE_CONFIGMAP_NAME"`

	// CAValidity is the validity duration of the CA certificate.
	// Env: ACW_CA_VALIDITY (e.g., "48h")
	CAValidity time.Duration `envconfig:"CA_VALIDITY" default:"48h"`

	// CARefresh is the refresh interval for the CA certificate.
	// Env: ACW_CA_REFRESH (e.g., "24h")
	CARefresh time.Duration `envconfig:"CA_REFRESH" default:"24h"`

	// CertValidity is the validity duration of the server certificate.
	// Env: ACW_CERT_VALIDITY (e.g., "24h")
	CertValidity time.Duration `envconfig:"CERT_VALIDITY" default:"24h"`

	// CertRefresh is the refresh interval for the server certificate.
	// Env: ACW_CERT_REFRESH (e.g., "12h")
	CertRefresh time.Duration `envconfig:"CERT_REFRESH" default:"12h"`

	// CertSyncInterval is the interval between certificate sync checks.
	// Env: ACW_CERT_SYNC_INTERVAL (e.g., "1m")
	CertSyncInterval time.Duration `envconfig:"CERT_SYNC_INTERVAL" default:"1m"`

	// LeaderElection enables leader election for certificate rotation.
	// Env: ACW_LEADER_ELECTION
	LeaderElection *bool `envconfig:"LEADER_ELECTION"`

	// LeaderElectionID is the name of the lease resource for leader election.
	// If empty, defaults to "<Name>-leader".
	// Env: ACW_LEADER_ELECTION_ID
	LeaderElectionID string `envconfig:"LEADER_ELECTION_ID"`

	// LeaseDuration is the duration of the leader election lease.
	// Env: ACW_LEASE_DURATION (e.g., "30s")
	LeaseDuration time.Duration `envconfig:"LEASE_DURATION" default:"30s"`

	// RenewDeadline is the deadline for renewing the leader election lease.
	// Env: ACW_RENEW_DEADLINE (e.g., "10s")
	RenewDeadline time.Duration `envconfig:"RENEW_DEADLINE" default:"10s"`

	// RetryPeriod is the period between leader election retries.
	// Env: ACW_RETRY_PERIOD (e.g., "5s")
	RetryPeriod time.Duration `envconfig:"RETRY_PERIOD" default:"5s"`
}

Config contains all configuration for the webhook server. Configuration priority: code > environment variables > defaults. All environment variables use the "ACW_" prefix.

IMPORTANT: The framework expects the MutatingWebhookConfiguration and/or ValidatingWebhookConfiguration resources to have the same name as Config.Name. The framework will automatically patch the caBundle field of these resources.

type Hook

type Hook struct {
	// Path is the URL path for this webhook, e.g., "/mutate-pods".
	Path string

	// Type is the webhook type: Mutating or Validating.
	Type HookType

	// Admit handles the admission request.
	Admit AdmitFunc
}

Hook defines a single admission webhook endpoint.

type HookType

type HookType string

HookType defines the type of admission webhook.

const (
	// Mutating indicates a mutating admission webhook.
	Mutating HookType = "Mutating"
	// Validating indicates a validating admission webhook.
	Validating HookType = "Validating"
)

Directories

Path Synopsis
examples
pod-mutating command
Package main demonstrates a mutating admission webhook that injects labels into pods.
Package main demonstrates a mutating admission webhook that injects labels into pods.
pod-validating command
Package main demonstrates a validating admission webhook that enforces pod policies.
Package main demonstrates a validating admission webhook that enforces pod policies.
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL