knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/webhook/webhook_test.go (about) 1 /* 2 Copyright 2019 The Knative Authors 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 webhook 18 19 import ( 20 "context" 21 "crypto/tls" 22 "net" 23 "testing" 24 "time" 25 26 "golang.org/x/sync/errgroup" 27 28 // Make system.Namespace() work in tests. 29 _ "knative.dev/pkg/system/testing" 30 31 . "knative.dev/pkg/reconciler/testing" 32 ) 33 34 func newDefaultOptions() Options { 35 return Options{ 36 ServiceName: "webhook", 37 Port: 8443, 38 SecretName: "webhook-certs", 39 } 40 } 41 42 const ( 43 testResourceName = "test-resource" 44 user1 = "brutto@knative.dev" 45 ) 46 47 func newNonRunningTestWebhook(t *testing.T, options Options, acs ...interface{}) ( 48 ctx context.Context, ac *Webhook, cancel context.CancelFunc, 49 ) { 50 t.Helper() 51 52 // override the grace period so it drains quickly 53 options.GracePeriod = 100 * time.Millisecond 54 55 // Create fake clients 56 ctx, ctxCancel, informers := SetupFakeContextWithCancel(t) 57 ctx = WithOptions(ctx, options) 58 59 stopCb, err := RunAndSyncInformers(ctx, informers...) 60 if err != nil { 61 t.Fatal("StartInformers() =", err) 62 } 63 cancel = func() { 64 ctxCancel() 65 stopCb() 66 } 67 68 ac, err = New(ctx, acs) 69 if err != nil { 70 t.Fatal("Failed to create new admission controller:", err) 71 } 72 return ctx, ac, cancel 73 } 74 75 func TestRegistrationStopChanFire(t *testing.T) { 76 test := testSetup(t, withNoTLS()) 77 defer test.cancel() 78 79 stopCh := make(chan struct{}) 80 81 var g errgroup.Group 82 g.Go(func() error { 83 return test.webhook.Run(stopCh) 84 }) 85 close(stopCh) 86 87 if err := g.Wait(); err != nil { 88 t.Fatal("Error during run: ", err) 89 } 90 conn, err := net.Dial("tcp", test.addr) 91 if err == nil { 92 conn.Close() 93 t.Error("Unexpected success to dial to ", test.addr) 94 } 95 } 96 97 func newAdmissionControllerWebhook(t *testing.T, options Options, acs ...interface{}) (*Webhook, error) { 98 ctx, cancel, _ := SetupFakeContextWithCancel(t) 99 defer cancel() 100 ctx = WithOptions(ctx, options) 101 return New(ctx, acs) 102 } 103 104 func TestTLSMinVersionWebhookOption(t *testing.T) { 105 opts := newDefaultOptions() 106 t.Run("when TLSMinVersion is not configured, default is TLS 1.3", func(t *testing.T) { 107 wh, err := newAdmissionControllerWebhook(t, opts) 108 if err != nil { 109 t.Fatal("Unexpected error", err) 110 } 111 if wh.tlsConfig == nil { 112 t.Fatal("Expected tlsConfig to be set") 113 } 114 if wh.tlsConfig.MinVersion != tls.VersionTLS13 { 115 t.Errorf("Expected default MinVersion to be TLS 1.3 (%#x), got %#x", tls.VersionTLS13, wh.tlsConfig.MinVersion) 116 } 117 }) 118 t.Run("when the TLS minimum version configured is supported", func(t *testing.T) { 119 opts.TLSMinVersion = tls.VersionTLS12 120 _, err := newAdmissionControllerWebhook(t, opts) 121 if err != nil { 122 t.Fatal("Unexpected error", err) 123 } 124 }) 125 t.Run("when the TLS minimum version configured is not supported", func(t *testing.T) { 126 opts.TLSMinVersion = tls.VersionTLS11 127 _, err := newAdmissionControllerWebhook(t, opts) 128 if err == nil { 129 t.Fatal("Admission Controller Webhook creation expected to fail due to unsupported TLS version") 130 } 131 }) 132 } 133 134 func TestTLSMaxVersionWebhookOption(t *testing.T) { 135 opts := newDefaultOptions() 136 t.Run("when TLSMaxVersion is not configured, default is used", func(t *testing.T) { 137 wh, err := newAdmissionControllerWebhook(t, opts) 138 if err != nil { 139 t.Fatal("Unexpected error", err) 140 } 141 142 if wh.tlsConfig != nil && wh.tlsConfig.MaxVersion != 0 { 143 t.Errorf("Expected MaxVersion to be 0 (default), got %d", wh.tlsConfig.MaxVersion) 144 } 145 }) 146 t.Run("when TLSMaxVersion is configured to TLS 1.3", func(t *testing.T) { 147 opts.TLSMaxVersion = tls.VersionTLS13 148 wh, err := newAdmissionControllerWebhook(t, opts) 149 if err != nil { 150 t.Fatal("Unexpected error", err) 151 } 152 if wh.tlsConfig == nil { 153 t.Fatal("Expected tlsConfig to be set") 154 } 155 if wh.tlsConfig.MaxVersion != tls.VersionTLS13 { 156 t.Errorf("Expected MaxVersion to be TLS 1.3, got %d", wh.tlsConfig.MaxVersion) 157 } 158 }) 159 t.Run("when both TLSMinVersion and TLSMaxVersion are TLS 1.3 (Modern profile)", func(t *testing.T) { 160 opts.TLSMinVersion = tls.VersionTLS13 161 opts.TLSMaxVersion = tls.VersionTLS13 162 wh, err := newAdmissionControllerWebhook(t, opts) 163 if err != nil { 164 t.Fatal("Unexpected error", err) 165 } 166 if wh.tlsConfig == nil { 167 t.Fatal("Expected tlsConfig to be set") 168 } 169 if wh.tlsConfig.MinVersion != tls.VersionTLS13 { 170 t.Errorf("Expected MinVersion to be TLS 1.3, got %d", wh.tlsConfig.MinVersion) 171 } 172 if wh.tlsConfig.MaxVersion != tls.VersionTLS13 { 173 t.Errorf("Expected MaxVersion to be TLS 1.3, got %d", wh.tlsConfig.MaxVersion) 174 } 175 }) 176 } 177 178 func TestTLSMinMaxVersionValidation(t *testing.T) { 179 t.Run("max version less than min version returns error", func(t *testing.T) { 180 opts := newDefaultOptions() 181 opts.TLSMinVersion = tls.VersionTLS13 182 opts.TLSMaxVersion = tls.VersionTLS12 183 _, err := newAdmissionControllerWebhook(t, opts) 184 if err == nil { 185 t.Fatal("Expected error when TLS max version is less than min version") 186 } 187 }) 188 t.Run("max version equal to min version is ok", func(t *testing.T) { 189 opts := newDefaultOptions() 190 opts.TLSMinVersion = tls.VersionTLS13 191 opts.TLSMaxVersion = tls.VersionTLS13 192 _, err := newAdmissionControllerWebhook(t, opts) 193 if err != nil { 194 t.Fatal("Unexpected error:", err) 195 } 196 }) 197 t.Run("max version zero skips validation", func(t *testing.T) { 198 opts := newDefaultOptions() 199 opts.TLSMinVersion = tls.VersionTLS13 200 opts.TLSMaxVersion = 0 201 _, err := newAdmissionControllerWebhook(t, opts) 202 if err != nil { 203 t.Fatal("Unexpected error:", err) 204 } 205 }) 206 } 207 208 func TestTLSCipherSuitesWebhookOption(t *testing.T) { 209 opts := newDefaultOptions() 210 t.Run("when TLSCipherSuites is not configured", func(t *testing.T) { 211 wh, err := newAdmissionControllerWebhook(t, opts) 212 if err != nil { 213 t.Fatal("Unexpected error", err) 214 } 215 216 if wh.tlsConfig != nil && wh.tlsConfig.CipherSuites != nil { 217 t.Errorf("Expected CipherSuites to be nil (default), got %v", wh.tlsConfig.CipherSuites) 218 } 219 }) 220 t.Run("when TLSCipherSuites is configured with specific ciphers", func(t *testing.T) { 221 expectedCiphers := []uint16{ 222 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 223 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 224 } 225 opts.TLSCipherSuites = expectedCiphers 226 wh, err := newAdmissionControllerWebhook(t, opts) 227 if err != nil { 228 t.Fatal("Unexpected error", err) 229 } 230 if wh.tlsConfig == nil { 231 t.Fatal("Expected tlsConfig to be set") 232 } 233 if len(wh.tlsConfig.CipherSuites) != len(expectedCiphers) { 234 t.Errorf("Expected %d cipher suites, got %d", len(expectedCiphers), len(wh.tlsConfig.CipherSuites)) 235 } 236 for i, cipher := range expectedCiphers { 237 if wh.tlsConfig.CipherSuites[i] != cipher { 238 t.Errorf("Expected cipher suite at index %d to be %d, got %d", i, cipher, wh.tlsConfig.CipherSuites[i]) 239 } 240 } 241 }) 242 } 243 244 func TestTLSCurvePreferencesWebhookOption(t *testing.T) { 245 opts := newDefaultOptions() 246 t.Run("when TLSCurvePreferences is not configured", func(t *testing.T) { 247 wh, err := newAdmissionControllerWebhook(t, opts) 248 if err != nil { 249 t.Fatal("Unexpected error", err) 250 } 251 252 if wh.tlsConfig != nil && wh.tlsConfig.CurvePreferences != nil { 253 t.Errorf("Expected CurvePreferences to be nil (default), got %v", wh.tlsConfig.CurvePreferences) 254 } 255 }) 256 t.Run("when TLSCurvePreferences is configured with specific curves", func(t *testing.T) { 257 expectedCurves := []tls.CurveID{ 258 tls.CurveP256, 259 tls.CurveP384, 260 tls.X25519, 261 } 262 opts.TLSCurvePreferences = expectedCurves 263 wh, err := newAdmissionControllerWebhook(t, opts) 264 if err != nil { 265 t.Fatal("Unexpected error", err) 266 } 267 if wh.tlsConfig == nil { 268 t.Fatal("Expected tlsConfig to be set") 269 } 270 if len(wh.tlsConfig.CurvePreferences) != len(expectedCurves) { 271 t.Errorf("Expected %d curve preferences, got %d", len(expectedCurves), len(wh.tlsConfig.CurvePreferences)) 272 } 273 for i, curve := range expectedCurves { 274 if wh.tlsConfig.CurvePreferences[i] != curve { 275 t.Errorf("Expected curve at index %d to be %d, got %d", i, curve, wh.tlsConfig.CurvePreferences[i]) 276 } 277 } 278 }) 279 } 280 281 func TestTLSConfigFromEnvironment(t *testing.T) { 282 t.Run("env min version used when opts min version is zero", func(t *testing.T) { 283 t.Setenv("WEBHOOK_TLS_MIN_VERSION", "1.2") 284 opts := newDefaultOptions() 285 wh, err := newAdmissionControllerWebhook(t, opts) 286 if err != nil { 287 t.Fatal("Unexpected error", err) 288 } 289 if wh.tlsConfig == nil { 290 t.Fatal("Expected tlsConfig to be set") 291 } 292 if wh.tlsConfig.MinVersion != tls.VersionTLS12 { 293 t.Errorf("Expected MinVersion from env to be TLS 1.2, got %d", wh.tlsConfig.MinVersion) 294 } 295 }) 296 297 t.Run("opts min version takes precedence over env", func(t *testing.T) { 298 t.Setenv("WEBHOOK_TLS_MIN_VERSION", "1.2") 299 opts := newDefaultOptions() 300 opts.TLSMinVersion = tls.VersionTLS13 301 wh, err := newAdmissionControllerWebhook(t, opts) 302 if err != nil { 303 t.Fatal("Unexpected error", err) 304 } 305 if wh.tlsConfig == nil { 306 t.Fatal("Expected tlsConfig to be set") 307 } 308 if wh.tlsConfig.MinVersion != tls.VersionTLS13 { 309 t.Errorf("Expected MinVersion from opts (TLS 1.3), got %d", wh.tlsConfig.MinVersion) 310 } 311 }) 312 313 t.Run("env max version used when opts max version is zero", func(t *testing.T) { 314 t.Setenv("WEBHOOK_TLS_MAX_VERSION", "1.3") 315 opts := newDefaultOptions() 316 wh, err := newAdmissionControllerWebhook(t, opts) 317 if err != nil { 318 t.Fatal("Unexpected error", err) 319 } 320 if wh.tlsConfig == nil { 321 t.Fatal("Expected tlsConfig to be set") 322 } 323 if wh.tlsConfig.MaxVersion != tls.VersionTLS13 { 324 t.Errorf("Expected MaxVersion from env to be TLS 1.3, got %d", wh.tlsConfig.MaxVersion) 325 } 326 }) 327 328 t.Run("opts max version takes precedence over env", func(t *testing.T) { 329 t.Setenv("WEBHOOK_TLS_MAX_VERSION", "1.2") 330 opts := newDefaultOptions() 331 opts.TLSMaxVersion = tls.VersionTLS13 332 wh, err := newAdmissionControllerWebhook(t, opts) 333 if err != nil { 334 t.Fatal("Unexpected error", err) 335 } 336 if wh.tlsConfig == nil { 337 t.Fatal("Expected tlsConfig to be set") 338 } 339 if wh.tlsConfig.MaxVersion != tls.VersionTLS13 { 340 t.Errorf("Expected MaxVersion from opts (TLS 1.3), got %d", wh.tlsConfig.MaxVersion) 341 } 342 }) 343 344 t.Run("env cipher suites used when opts cipher suites is nil", func(t *testing.T) { 345 t.Setenv("WEBHOOK_TLS_CIPHER_SUITES", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256") 346 opts := newDefaultOptions() 347 wh, err := newAdmissionControllerWebhook(t, opts) 348 if err != nil { 349 t.Fatal("Unexpected error", err) 350 } 351 if wh.tlsConfig == nil { 352 t.Fatal("Expected tlsConfig to be set") 353 } 354 if len(wh.tlsConfig.CipherSuites) != 1 { 355 t.Fatalf("Expected 1 cipher suite from env, got %d", len(wh.tlsConfig.CipherSuites)) 356 } 357 if wh.tlsConfig.CipherSuites[0] != tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 { 358 t.Errorf("Expected CipherSuites from env, got %v", wh.tlsConfig.CipherSuites) 359 } 360 }) 361 362 t.Run("opts cipher suites take precedence over env", func(t *testing.T) { 363 t.Setenv("WEBHOOK_TLS_CIPHER_SUITES", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256") 364 opts := newDefaultOptions() 365 opts.TLSCipherSuites = []uint16{tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384} 366 wh, err := newAdmissionControllerWebhook(t, opts) 367 if err != nil { 368 t.Fatal("Unexpected error", err) 369 } 370 if wh.tlsConfig == nil { 371 t.Fatal("Expected tlsConfig to be set") 372 } 373 if len(wh.tlsConfig.CipherSuites) != 1 { 374 t.Fatalf("Expected 1 cipher suite from opts, got %d", len(wh.tlsConfig.CipherSuites)) 375 } 376 if wh.tlsConfig.CipherSuites[0] != tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 { 377 t.Errorf("Expected CipherSuites from opts, got %v", wh.tlsConfig.CipherSuites) 378 } 379 }) 380 381 t.Run("env curve preferences used when opts curve preferences is nil", func(t *testing.T) { 382 t.Setenv("WEBHOOK_TLS_CURVE_PREFERENCES", "X25519,CurveP256") 383 opts := newDefaultOptions() 384 wh, err := newAdmissionControllerWebhook(t, opts) 385 if err != nil { 386 t.Fatal("Unexpected error", err) 387 } 388 if wh.tlsConfig == nil { 389 t.Fatal("Expected tlsConfig to be set") 390 } 391 if len(wh.tlsConfig.CurvePreferences) != 2 { 392 t.Fatalf("Expected 2 curve preferences from env, got %d", len(wh.tlsConfig.CurvePreferences)) 393 } 394 if wh.tlsConfig.CurvePreferences[0] != tls.X25519 { 395 t.Errorf("Expected CurvePreferences[0] = X25519, got %d", wh.tlsConfig.CurvePreferences[0]) 396 } 397 if wh.tlsConfig.CurvePreferences[1] != tls.CurveP256 { 398 t.Errorf("Expected CurvePreferences[1] = CurveP256, got %d", wh.tlsConfig.CurvePreferences[1]) 399 } 400 }) 401 402 t.Run("opts curve preferences take precedence over env", func(t *testing.T) { 403 t.Setenv("WEBHOOK_TLS_CURVE_PREFERENCES", "X25519") 404 opts := newDefaultOptions() 405 opts.TLSCurvePreferences = []tls.CurveID{tls.CurveP384} 406 wh, err := newAdmissionControllerWebhook(t, opts) 407 if err != nil { 408 t.Fatal("Unexpected error", err) 409 } 410 if wh.tlsConfig == nil { 411 t.Fatal("Expected tlsConfig to be set") 412 } 413 if len(wh.tlsConfig.CurvePreferences) != 1 { 414 t.Fatalf("Expected 1 curve preference from opts, got %d", len(wh.tlsConfig.CurvePreferences)) 415 } 416 if wh.tlsConfig.CurvePreferences[0] != tls.CurveP384 { 417 t.Errorf("Expected CurvePreferences from opts, got %v", wh.tlsConfig.CurvePreferences) 418 } 419 }) 420 421 t.Run("invalid env TLS config returns error", func(t *testing.T) { 422 t.Setenv("WEBHOOK_TLS_MIN_VERSION", "bad") 423 opts := newDefaultOptions() 424 _, err := newAdmissionControllerWebhook(t, opts) 425 if err == nil { 426 t.Fatal("Expected error for invalid env TLS min version") 427 } 428 }) 429 } 430 431 func TestTLSConfigCombinedOptions(t *testing.T) { 432 opts := newDefaultOptions() 433 t.Run("when all TLS options are configured together", func(t *testing.T) { 434 opts.TLSMinVersion = tls.VersionTLS12 435 opts.TLSMaxVersion = tls.VersionTLS13 436 opts.TLSCipherSuites = []uint16{ 437 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 438 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 439 } 440 opts.TLSCurvePreferences = []tls.CurveID{ 441 tls.CurveP256, 442 tls.X25519, 443 } 444 445 wh, err := newAdmissionControllerWebhook(t, opts) 446 if err != nil { 447 t.Fatal("Unexpected error", err) 448 } 449 450 if wh.tlsConfig == nil { 451 t.Fatal("Expected tlsConfig to be set") 452 } 453 454 if wh.tlsConfig.MinVersion != tls.VersionTLS12 { 455 t.Errorf("Expected MinVersion to be TLS 1.2, got %d", wh.tlsConfig.MinVersion) 456 } 457 if wh.tlsConfig.MaxVersion != tls.VersionTLS13 { 458 t.Errorf("Expected MaxVersion to be TLS 1.3, got %d", wh.tlsConfig.MaxVersion) 459 } 460 if len(wh.tlsConfig.CipherSuites) != 2 { 461 t.Errorf("Expected 2 cipher suites, got %d", len(wh.tlsConfig.CipherSuites)) 462 } 463 if len(wh.tlsConfig.CurvePreferences) != 2 { 464 t.Errorf("Expected 2 curve preferences, got %d", len(wh.tlsConfig.CurvePreferences)) 465 } 466 }) 467 }