github.com/cilium/cilium@v1.16.2/pkg/policy/api/egress.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 // EgressCommonRule is a rule that shares some of its fields across the 14 // EgressRule and EgressDenyRule. It's publicly exported so the code generators 15 // can generate code for this structure. 16 // 17 // +deepequal-gen:private-method=true 18 type EgressCommonRule struct { 19 // ToEndpoints is a list of endpoints identified by an EndpointSelector to 20 // which the endpoints subject to the rule are allowed to communicate. 21 // 22 // Example: 23 // Any endpoint with the label "role=frontend" can communicate with any 24 // endpoint carrying the label "role=backend". 25 // 26 // +kubebuilder:validation:Optional 27 ToEndpoints []EndpointSelector `json:"toEndpoints,omitempty"` 28 29 // ToRequires is a list of additional constraints which must be met 30 // in order for the selected endpoints to be able to connect to other 31 // endpoints. These additional constraints do no by itself grant access 32 // privileges and must always be accompanied with at least one matching 33 // ToEndpoints. 34 // 35 // Example: 36 // Any Endpoint with the label "team=A" requires any endpoint to which it 37 // communicates to also carry the label "team=A". 38 // 39 // +kubebuilder:validation:Optional 40 ToRequires []EndpointSelector `json:"toRequires,omitempty"` 41 42 // ToCIDR is a list of IP blocks which the endpoint subject to the rule 43 // is allowed to initiate connections. Only connections destined for 44 // outside of the cluster and not targeting the host will be subject 45 // to CIDR rules. This will match on the destination IP address of 46 // outgoing connections. Adding a prefix into ToCIDR or into ToCIDRSet 47 // with no ExcludeCIDRs is equivalent. Overlaps are allowed between 48 // ToCIDR and ToCIDRSet. 49 // 50 // Example: 51 // Any endpoint with the label "app=database-proxy" is allowed to 52 // initiate connections to 10.2.3.0/24 53 // 54 // +kubebuilder:validation:Optional 55 ToCIDR CIDRSlice `json:"toCIDR,omitempty"` 56 57 // ToCIDRSet is a list of IP blocks which the endpoint subject to the rule 58 // is allowed to initiate connections to in addition to connections 59 // which are allowed via ToEndpoints, along with a list of subnets contained 60 // within their corresponding IP block to which traffic should not be 61 // allowed. This will match on the destination IP address of outgoing 62 // connections. Adding a prefix into ToCIDR or into ToCIDRSet with no 63 // ExcludeCIDRs is equivalent. Overlaps are allowed between ToCIDR and 64 // ToCIDRSet. 65 // 66 // Example: 67 // Any endpoint with the label "app=database-proxy" is allowed to 68 // initiate connections to 10.2.3.0/24 except from IPs in subnet 10.2.3.0/28. 69 // 70 // +kubebuilder:validation:Optional 71 ToCIDRSet CIDRRuleSlice `json:"toCIDRSet,omitempty"` 72 73 // ToEntities is a list of special entities to which the endpoint subject 74 // to the rule is allowed to initiate connections. Supported entities are 75 // `world`, `cluster`,`host`,`remote-node`,`kube-apiserver`, `init`, 76 // `health`,`unmanaged` and `all`. 77 // 78 // +kubebuilder:validation:Optional 79 ToEntities EntitySlice `json:"toEntities,omitempty"` 80 81 // ToServices is a list of services to which the endpoint subject 82 // to the rule is allowed to initiate connections. 83 // Currently Cilium only supports toServices for K8s services without 84 // selectors. 85 // 86 // Example: 87 // Any endpoint with the label "app=backend-app" is allowed to 88 // initiate connections to all cidrs backing the "external-service" service 89 // 90 // +kubebuilder:validation:Optional 91 ToServices []Service `json:"toServices,omitempty"` 92 93 // ToGroups is a directive that allows the integration with multiple outside 94 // providers. Currently, only AWS is supported, and the rule can select by 95 // multiple sub directives: 96 // 97 // Example: 98 // toGroups: 99 // - aws: 100 // securityGroupsIds: 101 // - 'sg-XXXXXXXXXXXXX' 102 // 103 // +kubebuilder:validation:Optional 104 ToGroups []Groups `json:"toGroups,omitempty"` 105 106 // ToNodes is a list of nodes identified by an 107 // EndpointSelector to which endpoints subject to the rule is allowed to communicate. 108 // 109 // +kubebuilder:validation:Optional 110 ToNodes []EndpointSelector `json:"toNodes,omitempty"` 111 112 // TODO: Move this to the policy package 113 // (https://github.com/cilium/cilium/issues/8353) 114 aggregatedSelectors EndpointSelectorSlice `json:"-"` 115 } 116 117 // DeepEqual returns true if both EgressCommonRule are deep equal. 118 // The semantic of a nil slice in one of its fields is different from the semantic 119 // of an empty non-nil slice, thus it explicitly checks for that case before calling 120 // the autogenerated method. 121 func (in *EgressCommonRule) DeepEqual(other *EgressCommonRule) bool { 122 if slices.XorNil(in.ToEndpoints, other.ToEndpoints) { 123 return false 124 } 125 if slices.XorNil(in.ToCIDR, other.ToCIDR) { 126 return false 127 } 128 if slices.XorNil(in.ToCIDRSet, other.ToCIDRSet) { 129 return false 130 } 131 if slices.XorNil(in.ToEntities, other.ToEntities) { 132 return false 133 } 134 135 return in.deepEqual(other) 136 } 137 138 // EgressRule contains all rule types which can be applied at egress, i.e. 139 // network traffic that originates inside the endpoint and exits the endpoint 140 // selected by the endpointSelector. 141 // 142 // - All members of this structure are optional. If omitted or empty, the 143 // member will have no effect on the rule. 144 // 145 // - If multiple members of the structure are specified, then all members 146 // must match in order for the rule to take effect. The exception to this 147 // rule is the ToRequires member; the effects of any Requires field in any 148 // rule will apply to all other rules as well. 149 // 150 // - ToEndpoints, ToCIDR, ToCIDRSet, ToEntities, ToServices and ToGroups are 151 // mutually exclusive. Only one of these members may be present within an 152 // individual rule. 153 type EgressRule struct { 154 EgressCommonRule `json:",inline"` 155 156 // ToPorts is a list of destination ports identified by port number and 157 // protocol which the endpoint subject to the rule is allowed to 158 // connect to. 159 // 160 // Example: 161 // Any endpoint with the label "role=frontend" is allowed to initiate 162 // connections to destination port 8080/tcp 163 // 164 // +kubebuilder:validation:Optional 165 ToPorts PortRules `json:"toPorts,omitempty"` 166 167 // ToFQDN allows whitelisting DNS names in place of IPs. The IPs that result 168 // from DNS resolution of `ToFQDN.MatchName`s are added to the same 169 // EgressRule object as ToCIDRSet entries, and behave accordingly. Any L4 and 170 // L7 rules within this EgressRule will also apply to these IPs. 171 // The DNS -> IP mapping is re-resolved periodically from within the 172 // cilium-agent, and the IPs in the DNS response are effected in the policy 173 // for selected pods as-is (i.e. the list of IPs is not modified in any way). 174 // Note: An explicit rule to allow for DNS traffic is needed for the pods, as 175 // ToFQDN counts as an egress rule and will enforce egress policy when 176 // PolicyEnforcment=default. 177 // Note: If the resolved IPs are IPs within the kubernetes cluster, the 178 // ToFQDN rule will not apply to that IP. 179 // Note: ToFQDN cannot occur in the same policy as other To* rules. 180 // 181 // +kubebuilder:validation:Optional 182 ToFQDNs FQDNSelectorSlice `json:"toFQDNs,omitempty"` 183 184 // ICMPs is a list of ICMP rule identified by type number 185 // which the endpoint subject to the rule is allowed to connect to. 186 // 187 // Example: 188 // Any endpoint with the label "app=httpd" is allowed to initiate 189 // type 8 ICMP connections. 190 // 191 // +kubebuilder:validation:Optional 192 ICMPs ICMPRules `json:"icmps,omitempty"` 193 194 // Authentication is the required authentication type for the allowed traffic, if any. 195 // 196 // +kubebuilder:validation:Optional 197 Authentication *Authentication `json:"authentication,omitempty"` 198 } 199 200 // EgressDenyRule contains all rule types which can be applied at egress, i.e. 201 // network traffic that originates inside the endpoint and exits the endpoint 202 // selected by the endpointSelector. 203 // 204 // - All members of this structure are optional. If omitted or empty, the 205 // member will have no effect on the rule. 206 // 207 // - If multiple members of the structure are specified, then all members 208 // must match in order for the rule to take effect. The exception to this 209 // rule is the ToRequires member; the effects of any Requires field in any 210 // rule will apply to all other rules as well. 211 // 212 // - ToEndpoints, ToCIDR, ToCIDRSet, ToEntities, ToServices and ToGroups are 213 // mutually exclusive. Only one of these members may be present within an 214 // individual rule. 215 type EgressDenyRule struct { 216 EgressCommonRule `json:",inline"` 217 218 // ToPorts is a list of destination ports identified by port number and 219 // protocol which the endpoint subject to the rule is not allowed to connect 220 // to. 221 // 222 // Example: 223 // Any endpoint with the label "role=frontend" is not allowed to initiate 224 // connections to destination port 8080/tcp 225 // 226 // +kubebuilder:validation:Optional 227 ToPorts PortDenyRules `json:"toPorts,omitempty"` 228 229 // ICMPs is a list of ICMP rule identified by type number 230 // which the endpoint subject to the rule is not allowed to connect to. 231 // 232 // Example: 233 // Any endpoint with the label "app=httpd" is not allowed to initiate 234 // type 8 ICMP connections. 235 // 236 // +kubebuilder:validation:Optional 237 ICMPs ICMPRules `json:"icmps,omitempty"` 238 } 239 240 // SetAggregatedSelectors creates a single slice containing all of the following 241 // fields within the EgressCommonRule, converted to EndpointSelector, to be 242 // stored by the caller of the EgressCommonRule for easy lookup while performing 243 // policy evaluation for the rule: 244 // * ToEntities 245 // * ToCIDR 246 // * ToCIDRSet 247 // * ToFQDNs 248 // 249 // ToEndpoints is not aggregated due to requirement folding in 250 // GetDestinationEndpointSelectorsWithRequirements() 251 func (e *EgressCommonRule) getAggregatedSelectors() EndpointSelectorSlice { 252 // explicitly check for empty non-nil slices, it should not result in any identity being selected. 253 if (e.ToEntities != nil && len(e.ToEntities) == 0) || 254 (e.ToCIDR != nil && len(e.ToCIDR) == 0) || 255 (e.ToCIDRSet != nil && len(e.ToCIDRSet) == 0) { 256 return nil 257 } 258 259 res := make(EndpointSelectorSlice, 0, len(e.ToEntities)+len(e.ToCIDR)+len(e.ToCIDRSet)) 260 res = append(res, e.ToEntities.GetAsEndpointSelectors()...) 261 res = append(res, e.ToCIDR.GetAsEndpointSelectors()...) 262 res = append(res, e.ToCIDRSet.GetAsEndpointSelectors()...) 263 return res 264 } 265 266 // SetAggregatedSelectors creates a single slice containing all of the following 267 // fields within the EgressRule, converted to EndpointSelector, to be stored 268 // within the EgressRule for easy lookup while performing policy evaluation 269 // for the rule: 270 // * ToEntities 271 // * ToCIDR 272 // * ToCIDRSet 273 // * ToFQDNs 274 // 275 // ToEndpoints is not aggregated due to requirement folding in 276 // GetDestinationEndpointSelectorsWithRequirements() 277 func (e *EgressRule) SetAggregatedSelectors() { 278 ess := e.getAggregatedSelectors() 279 ess = append(ess, e.ToFQDNs.GetAsEndpointSelectors()...) 280 e.aggregatedSelectors = ess 281 } 282 283 // SetAggregatedSelectors creates a single slice containing all of the following 284 // fields within the EgressRule, converted to EndpointSelector, to be stored 285 // within the EgressRule for easy lookup while performing policy evaluation 286 // for the rule: 287 // * ToEntities 288 // * ToCIDR 289 // * ToCIDRSet 290 // * ToFQDNs 291 // 292 // ToEndpoints is not aggregated due to requirement folding in 293 // GetDestinationEndpointSelectorsWithRequirements() 294 func (e *EgressCommonRule) SetAggregatedSelectors() { 295 e.aggregatedSelectors = e.getAggregatedSelectors() 296 } 297 298 // GetDestinationEndpointSelectorsWithRequirements returns a slice of endpoints selectors covering 299 // all L3 dst selectors of the egress rule 300 func (e *EgressRule) GetDestinationEndpointSelectorsWithRequirements(requirements []slim_metav1.LabelSelectorRequirement) EndpointSelectorSlice { 301 if e.aggregatedSelectors == nil { 302 e.SetAggregatedSelectors() 303 } 304 return e.EgressCommonRule.getDestinationEndpointSelectorsWithRequirements(requirements) 305 } 306 307 // GetDestinationEndpointSelectorsWithRequirements returns a slice of endpoints selectors covering 308 // all L3 source selectors of the ingress rule 309 func (e *EgressDenyRule) GetDestinationEndpointSelectorsWithRequirements(requirements []slim_metav1.LabelSelectorRequirement) EndpointSelectorSlice { 310 if e.aggregatedSelectors == nil { 311 e.SetAggregatedSelectors() 312 } 313 return e.EgressCommonRule.getDestinationEndpointSelectorsWithRequirements(requirements) 314 } 315 316 // GetDestinationEndpointSelectorsWithRequirements returns a slice of endpoints selectors covering 317 // all L3 source selectors of the ingress rule 318 func (e *EgressCommonRule) getDestinationEndpointSelectorsWithRequirements( 319 requirements []slim_metav1.LabelSelectorRequirement, 320 ) EndpointSelectorSlice { 321 322 // explicitly check for empty non-nil slices, it should not result in any identity being selected. 323 if e.aggregatedSelectors == nil || (e.ToEndpoints != nil && len(e.ToEndpoints) == 0) || 324 (e.ToNodes != nil && len(e.ToNodes) == 0) { 325 return nil 326 } 327 328 res := make(EndpointSelectorSlice, 0, len(e.ToEndpoints)+len(e.aggregatedSelectors)+len(e.ToNodes)) 329 330 if len(requirements) > 0 && len(e.ToEndpoints) > 0 { 331 for idx := range e.ToEndpoints { 332 sel := *e.ToEndpoints[idx].DeepCopy() 333 sel.MatchExpressions = append(sel.MatchExpressions, requirements...) 334 sel.SyncRequirementsWithLabelSelector() 335 // Even though this string is deep copied, we need to override it 336 // because we are updating the contents of the MatchExpressions. 337 sel.cachedLabelSelectorString = sel.LabelSelector.String() 338 res = append(res, sel) 339 } 340 } else { 341 res = append(res, e.ToEndpoints...) 342 res = append(res, e.ToNodes...) 343 } 344 return append(res, e.aggregatedSelectors...) 345 } 346 347 // AllowsWildcarding returns true if wildcarding should be performed upon 348 // policy evaluation for the given rule. 349 func (e *EgressRule) AllowsWildcarding() bool { 350 return e.EgressCommonRule.AllowsWildcarding() && len(e.ToFQDNs) == 0 351 } 352 353 // AllowsWildcarding returns true if wildcarding should be performed upon 354 // policy evaluation for the given rule. 355 func (e *EgressCommonRule) AllowsWildcarding() bool { 356 return len(e.ToRequires)+len(e.ToServices) == 0 357 } 358 359 // RequiresDerivative returns true when the EgressCommonRule contains sections 360 // that need a derivative policy created in order to be enforced 361 // (e.g. ToGroups). 362 func (e *EgressCommonRule) RequiresDerivative() bool { 363 return len(e.ToGroups) > 0 364 } 365 366 // CreateDerivative will return a new rule based on the data gathered by the 367 // rules that creates a new derivative policy. 368 // In the case of ToGroups will call outside using the groups callback and this 369 // function can take a bit of time. 370 func (e *EgressRule) CreateDerivative(ctx context.Context) (*EgressRule, error) { 371 newRule := e.DeepCopy() 372 if !e.RequiresDerivative() { 373 return newRule, nil 374 } 375 newRule.ToCIDRSet = make(CIDRRuleSlice, 0, len(e.ToGroups)) 376 cidrSet, err := ExtractCidrSet(ctx, e.ToGroups) 377 if err != nil { 378 return &EgressRule{}, err 379 } 380 newRule.ToCIDRSet = append(e.ToCIDRSet, cidrSet...) 381 newRule.ToGroups = nil 382 e.SetAggregatedSelectors() 383 return newRule, nil 384 } 385 386 // CreateDerivative will return a new rule based on the data gathered by the 387 // rules that creates a new derivative policy. 388 // In the case of ToGroups will call outside using the groups callback and this 389 // function can take a bit of time. 390 func (e *EgressDenyRule) CreateDerivative(ctx context.Context) (*EgressDenyRule, error) { 391 newRule := e.DeepCopy() 392 if !e.RequiresDerivative() { 393 return newRule, nil 394 } 395 newRule.ToCIDRSet = make(CIDRRuleSlice, 0, len(e.ToGroups)) 396 cidrSet, err := ExtractCidrSet(ctx, e.ToGroups) 397 if err != nil { 398 return &EgressDenyRule{}, err 399 } 400 newRule.ToCIDRSet = append(e.ToCIDRSet, cidrSet...) 401 newRule.ToGroups = nil 402 e.SetAggregatedSelectors() 403 return newRule, nil 404 }