github.com/imran-kn/cilium-fork@v1.6.9/pkg/k8s/network_policy.go (about) 1 // Copyright 2016-2019 Authors of Cilium 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 k8s 16 17 import ( 18 "fmt" 19 20 "github.com/cilium/cilium/pkg/annotation" 21 k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" 22 k8sCiliumUtils "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/utils" 23 k8sUtils "github.com/cilium/cilium/pkg/k8s/utils" 24 "github.com/cilium/cilium/pkg/labels" 25 "github.com/cilium/cilium/pkg/logging/logfields" 26 "github.com/cilium/cilium/pkg/policy" 27 "github.com/cilium/cilium/pkg/policy/api" 28 29 networkingv1 "k8s.io/api/networking/v1" 30 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 ) 32 33 const ( 34 resourceTypeNetworkPolicy = "NetworkPolicy" 35 ) 36 37 var ( 38 allowAllNamespacesRequirement = v1.LabelSelectorRequirement{ 39 Key: k8sConst.PodNamespaceLabel, 40 Operator: v1.LabelSelectorOpExists, 41 } 42 ) 43 44 // GetPolicyLabelsv1 extracts the name of np. It uses the name from the Cilium 45 // annotation if present. If the policy's annotations do not contain 46 // the Cilium annotation, the policy's name field is used instead. 47 func GetPolicyLabelsv1(np *networkingv1.NetworkPolicy) labels.LabelArray { 48 if np == nil { 49 log.Warningf("unable to extract policy labels because provided NetworkPolicy is nil") 50 return nil 51 } 52 53 policyName := np.Annotations[annotation.Name] 54 policyUID := np.UID 55 56 if policyName == "" { 57 policyName = np.Name 58 } 59 60 ns := k8sUtils.ExtractNamespace(&np.ObjectMeta) 61 62 return k8sCiliumUtils.GetPolicyLabels(ns, policyName, policyUID, resourceTypeNetworkPolicy) 63 } 64 65 func parseNetworkPolicyPeer(namespace string, peer *networkingv1.NetworkPolicyPeer) *api.EndpointSelector { 66 if peer == nil { 67 return nil 68 } 69 70 var retSel *api.EndpointSelector 71 72 if peer.NamespaceSelector != nil { 73 labelSelector := peer.NamespaceSelector 74 matchLabels := map[string]string{} 75 // We use our own special label prefix for namespace metadata, 76 // thus we need to prefix that prefix to all NamespaceSelector.MatchLabels 77 for k, v := range peer.NamespaceSelector.MatchLabels { 78 matchLabels[policy.JoinPath(k8sConst.PodNamespaceMetaLabels, k)] = v 79 } 80 peer.NamespaceSelector.MatchLabels = matchLabels 81 82 // We use our own special label prefix for namespace metadata, 83 // thus we need to prefix that prefix to all NamespaceSelector.MatchLabels 84 for i, lsr := range peer.NamespaceSelector.MatchExpressions { 85 lsr.Key = policy.JoinPath(k8sConst.PodNamespaceMetaLabels, lsr.Key) 86 peer.NamespaceSelector.MatchExpressions[i] = lsr 87 } 88 89 // Empty namespace selector selects all namespaces (i.e., a namespace 90 // label exists). 91 if len(peer.NamespaceSelector.MatchLabels) == 0 && len(peer.NamespaceSelector.MatchExpressions) == 0 { 92 peer.NamespaceSelector.MatchExpressions = []v1.LabelSelectorRequirement{allowAllNamespacesRequirement} 93 } 94 95 selector := api.NewESFromK8sLabelSelector(labels.LabelSourceK8sKeyPrefix, labelSelector, peer.PodSelector) 96 retSel = &selector 97 } else if peer.PodSelector != nil { 98 labelSelector := peer.PodSelector 99 if peer.PodSelector.MatchLabels == nil { 100 peer.PodSelector.MatchLabels = map[string]string{} 101 } 102 // The PodSelector should only reflect to the same namespace 103 // the policy is being stored, thus we add the namespace to 104 // the MatchLabels map. 105 peer.PodSelector.MatchLabels[k8sConst.PodNamespaceLabel] = namespace 106 107 selector := api.NewESFromK8sLabelSelector(labels.LabelSourceK8sKeyPrefix, labelSelector) 108 retSel = &selector 109 } 110 111 return retSel 112 } 113 114 func hasV1PolicyType(pTypes []networkingv1.PolicyType, typ networkingv1.PolicyType) bool { 115 for _, pType := range pTypes { 116 if pType == typ { 117 return true 118 } 119 } 120 return false 121 } 122 123 // ParseNetworkPolicy parses a k8s NetworkPolicy. Returns a list of 124 // Cilium policy rules that can be added, along with an error if there was an 125 // error sanitizing the rules. 126 func ParseNetworkPolicy(np *networkingv1.NetworkPolicy) (api.Rules, error) { 127 128 if np == nil { 129 return nil, fmt.Errorf("cannot parse NetworkPolicy because it is nil") 130 } 131 132 ingresses := []api.IngressRule{} 133 egresses := []api.EgressRule{} 134 135 namespace := k8sUtils.ExtractNamespace(&np.ObjectMeta) 136 137 for _, iRule := range np.Spec.Ingress { 138 fromRules := []api.IngressRule{} 139 if iRule.From != nil && len(iRule.From) > 0 { 140 for _, rule := range iRule.From { 141 ingress := api.IngressRule{} 142 endpointSelector := parseNetworkPolicyPeer(namespace, &rule) 143 144 if endpointSelector != nil { 145 ingress.FromEndpoints = append(ingress.FromEndpoints, *endpointSelector) 146 } else { 147 // No label-based selectors were in NetworkPolicyPeer. 148 log.WithField(logfields.K8sNetworkPolicyName, np.Name).Debug("NetworkPolicyPeer does not have PodSelector or NamespaceSelector") 149 } 150 151 // Parse CIDR-based parts of rule. 152 if rule.IPBlock != nil { 153 ingress.FromCIDRSet = append(ingress.FromCIDRSet, ipBlockToCIDRRule(rule.IPBlock)) 154 } 155 156 fromRules = append(fromRules, ingress) 157 } 158 } else { 159 // Based on NetworkPolicyIngressRule docs: 160 // From []NetworkPolicyPeer 161 // If this field is empty or missing, this rule matches all 162 // sources (traffic not restricted by source). 163 ingress := api.IngressRule{} 164 ingress.FromEndpoints = append(ingress.FromEndpoints, api.WildcardEndpointSelector) 165 166 fromRules = append(fromRules, ingress) 167 } 168 169 // We apply the ports to all rules generated from the From section 170 if iRule.Ports != nil && len(iRule.Ports) > 0 { 171 toPorts := parsePorts(iRule.Ports) 172 for i := range fromRules { 173 fromRules[i].ToPorts = toPorts 174 } 175 } 176 177 ingresses = append(ingresses, fromRules...) 178 } 179 180 for _, eRule := range np.Spec.Egress { 181 toRules := []api.EgressRule{} 182 183 if eRule.To != nil && len(eRule.To) > 0 { 184 for _, rule := range eRule.To { 185 egress := api.EgressRule{} 186 if rule.NamespaceSelector != nil || rule.PodSelector != nil { 187 endpointSelector := parseNetworkPolicyPeer(namespace, &rule) 188 189 if endpointSelector != nil { 190 egress.ToEndpoints = append(egress.ToEndpoints, *endpointSelector) 191 } else { 192 log.WithField(logfields.K8sNetworkPolicyName, np.Name).Debug("NetworkPolicyPeer does not have PodSelector or NamespaceSelector") 193 } 194 } 195 if rule.IPBlock != nil { 196 egress.ToCIDRSet = append(egress.ToCIDRSet, ipBlockToCIDRRule(rule.IPBlock)) 197 } 198 199 toRules = append(toRules, egress) 200 } 201 } else { 202 // Based on NetworkPolicyEgressRule docs: 203 // To []NetworkPolicyPeer 204 // If this field is empty or missing, this rule matches all 205 // destinations (traffic not restricted by destination) 206 egress := api.EgressRule{} 207 egress.ToEndpoints = append(egress.ToEndpoints, api.WildcardEndpointSelector) 208 209 toRules = append(toRules, egress) 210 } 211 212 // We apply the ports to all rules generated from the To section 213 if eRule.Ports != nil && len(eRule.Ports) > 0 { 214 toPorts := parsePorts(eRule.Ports) 215 for i := range toRules { 216 toRules[i].ToPorts = toPorts 217 } 218 } 219 220 egresses = append(egresses, toRules...) 221 } 222 223 // Convert the k8s default-deny model to the Cilium default-deny model 224 //spec: 225 // podSelector: {} 226 // policyTypes: 227 // - Ingress 228 // Since k8s 1.7 doesn't contain any PolicyTypes, we default deny 229 // if podSelector is empty and the policyTypes is not egress 230 if len(ingresses) == 0 && 231 (hasV1PolicyType(np.Spec.PolicyTypes, networkingv1.PolicyTypeIngress) || 232 !hasV1PolicyType(np.Spec.PolicyTypes, networkingv1.PolicyTypeEgress)) { 233 ingresses = []api.IngressRule{{}} 234 } 235 236 // Convert the k8s default-deny model to the Cilium default-deny model 237 //spec: 238 // podSelector: {} 239 // policyTypes: 240 // - Egress 241 if len(egresses) == 0 && hasV1PolicyType(np.Spec.PolicyTypes, networkingv1.PolicyTypeEgress) { 242 egresses = []api.EgressRule{{}} 243 } 244 245 if np.Spec.PodSelector.MatchLabels == nil { 246 np.Spec.PodSelector.MatchLabels = map[string]string{} 247 } 248 np.Spec.PodSelector.MatchLabels[k8sConst.PodNamespaceLabel] = namespace 249 250 // The next patch will pass the UID. 251 rule := api.NewRule(). 252 WithEndpointSelector(api.NewESFromK8sLabelSelector(labels.LabelSourceK8sKeyPrefix, &np.Spec.PodSelector)). 253 WithLabels(GetPolicyLabelsv1(np)). 254 WithIngressRules(ingresses). 255 WithEgressRules(egresses) 256 257 if err := rule.Sanitize(); err != nil { 258 return nil, err 259 } 260 261 return api.Rules{rule}, nil 262 } 263 264 func ipBlockToCIDRRule(block *networkingv1.IPBlock) api.CIDRRule { 265 cidrRule := api.CIDRRule{} 266 cidrRule.Cidr = api.CIDR(block.CIDR) 267 for _, v := range block.Except { 268 cidrRule.ExceptCIDRs = append(cidrRule.ExceptCIDRs, api.CIDR(v)) 269 } 270 return cidrRule 271 } 272 273 // parsePorts converts list of K8s NetworkPolicyPorts to Cilium PortRules. 274 func parsePorts(ports []networkingv1.NetworkPolicyPort) []api.PortRule { 275 portRules := []api.PortRule{} 276 for _, port := range ports { 277 if port.Protocol == nil && port.Port == nil { 278 continue 279 } 280 281 protocol := api.ProtoTCP 282 if port.Protocol != nil { 283 protocol, _ = api.ParseL4Proto(string(*port.Protocol)) 284 } 285 286 portStr := "" 287 if port.Port != nil { 288 portStr = port.Port.String() 289 } 290 291 portRule := api.PortRule{ 292 Ports: []api.PortProtocol{ 293 {Port: portStr, Protocol: protocol}, 294 }, 295 } 296 297 portRules = append(portRules, portRule) 298 } 299 300 return portRules 301 }