github.com/cilium/cilium@v1.16.2/operator/pkg/gateway-api/routechecks/gateway_checks.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package routechecks 5 6 import ( 7 "fmt" 8 9 corev1 "k8s.io/api/core/v1" 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 "sigs.k8s.io/controller-runtime/pkg/client" 12 gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" 13 ) 14 15 func CheckGatewayAllowedForNamespace(input Input, parentRef gatewayv1.ParentReference) (bool, error) { 16 gw, err := input.GetGateway(parentRef) 17 if err != nil { 18 input.SetParentCondition(parentRef, metav1.Condition{ 19 Type: "Accepted", 20 Status: metav1.ConditionFalse, 21 Reason: "Invalid" + input.GetGVK().Kind, 22 Message: err.Error(), 23 }) 24 25 return false, nil 26 } 27 28 allListenerHostNames := GetAllListenerHostNames(gw.Spec.Listeners) 29 hasNamespaceRestriction := false 30 for _, listener := range gw.Spec.Listeners { 31 32 if listener.AllowedRoutes == nil { 33 continue 34 } 35 36 if listener.AllowedRoutes.Namespaces == nil { 37 continue 38 } 39 40 if parentRef.SectionName != nil && listener.Name != *parentRef.SectionName { 41 continue 42 } 43 44 if listener.Hostname != nil && (len(computeHostsForListener(&listener, input.GetHostnames(), allListenerHostNames)) > 0) { 45 continue 46 } 47 48 // if gateway allows all namespaces, we do not need to check anything here 49 if *listener.AllowedRoutes.Namespaces.From == gatewayv1.NamespacesFromAll { 50 return true, nil 51 } 52 53 if *listener.AllowedRoutes.Namespaces.From == gatewayv1.NamespacesFromSelector { 54 nsList := &corev1.NamespaceList{} 55 selector, _ := metav1.LabelSelectorAsSelector(listener.AllowedRoutes.Namespaces.Selector) 56 if err := input.GetClient().List(input.GetContext(), nsList, client.MatchingLabelsSelector{Selector: selector}); err != nil { 57 return false, fmt.Errorf("unable to list namespaces: %w", err) 58 } 59 60 allowed := false 61 for _, ns := range nsList.Items { 62 if ns.Name == input.GetNamespace() { 63 allowed = true 64 } 65 } 66 if !allowed { 67 input.SetParentCondition(parentRef, metav1.Condition{ 68 Type: "Accepted", 69 Status: metav1.ConditionFalse, 70 Reason: string(gatewayv1.RouteReasonNotAllowedByListeners), 71 Message: input.GetGVK().Kind + " is not allowed to attach to this Gateway due to namespace selector restrictions", 72 }) 73 74 return false, nil 75 } 76 return true, nil 77 } 78 79 // check if the gateway allows the same namespace as the route 80 if *listener.AllowedRoutes.Namespaces.From == gatewayv1.NamespacesFromSame && 81 input.GetNamespace() == gw.GetNamespace() { 82 return true, nil 83 } 84 hasNamespaceRestriction = true 85 } 86 if hasNamespaceRestriction { 87 input.SetParentCondition(parentRef, metav1.Condition{ 88 Type: "Accepted", 89 Status: metav1.ConditionFalse, 90 Reason: string(gatewayv1.RouteReasonNotAllowedByListeners), 91 Message: input.GetGVK().Kind + " is not allowed to attach to this Gateway due to namespace restrictions", 92 }) 93 return false, nil 94 } 95 return true, nil 96 } 97 98 func CheckGatewayRouteKindAllowed(input Input, parentRef gatewayv1.ParentReference) (bool, error) { 99 gw, err := input.GetGateway(parentRef) 100 if err != nil { 101 input.SetParentCondition(parentRef, metav1.Condition{ 102 Type: "Accepted", 103 Status: metav1.ConditionFalse, 104 Reason: "Invalid" + input.GetGVK().Kind, 105 Message: err.Error(), 106 }) 107 108 return false, nil 109 } 110 111 for _, listener := range gw.Spec.Listeners { 112 if listener.AllowedRoutes == nil || len(listener.AllowedRoutes.Kinds) == 0 { 113 continue 114 } 115 116 allowed := false 117 routeGVK := input.GetGVK() 118 for _, kind := range listener.AllowedRoutes.Kinds { 119 if (kind.Group == nil || (kind.Group != nil && *kind.Group == gatewayv1.Group(routeGVK.Group))) && 120 kind.Kind == gatewayv1.Kind(routeGVK.Kind) { 121 allowed = true 122 break 123 } 124 } 125 126 if !allowed { 127 input.SetParentCondition(parentRef, metav1.Condition{ 128 Type: string(gatewayv1.RouteConditionAccepted), 129 Status: metav1.ConditionFalse, 130 Reason: string(gatewayv1.RouteReasonNotAllowedByListeners), 131 Message: routeGVK.Kind + " is not allowed to attach to this Gateway due to route kind restrictions", 132 }) 133 134 return false, nil 135 } 136 } 137 138 return true, nil 139 } 140 141 func CheckGatewayMatchingHostnames(input Input, parentRef gatewayv1.ParentReference) (bool, error) { 142 gw, err := input.GetGateway(parentRef) 143 if err != nil { 144 input.SetParentCondition(parentRef, metav1.Condition{ 145 Type: "Accepted", 146 Status: metav1.ConditionFalse, 147 Reason: "Invalid" + input.GetGVK().Kind, 148 Message: err.Error(), 149 }) 150 151 return false, nil 152 } 153 154 if len(computeHosts(gw, input.GetHostnames(), nil)) == 0 { 155 156 input.SetParentCondition(parentRef, metav1.Condition{ 157 Type: string(gatewayv1.RouteConditionAccepted), 158 Status: metav1.ConditionFalse, 159 Reason: string(gatewayv1.RouteReasonNoMatchingListenerHostname), 160 Message: "No matching listener hostname", 161 }) 162 163 return false, nil 164 } 165 166 return true, nil 167 } 168 169 func CheckGatewayMatchingPorts(input Input, parentRef gatewayv1.ParentReference) (bool, error) { 170 gw, err := input.GetGateway(parentRef) 171 if err != nil { 172 input.SetParentCondition(parentRef, metav1.Condition{ 173 Type: "Accepted", 174 Status: metav1.ConditionFalse, 175 Reason: "Invalid" + input.GetGVK().Kind, 176 Message: err.Error(), 177 }) 178 179 return false, nil 180 } 181 182 if parentRef.Port != nil { 183 for _, listener := range gw.Spec.Listeners { 184 if listener.Port == *parentRef.Port { 185 return true, nil 186 } 187 } 188 input.SetParentCondition(parentRef, metav1.Condition{ 189 Type: string(gatewayv1.RouteConditionAccepted), 190 Status: metav1.ConditionFalse, 191 Reason: "NoMatchingParent", 192 Message: fmt.Sprintf("No matching listener with port %d", *parentRef.Port), 193 }) 194 195 return false, nil 196 } 197 198 return true, nil 199 } 200 201 func CheckGatewayMatchingSection(input Input, parentRef gatewayv1.ParentReference) (bool, error) { 202 gw, err := input.GetGateway(parentRef) 203 if err != nil { 204 input.SetParentCondition(parentRef, metav1.Condition{ 205 Type: "Accepted", 206 Status: metav1.ConditionFalse, 207 Reason: "Invalid" + input.GetGVK().Kind, 208 Message: err.Error(), 209 }) 210 211 return false, nil 212 } 213 214 if parentRef.SectionName != nil { 215 found := false 216 for _, listener := range gw.Spec.Listeners { 217 if listener.Name == *parentRef.SectionName { 218 found = true 219 break 220 } 221 } 222 if !found { 223 input.SetParentCondition(parentRef, metav1.Condition{ 224 Type: string(gatewayv1.RouteConditionAccepted), 225 Status: metav1.ConditionFalse, 226 Reason: "NoMatchingParent", 227 Message: fmt.Sprintf("No matching listener with sectionName %s", *parentRef.SectionName), 228 }) 229 230 return false, nil 231 } 232 } 233 234 return true, nil 235 } 236 237 func GetAllListenerHostNames(listeners []gatewayv1.Listener) []gatewayv1.Hostname { 238 var hosts []gatewayv1.Hostname 239 for _, listener := range listeners { 240 if listener.Hostname != nil { 241 hosts = append(hosts, *listener.Hostname) 242 } 243 } 244 return hosts 245 }