npm/pkg/controlplane/translation/translatePolicy.go (491 lines of code) (raw):

package translation import ( "errors" "fmt" "github.com/Azure/azure-container-networking/npm/pkg/dataplane/ipsets" "github.com/Azure/azure-container-networking/npm/pkg/dataplane/policies" "github.com/Azure/azure-container-networking/npm/util" networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) /* TODO(jungukcho) 1. namespace is default in label in K8s. Need to check whether I missed something. - Targeting a Namespace by its name (https://kubernetes.io/docs/concepts/services-networking/network-policies/#targeting-a-namespace-by-its-name) */ var ( errUnknownPortType = errors.New("unknown port Type") // ErrUnsupportedNamedPort is returned when named port translation feature is used in windows. ErrUnsupportedNamedPort = errors.New("unsupported namedport translation features used on windows") // ErrUnsupportedNegativeMatch is returned when negative match translation feature is used in windows. ErrUnsupportedNegativeMatch = errors.New("unsupported NotExist operator translation features used on windows") // ErrUnsupportedExceptCIDR is returned when Except CIDR block translation feature is used in windows. ErrUnsupportedExceptCIDR = errors.New("unsupported Except CIDR block translation features used on windows") // ErrUnsupportedSCTP is returned when SCTP protocol is used in windows. ErrUnsupportedSCTP = errors.New("unsupported SCTP protocol used on windows") // ErrInvalidMatchExpressionValues ensures proper matchExpression label values since k8s doesn't perform this check. ErrInvalidMatchExpressionValues = errors.New( "matchExpression label values must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character", ) // ErrUnsupportedIPAddress is returned when an unsupported IP address, such as IPV6, is used ErrUnsupportedIPAddress = errors.New("unsupported IP address") // ErrUnsupportedNonCIDR is returned when non-CIDR blocks are passed in with NPM Lite enabled. NPM Lite allows deny-all and allow-all policies ErrUnsupportedNonCIDR = errors.New("Non-CIDR blocks, named ports, and ingress/egress namespace/pod selectors are not supported when NPM Lite is enabled, allowing only CIDR-based policies") ) type podSelectorResult struct { psSets []*ipsets.TranslatedIPSet childPSSets []*ipsets.TranslatedIPSet psList []policies.SetInfo } type netpolPortType string const ( numericPortType netpolPortType = "validport" namedPortType netpolPortType = "namedport" included bool = true ipBlocksetNameFormat = "%s-in-ns-%s-%d-%d%s" ) // portType returns type of ports (e.g., numeric port or namedPort) given NetworkPolicyPort object. func portType(portRule networkingv1.NetworkPolicyPort) (netpolPortType, error) { if portRule.Port == nil || portRule.Port.IntValue() != 0 { return numericPortType, nil } else if portRule.Port.IntValue() == 0 && portRule.Port.String() != "" { if util.IsWindowsDP() { return "", ErrUnsupportedNamedPort } return namedPortType, nil } // TODO (jungukcho): check whether this can be possible or not. return "", errUnknownPortType } // numericPortRule returns policies.Ports (port, endport) and protocol type // based on NetworkPolicyPort holding numeric port information. func numericPortRule(portRule *networkingv1.NetworkPolicyPort) (portRuleInfo policies.Ports, protocol string) { portRuleInfo = policies.Ports{} protocol = "TCP" if portRule.Protocol != nil { protocol = string(*portRule.Protocol) } if portRule.Port == nil { return portRuleInfo, protocol } portRuleInfo.Port = int32(portRule.Port.IntValue()) if portRule.EndPort != nil { portRuleInfo.EndPort = *portRule.EndPort } return portRuleInfo, protocol } // namedPortRuleInfo returns translatedIPSet and protocol type // based on NetworkPolicyPort holding named port information. func namedPortRuleInfo(portRule *networkingv1.NetworkPolicyPort) (namedPortIPSet *ipsets.TranslatedIPSet, protocol string) { if portRule == nil { return nil, "" } protocol = "TCP" if portRule.Protocol != nil { protocol = string(*portRule.Protocol) } if portRule.Port == nil { return nil, protocol } namedPortIPSet = ipsets.NewTranslatedIPSet(portRule.Port.String(), ipsets.NamedPorts) return namedPortIPSet, protocol } func namedPortRule(portRule *networkingv1.NetworkPolicyPort) (*ipsets.TranslatedIPSet, policies.SetInfo, string) { if portRule == nil { return nil, policies.SetInfo{}, "" } namedPortIPSet, protocol := namedPortRuleInfo(portRule) setInfo := policies.NewSetInfo(portRule.Port.String(), ipsets.NamedPorts, included, policies.DstDstMatch) return namedPortIPSet, setInfo, protocol } func portRule(ruleIPSets []*ipsets.TranslatedIPSet, acl *policies.ACLPolicy, portRule *networkingv1.NetworkPolicyPort, portType netpolPortType) []*ipsets.TranslatedIPSet { // port rule is always applied to destination side. if portType == namedPortType { namedPortIPSet, namedPortRuleDstList, protocol := namedPortRule(portRule) acl.AddSetInfo([]policies.SetInfo{namedPortRuleDstList}) acl.Protocol = policies.Protocol(protocol) ruleIPSets = append(ruleIPSets, namedPortIPSet) } else if portType == numericPortType { portInfo, protocol := numericPortRule(portRule) acl.DstPorts = portInfo acl.Protocol = policies.Protocol(protocol) } return ruleIPSets } // ipBlockSetName returns ipset name of the IPBlock. // It is our contract to format "<policyname>-in-ns-<namespace>-<ipblock index><direction of ipblock (i.e., ingress: IN, egress: OUT>" // as ipset name of the IPBlock. // For example, in case network policy object has // name: "test" // namespace: "default" // ingress rule // it returns "test-in-ns-default-0IN". func ipBlockSetName(policyName, ns string, direction policies.Direction, ipBlockSetIndex, ipBlockPeerIndex int) string { return fmt.Sprintf(ipBlocksetNameFormat, policyName, ns, ipBlockSetIndex, ipBlockPeerIndex, direction) } // exceptCidr returns "cidr + " " (space) + nomatch" format. // e.g., "10.0.0.0/1 nomatch" func exceptCidr(exceptCidr string) string { return exceptCidr + " " + util.IpsetNomatch } // deDuplicateExcept removes redundance elements and return slices which has only unique element. func deDuplicateExcept(exceptInIPBlock []string) []string { deDupExcepts := []string{} exceptsSet := make(map[string]struct{}) for _, except := range exceptInIPBlock { if _, exist := exceptsSet[except]; !exist { deDupExcepts = append(deDupExcepts, except) exceptsSet[except] = struct{}{} } } return deDupExcepts } // ipBlockIPSet return translatedIPSet based based on ipBlockRule. func ipBlockIPSet(policyName, ns string, direction policies.Direction, ipBlockSetIndex, ipBlockPeerIndex int, ipBlockRule *networkingv1.IPBlock) (*ipsets.TranslatedIPSet, error) { if ipBlockRule == nil || ipBlockRule.CIDR == "" { return nil, nil } // de-duplicated Except if there are redundance elements. deDupExcepts := deDuplicateExcept(ipBlockRule.Except) lenOfDeDupExcepts := len(deDupExcepts) if util.IsWindowsDP() && lenOfDeDupExcepts > 0 { return nil, ErrUnsupportedExceptCIDR } var members []string indexOfMembers := 0 // Ipset doesn't allow 0.0.0.0/0 to be added. // A solution is split 0.0.0.0/0 in half which convert to 0.0.0.0/1 and 128.0.0.0/1. // splitCIDRSet is used to handle case where IPBlock has "0.0.0.0/0" in CIDR and "0.0.0.0/1" or "128.0.0.0/1" in Except. // splitCIDRSet has two entries ("0.0.0.0/1" and "128.0.0.0/1") as key. splitCIDRLen := 2 splitCIDRSet := make(map[string]int, splitCIDRLen) if ipBlockRule.CIDR == "0.0.0.0/0" { // two cidrs (0.0.0.0/1 and 128.0.0.0/1) for 0.0.0.0/0 + except. members = make([]string, lenOfDeDupExcepts+splitCIDRLen) // in case of "0.0.0.0/0", "0.0.0.0/1" or "0.0.0.0/1 nomatch" comes eariler than "128.0.0.0/1" or "128.0.0.0/1 nomatch". splitCIDRs := []string{"0.0.0.0/1", "128.0.0.0/1"} for _, cidr := range splitCIDRs { members[indexOfMembers] = cidr splitCIDRSet[cidr] = indexOfMembers indexOfMembers++ } } else { // one cidr + except members = make([]string, lenOfDeDupExcepts+1) members[indexOfMembers] = ipBlockRule.CIDR indexOfMembers++ } for i := 0; i < lenOfDeDupExcepts; i++ { except := deDupExcepts[i] if splitCIDRIndex, exist := splitCIDRSet[except]; exist { // replace stored splitCIDR with "nomatch" option members[splitCIDRIndex] = exceptCidr(except) indexOfMembers-- members = members[:len(members)-1] } else { members[i+indexOfMembers] = exceptCidr(except) } } ipBlockIPSetName := ipBlockSetName(policyName, ns, direction, ipBlockSetIndex, ipBlockPeerIndex) ipBlockIPSet := ipsets.NewTranslatedIPSet(ipBlockIPSetName, ipsets.CIDRBlocks, members...) return ipBlockIPSet, nil } // ipBlockRule translates IPBlock field in networkpolicy object to translatedIPSet and SetInfo. // ipBlockSetIndex parameter is used to diffentiate ipBlock fields in one networkpolicy object. func ipBlockRule(policyName, ns string, direction policies.Direction, matchType policies.MatchType, ipBlockSetIndex, ipBlockPeerIndex int, ipBlockRule *networkingv1.IPBlock, ) (*ipsets.TranslatedIPSet, policies.SetInfo, error) { //nolint // gofumpt if ipBlockRule == nil || ipBlockRule.CIDR == "" { return nil, policies.SetInfo{}, nil } if !util.IsIPV4(ipBlockRule.CIDR) { return nil, policies.SetInfo{}, ErrUnsupportedIPAddress } ipBlockIPSet, err := ipBlockIPSet(policyName, ns, direction, ipBlockSetIndex, ipBlockPeerIndex, ipBlockRule) if err != nil { return nil, policies.SetInfo{}, err } setInfo := policies.NewSetInfo(ipBlockIPSet.Metadata.Name, ipsets.CIDRBlocks, included, matchType) return ipBlockIPSet, setInfo, err } // PodSelector translates podSelector of NetworkPolicyPeer field in networkpolicy object to translatedIPSets, children of translated IPSets, and SetInfo. // Children are members of a list-type IPSet. // This function is called only when the NetworkPolicyPeer has namespaceSelector field. func podSelector(policyKey string, matchType policies.MatchType, selector *metav1.LabelSelector) (*podSelectorResult, error) { podSelectors, err := parsePodSelector(policyKey, selector) if err != nil { return nil, err } lenOfPodSelectors := len(podSelectors) psResult := &podSelectorResult{ psSets: make([]*ipsets.TranslatedIPSet, 0), childPSSets: make([]*ipsets.TranslatedIPSet, 0), psList: make([]policies.SetInfo, lenOfPodSelectors), } for i := 0; i < lenOfPodSelectors; i++ { ps := podSelectors[i] psResult.psSets = append(psResult.psSets, ipsets.NewTranslatedIPSet(ps.setName, ps.setType, ps.members...)) // if value is nested value, create translatedIPSet with the nested value for j := 0; j < len(ps.members); j++ { psResult.childPSSets = append(psResult.childPSSets, ipsets.NewTranslatedIPSet(ps.members[j], ipsets.KeyValueLabelOfPod)) } psResult.psList[i] = policies.NewSetInfo(ps.setName, ps.setType, ps.include, matchType) } return psResult, nil } // podSelectorWithNS translates podSelector of spec and NetworkPolicyPeer in networkpolicy object to translatedIPSets, children of translated IPSets, and SetInfo. // Children are members of a list-type IPSet. // This function is called only when the NetworkPolicyPeer does not have namespaceSelector field. func podSelectorWithNS(policyKey, ns string, matchType policies.MatchType, selector *metav1.LabelSelector) (*podSelectorResult, error) { psResult, err := podSelector(policyKey, matchType, selector) if err != nil { return nil, err } // Add translatedIPSet and SetInfo based on namespace psResult.psSets = append(psResult.psSets, ipsets.NewTranslatedIPSet(ns, ipsets.Namespace)) psResult.psList = append(psResult.psList, policies.NewSetInfo(ns, ipsets.Namespace, included, matchType)) return psResult, nil } // nameSpaceSelector translates namespaceSelector of NetworkPolicyPeer in networkpolicy object to translatedIPSet and SetInfo. func nameSpaceSelector(matchType policies.MatchType, selector *metav1.LabelSelector) ([]*ipsets.TranslatedIPSet, []policies.SetInfo) { nsSelectors := parseNSSelector(selector) lenOfnsSelectors := len(nsSelectors) nsSelectorIPSets := make([]*ipsets.TranslatedIPSet, lenOfnsSelectors) nsSelectorList := make([]policies.SetInfo, lenOfnsSelectors) for i := 0; i < lenOfnsSelectors; i++ { nsc := nsSelectors[i] nsSelectorIPSets[i] = ipsets.NewTranslatedIPSet(nsc.setName, nsc.setType) nsSelectorList[i] = policies.NewSetInfo(nsc.setName, nsc.setType, nsc.include, matchType) } return nsSelectorIPSets, nsSelectorList } // allowAllInternal returns translatedIPSet and SetInfo in case of allowing all internal traffic excluding external. func allowAllInternal(matchType policies.MatchType) (*ipsets.TranslatedIPSet, policies.SetInfo) { allowAllIPSets := ipsets.NewTranslatedIPSet(util.KubeAllNamespacesFlag, ipsets.KeyLabelOfNamespace) setInfo := policies.NewSetInfo(util.KubeAllNamespacesFlag, ipsets.KeyLabelOfNamespace, included, matchType) return allowAllIPSets, setInfo } // ruleExists returns type of rules from networkingv1.NetworkPolicyIngressRule or networkingv1.NetworkPolicyEgressRule func ruleExists(ports []networkingv1.NetworkPolicyPort, peer []networkingv1.NetworkPolicyPeer) (allowExternal, portRuleExists, peerRuleExists bool) { // TODO(jungukcho): need to clarify and summarize below flags + more comments portRuleExists = len(ports) > 0 if peer != nil { if len(peer) == 0 { peerRuleExists = true allowExternal = true } for _, peerRule := range peer { if peerRule.PodSelector != nil || peerRule.NamespaceSelector != nil || peerRule.IPBlock != nil { peerRuleExists = true break } } } else if !portRuleExists { allowExternal = true } return allowExternal, portRuleExists, peerRuleExists } // peerAndPortRule deals with composite rules including ports and peers // (e.g., IPBlock, podSelector, namespaceSelector, or both podSelector and namespaceSelector). func peerAndPortRule(npmNetPol *policies.NPMNetworkPolicy, direction policies.Direction, ports []networkingv1.NetworkPolicyPort, setInfo []policies.SetInfo, npmLiteToggle bool) error { if len(ports) == 0 { acl := policies.NewACLPolicy(policies.Allowed, direction) acl.AddSetInfo(setInfo) npmNetPol.ACLs = append(npmNetPol.ACLs, acl) return nil } for i := range ports { portKind, err := portType(ports[i]) if err != nil { return err } err = checkForNamedPortType(portKind, npmLiteToggle) if err != nil { return err } acl := policies.NewACLPolicy(policies.Allowed, direction) acl.AddSetInfo(setInfo) npmNetPol.RuleIPSets = portRule(npmNetPol.RuleIPSets, acl, &ports[i], portKind) npmNetPol.ACLs = append(npmNetPol.ACLs, acl) } return nil } // translateRule translates ingress or egress rules and update npmNetPol object. func translateRule(npmNetPol *policies.NPMNetworkPolicy, netPolName string, direction policies.Direction, matchType policies.MatchType, ruleIndex int, ports []networkingv1.NetworkPolicyPort, peers []networkingv1.NetworkPolicyPeer, npmLiteToggle bool, ) error { // TODO(jungukcho): need to clean up it. // Leave allowExternal variable now while the condition is checked before calling this function. allowExternal, portRuleExists, peerRuleExists := ruleExists(ports, peers) // #0. TODO(jungukcho): cannot come up when this condition is met. // The code inside if condition is to handle allowing all internal traffic, but the case is handled in #2.4. // So, this code may not execute. After confirming this, need to delete it. if !portRuleExists && !peerRuleExists && !allowExternal { if npmLiteToggle { return ErrUnsupportedNonCIDR } acl := policies.NewACLPolicy(policies.Allowed, direction) ruleIPSets, allowAllInternalSetInfo := allowAllInternal(matchType) npmNetPol.RuleIPSets = append(npmNetPol.RuleIPSets, ruleIPSets) acl.AddSetInfo([]policies.SetInfo{allowAllInternalSetInfo}) npmNetPol.ACLs = append(npmNetPol.ACLs, acl) return nil } err := checkOnlyPortRuleExists(portRuleExists, peerRuleExists, allowExternal, ports, npmLiteToggle, direction, npmNetPol) if err != nil { return err } // #2. From or To fields exist in rule for peerIdx, peer := range peers { // NPM Lite is enabled and peer is non-cidr block if npmLiteToggle && peer.IPBlock == nil { return ErrUnsupportedNonCIDR } // #2.1 Handle IPBlock and port if exist if peer.IPBlock != nil { if len(peer.IPBlock.CIDR) > 0 { ipBlockIPSet, ipBlockSetInfo, err := ipBlockRule(netPolName, npmNetPol.Namespace, direction, matchType, ruleIndex, peerIdx, peer.IPBlock) if err != nil { return err } npmNetPol.RuleIPSets = append(npmNetPol.RuleIPSets, ipBlockIPSet) err = peerAndPortRule(npmNetPol, direction, ports, []policies.SetInfo{ipBlockSetInfo}, npmLiteToggle) if err != nil { return err } } // if npm lite is configured, check network policy only consists of CIDR blocks err := npmLiteValidPolicy(peer, npmLiteToggle) if err != nil { return err } // Do not need to run below code to translate PodSelector and NamespaceSelector // since IPBlock field is exclusive in NetworkPolicyPeer (i.e., peer in this code). continue } // if there is no PodSelector or NamespaceSelector in peer, no need to run the rest of codes. if peer.PodSelector == nil && peer.NamespaceSelector == nil { continue } // #2.2 handle nameSpaceSelector and port if exist if peer.PodSelector == nil && peer.NamespaceSelector != nil { // Before translating NamespaceSelector, flattenNameSpaceSelector function call should be called // to handle multiple values in matchExpressions spec. flattenNSSelector, err := flattenNameSpaceSelector(peer.NamespaceSelector) if err != nil { return err } for i := range flattenNSSelector { nsSelectorIPSets, nsSelectorList := nameSpaceSelector(matchType, &flattenNSSelector[i]) npmNetPol.RuleIPSets = append(npmNetPol.RuleIPSets, nsSelectorIPSets...) err := peerAndPortRule(npmNetPol, direction, ports, nsSelectorList, npmLiteToggle) if err != nil { return err } } continue } // #2.3 handle podSelector and port if exist if peer.PodSelector != nil && peer.NamespaceSelector == nil { psResult, err := podSelectorWithNS(npmNetPol.PolicyKey, npmNetPol.Namespace, matchType, peer.PodSelector) if err != nil { return err } npmNetPol.RuleIPSets = append(npmNetPol.RuleIPSets, psResult.psSets...) npmNetPol.RuleIPSets = append(npmNetPol.RuleIPSets, psResult.childPSSets...) err = peerAndPortRule(npmNetPol, direction, ports, psResult.psList, npmLiteToggle) if err != nil { return err } continue } // #2.4 handle namespaceSelector and podSelector and port if exist psResult, err := podSelector(npmNetPol.PolicyKey, matchType, peer.PodSelector) if err != nil { return err } npmNetPol.RuleIPSets = append(npmNetPol.RuleIPSets, psResult.psSets...) npmNetPol.RuleIPSets = append(npmNetPol.RuleIPSets, psResult.childPSSets...) // Before translating NamespaceSelector, flattenNameSpaceSelector function call should be called // to handle multiple values in matchExpressions spec. flattenNSSelector, err := flattenNameSpaceSelector(peer.NamespaceSelector) if err != nil { return err } for i := range flattenNSSelector { nsSelectorIPSets, nsSelectorList := nameSpaceSelector(matchType, &flattenNSSelector[i]) npmNetPol.RuleIPSets = append(npmNetPol.RuleIPSets, nsSelectorIPSets...) nsSelectorList = append(nsSelectorList, psResult.psList...) err := peerAndPortRule(npmNetPol, direction, ports, nsSelectorList, npmLiteToggle) if err != nil { return err } } } return nil } // defaultDropACL returns ACLPolicy to drop traffic which is not allowed. func defaultDropACL(direction policies.Direction) *policies.ACLPolicy { dropACL := policies.NewACLPolicy(policies.Dropped, direction) return dropACL } // allowAllPolicy adds acl to allow all traffic including internal (i.e,. K8s cluster) and external (i.e., internet) func allowAllPolicy(npmNetPol *policies.NPMNetworkPolicy, direction policies.Direction) { allowAllACL := policies.NewACLPolicy(policies.Allowed, direction) npmNetPol.ACLs = append(npmNetPol.ACLs, allowAllACL) } // isAllowAllToIngress returns true if this network policy allows all traffic from internal (i.e,. K8s cluster) and external (i.e., internet) // Otherwise, it returns false. func isAllowAllToIngress(ingress []networkingv1.NetworkPolicyIngressRule) bool { for _, ingressRule := range ingress { // Allow all if any of the ingress rules are allow all. if len(ingressRule.Ports) == 0 && len(ingressRule.From) == 0 { return true } } return false } // ingressPolicy traslates NetworkPolicyIngressRule in NetworkPolicy object // to NPMNetworkPolicy object. func ingressPolicy(npmNetPol *policies.NPMNetworkPolicy, netPolName string, ingress []networkingv1.NetworkPolicyIngressRule, npmLiteToggle bool) error { // #1. Allow all traffic from both internal and external. // In yaml file, it is specified with '{}'. if isAllowAllToIngress(ingress) { allowAllPolicy(npmNetPol, policies.Ingress) return nil } // #2. If ingress is nil (in yaml file, it is specified with '[]'), it means "Deny all" - it does not allow receiving any traffic from others. if ingress == nil { // Except for allow all traffic case in #1, the rest of them should have default drop rules. dropACL := defaultDropACL(policies.Ingress) npmNetPol.ACLs = append(npmNetPol.ACLs, dropACL) return nil } // #3. Ingress rule is not AllowAll (including internal and external) and DenyAll policy. // So, start translating ingress policy. for i, rule := range ingress { if err := translateRule(npmNetPol, netPolName, policies.Ingress, policies.SrcMatch, i, rule.Ports, rule.From, npmLiteToggle); err != nil { return err } } // Except for allow all traffic case in #1, the rest of them should have default drop rules. dropACL := defaultDropACL(policies.Ingress) npmNetPol.ACLs = append(npmNetPol.ACLs, dropACL) return nil } // isAllowAllToEgress returns true if this network policy allows all traffic to internal (i.e,. K8s cluster) and external (i.e., internet) // Otherwise, it returns false. func isAllowAllToEgress(egress []networkingv1.NetworkPolicyEgressRule) bool { for _, egressRule := range egress { // Allow all if any of the egress rules are allow all. if len(egressRule.Ports) == 0 && len(egressRule.To) == 0 { return true } } return false } // egressPolicy traslates NetworkPolicyEgressRule in networkpolicy object // to NPMNetworkPolicy object. func egressPolicy(npmNetPol *policies.NPMNetworkPolicy, netPolName string, egress []networkingv1.NetworkPolicyEgressRule, npmLiteToggle bool) error { // #1. Allow all traffic to both internal and external. // In yaml file, it is specified with '{}'. if isAllowAllToEgress(egress) { allowAllPolicy(npmNetPol, policies.Egress) return nil } // #2. If egress is nil (in yaml file, it is specified with '[]'), it means "Deny all" - it does not allow sending traffic to others. if egress == nil { // Except for allow all traffic case in #1, the rest of them should have default drop rules. dropACL := defaultDropACL(policies.Egress) npmNetPol.ACLs = append(npmNetPol.ACLs, dropACL) return nil } // #3. Egress rule is not AllowAll (including internal and external) and DenyAll. // So, start translating egress policy. for i, rule := range egress { err := translateRule(npmNetPol, netPolName, policies.Egress, policies.DstMatch, i, rule.Ports, rule.To, npmLiteToggle) if err != nil { return err } } // #3. Except for allow all traffic case in #1, the rest of them should have default drop rules. // Add drop ACL to drop the rest of traffic which is not specified in Egress Spec. dropACL := defaultDropACL(policies.Egress) npmNetPol.ACLs = append(npmNetPol.ACLs, dropACL) return nil } // TranslatePolicy translates networkpolicy object to NPMNetworkPolicy object // and returns the NPMNetworkPolicy object. func TranslatePolicy(npObj *networkingv1.NetworkPolicy, npmLiteToggle bool) (*policies.NPMNetworkPolicy, error) { netPolName := npObj.Name npmNetPol := policies.NewNPMNetworkPolicy(netPolName, npObj.Namespace) // podSelector in spec.PodSelector is common for ingress and egress. // Process this podSelector first. psResult, err := podSelectorWithNS(npmNetPol.PolicyKey, npmNetPol.Namespace, policies.EitherMatch, &npObj.Spec.PodSelector) if err != nil { return nil, err } npmNetPol.PodSelectorIPSets = psResult.psSets npmNetPol.ChildPodSelectorIPSets = psResult.childPSSets npmNetPol.PodSelectorList = psResult.psList // Each NetworkPolicy includes a policyTypes list which may include either Ingress, Egress, or both. // If no policyTypes are specified on a NetworkPolicy then by default Ingress will always be set // and Egress will be set if the NetworkPolicy has any egress rules. for _, ptype := range npObj.Spec.PolicyTypes { if ptype == networkingv1.PolicyTypeIngress { err := ingressPolicy(npmNetPol, netPolName, npObj.Spec.Ingress, npmLiteToggle) if err != nil { return nil, err } } else { err := egressPolicy(npmNetPol, netPolName, npObj.Spec.Egress, npmLiteToggle) if err != nil { return nil, err } } } // ad-hoc validation to reduce code changes (modifying function signatures and returning errors in all the correct places) if util.IsWindowsDP() { for _, acl := range npmNetPol.ACLs { if acl.Protocol == policies.SCTP { return nil, ErrUnsupportedSCTP } } } return npmNetPol, nil } // validates only CIDR based peer is present + no combination of CIDR with pod/namespace selectors are present func npmLiteValidPolicy(peer networkingv1.NetworkPolicyPeer, npmLiteEnabled bool) error { if npmLiteEnabled && (peer.PodSelector != nil || peer.NamespaceSelector != nil) { return ErrUnsupportedNonCIDR } return nil } func checkForNamedPortType(portKind netpolPortType, npmLiteToggle bool) error { if npmLiteToggle && portKind == namedPortType { return ErrUnsupportedNonCIDR } return nil } func checkOnlyPortRuleExists( portRuleExists, peerRuleExists, allowExternal bool, ports []networkingv1.NetworkPolicyPort, npmLiteToggle bool, direction policies.Direction, npmNetPol *policies.NPMNetworkPolicy, ) error { // #1. Only Ports fields exist in rule if portRuleExists && !peerRuleExists && !allowExternal { for i := range ports { portKind, err := portType(ports[i]) if err != nil { return err } err = checkForNamedPortType(portKind, npmLiteToggle) if err != nil { return err } portACL := policies.NewACLPolicy(policies.Allowed, direction) npmNetPol.RuleIPSets = portRule(npmNetPol.RuleIPSets, portACL, &ports[i], portKind) npmNetPol.ACLs = append(npmNetPol.ACLs, portACL) } } return nil }