istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/model/policyattachment.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package model 16 17 import ( 18 "k8s.io/apimachinery/pkg/types" 19 20 "istio.io/api/type/v1beta1" 21 "istio.io/istio/pilot/pkg/features" 22 "istio.io/istio/pkg/config" 23 "istio.io/istio/pkg/config/constants" 24 "istio.io/istio/pkg/config/labels" 25 "istio.io/istio/pkg/config/schema/gvk" 26 ) 27 28 // this can be any type from istio/api that uses these types of selectors 29 type TargetablePolicy interface { 30 GetTargetRef() *v1beta1.PolicyTargetReference 31 GetTargetRefs() []*v1beta1.PolicyTargetReference 32 GetSelector() *v1beta1.WorkloadSelector 33 } 34 35 // WorkloadPolicyMatcher performs policy selection either using targetRef or label selectors. 36 // Label selection uses the workload labels. 37 // TargetRef selection uses either the workload's namespace + the gateway name based on labels, 38 // or the Services the workload is a part of. 39 type WorkloadPolicyMatcher struct { 40 Namespace string 41 WorkloadLabels labels.Instance 42 IsWaypoint bool 43 Service string 44 } 45 46 func PolicyMatcherFor(workloadNamespace string, labels labels.Instance, isWaypoint bool) WorkloadPolicyMatcher { 47 return WorkloadPolicyMatcher{ 48 Namespace: workloadNamespace, 49 WorkloadLabels: labels, 50 IsWaypoint: isWaypoint, 51 } 52 } 53 54 func PolicyMatcherForProxy(proxy *Proxy) WorkloadPolicyMatcher { 55 return WorkloadPolicyMatcher{ 56 Namespace: proxy.ConfigNamespace, 57 WorkloadLabels: proxy.Labels, 58 IsWaypoint: proxy.IsWaypointProxy(), 59 } 60 } 61 62 func (p WorkloadPolicyMatcher) WithService(service *Service) WorkloadPolicyMatcher { 63 if service == nil { 64 return p 65 } 66 if service.Attributes.Namespace != p.Namespace { 67 log.Debugf("matching policy for service in namespace %s for workload in %s", service.Attributes.Namespace, p.Namespace) 68 } 69 70 p.Service = service.Attributes.Name 71 return p 72 } 73 74 // workloadGatewayName returns the name of the gateway for which a workload is an instance. 75 // This is based on the gateway.networking.k8s.io/gateway-name label. 76 func workloadGatewayName(l labels.Instance) (string, bool) { 77 gwName, exists := l[constants.GatewayNameLabel] 78 if !exists { 79 // TODO: Remove deprecated gateway name label (1.22 or 1.23) 80 gwName, exists = l[constants.DeprecatedGatewayNameLabel] 81 } 82 83 return gwName, exists 84 } 85 86 func (p WorkloadPolicyMatcher) isSelected(policy TargetablePolicy) bool { 87 selector := policy.GetSelector() 88 return selector == nil || labels.Instance(selector.GetMatchLabels()).SubsetOf(p.WorkloadLabels) 89 } 90 91 // GetTargetRefs returns the list of targetRefs, taking into account the legacy targetRef 92 func GetTargetRefs(p TargetablePolicy) []*v1beta1.PolicyTargetReference { 93 targetRefs := p.GetTargetRefs() 94 if len(targetRefs) == 0 && p.GetTargetRef() != nil { 95 targetRefs = []*v1beta1.PolicyTargetReference{p.GetTargetRef()} 96 } 97 return targetRefs 98 } 99 100 func (p WorkloadPolicyMatcher) ShouldAttachPolicy(kind config.GroupVersionKind, policyName types.NamespacedName, policy TargetablePolicy) bool { 101 gatewayName, isGatewayAPI := workloadGatewayName(p.WorkloadLabels) 102 targetRefs := GetTargetRefs(policy) 103 104 // non-gateway: use selector 105 if !isGatewayAPI { 106 // if targetRef is specified, ignore the policy altogether 107 if len(targetRefs) > 0 { 108 return false 109 } 110 return p.isSelected(policy) 111 } 112 113 // gateway with no targetRefs: (sometimes) fallback to selector 114 if len(targetRefs) == 0 { 115 // gateways require the feature flag for selector-based policy 116 // waypoints never use selector 117 if p.IsWaypoint || !features.EnableSelectorBasedK8sGatewayPolicy { 118 log.Debugf("Ignoring workload-scoped %s/%s %s for gateway %s.%s because it has no targetRef", kind.Group, kind.Kind, policyName, gatewayName, p.Namespace) 119 return false 120 } 121 return p.isSelected(policy) 122 } 123 124 for _, targetRef := range targetRefs { 125 target := types.NamespacedName{ 126 Name: targetRef.GetName(), 127 Namespace: GetOrDefault(targetRef.GetNamespace(), policyName.Namespace), 128 } 129 130 // Gateway attached 131 if config.CanonicalGroup(targetRef.GetGroup()) == gvk.KubernetesGateway.CanonicalGroup() && 132 targetRef.GetKind() == gvk.KubernetesGateway.Kind && 133 target.Name == gatewayName && 134 (targetRef.GetNamespace() == "" || targetRef.GetNamespace() == p.Namespace) { 135 return true 136 } 137 138 // Service attached 139 if p.IsWaypoint && 140 config.CanonicalGroup(targetRef.GetGroup()) == gvk.Service.CanonicalGroup() && 141 targetRef.GetKind() == gvk.Service.Kind && 142 targetRef.GetName() == p.Service && 143 (targetRef.GetNamespace() == "" || targetRef.GetNamespace() == p.Namespace) { 144 return true 145 } 146 } 147 148 return false 149 }