knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/webhook/README.md (about)

     1  ## Knative Webhooks
     2  
     3  Knative provides infrastructure for authoring webhooks under
     4  `knative.dev/pkg/webhook` and has a few built-in helpers for certain common
     5  admission control scenarios. The built-in admission controllers are:
     6  
     7  1. Resource validation and defaulting (builds around `apis.Validatable` and
     8     `apis.Defaultable` under `knative.dev/pkg/apis`).
     9  2. ConfigMap validation, which builds around similar patterns from
    10     `knative.dev/pkg/configmap` (in particular the `store` concept)
    11  
    12  To illustrate standing up the webhook, let's start with one of these built-in
    13  admission controllers and then talk about how you can write your own admission
    14  controller.
    15  
    16  ## Standing up a Webhook from an Admission Controller
    17  
    18  We provide facilities in `knative.dev/pkg/injection/sharedmain` to try and
    19  eliminate much of the boilerplate involved in standing up a webhook. For this
    20  example we will show how to stand up the webhook using the built-in admission
    21  controller for validating and defaulting resources.
    22  
    23  The code to stand up such a webhook looks roughly like this:
    24  
    25  ```go
    26  // Create a function matching this signature to pass into sharedmain.
    27  func NewResourceAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
    28  	return validation.NewAdmissionController(ctx,
    29  		// Name of the resource webhook (created via yaml)
    30  		fmt.Sprintf("resources.webhook.%s.knative.dev", system.Namespace()),
    31  
    32  		// The path on which to serve the webhook.
    33  		"/resource-validation",
    34  
    35  		// The resources to validate and default.
    36  		map[schema.GroupVersionKind]resourcesemantics.GenericCRD{
    37  			// List the types to validate, this from knative.dev/sample-controller
    38  			v1alpha1.SchemeGroupVersion.WithKind("AddressableService"): &v1alpha1.AddressableService{},
    39  		},
    40  
    41  		// A function that infuses the context passed to Validate/SetDefaults with custom metadata.
    42  		func(ctx context.Context) context.Context {
    43  			// Here is where you would infuse the context with state
    44  			// (e.g. attach a store with configmap data, like knative.dev/serving attaches config-defaults)
    45  			return ctx
    46  		},
    47  
    48  		// Whether to disallow unknown fields when parsing the resources' JSON.
    49  		true,
    50  	)
    51  }
    52  
    53  func main() {
    54  	// Set up a signal context with our webhook options.
    55  	ctx := webhook.WithOptions(signals.NewContext(), webhook.Options{
    56  		// The name of the Kubernetes service selecting over this deployment's pods.
    57  		ServiceName: "webhook",
    58  
    59  		// The port on which to serve.
    60  		Port:        8443,
    61  
    62  		// The name of the secret containing certificate data.
    63  		SecretName:  "webhook-certs",
    64  	})
    65  
    66  	sharedmain.MainWithContext(ctx, "webhook",
    67  		// The certificate controller will ensure that the named secret (above) has
    68  		// the appropriate shape for our webhook's admission controllers.
    69  		certificates.NewController,
    70  
    71  		// This invokes the method defined above to instantiate the resource admission
    72  		// controller.
    73  		NewResourceAdmissionController,
    74  	)
    75  }
    76  ```
    77  
    78  There is also a config map validation admission controller built in under
    79  `knative.dev/pkg/webhook/configmaps`.
    80  
    81  ## TLS Configuration
    82  
    83  The webhook server supports configuring TLS parameters through the `webhook.Options` struct. This allows you to control the TLS version, cipher suites, and elliptic curve preferences for enhanced security.
    84  
    85  ### Available TLS Options
    86  
    87  ```go
    88  type Options struct {
    89      // ... other fields ...
    90  
    91      // TLSMinVersion contains the minimum TLS version that is acceptable.
    92      // Default is TLS 1.3 if not specified.
    93      // Supported values: tls.VersionTLS12, tls.VersionTLS13
    94      TLSMinVersion uint16
    95  
    96      // TLSMaxVersion contains the maximum TLS version that is acceptable.
    97      // If not set (0), the maximum version supported by the implementation will be used.
    98      // Useful for enforcing Modern profile (TLS 1.3 only) by setting both
    99      // TLSMinVersion and TLSMaxVersion to tls.VersionTLS13.
   100      TLSMaxVersion uint16
   101  
   102      // TLSCipherSuites specifies the list of enabled cipher suites.
   103      // If empty, a default list of secure cipher suites will be used.
   104      // Note: Cipher suites are not configurable in TLS 1.3; they are
   105      // determined by the implementation.
   106      TLSCipherSuites []uint16
   107  
   108      // TLSCurvePreferences specifies the elliptic curves that will be used
   109      // in an ECDHE handshake. If empty, the default curves will be used.
   110      TLSCurvePreferences []tls.CurveID
   111  }
   112  ```
   113  
   114  ### Environment Variable Configuration
   115  
   116  You can also configure the minimum TLS version via the `WEBHOOK_TLS_MIN_VERSION` environment variable:
   117  
   118  ```yaml
   119  env:
   120    - name: WEBHOOK_TLS_MIN_VERSION
   121      value: "1.3"  # or "1.2"
   122  ```
   123  
   124  ### Usage Examples
   125  
   126  #### Example 1: Default Configuration (Recommended)
   127  
   128  By default, the webhook uses TLS 1.3 as the minimum version with secure defaults:
   129  
   130  ```go
   131  ctx := webhook.WithOptions(signals.NewContext(), webhook.Options{
   132      ServiceName: "webhook",
   133      Port:        8443,
   134      SecretName:  "webhook-certs",
   135      // TLS defaults: MinVersion=1.3, secure cipher suites and curves
   136  })
   137  ```
   138  
   139  #### Example 2: Modern Profile (TLS 1.3 Only)
   140  
   141  To enforce TLS 1.3 only (highest security profile):
   142  
   143  ```go
   144  ctx := webhook.WithOptions(signals.NewContext(), webhook.Options{
   145      ServiceName:   "webhook",
   146      Port:          8443,
   147      SecretName:    "webhook-certs",
   148      TLSMinVersion: tls.VersionTLS13,
   149      TLSMaxVersion: tls.VersionTLS13,  // Enforce TLS 1.3 only
   150  })
   151  ```
   152  
   153  #### Example 3: Intermediate Profile (TLS 1.2+)
   154  
   155  For broader compatibility while maintaining security:
   156  
   157  ```go
   158  ctx := webhook.WithOptions(signals.NewContext(), webhook.Options{
   159      ServiceName:   "webhook",
   160      Port:          8443,
   161      SecretName:    "webhook-certs",
   162      TLSMinVersion: tls.VersionTLS12,
   163  })
   164  ```
   165  
   166  #### Example 4: Custom Cipher Suites
   167  
   168  To specify custom cipher suites (for TLS 1.2):
   169  
   170  ```go
   171  ctx := webhook.WithOptions(signals.NewContext(), webhook.Options{
   172      ServiceName:   "webhook",
   173      Port:          8443,
   174      SecretName:    "webhook-certs",
   175      TLSMinVersion: tls.VersionTLS12,
   176      TLSCipherSuites: []uint16{
   177          tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
   178          tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
   179          tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
   180          tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
   181      },
   182  })
   183  ```
   184  
   185  #### Example 5: Custom Elliptic Curves
   186  
   187  To specify elliptic curve preferences:
   188  
   189  ```go
   190  ctx := webhook.WithOptions(signals.NewContext(), webhook.Options{
   191      ServiceName:   "webhook",
   192      Port:          8443,
   193      SecretName:    "webhook-certs",
   194      TLSCurvePreferences: []tls.CurveID{
   195          tls.X25519,    // Preferred
   196          tls.CurveP256,
   197          tls.CurveP384,
   198      },
   199  })
   200  ```
   201  
   202  #### Example 6: Complete Custom Configuration
   203  
   204  For full control over TLS parameters:
   205  
   206  ```go
   207  ctx := webhook.WithOptions(signals.NewContext(), webhook.Options{
   208      ServiceName:   "webhook",
   209      Port:          8443,
   210      SecretName:    "webhook-certs",
   211      TLSMinVersion: tls.VersionTLS12,
   212      TLSMaxVersion: tls.VersionTLS13,
   213      TLSCipherSuites: []uint16{
   214          tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
   215          tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
   216      },
   217      TLSCurvePreferences: []tls.CurveID{
   218          tls.X25519,
   219          tls.CurveP256,
   220      },
   221  })
   222  ```
   223  
   224  ## Writing new Admission Controllers
   225  
   226  To implement your own admission controller akin to the resource defaulting and
   227  validation controller above, you implement a
   228  `knative.dev/pkg/controller.Reconciler` as with any you would with any other
   229  type of controller, but the `Reconciler` that gets embedded in the
   230  `*controller.Impl` should _also_ implement:
   231  
   232  ```go
   233  // AdmissionController provides the interface for different admission controllers
   234  type AdmissionController interface {
   235  	// Path returns the path that this particular admission controller serves on.
   236  	Path() string
   237  
   238  	// Admit is the callback which is invoked when an HTTPS request comes in on Path().
   239  	Admit(context.Context, *admissionv1beta1.AdmissionRequest) *admissionv1beta1.AdmissionResponse
   240  }
   241  ```
   242  
   243  The `Reconciler` part is responsible for the mutating or validating webhook
   244  configuration. The `AdmissionController` part is responsible for guiding request
   245  dispatch (`Path()`) and handling admission requests (`Admit()`).