@@ -17,54 +17,103 @@ limitations under the License.
1717package kgateway
1818
1919import (
20+ "fmt"
21+
2022 "github.com/kgateway-dev/ingress2gateway/pkg/i2gw/intermediate"
2123 "k8s.io/apimachinery/pkg/types"
2224 "k8s.io/utils/ptr"
2325 gwv1 "sigs.k8s.io/gateway-api/apis/v1"
2426)
2527
26- // applySSLRedirectPolicy applies SSL redirect by adding a RequestRedirect filter
27- // to HTTPRoute rules when SSLRedirect is enabled in the policy .
28+ // applySSLRedirectPolicy marks rules that need SSL redirect handling.
29+ // The actual route splitting happens later in the emitter .
2830//
2931// Semantics:
30- // - If SSLRedirect is enabled, add a RequestRedirect filter to rule-level Filters
31- // - The filter redirects HTTP to HTTPS with a 301 status code
32- // - The filter is applied to all rules covered by the policy
32+ // - If SSLRedirect is enabled, mark the HTTPRoute for later splitting
33+ // - Returns true if SSL redirect is enabled for this policy
3334func applySSLRedirectPolicy (
3435 pol intermediate.Policy ,
3536 httpRouteKey types.NamespacedName ,
3637 httpRouteContext * intermediate.HTTPRouteContext ,
3738 coverage []intermediate.PolicyIndex ,
38- ) {
39+ ) bool {
3940 if pol .SSLRedirect == nil || ! * pol .SSLRedirect {
40- return
41+ return false
42+ }
43+ // SSL redirect will be handled by splitting the route later
44+ // Don't modify the route here - preserve backendRefs for the HTTPS route
45+ return true
46+ }
47+
48+ // splitHTTPRouteForSSLRedirect splits an HTTPRoute into two routes when SSL redirect is enabled:
49+ // 1. HTTP redirect route: bound to HTTP listener, has RequestRedirect filter, no backendRefs
50+ // 2. HTTPS backend route: bound to HTTPS listener, has backendRefs, no redirect filter
51+ //
52+ // Returns the HTTP redirect route, HTTPS backend route, and whether splitting was successful.
53+ func splitHTTPRouteForSSLRedirect (
54+ httpRouteContext intermediate.HTTPRouteContext ,
55+ httpRouteKey types.NamespacedName ,
56+ gatewayCtx * intermediate.GatewayContext ,
57+ ) (* intermediate.HTTPRouteContext , * intermediate.HTTPRouteContext , bool ) {
58+ // Find HTTP and HTTPS listeners by hostname
59+ var httpListenerName , httpsListenerName * gwv1.SectionName
60+ hostname := ""
61+ if len (httpRouteContext .Spec .Hostnames ) > 0 {
62+ hostname = string (httpRouteContext .Spec .Hostnames [0 ])
4163 }
4264
43- // Get unique rule indices from coverage
44- ruleSet := make (map [int ]struct {})
45- for _ , idx := range coverage {
46- ruleSet [idx .Rule ] = struct {}{}
65+ for _ , listener := range gatewayCtx .Spec .Listeners {
66+ if listener .Protocol == gwv1 .HTTPProtocolType {
67+ // Check if hostname matches
68+ if hostname == "" || (listener .Hostname != nil && string (* listener .Hostname ) == hostname ) {
69+ name := listener .Name
70+ httpListenerName = & name
71+ }
72+ } else if listener .Protocol == gwv1 .HTTPSProtocolType {
73+ // Check if hostname matches
74+ if hostname == "" || (listener .Hostname != nil && string (* listener .Hostname ) == hostname ) {
75+ name := listener .Name
76+ httpsListenerName = & name
77+ }
78+ }
4779 }
4880
49- // Add RequestRedirect filter to each covered rule
50- for ruleIdx := range ruleSet {
51- if ruleIdx >= len (httpRouteContext .Spec .Rules ) {
52- continue
81+ // If HTTPS listener doesn't exist, we can't create the HTTPS route
82+ // Still create HTTP redirect route though
83+ if httpsListenerName == nil {
84+ // Only create HTTP redirect route if HTTP listener exists
85+ if httpListenerName == nil {
86+ return nil , nil , false
5387 }
88+ }
89+
90+ // Create HTTP redirect route
91+ httpRedirectRoute := intermediate.HTTPRouteContext {
92+ HTTPRoute : * httpRouteContext .HTTPRoute .DeepCopy (),
93+ ProviderSpecificIR : httpRouteContext .ProviderSpecificIR ,
94+ RuleBackendSources : httpRouteContext .RuleBackendSources ,
95+ }
96+ httpRedirectRoute .ObjectMeta .Name = fmt .Sprintf ("%s-http-redirect" , httpRouteKey .Name )
97+ httpRedirectRoute .ObjectMeta .Namespace = httpRouteKey .Namespace
5498
55- // Check if RequestRedirect filter already exists
99+ // Update parentRefs to bind to HTTP listener
100+ if len (httpRedirectRoute .Spec .ParentRefs ) > 0 && httpListenerName != nil {
101+ httpRedirectRoute .Spec .ParentRefs [0 ].SectionName = httpListenerName
102+ }
103+
104+ // Add RequestRedirect filter and remove backendRefs from all rules
105+ for i := range httpRedirectRoute .Spec .Rules {
106+ // Add RequestRedirect filter
56107 hasRedirect := false
57- for _ , filter := range httpRouteContext .Spec .Rules [ruleIdx ].Filters {
108+ for _ , filter := range httpRedirectRoute .Spec .Rules [i ].Filters {
58109 if filter .Type == gwv1 .HTTPRouteFilterRequestRedirect {
59110 hasRedirect = true
60111 break
61112 }
62113 }
63-
64114 if ! hasRedirect {
65- // Add RequestRedirect filter to redirect HTTP to HTTPS
66- httpRouteContext .Spec .Rules [ruleIdx ].Filters = append (
67- httpRouteContext .Spec .Rules [ruleIdx ].Filters ,
115+ httpRedirectRoute .Spec .Rules [i ].Filters = append (
116+ httpRedirectRoute .Spec .Rules [i ].Filters ,
68117 gwv1.HTTPRouteFilter {
69118 Type : gwv1 .HTTPRouteFilterRequestRedirect ,
70119 RequestRedirect : & gwv1.HTTPRequestRedirectFilter {
@@ -73,9 +122,40 @@ func applySSLRedirectPolicy(
73122 },
74123 },
75124 )
76- // RequestRedirect filters cannot be used with backendRefs per Gateway API spec
77- // Remove backendRefs when redirecting
78- httpRouteContext .Spec .Rules [ruleIdx ].BackendRefs = nil
79125 }
126+ // Remove backendRefs (RequestRedirect filters cannot coexist with backendRefs)
127+ httpRedirectRoute .Spec .Rules [i ].BackendRefs = nil
128+ }
129+
130+ // Create HTTPS backend route (only if HTTPS listener exists)
131+ var httpsBackendRoute * intermediate.HTTPRouteContext
132+ if httpsListenerName != nil {
133+ route := intermediate.HTTPRouteContext {
134+ HTTPRoute : * httpRouteContext .HTTPRoute .DeepCopy (),
135+ ProviderSpecificIR : httpRouteContext .ProviderSpecificIR ,
136+ RuleBackendSources : httpRouteContext .RuleBackendSources ,
137+ }
138+ route .ObjectMeta .Name = fmt .Sprintf ("%s-https" , httpRouteKey .Name )
139+ route .ObjectMeta .Namespace = httpRouteKey .Namespace
140+ httpsBackendRoute = & route
141+
142+ // Update parentRefs to bind to HTTPS listener
143+ if len (httpsBackendRoute .Spec .ParentRefs ) > 0 {
144+ httpsBackendRoute .Spec .ParentRefs [0 ].SectionName = httpsListenerName
145+ }
146+
147+ // Remove any RequestRedirect filters from HTTPS route
148+ for i := range httpsBackendRoute .Spec .Rules {
149+ var filtersWithoutRedirect []gwv1.HTTPRouteFilter
150+ for _ , filter := range httpsBackendRoute .Spec .Rules [i ].Filters {
151+ if filter .Type != gwv1 .HTTPRouteFilterRequestRedirect {
152+ filtersWithoutRedirect = append (filtersWithoutRedirect , filter )
153+ }
154+ }
155+ httpsBackendRoute .Spec .Rules [i ].Filters = filtersWithoutRedirect
156+ }
157+ // Keep backendRefs for HTTPS route
80158 }
159+
160+ return & httpRedirectRoute , httpsBackendRoute , true
81161}
0 commit comments