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  }