github.com/cilium/cilium@v1.16.2/pkg/policy/api/ingress.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package api
     5  
     6  import (
     7  	"context"
     8  
     9  	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    10  	"github.com/cilium/cilium/pkg/slices"
    11  )
    12  
    13  // IngressCommonRule is a rule that shares some of its fields across the
    14  // IngressRule and IngressDenyRule. It's publicly exported so the code
    15  // generators can generate code for this structure.
    16  //
    17  // +deepequal-gen:private-method=true
    18  type IngressCommonRule struct {
    19  	// FromEndpoints is a list of endpoints identified by an
    20  	// EndpointSelector which are allowed to communicate with the endpoint
    21  	// subject to the rule.
    22  	//
    23  	// Example:
    24  	// Any endpoint with the label "role=backend" can be consumed by any
    25  	// endpoint carrying the label "role=frontend".
    26  	//
    27  	// +kubebuilder:validation:Optional
    28  	FromEndpoints []EndpointSelector `json:"fromEndpoints,omitempty"`
    29  
    30  	// FromRequires is a list of additional constraints which must be met
    31  	// in order for the selected endpoints to be reachable. These
    32  	// additional constraints do no by itself grant access privileges and
    33  	// must always be accompanied with at least one matching FromEndpoints.
    34  	//
    35  	// Example:
    36  	// Any Endpoint with the label "team=A" requires consuming endpoint
    37  	// to also carry the label "team=A".
    38  	//
    39  	// +kubebuilder:validation:Optional
    40  	FromRequires []EndpointSelector `json:"fromRequires,omitempty"`
    41  
    42  	// FromCIDR is a list of IP blocks which the endpoint subject to the
    43  	// rule is allowed to receive connections from. Only connections which
    44  	// do *not* originate from the cluster or from the local host are subject
    45  	// to CIDR rules. In order to allow in-cluster connectivity, use the
    46  	// FromEndpoints field.  This will match on the source IP address of
    47  	// incoming connections. Adding  a prefix into FromCIDR or into
    48  	// FromCIDRSet with no ExcludeCIDRs is  equivalent.  Overlaps are
    49  	// allowed between FromCIDR and FromCIDRSet.
    50  	//
    51  	// Example:
    52  	// Any endpoint with the label "app=my-legacy-pet" is allowed to receive
    53  	// connections from 10.3.9.1
    54  	//
    55  	// +kubebuilder:validation:Optional
    56  	FromCIDR CIDRSlice `json:"fromCIDR,omitempty"`
    57  
    58  	// FromCIDRSet is a list of IP blocks which the endpoint subject to the
    59  	// rule is allowed to receive connections from in addition to FromEndpoints,
    60  	// along with a list of subnets contained within their corresponding IP block
    61  	// from which traffic should not be allowed.
    62  	// This will match on the source IP address of incoming connections. Adding
    63  	// a prefix into FromCIDR or into FromCIDRSet with no ExcludeCIDRs is
    64  	// equivalent. Overlaps are allowed between FromCIDR and FromCIDRSet.
    65  	//
    66  	// Example:
    67  	// Any endpoint with the label "app=my-legacy-pet" is allowed to receive
    68  	// connections from 10.0.0.0/8 except from IPs in subnet 10.96.0.0/12.
    69  	//
    70  	// +kubebuilder:validation:Optional
    71  	FromCIDRSet CIDRRuleSlice `json:"fromCIDRSet,omitempty"`
    72  
    73  	// FromEntities is a list of special entities which the endpoint subject
    74  	// to the rule is allowed to receive connections from. Supported entities are
    75  	// `world`, `cluster` and `host`
    76  	//
    77  	// +kubebuilder:validation:Optional
    78  	FromEntities EntitySlice `json:"fromEntities,omitempty"`
    79  
    80  	// FromGroups is a directive that allows the integration with multiple outside
    81  	// providers. Currently, only AWS is supported, and the rule can select by
    82  	// multiple sub directives:
    83  	//
    84  	// Example:
    85  	// FromGroups:
    86  	// - aws:
    87  	//     securityGroupsIds:
    88  	//     - 'sg-XXXXXXXXXXXXX'
    89  	//
    90  	// +kubebuilder:validation:Optional
    91  	FromGroups []Groups `json:"fromGroups,omitempty"`
    92  
    93  	// FromNodes is a list of nodes identified by an
    94  	// EndpointSelector which are allowed to communicate with the endpoint
    95  	// subject to the rule.
    96  	//
    97  	// +kubebuilder:validation:Optional
    98  	FromNodes []EndpointSelector `json:"fromNodes,omitempty"`
    99  
   100  	// TODO: Move this to the policy package
   101  	// (https://github.com/cilium/cilium/issues/8353)
   102  	aggregatedSelectors EndpointSelectorSlice `json:"-"`
   103  }
   104  
   105  // DeepEqual returns true if both IngressCommonRule are deep equal.
   106  // The semantic of a nil slice in one of its fields is different from the semantic
   107  // of an empty non-nil slice, thus it explicitly checks for that case before calling
   108  // the autogenerated method.
   109  func (in *IngressCommonRule) DeepEqual(other *IngressCommonRule) bool {
   110  	if slices.XorNil(in.FromEndpoints, other.FromEndpoints) {
   111  		return false
   112  	}
   113  	if slices.XorNil(in.FromCIDR, other.FromCIDR) {
   114  		return false
   115  	}
   116  	if slices.XorNil(in.FromCIDRSet, other.FromCIDRSet) {
   117  		return false
   118  	}
   119  	if slices.XorNil(in.FromEntities, other.FromEntities) {
   120  		return false
   121  	}
   122  
   123  	return in.deepEqual(other)
   124  }
   125  
   126  // IngressRule contains all rule types which can be applied at ingress,
   127  // i.e. network traffic that originates outside of the endpoint and
   128  // is entering the endpoint selected by the endpointSelector.
   129  //
   130  //   - All members of this structure are optional. If omitted or empty, the
   131  //     member will have no effect on the rule.
   132  //
   133  //   - If multiple members are set, all of them need to match in order for
   134  //     the rule to take effect. The exception to this rule is FromRequires field;
   135  //     the effects of any Requires field in any rule will apply to all other
   136  //     rules as well.
   137  //
   138  //   - FromEndpoints, FromCIDR, FromCIDRSet and FromEntities are mutually
   139  //     exclusive. Only one of these members may be present within an individual
   140  //     rule.
   141  type IngressRule struct {
   142  	IngressCommonRule `json:",inline"`
   143  
   144  	// ToPorts is a list of destination ports identified by port number and
   145  	// protocol which the endpoint subject to the rule is allowed to
   146  	// receive connections on.
   147  	//
   148  	// Example:
   149  	// Any endpoint with the label "app=httpd" can only accept incoming
   150  	// connections on port 80/tcp.
   151  	//
   152  	// +kubebuilder:validation:Optional
   153  	ToPorts PortRules `json:"toPorts,omitempty"`
   154  
   155  	// ICMPs is a list of ICMP rule identified by type number
   156  	// which the endpoint subject to the rule is allowed to
   157  	// receive connections on.
   158  	//
   159  	// Example:
   160  	// Any endpoint with the label "app=httpd" can only accept incoming
   161  	// type 8 ICMP connections.
   162  	//
   163  	// +kubebuilder:validation:Optional
   164  	ICMPs ICMPRules `json:"icmps,omitempty"`
   165  
   166  	// Authentication is the required authentication type for the allowed traffic, if any.
   167  	//
   168  	// +kubebuilder:validation:Optional
   169  	Authentication *Authentication `json:"authentication,omitempty"`
   170  }
   171  
   172  // IngressDenyRule contains all rule types which can be applied at ingress,
   173  // i.e. network traffic that originates outside of the endpoint and
   174  // is entering the endpoint selected by the endpointSelector.
   175  //
   176  //   - All members of this structure are optional. If omitted or empty, the
   177  //     member will have no effect on the rule.
   178  //
   179  //   - If multiple members are set, all of them need to match in order for
   180  //     the rule to take effect. The exception to this rule is FromRequires field;
   181  //     the effects of any Requires field in any rule will apply to all other
   182  //     rules as well.
   183  //
   184  //   - FromEndpoints, FromCIDR, FromCIDRSet, FromGroups and FromEntities are mutually
   185  //     exclusive. Only one of these members may be present within an individual
   186  //     rule.
   187  type IngressDenyRule struct {
   188  	IngressCommonRule `json:",inline"`
   189  
   190  	// ToPorts is a list of destination ports identified by port number and
   191  	// protocol which the endpoint subject to the rule is not allowed to
   192  	// receive connections on.
   193  	//
   194  	// Example:
   195  	// Any endpoint with the label "app=httpd" can not accept incoming
   196  	// connections on port 80/tcp.
   197  	//
   198  	// +kubebuilder:validation:Optional
   199  	ToPorts PortDenyRules `json:"toPorts,omitempty"`
   200  
   201  	// ICMPs is a list of ICMP rule identified by type number
   202  	// which the endpoint subject to the rule is not allowed to
   203  	// receive connections on.
   204  	//
   205  	// Example:
   206  	// Any endpoint with the label "app=httpd" can not accept incoming
   207  	// type 8 ICMP connections.
   208  	//
   209  	// +kubebuilder:validation:Optional
   210  	ICMPs ICMPRules `json:"icmps,omitempty"`
   211  }
   212  
   213  // SetAggregatedSelectors creates a single slice containing all of the following
   214  // fields within the IngressRule, converted to EndpointSelector, to be stored
   215  // within the IngressRule for easy lookup while performing policy evaluation
   216  // for the rule:
   217  // * FromEntities
   218  // * FromCIDR
   219  // * FromCIDRSet
   220  //
   221  // FromEndpoints is not aggregated due to requirement folding in
   222  // GetSourceEndpointSelectorsWithRequirements()
   223  func (i *IngressCommonRule) SetAggregatedSelectors() {
   224  	// Goroutines can race setting i.aggregatedSelectors, but they will all compute the same result, so it does not matter.
   225  
   226  	// explicitly check for empty non-nil slices, it should not result in any identity being selected.
   227  	if (i.FromCIDR != nil && len(i.FromCIDR) == 0) ||
   228  		(i.FromCIDRSet != nil && len(i.FromCIDRSet) == 0) ||
   229  		(i.FromEntities != nil && len(i.FromEntities) == 0) {
   230  		i.aggregatedSelectors = nil
   231  		return
   232  	}
   233  
   234  	res := make(EndpointSelectorSlice, 0, len(i.FromEntities)+len(i.FromCIDR)+len(i.FromCIDRSet))
   235  	res = append(res, i.FromEntities.GetAsEndpointSelectors()...)
   236  	res = append(res, i.FromCIDR.GetAsEndpointSelectors()...)
   237  	res = append(res, i.FromCIDRSet.GetAsEndpointSelectors()...)
   238  
   239  	i.aggregatedSelectors = res
   240  }
   241  
   242  // GetSourceEndpointSelectorsWithRequirements returns a slice of endpoints selectors covering
   243  // all L3 source selectors of the ingress rule
   244  func (i *IngressCommonRule) GetSourceEndpointSelectorsWithRequirements(requirements []slim_metav1.LabelSelectorRequirement) EndpointSelectorSlice {
   245  	if i.aggregatedSelectors == nil {
   246  		i.SetAggregatedSelectors()
   247  	}
   248  
   249  	// explicitly check for empty non-nil slices, it should not result in any identity being selected.
   250  	if i.aggregatedSelectors == nil || (i.FromEndpoints != nil && len(i.FromEndpoints) == 0) ||
   251  		(i.FromNodes != nil && len(i.FromNodes) == 0) {
   252  		return nil
   253  	}
   254  
   255  	res := make(EndpointSelectorSlice, 0, len(i.FromEndpoints)+len(i.aggregatedSelectors)+len(i.FromNodes))
   256  	if len(requirements) > 0 && len(i.FromEndpoints) > 0 {
   257  		for idx := range i.FromEndpoints {
   258  			sel := *i.FromEndpoints[idx].DeepCopy()
   259  			sel.MatchExpressions = append(sel.MatchExpressions, requirements...)
   260  			sel.SyncRequirementsWithLabelSelector()
   261  			// Even though this string is deep copied, we need to override it
   262  			// because we are updating the contents of the MatchExpressions.
   263  			sel.cachedLabelSelectorString = sel.LabelSelector.String()
   264  			res = append(res, sel)
   265  		}
   266  	} else {
   267  		res = append(res, i.FromEndpoints...)
   268  		res = append(res, i.FromNodes...)
   269  	}
   270  
   271  	return append(res, i.aggregatedSelectors...)
   272  }
   273  
   274  // AllowsWildcarding returns true if wildcarding should be performed upon
   275  // policy evaluation for the given rule.
   276  func (i *IngressCommonRule) AllowsWildcarding() bool {
   277  	return len(i.FromRequires) == 0
   278  }
   279  
   280  // RequiresDerivative returns true when the EgressCommonRule contains sections
   281  // that need a derivative policy created in order to be enforced
   282  // (e.g. FromGroups).
   283  func (e *IngressCommonRule) RequiresDerivative() bool {
   284  	return len(e.FromGroups) > 0
   285  }
   286  
   287  // CreateDerivative will return a new rule based on the data gathered by the
   288  // rules that creates a new derivative policy.
   289  // In the case of FromGroups will call outside using the groups callback and this
   290  // function can take a bit of time.
   291  func (e *IngressRule) CreateDerivative(ctx context.Context) (*IngressRule, error) {
   292  	newRule := e.DeepCopy()
   293  	if !e.RequiresDerivative() {
   294  		return newRule, nil
   295  	}
   296  	newRule.FromCIDRSet = make(CIDRRuleSlice, 0, len(e.FromGroups))
   297  	cidrSet, err := ExtractCidrSet(ctx, e.FromGroups)
   298  	if err != nil {
   299  		return &IngressRule{}, err
   300  	}
   301  	newRule.FromCIDRSet = append(e.FromCIDRSet, cidrSet...)
   302  	newRule.FromGroups = nil
   303  	e.SetAggregatedSelectors()
   304  	return newRule, nil
   305  }
   306  
   307  // CreateDerivative will return a new rule based on the data gathered by the
   308  // rules that creates a new derivative policy.
   309  // In the case of FromGroups will call outside using the groups callback and this
   310  // function can take a bit of time.
   311  func (e *IngressDenyRule) CreateDerivative(ctx context.Context) (*IngressDenyRule, error) {
   312  	newRule := e.DeepCopy()
   313  	if !e.RequiresDerivative() {
   314  		return newRule, nil
   315  	}
   316  	newRule.FromCIDRSet = make(CIDRRuleSlice, 0, len(e.FromGroups))
   317  	cidrSet, err := ExtractCidrSet(ctx, e.FromGroups)
   318  	if err != nil {
   319  		return &IngressDenyRule{}, err
   320  	}
   321  	newRule.FromCIDRSet = append(e.FromCIDRSet, cidrSet...)
   322  	newRule.FromGroups = nil
   323  	e.SetAggregatedSelectors()
   324  	return newRule, nil
   325  }