Skip to content

feat: set OverlappingTLSConfig condition for merged Gateways #5862

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 97 additions & 37 deletions internal/gatewayapi/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,30 +176,53 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR resource
foundPorts[irKey] = append(foundPorts[irKey], servicePort)
}
}

checkOverlappingTLSConfig(gateway)
}

t.checkOverlappingTLSConfig(gateways)
}

// checkOverlappingTLSConfig checks for overlapping hostnames and certificates between listeners and sets
// the `OverlappingTLSConfig` condition if there are overlapping hostnames or certificates.
func checkOverlappingTLSConfig(gateway *GatewayContext) {
// Note: order of processing matters here.
// According to the Gateway API spec, If both hostname and certificate overlap,
// the controller SHOULD set the "OverlappingCertificates" Reason.
checkOverlappingHostnames(gateway)
checkOverlappingCertificates(gateway)
}

func checkOverlappingHostnames(gateway *GatewayContext) {
httpsListeners := []*ListenerContext{}
for _, listener := range gateway.listeners {
if listener.Protocol == gwapiv1.HTTPSProtocolType {
httpsListeners = append(httpsListeners, listener)
func (t *Translator) checkOverlappingTLSConfig(gateways []*GatewayContext) {
// If merging gateways, check overlapping hostnames and certificates between listeners in all merged gateways.
if t.MergeGateways {
httpsListeners := []*ListenerContext{}
for _, gateway := range gateways {
for _, listener := range gateway.listeners {
if listener.Protocol == gwapiv1.HTTPSProtocolType {
httpsListeners = append(httpsListeners, listener)
}
}
}
// Note: order of processing matters here.
// According to the Gateway API spec, If both hostname and certificate overlap,
// the controller SHOULD set the "OverlappingCertificates" Reason.
checkOverlappingHostnames(httpsListeners)
checkOverlappingCertificates(httpsListeners)
} else {
// Check overlapping hostnames and certificates between listeners in each gateway.
for _, gateway := range gateways {
httpsListeners := []*ListenerContext{}
for _, listener := range gateway.listeners {
if listener.Protocol == gwapiv1.HTTPSProtocolType {
httpsListeners = append(httpsListeners, listener)
}
}
// Note: order of processing matters here.
// According to the Gateway API spec, If both hostname and certificate overlap,
// the controller SHOULD set the "OverlappingCertificates" Reason.
checkOverlappingHostnames(httpsListeners)
checkOverlappingCertificates(httpsListeners)
}
}
}

// checkOverlappingHostnames checks for overlapping hostnames between HTTPS listeners and sets
// the `OverlappingTLSConfig` condition if there are overlapping hostnames.
func checkOverlappingHostnames(httpsListeners []*ListenerContext) {
type overlappingListener struct {
gateway1 *GatewayContext
gateway2 *GatewayContext
listener1 string
listener2 string
hostname1 string
Expand All @@ -220,12 +243,16 @@ func checkOverlappingHostnames(gateway *GatewayContext) {
if isOverlappingHostname(httpsListeners[i].Hostname, httpsListeners[j].Hostname) {
// Overlapping listeners can be more than two, we only report the first two for simplicity.
overlappingListeners[i] = &overlappingListener{
gateway1: httpsListeners[i].gateway,
gateway2: httpsListeners[j].gateway,
listener1: string(httpsListeners[i].Name),
listener2: string(httpsListeners[j].Name),
hostname1: string(ptr.Deref(httpsListeners[i].Hostname, "")),
hostname2: string(ptr.Deref(httpsListeners[j].Hostname, "")),
}
overlappingListeners[j] = &overlappingListener{
gateway1: httpsListeners[j].gateway,
gateway2: httpsListeners[i].gateway,
listener1: string(httpsListeners[j].Name),
listener2: string(httpsListeners[i].Name),
hostname1: string(ptr.Deref(httpsListeners[j].Hostname, "")),
Expand All @@ -237,17 +264,33 @@ func checkOverlappingHostnames(gateway *GatewayContext) {

for i, listener := range httpsListeners {
if overlappingListeners[i] != nil {
status.SetGatewayListenerStatusCondition(gateway.Gateway,
listener.listenerStatusIdx,
gwapiv1.ListenerConditionOverlappingTLSConfig,
metav1.ConditionTrue,
gwapiv1.ListenerReasonOverlappingHostnames,
fmt.Sprintf(
var message string
gateway1 := overlappingListeners[i].gateway1
gateway2 := overlappingListeners[i].gateway2
if gateway1.Name == gateway2.Name &&
gateway1.Namespace == gateway2.Namespace {
message = fmt.Sprintf(
"The hostname %s overlaps with the hostname %s in listener %s. ALPN is set to HTTP/1.1 to prevent HTTP/2 connection coalescing",
overlappingListeners[i].hostname1,
overlappingListeners[i].hostname2,
overlappingListeners[i].listener2,
),
)
} else {
message = fmt.Sprintf(
"The hostname %s overlaps with the hostname %s in listener %s of gateway %s. ALPN is set to HTTP/1.1 to prevent HTTP/2 connection coalescing",
overlappingListeners[i].hostname1,
overlappingListeners[i].hostname2,
overlappingListeners[i].listener2,
gateway2.GetName(),
)
}

status.SetGatewayListenerStatusCondition(listener.gateway.Gateway,
listener.listenerStatusIdx,
gwapiv1.ListenerConditionOverlappingTLSConfig,
metav1.ConditionTrue,
gwapiv1.ListenerReasonOverlappingHostnames,
message,
)
if listener.httpIR != nil {
listener.httpIR.TLSOverlaps = true
Expand All @@ -256,15 +299,12 @@ func checkOverlappingHostnames(gateway *GatewayContext) {
}
}

func checkOverlappingCertificates(gateway *GatewayContext) {
httpsListeners := []*ListenerContext{}
for _, listener := range gateway.listeners {
if listener.Protocol == gwapiv1.HTTPSProtocolType {
httpsListeners = append(httpsListeners, listener)
}
}

// checkOverlappingCertificates checks for overlapping certificates SANs between HTTPSlisteners and sets
// the `OverlappingTLSConfig` condition if there are overlapping certificates.
func checkOverlappingCertificates(httpsListeners []*ListenerContext) {
type overlappingListener struct {
gateway1 *GatewayContext
gateway2 *GatewayContext
listener1 string
listener2 string
san1 string
Expand All @@ -288,12 +328,16 @@ func checkOverlappingCertificates(gateway *GatewayContext) {
if overlappingCertificate != nil {
// Overlapping listeners can be more than two, we only report the first two for simplicity.
overlappingListeners[i] = &overlappingListener{
gateway1: httpsListeners[i].gateway,
gateway2: httpsListeners[j].gateway,
listener1: string(httpsListeners[i].Name),
listener2: string(httpsListeners[j].Name),
san1: overlappingCertificate.san1,
san2: overlappingCertificate.san2,
}
overlappingListeners[j] = &overlappingListener{
gateway1: httpsListeners[j].gateway,
gateway2: httpsListeners[i].gateway,
listener1: string(httpsListeners[j].Name),
listener2: string(httpsListeners[i].Name),
san1: overlappingCertificate.san2,
Expand All @@ -305,17 +349,33 @@ func checkOverlappingCertificates(gateway *GatewayContext) {

for i, listener := range httpsListeners {
if overlappingListeners[i] != nil {
status.SetGatewayListenerStatusCondition(gateway.Gateway,
listener.listenerStatusIdx,
gwapiv1.ListenerConditionOverlappingTLSConfig,
metav1.ConditionTrue,
gwapiv1.ListenerReasonOverlappingCertificates,
fmt.Sprintf(
var message string
gateway1 := overlappingListeners[i].gateway1
gateway2 := overlappingListeners[i].gateway2
if gateway1.Name == gateway2.Name &&
gateway1.Namespace == gateway2.Namespace {
message = fmt.Sprintf(
"The certificate san %s overlaps with the certificate san %s in listener %s. ALPN is set to HTTP/1.1 to prevent HTTP/2 connection coalescing",
overlappingListeners[i].san1,
overlappingListeners[i].san2,
overlappingListeners[i].listener2,
),
)
} else {
message = fmt.Sprintf(
"The certificate san %s overlaps with the certificate san %s in listener %s of gateway %s. ALPN is set to HTTP/1.1 to prevent HTTP/2 connection coalescing",
overlappingListeners[i].san1,
overlappingListeners[i].san2,
overlappingListeners[i].listener2,
gateway2.GetName(),
)
}

status.SetGatewayListenerStatusCondition(listener.gateway.Gateway,
listener.listenerStatusIdx,
gwapiv1.ListenerConditionOverlappingTLSConfig,
metav1.ConditionTrue,
gwapiv1.ListenerReasonOverlappingCertificates,
message,
)
if listener.httpIR != nil {
listener.httpIR.TLSOverlaps = true
Expand Down
33 changes: 6 additions & 27 deletions internal/gatewayapi/listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,30 +306,6 @@ func TestCheckOverlappingHostnames(t *testing.T) {
2: "*.example.com",
},
},
{
name: "non-HTTPS listeners",
gateway: &GatewayContext{
listeners: []*ListenerContext{
{
Listener: &gwapiv1.Listener{
Name: "listener-1",
Protocol: gwapiv1.HTTPProtocolType,
Port: 80,
Hostname: ptr.To(gwapiv1.Hostname("example.com")),
},
},
{
Listener: &gwapiv1.Listener{
Name: "listener-2",
Protocol: gwapiv1.HTTPProtocolType,
Port: 80,
Hostname: ptr.To(gwapiv1.Hostname("example.com")),
},
},
},
},
expected: map[int]string{},
},
{
name: "nil hostnames",
gateway: &GatewayContext{
Expand Down Expand Up @@ -369,13 +345,14 @@ func TestCheckOverlappingHostnames(t *testing.T) {
}
for i := range tt.gateway.listeners {
tt.gateway.listeners[i].listenerStatusIdx = i
tt.gateway.listeners[i].gateway = tt.gateway
tt.gateway.Status.Listeners[i] = gwapiv1.ListenerStatus{
Name: tt.gateway.listeners[i].Name,
Conditions: []metav1.Condition{},
}
}

checkOverlappingHostnames(tt.gateway)
checkOverlappingHostnames(tt.gateway.listeners)

// Verify the status conditions
for idx, expectedHostname := range tt.expected {
Expand Down Expand Up @@ -603,16 +580,18 @@ func TestCheckOverlappingCertificates(t *testing.T) {
listeners: tt.listeners,
}

// Initialize listener status indices
// Initialize listener
for i := range gateway.Status.Listeners {
gateway.Status.Listeners[i] = gwapiv1.ListenerStatus{
Name: tt.listeners[i].Name,
Conditions: []metav1.Condition{},
}
gateway.listeners[i].listenerStatusIdx = i
gateway.listeners[i].gateway = gateway
}

// Process overlapping certificates
checkOverlappingCertificates(gateway)
checkOverlappingCertificates(tt.listeners)

// Verify the status conditions
for _, expected := range tt.expectedStatus {
Expand Down
Loading