@marcel91200 / caddy Mirror · read-only / commits / c1c0ee5d
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

httpcaddyfile: reject conflicting client_auth when merging TLS policies (#8306)

* httpcaddyfile: reject conflicting client_auth when merging TLS policies * httpcaddyfile: use host and host:port site addresses in client auth conflict test * httpcaddyfile: enhance client auth consolidation test to handle conflicting configurations * httpcaddyfile: remove unnecessary blank line in conflict error handling

sam-kota committed Nov 4, 2026 at 12:09 UTC c1c0ee5dadc902b298c81036141cd8d15b0f85e3
2 files changed +61 -2
caddyconfig/httpcaddyfile/httptype.go
+1 -2
@@ -1288,8 +1288,7 @@ func consolidateConnPolicies(cps caddytls.ConnectionPolicies) (caddytls.Connecti
1288 return nil, fmt.Errorf("two policies with same match criteria have conflicting cipher suites: %v vs. %v",
1289 cps[i].CipherSuites, cps[j].CipherSuites)
1290 }
1291 - if cps[i].ClientAuthentication == nil &&
1292 - cps[j].ClientAuthentication != nil &&
1291 + if cps[i].ClientAuthentication != nil && cps[j].ClientAuthentication != nil &&
1292 !reflect.DeepEqual(cps[i].ClientAuthentication, cps[j].ClientAuthentication) {
1293 return nil, fmt.Errorf("two policies with same match criteria have conflicting client auth configuration: %+v vs. %+v",
1294 cps[i].ClientAuthentication, cps[j].ClientAuthentication)
caddyconfig/httpcaddyfile/httptype_test.go
+60
@@ -2,6 +2,7 @@ package httpcaddyfile
2
3 import (
4 "encoding/json"
5 + "fmt"
6 "slices"
7 "strings"
8 "testing"
@@ -403,3 +404,62 @@ func TestTLSConnectionPoliciesAreIsolatedAcrossListeners(t *testing.T) {
404 }
405 }
406 }
407 +
408 +func TestClientAuthConsolidation(t *testing.T) {
409 + const requestAuth = `client_auth {
410 + mode request
411 + }`
412 + const verifyAuth = `client_auth {
413 + mode require_and_verify
414 + trust_pool file /tmp/root.crt
415 + }`
416 +
417 + tlsBlock := func(auth string) string {
418 + if auth == "" {
419 + return ""
420 + }
421 + return fmt.Sprintf("tls {\n\t\t\t%s\n\t\t}", auth)
422 + }
423 +
424 + for _, tc := range []struct {
425 + name string
426 + firstAuth string
427 + secondAuth string
428 + wantErr bool
429 + }{
430 + {"conflicting, laxer policy first", requestAuth, verifyAuth, true},
431 + {"conflicting, stricter policy first", verifyAuth, requestAuth, true},
432 + {"only first sets client auth", verifyAuth, "", false},
433 + {"only second sets client auth", "", verifyAuth, false},
434 + {"identical client auth", verifyAuth, verifyAuth, false},
435 + } {
436 + t.Run(tc.name, func(t *testing.T) {
437 + caddyfileStr := fmt.Sprintf(`example.com {
438 + %s
439 + handle /public/* {
440 + respond "public"
441 + }
442 + }
443 +
444 + example.com:443 {
445 + %s
446 + handle /admin* {
447 + respond "admin"
448 + }
449 + }`, tlsBlock(tc.firstAuth), tlsBlock(tc.secondAuth))
450 +
451 + adapter := caddyfile.Adapter{ServerType: ServerType{}}
452 + result, _, err := adapter.Adapt([]byte(caddyfileStr), nil)
453 + if tc.wantErr {
454 + if err == nil {
455 + t.Fatalf("expected conflicting client auth to be rejected, got: %s", result)
456 + }
457 + if !strings.Contains(err.Error(), "conflicting client auth configuration") {
458 + t.Fatalf("expected conflicting client auth error, got: %v", err)
459 + }
460 + } else if err != nil {
461 + t.Fatalf("expected adapt to succeed, got: %v", err)
462 + }
463 + })
464 + }
465 +}