diff --git a/npm/ipsm/ipsm.go b/npm/ipsm/ipsm.go index 7b52c2d233..79c08fc853 100644 --- a/npm/ipsm/ipsm.go +++ b/npm/ipsm/ipsm.go @@ -119,6 +119,10 @@ func (ipsMgr *IpsetManager) DeleteList(listName string) error { // AddToList inserts an ipset to an ipset list. func (ipsMgr *IpsetManager) AddToList(listName string, setName string) error { + if listName == setName { + return nil + } + if ipsMgr.Exists(listName, setName, util.IpsetSetListFlag) { return nil } diff --git a/npm/iptm/iptm.go b/npm/iptm/iptm.go index ff12e413e2..5d2e0d74a2 100644 --- a/npm/iptm/iptm.go +++ b/npm/iptm/iptm.go @@ -27,7 +27,6 @@ const ( type IptEntry struct { Command string Name string - HashedName string Chain string Flag string LockWaitTimeInSeconds string @@ -80,9 +79,9 @@ func (iptMgr *IptablesManager) InitNpmChains() error { // Add default allow CONNECTED/RELATED rule to AZURE-NPM chain. entry.Chain = util.IptablesAzureChain entry.Specs = []string{ - util.IptablesMatchFlag, + util.IptablesModuleFlag, + util.IptablesStateModuleFlag, util.IptablesStateFlag, - util.IptablesMatchStateFlag, util.IptablesRelatedState + "," + util.IptablesEstablishedState, util.IptablesJumpFlag, util.IptablesAccept, @@ -100,12 +99,38 @@ func (iptMgr *IptablesManager) InitNpmChains() error { } } + // Create AZURE-NPM-KUBE-SYSTEM chain. + if err := iptMgr.AddChain(util.IptablesAzureKubeSystemChain); err != nil { + return err + } + + // Append AZURE-NPM-KUBE-SYSTEM chain to AZURE-NPM chain. + entry = &IptEntry{ + Chain: util.IptablesAzureChain, + Specs: []string{ + util.IptablesJumpFlag, + util.IptablesAzureKubeSystemChain, + }, + } + exists, err = iptMgr.Exists(entry) + if err != nil { + return err + } + + if !exists { + iptMgr.OperationFlag = util.IptablesAppendFlag + if _, err = iptMgr.Run(entry); err != nil { + log.Errorf("Error: failed to add AZURE-NPM-KUBE-SYSTEM chain to AZURE-NPM chain.") + return err + } + } + // Create AZURE-NPM-INGRESS-PORT chain. if err := iptMgr.AddChain(util.IptablesAzureIngressPortChain); err != nil { return err } - // Insert AZURE-NPM-INGRESS-PORT chain to AZURE-NPM chain. + // Append AZURE-NPM-INGRESS-PORT chain to AZURE-NPM chain. entry.Chain = util.IptablesAzureChain entry.Specs = []string{util.IptablesJumpFlag, util.IptablesAzureIngressPortChain} exists, err = iptMgr.Exists(entry) @@ -121,13 +146,8 @@ func (iptMgr *IptablesManager) InitNpmChains() error { } } - // Create AZURE-NPM-INGRESS-FROM-NS chain. - if err = iptMgr.AddChain(util.IptablesAzureIngressFromNsChain); err != nil { - return err - } - - // Create AZURE-NPM-INGRESS-FROM-POD chain. - if err = iptMgr.AddChain(util.IptablesAzureIngressFromPodChain); err != nil { + // Create AZURE-NPM-INGRESS-FROM chain. + if err = iptMgr.AddChain(util.IptablesAzureIngressFromChain); err != nil { return err } @@ -152,13 +172,8 @@ func (iptMgr *IptablesManager) InitNpmChains() error { } } - // Create AZURE-NPM-EGRESS-TO-NS chain. - if err = iptMgr.AddChain(util.IptablesAzureEgressToNsChain); err != nil { - return err - } - - // Create AZURE-NPM-EGRESS-TO-POD chain. - if err = iptMgr.AddChain(util.IptablesAzureEgressToPodChain); err != nil { + // Create AZURE-NPM-EGRESS-TO chain. + if err = iptMgr.AddChain(util.IptablesAzureEgressToChain); err != nil { return err } @@ -167,7 +182,7 @@ func (iptMgr *IptablesManager) InitNpmChains() error { return err } - // Insert AZURE-NPM-TARGET-SETS chain to AZURE-NPM chain. + // Append AZURE-NPM-TARGET-SETS chain to AZURE-NPM chain. entry.Chain = util.IptablesAzureChain entry.Specs = []string{util.IptablesJumpFlag, util.IptablesAzureTargetSetsChain} exists, err = iptMgr.Exists(entry) @@ -190,12 +205,11 @@ func (iptMgr *IptablesManager) InitNpmChains() error { func (iptMgr *IptablesManager) UninitNpmChains() error { IptablesAzureChainList := []string{ util.IptablesAzureChain, + util.IptablesAzureKubeSystemChain, util.IptablesAzureIngressPortChain, - util.IptablesAzureIngressFromNsChain, - util.IptablesAzureIngressFromPodChain, + util.IptablesAzureIngressFromChain, util.IptablesAzureEgressPortChain, - util.IptablesAzureEgressToNsChain, - util.IptablesAzureEgressToPodChain, + util.IptablesAzureEgressToChain, util.IptablesAzureTargetSetsChain, } @@ -282,6 +296,7 @@ func (iptMgr *IptablesManager) DeleteChain(chain string) error { log.Printf("Chain doesn't exist %s.", entry.Chain) return nil } + log.Errorf("Error: failed to delete iptables chain %s.", entry.Chain) return err } @@ -291,7 +306,7 @@ func (iptMgr *IptablesManager) DeleteChain(chain string) error { // Add adds a rule in iptables. func (iptMgr *IptablesManager) Add(entry *IptEntry) error { - log.Printf("Add iptables entry: %+v.", entry) + log.Printf("Adding iptables entry: %+v.", entry) exists, err := iptMgr.Exists(entry) if err != nil { @@ -302,7 +317,7 @@ func (iptMgr *IptablesManager) Add(entry *IptEntry) error { return nil } - iptMgr.OperationFlag = util.IptablesInsertionFlag + iptMgr.OperationFlag = util.IptablesAppendFlag if _, err := iptMgr.Run(entry); err != nil { log.Errorf("Error: failed to create iptables rules.") return err diff --git a/npm/namespace.go b/npm/namespace.go index d0af0d6528..28b46623e1 100644 --- a/npm/namespace.go +++ b/npm/namespace.go @@ -14,23 +14,25 @@ import ( ) type namespace struct { - name string - setMap map[string]string - podMap map[types.UID]*corev1.Pod - npMap map[string]*networkingv1.NetworkPolicy - ipsMgr *ipsm.IpsetManager - iptMgr *iptm.IptablesManager + name string + setMap map[string]string + podMap map[types.UID]*corev1.Pod + rawNpMap map[string]*networkingv1.NetworkPolicy + processedNpMap map[string]*networkingv1.NetworkPolicy + ipsMgr *ipsm.IpsetManager + iptMgr *iptm.IptablesManager } // newNS constructs a new namespace object. func newNs(name string) (*namespace, error) { ns := &namespace{ - name: name, - setMap: make(map[string]string), - podMap: make(map[types.UID]*corev1.Pod), - npMap: make(map[string]*networkingv1.NetworkPolicy), - ipsMgr: ipsm.NewIpsetManager(), - iptMgr: iptm.NewIptablesManager(), + name: name, + setMap: make(map[string]string), + podMap: make(map[types.UID]*corev1.Pod), + rawNpMap: make(map[string]*networkingv1.NetworkPolicy), + processedNpMap: make(map[string]*networkingv1.NetworkPolicy), + ipsMgr: ipsm.NewIpsetManager(), + iptMgr: iptm.NewIptablesManager(), } return ns, nil @@ -40,16 +42,26 @@ func isSystemNs(nsObj *corev1.Namespace) bool { return nsObj.ObjectMeta.Name == util.KubeSystemFlag } +func (ns *namespace) policyExists(npObj *networkingv1.NetworkPolicy) bool { + if np, exists := ns.rawNpMap[npObj.ObjectMeta.Name]; exists { + if isSamePolicy(np, npObj) { + return true + } + } + + return false +} + // InitAllNsList syncs all-namespace ipset list. func (npMgr *NetworkPolicyManager) InitAllNsList() error { allNs := npMgr.nsMap[util.KubeAllNamespacesFlag] - for nsName := range npMgr.nsMap { - if nsName == util.KubeAllNamespacesFlag { + for ns:= range npMgr.nsMap { + if ns == util.KubeAllNamespacesFlag { continue } - if err := allNs.ipsMgr.AddToList(util.KubeAllNamespacesFlag, nsName); err != nil { - log.Errorf("Error: failed to add namespace set %s to list %s", nsName, util.KubeAllNamespacesFlag) + if err := allNs.ipsMgr.AddToList(util.KubeAllNamespacesFlag, ns); err != nil { + log.Errorf("Error: failed to add namespace set %s to ipset list %s", ns, util.KubeAllNamespacesFlag) return err } } @@ -60,13 +72,13 @@ func (npMgr *NetworkPolicyManager) InitAllNsList() error { // UninitAllNsList cleans all-namespace ipset list. func (npMgr *NetworkPolicyManager) UninitAllNsList() error { allNs := npMgr.nsMap[util.KubeAllNamespacesFlag] - for nsName := range npMgr.nsMap { - if nsName == util.KubeAllNamespacesFlag { + for ns := range npMgr.nsMap { + if ns == util.KubeAllNamespacesFlag { continue } - if err := allNs.ipsMgr.DeleteFromList(util.KubeAllNamespacesFlag, nsName); err != nil { - log.Errorf("Error: failed to delete namespace set %s from list %s", nsName, util.KubeAllNamespacesFlag) + if err := allNs.ipsMgr.DeleteFromList(util.KubeAllNamespacesFlag, ns); err != nil { + log.Errorf("Error: failed to delete namespace set %s from list %s", ns, util.KubeAllNamespacesFlag) return err } } @@ -81,8 +93,8 @@ func (npMgr *NetworkPolicyManager) AddNamespace(nsObj *corev1.Namespace) error { var err error - nsName, nsNs, nsLabel := nsObj.ObjectMeta.Name, nsObj.ObjectMeta.Namespace, nsObj.ObjectMeta.Labels - log.Printf("NAMESPACE CREATING: [%s/%s/%+v]", nsName, nsNs, nsLabel) + nsName, nsLabel := "ns-" + nsObj.ObjectMeta.Name, nsObj.ObjectMeta.Labels + log.Printf("NAMESPACE CREATING: [%s/%v]", nsName, nsLabel) ipsMgr := npMgr.nsMap[util.KubeAllNamespacesFlag].ipsMgr // Create ipset for the namespace. @@ -97,16 +109,21 @@ func (npMgr *NetworkPolicyManager) AddNamespace(nsObj *corev1.Namespace) error { } // Add the namespace to its label's ipset list. - var labelKeys []string nsLabels := nsObj.ObjectMeta.Labels for nsLabelKey, nsLabelVal := range nsLabels { - labelKey := util.GetNsIpsetName(nsLabelKey, nsLabelVal) + labelKey := "ns-" + nsLabelKey log.Printf("Adding namespace %s to ipset list %s", nsName, labelKey) if err = ipsMgr.AddToList(labelKey, nsName); err != nil { log.Errorf("Error: failed to add namespace %s to ipset list %s", nsName, labelKey) return err } - labelKeys = append(labelKeys, labelKey) + + label := "ns-" + nsLabelKey + ":" + nsLabelVal + log.Printf("Adding namespace %s to ipset list %s", nsName, label) + if err = ipsMgr.AddToList(label, nsName); err != nil { + log.Errorf("Error: failed to add namespace %s to ipset list %s", nsName, label) + return err + } } ns, err := newNs(nsName) @@ -122,11 +139,11 @@ func (npMgr *NetworkPolicyManager) AddNamespace(nsObj *corev1.Namespace) error { func (npMgr *NetworkPolicyManager) UpdateNamespace(oldNsObj *corev1.Namespace, newNsObj *corev1.Namespace) error { var err error - oldNsName, oldNsNs, oldNsLabel := oldNsObj.ObjectMeta.Name, oldNsObj.ObjectMeta.Namespace, oldNsObj.ObjectMeta.Labels - newNsName, newNsNs, newNsLabel := newNsObj.ObjectMeta.Name, newNsObj.ObjectMeta.Namespace, newNsObj.ObjectMeta.Labels + oldNsNs, oldNsLabel := "ns-" + oldNsObj.ObjectMeta.Name, oldNsObj.ObjectMeta.Labels + newNsNs, newNsLabel := "ns-" + newNsObj.ObjectMeta.Name, newNsObj.ObjectMeta.Labels log.Printf( - "NAMESPACE UPDATING:\n old namespace: [%s/%s/%+v]\n new namespace: [%s/%s/%+v]", - oldNsName, oldNsNs, oldNsLabel, newNsName, newNsNs, newNsLabel, + "NAMESPACE UPDATING:\n old namespace: [%s/%v]\n new namespace: [%s/%v]", + oldNsNs, oldNsLabel, newNsNs, newNsLabel, ) if err = npMgr.DeleteNamespace(oldNsObj); err != nil { @@ -149,8 +166,8 @@ func (npMgr *NetworkPolicyManager) DeleteNamespace(nsObj *corev1.Namespace) erro var err error - nsName, nsNs, nsLabel := nsObj.ObjectMeta.Name, nsObj.ObjectMeta.Namespace, nsObj.ObjectMeta.Labels - log.Printf("NAMESPACE DELETING: [%s/%s/%+v]", nsName, nsNs, nsLabel) + nsName, nsLabel := "ns-" + nsObj.ObjectMeta.Name, nsObj.ObjectMeta.Labels + log.Printf("NAMESPACE DELETING: [%s/%v]", nsName, nsLabel) _, exists := npMgr.nsMap[nsName] if !exists { @@ -159,16 +176,21 @@ func (npMgr *NetworkPolicyManager) DeleteNamespace(nsObj *corev1.Namespace) erro // Delete the namespace from its label's ipset list. ipsMgr := npMgr.nsMap[util.KubeAllNamespacesFlag].ipsMgr - var labelKeys []string nsLabels := nsObj.ObjectMeta.Labels for nsLabelKey, nsLabelVal := range nsLabels { - labelKey := util.GetNsIpsetName(nsLabelKey, nsLabelVal) + labelKey := "ns-" + nsLabelKey log.Printf("Deleting namespace %s from ipset list %s", nsName, labelKey) if err = ipsMgr.DeleteFromList(labelKey, nsName); err != nil { log.Errorf("Error: failed to delete namespace %s from ipset list %s", nsName, labelKey) return err } - labelKeys = append(labelKeys, labelKey) + + label := "ns-" + nsLabelKey + ":" + nsLabelVal + log.Printf("Deleting namespace %s from ipset list %s", nsName, label) + if err = ipsMgr.DeleteFromList(label, nsName); err != nil { + log.Errorf("Error: failed to delete namespace %s from ipset list %s", nsName, label) + return err + } } // Delete the namespace from all-namespace ipset list. diff --git a/npm/npm.go b/npm/npm.go index fdab95e5c0..d7b39af855 100644 --- a/npm/npm.go +++ b/npm/npm.go @@ -48,6 +48,7 @@ type NetworkPolicyManager struct { nodeName string nsMap map[string]*namespace isAzureNpmChainCreated bool + isSafeToCleanUpAzureNpmChain bool clusterState telemetry.ClusterState reportManager *telemetry.ReportManager @@ -219,6 +220,7 @@ func NewNetworkPolicyManager(clientset *kubernetes.Clientset, informerFactory in nodeName: os.Getenv("HOSTNAME"), nsMap: make(map[string]*namespace), isAzureNpmChainCreated: false, + isSafeToCleanUpAzureNpmChain: false, clusterState: telemetry.ClusterState{ PodCount: 0, NsCount: 0, diff --git a/npm/nwpolicy.go b/npm/nwpolicy.go index cd5a27bd94..b2f8aa912e 100644 --- a/npm/nwpolicy.go +++ b/npm/nwpolicy.go @@ -3,21 +3,52 @@ package npm import ( + "github.com/Azure/azure-container-networking/npm/iptm" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/npm/util" networkingv1 "k8s.io/api/networking/v1" ) +func (npMgr *NetworkPolicyManager) canCleanUpNpmChains() bool { + if !npMgr.isSafeToCleanUpAzureNpmChain { + return false + } + + for _, ns := range npMgr.nsMap { + if len(ns.processedNpMap) > 0 { + return false + } + } + + return true +} + // AddNetworkPolicy handles adding network policy to iptables. func (npMgr *NetworkPolicyManager) AddNetworkPolicy(npObj *networkingv1.NetworkPolicy) error { npMgr.Lock() defer npMgr.Unlock() - var err error + var ( + err error + ns *namespace + ) - npNs, npName := npObj.ObjectMeta.Namespace, npObj.ObjectMeta.Name + npNs, npName := "ns-" + npObj.ObjectMeta.Namespace, npObj.ObjectMeta.Name log.Printf("NETWORK POLICY CREATING: %v", npObj) + var exists bool + if ns, exists = npMgr.nsMap[npNs]; !exists { + ns, err = newNs(npNs) + if err != nil { + log.Printf("Error creating namespace %s\n", npNs) + } + npMgr.nsMap[npNs] = ns + } + + if ns.policyExists(npObj) { + return nil + } + allNs := npMgr.nsMap[util.KubeAllNamespacesFlag] if !npMgr.isAzureNpmChainCreated { @@ -34,28 +65,49 @@ func (npMgr *NetworkPolicyManager) AddNetworkPolicy(npObj *networkingv1.NetworkP npMgr.isAzureNpmChainCreated = true } - podSets, nsLists, iptEntries := parsePolicy(npObj) + hashedSelector := HashSelector(&npObj.Spec.PodSelector) + + var addedPolicy *networkingv1.NetworkPolicy + addedPolicy = nil + if oldPolicy, oldPolicyExists := ns.processedNpMap[hashedSelector]; oldPolicyExists { + addedPolicy, err = addPolicy(oldPolicy, npObj) + if err != nil { + log.Printf("Error adding policy %s to %s", npName, oldPolicy.ObjectMeta.Name) + } + npMgr.isSafeToCleanUpAzureNpmChain = false + npMgr.Unlock() + npMgr.DeleteNetworkPolicy(oldPolicy) + npMgr.Lock() + npMgr.isSafeToCleanUpAzureNpmChain = true + } else { + ns.processedNpMap[hashedSelector] = npObj + } + var sets, lists []string + var iptEntries []*iptm.IptEntry + if addedPolicy != nil { + sets, lists, iptEntries = translatePolicy(addedPolicy) + } else { + sets, lists, iptEntries = translatePolicy(npObj) + } ipsMgr := allNs.ipsMgr - for _, set := range podSets { + for _, set := range sets { + log.Printf("Creating set: %v, hashedSet: %v", set, util.GetHashedName(set)) if err = ipsMgr.CreateSet(set); err != nil { - log.Errorf("Error: failed to create ipset %s-%s", npNs, set) + log.Printf("Error creating ipset %s", set) return err } } - - for _, list := range nsLists { + for _, list := range lists { if err = ipsMgr.CreateList(list); err != nil { - log.Errorf("Error: failed to create ipset list %s-%s", npNs, list) + log.Printf("Error creating ipset list %s", list) return err } } - if err = npMgr.InitAllNsList(); err != nil { - log.Errorf("Error: failed to initialize all-namespace ipset list.") + log.Printf("Error initializing all-namespace ipset list.") return err } - iptMgr := allNs.iptMgr for _, iptEntry := range iptEntries { if err = iptMgr.Add(iptEntry); err != nil { @@ -64,14 +116,6 @@ func (npMgr *NetworkPolicyManager) AddNetworkPolicy(npObj *networkingv1.NetworkP } } - allNs.npMap[npName] = npObj - - ns, err := newNs(npNs) - if err != nil { - log.Errorf("Error: failed to create namespace %s", npNs) - } - npMgr.nsMap[npNs] = ns - return nil } @@ -99,14 +143,26 @@ func (npMgr *NetworkPolicyManager) DeleteNetworkPolicy(npObj *networkingv1.Netwo npMgr.Lock() defer npMgr.Unlock() - var err error + var ( + err error + ns *namespace + ) - npName := npObj.ObjectMeta.Name + npNs, npName := "ns-" + npObj.ObjectMeta.Namespace, npObj.ObjectMeta.Name log.Printf("NETWORK POLICY DELETING: %v", npObj) + var exists bool + if ns, exists = npMgr.nsMap[npNs]; !exists { + ns, err = newNs(npName) + if err != nil { + log.Printf("Error creating namespace %s", npNs) + } + npMgr.nsMap[npNs] = ns + } + allNs := npMgr.nsMap[util.KubeAllNamespacesFlag] - _, _, iptEntries := parsePolicy(npObj) + _, _, iptEntries := translatePolicy(npObj) iptMgr := allNs.iptMgr for _, iptEntry := range iptEntries { @@ -116,9 +172,21 @@ func (npMgr *NetworkPolicyManager) DeleteNetworkPolicy(npObj *networkingv1.Netwo } } - delete(allNs.npMap, npName) - - if len(allNs.npMap) == 0 { + hashedSelector := HashSelector(&npObj.Spec.PodSelector) + if oldPolicy, oldPolicyExists := ns.processedNpMap[hashedSelector]; oldPolicyExists { + deductedPolicy, err := deductPolicy(oldPolicy, npObj) + if err != nil { + log.Printf("Error deducting policy %s from %s", npName, oldPolicy.ObjectMeta.Name) + } + + if deductedPolicy == nil { + delete(ns.processedNpMap, hashedSelector) + } else { + ns.processedNpMap[hashedSelector] = deductedPolicy + } + } + + if npMgr.canCleanUpNpmChains() { if err = iptMgr.UninitNpmChains(); err != nil { log.Errorf("Error: failed to uninitialize azure-npm chains.") return err diff --git a/npm/nwpolicy_test.go b/npm/nwpolicy_test.go index 1a2445929b..e6f48ce72d 100644 --- a/npm/nwpolicy_test.go +++ b/npm/nwpolicy_test.go @@ -54,7 +54,7 @@ func TestAddNetworkPolicy(t *testing.T) { nsObj := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-nwpolicy", + Namespace: "test-nwpolicy", Labels: map[string]string{ "app": "test-namespace", }, @@ -66,7 +66,8 @@ func TestAddNetworkPolicy(t *testing.T) { } tcp := corev1.ProtocolTCP - allow := &networkingv1.NetworkPolicy{ + port8000 := intstr.FromInt(8000) + allowIngress := &networkingv1.NetworkPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "allow-ingress", Namespace: "test-nwpolicy", @@ -81,17 +82,43 @@ func TestAddNetworkPolicy(t *testing.T) { }}, Ports: []networkingv1.NetworkPolicyPort{{ Protocol: &tcp, - Port: &intstr.IntOrString{ - StrVal: "8000", + Port: &port8000, + }}, + }, + }, + }, + } + + if err := npMgr.AddNetworkPolicy(allowIngress); err != nil { + t.Errorf("TestAddNetworkPolicy failed @ allowIngress AddNetworkPolicy") + t.Errorf("Error: %v", err) + } + + allowEgress := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "allow-egress", + Namespace: "test-nwpolicy", + }, + Spec: networkingv1.NetworkPolicySpec{ + Egress: []networkingv1.NetworkPolicyEgressRule{ + networkingv1.NetworkPolicyEgressRule{ + To: []networkingv1.NetworkPolicyPeer{{ + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "test"}, }, }}, + Ports: []networkingv1.NetworkPolicyPort{{ + Protocol: &tcp, + Port: &port8000, + }}, }, }, }, } - if err := npMgr.AddNetworkPolicy(allow); err != nil { - t.Errorf("TestAddNetworkPolicy failed @ AddNetworkPolicy") + if err := npMgr.AddNetworkPolicy(allowEgress); err != nil { + t.Errorf("TestAddNetworkPolicy failed @ allowEgress AddNetworkPolicy") + t.Errorf("Error: %v", err) } } diff --git a/npm/parse.go b/npm/parse.go deleted file mode 100644 index 977256c6a8..0000000000 --- a/npm/parse.go +++ /dev/null @@ -1,1003 +0,0 @@ -// Copyright 2018 Microsoft. All rights reserved. -// MIT License -package npm - -import ( - "fmt" - - "github.com/Azure/azure-container-networking/log" - "github.com/Azure/azure-container-networking/npm/iptm" - "github.com/Azure/azure-container-networking/npm/util" - networkingv1 "k8s.io/api/networking/v1" -) - -type portsInfo struct { - protocol string - port string -} - -func appendAndClearSets(podNsRuleSets *[]string, nsRuleLists *[]string, policyRuleSets *[]string, policyRuleLists *[]string) { - *policyRuleSets = append(*policyRuleSets, *podNsRuleSets...) - *policyRuleLists = append(*policyRuleLists, *nsRuleLists...) - podNsRuleSets, nsRuleLists = nil, nil -} - -func parseIngress(ns string, targetSets []string, rules []networkingv1.NetworkPolicyIngressRule) ([]string, []string, []*iptm.IptEntry) { - var ( - portRuleExists = false - fromRuleExists = false - isAppliedToNs = false - protPortPairSlice []*portsInfo - podNsRuleSets []string // pod sets listed in one ingress rules. - nsRuleLists []string // namespace sets listed in one ingress rule - policyRuleSets []string // policy-wise pod sets - policyRuleLists []string // policy-wise namespace sets - entries []*iptm.IptEntry - ) - - if len(targetSets) == 0 { - targetSets = append(targetSets, ns) - isAppliedToNs = true - } - - if isAppliedToNs { - hashedTargetSetName := util.GetHashedName(ns) - - nsDrop := &iptm.IptEntry{ - Name: ns, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureTargetSetsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesDrop, - }, - } - entries = append(entries, nsDrop) - } - - // Use hashed string for ipset name to avoid string length limit of ipset. - for _, targetSet := range targetSets { - log.Printf("Parsing iptables for label %s", targetSet) - - hashedTargetSetName := util.GetHashedName(targetSet) - - if len(rules) == 0 { - drop := &iptm.IptEntry{ - Name: targetSet, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureIngressPortChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesDrop, - }, - } - entries = append(entries, drop) - continue - } - - // allow kube-system - hashedKubeSystemSet := util.GetHashedName(util.KubeSystemFlag) - allowKubeSystemIngress := &iptm.IptEntry{ - Name: util.KubeSystemFlag, - HashedName: hashedKubeSystemSet, - Chain: util.IptablesAzureIngressPortChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedKubeSystemSet, - util.IptablesSrcFlag, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, allowKubeSystemIngress) - - for _, rule := range rules { - for _, portRule := range rule.Ports { - protPortPairSlice = append(protPortPairSlice, - &portsInfo{ - protocol: string(*portRule.Protocol), - port: fmt.Sprint(portRule.Port.IntVal), - }) - - portRuleExists = true - } - - if rule.From != nil { - for _, fromRule := range rule.From { - if fromRule.PodSelector != nil { - fromRuleExists = true - } - if fromRule.NamespaceSelector != nil { - fromRuleExists = true - } - if fromRule.IPBlock != nil { - fromRuleExists = true - } - } - } - } - - for _, rule := range rules { - if !portRuleExists && !fromRuleExists { - allow := &iptm.IptEntry{ - Name: targetSet, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureIngressPortChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, allow) - continue - } - - if !portRuleExists { - entry := &iptm.IptEntry{ - Name: targetSet, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureIngressPortChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAzureIngressFromNsChain, - }, - } - entries = append(entries, entry) - } else { - for _, protPortPair := range protPortPairSlice { - entry := &iptm.IptEntry{ - Name: targetSet, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureIngressPortChain, - Specs: []string{ - util.IptablesProtFlag, - protPortPair.protocol, - util.IptablesDstPortFlag, - protPortPair.port, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAzureIngressFromNsChain, - }, - } - entries = append(entries, entry) - } - } - - if !fromRuleExists { - entry := &iptm.IptEntry{ - Name: targetSet, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureIngressFromNsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, entry) - continue - } - - for _, fromRule := range rule.From { - // Handle IPBlock field of NetworkPolicyPeer - if fromRule.IPBlock != nil { - if len(fromRule.IPBlock.CIDR) > 0 { - cidrEntry := &iptm.IptEntry{ - Chain: util.IptablesAzureIngressFromNsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesSFlag, - fromRule.IPBlock.CIDR, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, cidrEntry) - } - - if len(fromRule.IPBlock.Except) > 0 { - for _, except := range fromRule.IPBlock.Except { - entry := &iptm.IptEntry{ - Chain: util.IptablesAzureIngressFromNsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesSFlag, - except, - util.IptablesJumpFlag, - util.IptablesDrop, - }, - } - entries = append(entries, entry) - } - } - } - - if fromRule.PodSelector == nil && fromRule.NamespaceSelector == nil { - continue - } - - // Allow traffic from namespaceSelector - if fromRule.PodSelector == nil && fromRule.NamespaceSelector != nil { - // allow traffic from all namespaces - if len(fromRule.NamespaceSelector.MatchLabels) == 0 { - nsRuleLists = append(nsRuleLists, util.KubeAllNamespacesFlag) - } - - for nsLabelKey, nsLabelVal := range fromRule.NamespaceSelector.MatchLabels { - nsRuleLists = append(nsRuleLists, util.GetNsIpsetName(nsLabelKey, nsLabelVal)) - } - - for _, nsRuleSet := range nsRuleLists { - hashedNsRuleSetName := util.GetHashedName(nsRuleSet) - entry := &iptm.IptEntry{ - Name: nsRuleSet, - HashedName: hashedNsRuleSetName, - Chain: util.IptablesAzureIngressFromNsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedNsRuleSetName, - util.IptablesSrcFlag, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, entry) - } - appendAndClearSets(&podNsRuleSets, &nsRuleLists, &policyRuleSets, &policyRuleLists) - continue - } - - // Allow traffic from podSelector - if fromRule.PodSelector != nil && fromRule.NamespaceSelector == nil { - // allow traffic from the same namespace - if len(fromRule.PodSelector.MatchLabels) == 0 { - podNsRuleSets = append(podNsRuleSets, ns) - } - - for podLabelKey, podLabelVal := range fromRule.PodSelector.MatchLabels { - podNsRuleSets = append(podNsRuleSets, util.KubeAllNamespacesFlag+"-"+podLabelKey+":"+podLabelVal) - } - - // Handle PodSelector field of NetworkPolicyPeer. - for _, podRuleSet := range podNsRuleSets { - hashedPodRuleSetName := util.GetHashedName(podRuleSet) - nsEntry := &iptm.IptEntry{ - Name: podRuleSet, - HashedName: hashedPodRuleSetName, - Chain: util.IptablesAzureIngressFromNsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAzureIngressFromPodChain, - }, - } - entries = append(entries, nsEntry) - - podEntry := &iptm.IptEntry{ - Name: podRuleSet, - HashedName: hashedPodRuleSetName, - Chain: util.IptablesAzureIngressFromPodChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedPodRuleSetName, - util.IptablesSrcFlag, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, podEntry) - } - appendAndClearSets(&podNsRuleSets, &nsRuleLists, &policyRuleSets, &policyRuleLists) - continue - } - - // Allow traffic from podSelector intersects namespaceSelector - // This is only supported in kubernetes version >= 1.11 - if util.IsNewNwPolicyVerFlag { - // allow traffic from all namespaces - if len(fromRule.NamespaceSelector.MatchLabels) == 0 { - nsRuleLists = append(nsRuleLists, util.KubeAllNamespacesFlag) - } - - for nsLabelKey, nsLabelVal := range fromRule.NamespaceSelector.MatchLabels { - nsRuleLists = append(nsRuleLists, util.GetNsIpsetName(nsLabelKey, nsLabelVal)) - } - - for _, nsRuleSet := range nsRuleLists { - hashedNsRuleSetName := util.GetHashedName(nsRuleSet) - entry := &iptm.IptEntry{ - Name: nsRuleSet, - HashedName: hashedNsRuleSetName, - Chain: util.IptablesAzureIngressFromNsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedNsRuleSetName, - util.IptablesSrcFlag, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAzureIngressFromPodChain, - }, - } - entries = append(entries, entry) - } - - // allow traffic from the same namespace - if len(fromRule.PodSelector.MatchLabels) == 0 { - podNsRuleSets = append(podNsRuleSets, ns) - } - - for podLabelKey, podLabelVal := range fromRule.PodSelector.MatchLabels { - podNsRuleSets = append(podNsRuleSets, util.KubeAllNamespacesFlag+"-"+podLabelKey+":"+podLabelVal) - } - - // Handle PodSelector field of NetworkPolicyPeer. - for _, podRuleSet := range podNsRuleSets { - hashedPodRuleSetName := util.GetHashedName(podRuleSet) - entry := &iptm.IptEntry{ - Name: podRuleSet, - HashedName: hashedPodRuleSetName, - Chain: util.IptablesAzureIngressFromPodChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedPodRuleSetName, - util.IptablesSrcFlag, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, entry) - } - appendAndClearSets(&podNsRuleSets, &nsRuleLists, &policyRuleSets, &policyRuleLists) - } - } - } - } - - log.Printf("finished parsing ingress rule") - return policyRuleSets, policyRuleLists, entries -} - -func parseEgress(ns string, targetSets []string, rules []networkingv1.NetworkPolicyEgressRule) ([]string, []string, []*iptm.IptEntry) { - var ( - portRuleExists = false - toRuleExists = false - isAppliedToNs = false - protPortPairSlice []*portsInfo - podNsRuleSets []string // pod sets listed in one ingress rules. - nsRuleLists []string // namespace sets listed in one ingress rule - policyRuleSets []string // policy-wise pod sets - policyRuleLists []string // policy-wise namespace sets - entries []*iptm.IptEntry - ) - - if len(targetSets) == 0 { - targetSets = append(targetSets, ns) - isAppliedToNs = true - } - - if isAppliedToNs { - hashedTargetSetName := util.GetHashedName(ns) - - nsDrop := &iptm.IptEntry{ - Name: ns, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureTargetSetsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesDrop, - }, - } - entries = append(entries, nsDrop) - } - - // Use hashed string for ipset name to avoid string length limit of ipset. - for _, targetSet := range targetSets { - log.Printf("Parsing iptables for label %s", targetSet) - - hashedTargetSetName := util.GetHashedName(targetSet) - - if len(rules) == 0 { - drop := &iptm.IptEntry{ - Name: targetSet, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureEgressPortChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesJumpFlag, - util.IptablesDrop, - }, - } - entries = append(entries, drop) - continue - } - - // allow kube-system - hashedKubeSystemSet := util.GetHashedName(util.KubeSystemFlag) - allowKubeSystemEgress := &iptm.IptEntry{ - Name: util.KubeSystemFlag, - HashedName: hashedKubeSystemSet, - Chain: util.IptablesAzureEgressPortChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedKubeSystemSet, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, allowKubeSystemEgress) - - for _, rule := range rules { - for _, portRule := range rule.Ports { - protPortPairSlice = append(protPortPairSlice, - &portsInfo{ - protocol: string(*portRule.Protocol), - port: fmt.Sprint(portRule.Port.IntVal), - }) - - portRuleExists = true - } - - if rule.To != nil { - for _, toRule := range rule.To { - if toRule.PodSelector != nil { - toRuleExists = true - } - if toRule.NamespaceSelector != nil { - toRuleExists = true - } - if toRule.IPBlock != nil { - toRuleExists = true - } - } - } - } - - for _, rule := range rules { - if !portRuleExists && !toRuleExists { - allow := &iptm.IptEntry{ - Name: targetSet, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureEgressPortChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, allow) - continue - } - - if !portRuleExists { - entry := &iptm.IptEntry{ - Name: targetSet, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureEgressPortChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesJumpFlag, - util.IptablesAzureEgressToNsChain, - }, - } - entries = append(entries, entry) - } else { - for _, protPortPair := range protPortPairSlice { - entry := &iptm.IptEntry{ - Name: targetSet, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureEgressPortChain, - Specs: []string{ - util.IptablesProtFlag, - protPortPair.protocol, - util.IptablesDstPortFlag, - protPortPair.port, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesJumpFlag, - util.IptablesAzureEgressToNsChain, - }, - } - entries = append(entries, entry) - } - } - - if !toRuleExists { - entry := &iptm.IptEntry{ - Name: targetSet, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureEgressToNsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, entry) - continue - } - - for _, toRule := range rule.To { - // Handle IPBlock field of NetworkPolicyPeer - if toRule.IPBlock != nil { - if len(toRule.IPBlock.CIDR) > 0 { - cidrEntry := &iptm.IptEntry{ - Chain: util.IptablesAzureEgressToNsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesDFlag, - toRule.IPBlock.CIDR, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, cidrEntry) - } - - if len(toRule.IPBlock.Except) > 0 { - for _, except := range toRule.IPBlock.Except { - entry := &iptm.IptEntry{ - Chain: util.IptablesAzureEgressToNsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesDFlag, - except, - util.IptablesJumpFlag, - util.IptablesDrop, - }, - } - entries = append(entries, entry) - } - } - } - - if toRule.PodSelector == nil && toRule.NamespaceSelector == nil { - continue - } - - // Allow traffic from namespaceSelector - if toRule.PodSelector == nil && toRule.NamespaceSelector != nil { - // allow traffic from all namespaces - if len(toRule.NamespaceSelector.MatchLabels) == 0 { - nsRuleLists = append(nsRuleLists, util.KubeAllNamespacesFlag) - } - - for nsLabelKey, nsLabelVal := range toRule.NamespaceSelector.MatchLabels { - nsRuleLists = append(nsRuleLists, util.GetNsIpsetName(nsLabelKey, nsLabelVal)) - } - - for _, nsRuleSet := range nsRuleLists { - hashedNsRuleSetName := util.GetHashedName(nsRuleSet) - entry := &iptm.IptEntry{ - Name: nsRuleSet, - HashedName: hashedNsRuleSetName, - Chain: util.IptablesAzureEgressToNsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedNsRuleSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, entry) - } - appendAndClearSets(&podNsRuleSets, &nsRuleLists, &policyRuleSets, &policyRuleLists) - continue - } - - // Allow traffic from podSelector - if toRule.PodSelector != nil && toRule.NamespaceSelector == nil { - // allow traffic from the same namespace - if len(toRule.PodSelector.MatchLabels) == 0 { - podNsRuleSets = append(podNsRuleSets, ns) - } - - for podLabelKey, podLabelVal := range toRule.PodSelector.MatchLabels { - podNsRuleSets = append(podNsRuleSets, util.KubeAllNamespacesFlag+"-"+podLabelKey+":"+podLabelVal) - } - - // Handle PodSelector field of NetworkPolicyPeer. - for _, podRuleSet := range podNsRuleSets { - hashedPodRuleSetName := util.GetHashedName(podRuleSet) - nsEntry := &iptm.IptEntry{ - Name: podRuleSet, - HashedName: hashedPodRuleSetName, - Chain: util.IptablesAzureEgressToNsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesJumpFlag, - util.IptablesAzureEgressToPodChain, - }, - } - entries = append(entries, nsEntry) - - podEntry := &iptm.IptEntry{ - Name: podRuleSet, - HashedName: hashedPodRuleSetName, - Chain: util.IptablesAzureEgressToPodChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedPodRuleSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, podEntry) - } - appendAndClearSets(&podNsRuleSets, &nsRuleLists, &policyRuleSets, &policyRuleLists) - continue - } - - // Allow traffic from podSelector intersects namespaceSelector - // This is only supported in kubernetes version >= 1.11 - if util.IsNewNwPolicyVerFlag { - log.Printf("Kubernetes version > 1.11, parsing podSelector AND namespaceSelector") - // allow traffic from all namespaces - if len(toRule.NamespaceSelector.MatchLabels) == 0 { - nsRuleLists = append(nsRuleLists, util.KubeAllNamespacesFlag) - } - - for nsLabelKey, nsLabelVal := range toRule.NamespaceSelector.MatchLabels { - nsRuleLists = append(nsRuleLists, util.GetNsIpsetName(nsLabelKey, nsLabelVal)) - } - - for _, nsRuleSet := range nsRuleLists { - hashedNsRuleSetName := util.GetHashedName(nsRuleSet) - entry := &iptm.IptEntry{ - Name: nsRuleSet, - HashedName: hashedNsRuleSetName, - Chain: util.IptablesAzureEgressToNsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedNsRuleSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAzureEgressToPodChain, - }, - } - entries = append(entries, entry) - } - - // allow traffic from the same namespace - if len(toRule.PodSelector.MatchLabels) == 0 { - podNsRuleSets = append(podNsRuleSets, ns) - } - - for podLabelKey, podLabelVal := range toRule.PodSelector.MatchLabels { - podNsRuleSets = append(podNsRuleSets, util.KubeAllNamespacesFlag+"-"+podLabelKey+":"+podLabelVal) - } - - // Handle PodSelector field of NetworkPolicyPeer. - for _, podRuleSet := range podNsRuleSets { - hashedPodRuleSetName := util.GetHashedName(podRuleSet) - entry := &iptm.IptEntry{ - Name: podRuleSet, - HashedName: hashedPodRuleSetName, - Chain: util.IptablesAzureEgressToPodChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedPodRuleSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, entry) - } - appendAndClearSets(&podNsRuleSets, &nsRuleLists, &policyRuleSets, &policyRuleLists) - } - } - } - } - - log.Printf("finished parsing ingress rule") - return policyRuleSets, policyRuleLists, entries -} - -// Drop all non-whitelisted packets. -func getDefaultDropEntries(targetSets []string) []*iptm.IptEntry { - var entries []*iptm.IptEntry - - for _, targetSet := range targetSets { - hashedTargetSetName := util.GetHashedName(targetSet) - entry := &iptm.IptEntry{ - Name: targetSet, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureTargetSetsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesJumpFlag, - util.IptablesDrop, - }, - } - entries = append(entries, entry) - - entry = &iptm.IptEntry{ - Name: targetSet, - HashedName: hashedTargetSetName, - Chain: util.IptablesAzureTargetSetsChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesDrop, - }, - } - entries = append(entries, entry) - } - - return entries -} - -// Allow traffic from/to kube-system pods -func getAllowKubeSystemEntries(ns string, targetSets []string) []*iptm.IptEntry { - var entries []*iptm.IptEntry - - if len(targetSets) == 0 { - targetSets = append(targetSets, ns) - } - - for _, targetSet := range targetSets { - hashedTargetSetName := util.GetHashedName(targetSet) - hashedKubeSystemSet := util.GetHashedName(util.KubeSystemFlag) - allowKubeSystemIngress := &iptm.IptEntry{ - Name: util.KubeSystemFlag, - HashedName: hashedKubeSystemSet, - Chain: util.IptablesAzureIngressPortChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedKubeSystemSet, - util.IptablesSrcFlag, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, allowKubeSystemIngress) - - allowKubeSystemEgress := &iptm.IptEntry{ - Name: util.KubeSystemFlag, - HashedName: hashedKubeSystemSet, - Chain: util.IptablesAzureEgressPortChain, - Specs: []string{ - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedTargetSetName, - util.IptablesSrcFlag, - util.IptablesMatchFlag, - util.IptablesSetFlag, - util.IptablesMatchSetFlag, - hashedKubeSystemSet, - util.IptablesDstFlag, - util.IptablesJumpFlag, - util.IptablesAccept, - }, - } - entries = append(entries, allowKubeSystemEgress) - } - - return entries -} - -// ParsePolicy parses network policy. -func parsePolicy(npObj *networkingv1.NetworkPolicy) ([]string, []string, []*iptm.IptEntry) { - var ( - resultPodSets []string - resultNsLists []string - affectedSets []string - entries []*iptm.IptEntry - ) - - // Get affected pods. - npNs, selector := npObj.ObjectMeta.Namespace, npObj.Spec.PodSelector.MatchLabels - for podLabelKey, podLabelVal := range selector { - affectedSet := util.KubeAllNamespacesFlag + "-" + podLabelKey + ":" + podLabelVal - affectedSets = append(affectedSets, affectedSet) - } - - if len(npObj.Spec.Ingress) > 0 || len(npObj.Spec.Egress) > 0 { - entries = append(entries, getAllowKubeSystemEntries(npNs, affectedSets)...) - } - - if len(npObj.Spec.PolicyTypes) == 0 { - ingressPodSets, ingressNsSets, ingressEntries := parseIngress(npNs, affectedSets, npObj.Spec.Ingress) - resultPodSets = append(resultPodSets, ingressPodSets...) - resultNsLists = append(resultNsLists, ingressNsSets...) - entries = append(entries, ingressEntries...) - - egressPodSets, egressNsSets, egressEntries := parseEgress(npNs, affectedSets, npObj.Spec.Egress) - resultPodSets = append(resultPodSets, egressPodSets...) - resultNsLists = append(resultNsLists, egressNsSets...) - entries = append(entries, egressEntries...) - - entries = append(entries, getDefaultDropEntries(affectedSets)...) - - resultPodSets = append(resultPodSets, affectedSets...) - - return util.UniqueStrSlice(resultPodSets), util.UniqueStrSlice(resultNsLists), entries - } - - for _, ptype := range npObj.Spec.PolicyTypes { - if ptype == networkingv1.PolicyTypeIngress { - ingressPodSets, ingressNsSets, ingressEntries := parseIngress(npNs, affectedSets, npObj.Spec.Ingress) - resultPodSets = append(resultPodSets, ingressPodSets...) - resultNsLists = append(resultNsLists, ingressNsSets...) - entries = append(entries, ingressEntries...) - } - - if ptype == networkingv1.PolicyTypeEgress { - egressPodSets, egressNsSets, egressEntries := parseEgress(npNs, affectedSets, npObj.Spec.Egress) - resultPodSets = append(resultPodSets, egressPodSets...) - resultNsLists = append(resultNsLists, egressNsSets...) - entries = append(entries, egressEntries...) - } - - entries = append(entries, getDefaultDropEntries(affectedSets)...) - } - - resultPodSets = append(resultPodSets, affectedSets...) - resultPodSets = append(resultPodSets, npNs) - - return util.UniqueStrSlice(resultPodSets), util.UniqueStrSlice(resultNsLists), entries -} diff --git a/npm/parsePolicy.go b/npm/parsePolicy.go new file mode 100644 index 0000000000..a7444247f1 --- /dev/null +++ b/npm/parsePolicy.go @@ -0,0 +1,141 @@ +// Copyright 2018 Microsoft. All rights reserved. +// MIT License +package npm + +import ( + "fmt" + "reflect" + + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func isSamePolicy(old, new *networkingv1.NetworkPolicy) bool { + if !reflect.DeepEqual(old.TypeMeta, new.TypeMeta) { + return false + } + + if old.ObjectMeta.Namespace != new.ObjectMeta.Namespace { + return false + } + + if !reflect.DeepEqual(old.Spec, new.Spec) { + return false + } + + return true +} + +// addPolicy merges policies based on labels. +func addPolicy(old, new *networkingv1.NetworkPolicy) (*networkingv1.NetworkPolicy, error) { + // if namespace matches && podSelector matches, then merge + // else return as is. + if !reflect.DeepEqual(old.TypeMeta, new.TypeMeta) { + return nil, fmt.Errorf("Old and new networkpolicies don't have the same TypeMeta") + } + + if old.ObjectMeta.Namespace != new.ObjectMeta.Namespace { + return nil, fmt.Errorf("Old and new networkpolicies don't have the same namespace") + } + + if !reflect.DeepEqual(old.Spec.PodSelector, new.Spec.PodSelector) { + return nil, fmt.Errorf("Old and new networkpolicies don't apply to the same set of target pods") + } + + addedPolicy := &networkingv1.NetworkPolicy{ + TypeMeta: old.TypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: old.ObjectMeta.Name, + Namespace: old.ObjectMeta.Namespace, + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: old.Spec.PodSelector, + }, + } + + spec := &(addedPolicy.Spec) + if len(old.Spec.PolicyTypes) == 1 && old.Spec.PolicyTypes[0] == networkingv1.PolicyTypeEgress && + len(new.Spec.PolicyTypes) == 1 && new.Spec.PolicyTypes[0] == networkingv1.PolicyTypeEgress { + spec.PolicyTypes = []networkingv1.PolicyType{networkingv1.PolicyTypeEgress} + } else { + spec.PolicyTypes = []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + networkingv1.PolicyTypeEgress, + } + } + + ingress := append(old.Spec.Ingress, new.Spec.Ingress...) + egress := append(old.Spec.Egress, new.Spec.Egress...) + addedPolicy.Spec.Ingress = ingress + addedPolicy.Spec.Egress = egress + + return addedPolicy, nil +} + +// deductPolicy deduct one policy from the other. +func deductPolicy(old, new *networkingv1.NetworkPolicy) (*networkingv1.NetworkPolicy, error) { + // if namespace matches && podSelector matches, then merge + // else return as is. + if !reflect.DeepEqual(old.TypeMeta, new.TypeMeta) { + return nil, fmt.Errorf("Old and new networkpolicy don't have the same TypeMeta") + } + + if old.ObjectMeta.Namespace != new.ObjectMeta.Namespace { + return nil, fmt.Errorf("Old and new networkpolicy don't have the same namespace") + } + + if !reflect.DeepEqual(old.Spec.PodSelector, new.Spec.PodSelector) { + return nil, fmt.Errorf("Old and new networkpolicy don't have apply to the same set of target pods") + } + + if reflect.DeepEqual(old.Spec, new.Spec) { + return nil, nil + } + + deductedPolicy := &networkingv1.NetworkPolicy{ + TypeMeta: old.TypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: old.ObjectMeta.Name, + Namespace: old.ObjectMeta.Namespace, + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: old.Spec.PodSelector, + }, + } + + deductedIngress, newIngress := old.Spec.Ingress, new.Spec.Ingress + deductedEgress, newEgress := old.Spec.Egress, new.Spec.Egress + for _, ni := range newIngress { + for i, di := range deductedIngress { + if reflect.DeepEqual(di, ni) { + deductedIngress = append(deductedIngress[:i], deductedIngress[i+1:]...) + break + } + } + } + + for _, ne := range newEgress { + for i, de := range deductedEgress { + if reflect.DeepEqual(de, ne) { + deductedEgress = append(deductedEgress[:i], deductedEgress[i+1:]...) + break + } + } + } + + deductedPolicy.Spec.Ingress = deductedIngress + deductedPolicy.Spec.Egress = deductedEgress + + if len(old.Spec.PolicyTypes) == 1 && old.Spec.PolicyTypes[0] == networkingv1.PolicyTypeEgress && + len(new.Spec.PolicyTypes) == 1 && new.Spec.PolicyTypes[0] == networkingv1.PolicyTypeEgress && + len(deductedIngress) == 0 { + deductedPolicy.Spec.PolicyTypes = []networkingv1.PolicyType{networkingv1.PolicyTypeEgress} + } else { + deductedPolicy.Spec.PolicyTypes = []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + networkingv1.PolicyTypeEgress, + } + } + + return deductedPolicy, nil +} diff --git a/npm/parsePolicy_test.go b/npm/parsePolicy_test.go new file mode 100644 index 0000000000..dd2b06eb08 --- /dev/null +++ b/npm/parsePolicy_test.go @@ -0,0 +1,358 @@ +package npm + +import ( + "reflect" + "testing" + + "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func TestAddPolicy(t *testing.T) { + tcp, udp := v1.ProtocolTCP, v1.ProtocolUDP + port6783, port6784 := intstr.FromInt(6783), intstr.FromInt(6784) + oldIngressPodSelector := &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "db", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "frontend", + "backend", + }, + }, + }, + } + oldIngressNamespaceSelector := &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "ns": "dev", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "frontend-ns", + "backend-ns", + }, + }, + }, + } + oldEgressPodSelector := &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "sql", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testNotIn", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{ + "frontend", + "backend", + }, + }, + }, + } + oldPolicy := networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "role": "client", + }, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Protocol: &tcp, + Port: &port6783, + }, + }, + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: oldIngressPodSelector, + }, + networkingv1.NetworkPolicyPeer{ + PodSelector: oldIngressNamespaceSelector, + }, + }, + }, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{ + networkingv1.NetworkPolicyEgressRule{ + Ports: []networkingv1.NetworkPolicyPort{}, + To: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: oldEgressPodSelector, + }, + }, + }, + }, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeEgress, + }, + }, + } + + newPolicy := oldPolicy + npPort6784 := networkingv1.NetworkPolicyPort{ + Protocol: &udp, + Port: &port6784, + } + newPolicy.Spec.Ingress[0].Ports = append(newPolicy.Spec.Ingress[0].Ports, npPort6784) + newPolicy.Spec.Ingress[0].From[0].PodSelector.MatchLabels["status"] = "ok" + newIngressNamespaceSelector := &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "ns": "new", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testNotIn", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{ + "frontend-ns", + "backend-ns", + }, + }, + }, + } + newPolicy.Spec.Ingress[0].From[1].PodSelector = newIngressNamespaceSelector + + expectedIngress := append(oldPolicy.Spec.Ingress, newPolicy.Spec.Ingress...) + expectedEgress := append(oldPolicy.Spec.Egress, newPolicy.Spec.Egress...) + expectedPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "role": "client", + }, + }, + Ingress: expectedIngress, + Egress: expectedEgress, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeEgress, + }, + }, + } + + addedPolicy, err := addPolicy(&oldPolicy, &newPolicy) + if err != nil || !reflect.DeepEqual(addedPolicy, expectedPolicy) { + t.Errorf("TestAddPolicy failed") + } +} + +func TestDeductPolicy(t *testing.T) { + tcp := v1.ProtocolTCP + port6783 := intstr.FromInt(6783) + oldIngressPodSelector := &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "db", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "frontend", + "backend", + }, + }, + }, + } + oldIngressNamespaceSelector := &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "ns": "dev", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "frontend-ns", + "backend-ns", + }, + }, + }, + } + oldEgressPodSelector := &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "sql", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testNotIn", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{ + "frontend", + "backend", + }, + }, + }, + } + oldPolicy := networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "role": "client", + }, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Protocol: &tcp, + Port: &port6783, + }, + }, + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: oldIngressPodSelector, + }, + networkingv1.NetworkPolicyPeer{ + PodSelector: oldIngressNamespaceSelector, + }, + }, + }, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{ + networkingv1.NetworkPolicyEgressRule{ + Ports: []networkingv1.NetworkPolicyPort{}, + To: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: oldEgressPodSelector, + }, + }, + }, + }, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeEgress, + }, + }, + } + + newPolicy := networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "role": "client", + }, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Protocol: &tcp, + Port: &port6783, + }, + }, + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: oldIngressPodSelector, + }, + }, + }, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{ + networkingv1.NetworkPolicyEgressRule{ + Ports: []networkingv1.NetworkPolicyPort{}, + To: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeEgress, + }, + }, + } + + expectedPolicy := oldPolicy + expectedPolicy.Spec.PolicyTypes = []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + networkingv1.PolicyTypeEgress, + } + deductedPolicy, err := deductPolicy(&oldPolicy, &newPolicy) + if err != nil || !reflect.DeepEqual(deductedPolicy, &expectedPolicy) { + t.Errorf( + "TestDeductPolicy failed.\n"+ + "Expected Policy: %v\n"+ + "DeductedPolicy: %v\n", + &expectedPolicy, + deductedPolicy, + ) + } + + newPolicy = oldPolicy + deductedPolicy, err = deductPolicy(&oldPolicy, &newPolicy) + if err != nil || deductedPolicy != nil { + t.Errorf( + "TestDeductPolicy failed.\n"+ + "Expected Policy: %v\n"+ + "DeductedPolicy: %v\n", + &expectedPolicy, + deductedPolicy, + ) + } + + newPolicy = networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "role": "client", + }, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{ + networkingv1.NetworkPolicyEgressRule{ + Ports: []networkingv1.NetworkPolicyPort{}, + To: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: oldEgressPodSelector, + }, + }, + }, + }, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeEgress, + }, + }, + } + + expectedPolicy = oldPolicy + expectedPolicy.Spec.Egress = []networkingv1.NetworkPolicyEgressRule{} + expectedPolicy.Spec.PolicyTypes = []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + networkingv1.PolicyTypeEgress, + } + deductedPolicy, err = deductPolicy(&oldPolicy, &newPolicy) + if err != nil || !reflect.DeepEqual(deductedPolicy, &expectedPolicy) { + t.Errorf( + "TestDeductPolicy failed.\n"+ + "Expected Policy: %v\n"+ + "DeductedPolicy: %v\n", + &expectedPolicy, + deductedPolicy, + ) + } +} diff --git a/npm/parseSelector.go b/npm/parseSelector.go new file mode 100644 index 0000000000..f762aef3b1 --- /dev/null +++ b/npm/parseSelector.go @@ -0,0 +1,193 @@ +package npm + +import ( + "fmt" + "sort" + "container/heap" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/Azure/azure-container-networking/log" + "github.com/Azure/azure-container-networking/npm/util" +) + +// An ReqHeap is a min-heap of labelSelectorRequirements. +type ReqHeap []metav1.LabelSelectorRequirement + +func (h ReqHeap) Len() int { + return len(h) +} + +func (h ReqHeap) Less(i, j int) bool { + sort.Strings(h[i].Values) + sort.Strings(h[j].Values) + + if int(h[i].Key[0]) < int(h[j].Key[0]) { + return true + } + + if int(h[i].Key[0]) > int(h[j].Key[0]) { + return false + } + + if len(h[i].Values) == 0 { + return true + } + + if len(h[j].Values) == 0 { + return false + } + + if len(h[i].Values[0]) == 0 { + return true + } + + if len(h[j].Values[0]) == 0 { + return false + } + + return int(h[i].Values[0][0]) < int(h[j].Values[0][0]) +} + +func (h ReqHeap) Swap(i, j int) { + h[i], h[j] = h[j], h[i] +} + +func (h *ReqHeap) Push(x interface{}) { + sort.Strings(x.(metav1.LabelSelectorRequirement).Values) + *h = append(*h, x.(metav1.LabelSelectorRequirement)) +} + +func (h *ReqHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n -1] + *h = old[0 : n - 1] + + return x +} + +// ParseLabel takes a Azure-NPM processed label then returns if it's referring to complement set, +// and if so, returns the original set as well. +func ParseLabel(label string) (string, bool) { + //The input label is guaranteed to have a non-zero length validated by k8s. + //For label definition, see below parseSelector() function. + if label[0:1] == util.IptablesNotFlag { + return label[1:], true + } + return label, false +} + +// GetOperatorAndLabel returns the operator associated with the label and the label without operator. +func GetOperatorAndLabel(label string) (string, string) { + if len(label) == 0 { + return "", "" + } + + if string(label[0]) == util.IptablesNotFlag { + return util.IptablesNotFlag, label[1:] + } + + return "", label +} + +// GetOperatorsAndLabels returns the operators along with the associated labels. +func GetOperatorsAndLabels(labelsWithOps []string) ([]string, []string) { + var ops, labelsWithoutOps []string + for _, labelWithOp := range labelsWithOps { + op, labelWithoutOp := GetOperatorAndLabel(labelWithOp) + ops = append(ops, op) + labelsWithoutOps = append(labelsWithoutOps, labelWithoutOp) + } + + return ops, labelsWithoutOps +} + +// sortSelector sorts the member fields of the selector in an alphebatical order. +func sortSelector(selector *metav1.LabelSelector) { + _, _ = util.SortMap(&selector.MatchLabels) + + reqHeap := &ReqHeap{} + heap.Init(reqHeap) + for _, req := range selector.MatchExpressions { + heap.Push(reqHeap, req) + } + + var sortedReqs []metav1.LabelSelectorRequirement + for reqHeap.Len() > 0 { + sortedReqs = append(sortedReqs, heap.Pop(reqHeap).(metav1.LabelSelectorRequirement)) + + } + selector.MatchExpressions = sortedReqs +} + +// HashSelector returns the hash value of the selector. +func HashSelector(selector *metav1.LabelSelector) string { + sortSelector(selector) + return util.Hash(fmt.Sprintf("%v", selector)) +} + +// parseSelector takes a LabelSelector and returns a slice of processed labels, keys and values. +func parseSelector(selector *metav1.LabelSelector) ([]string, []string, []string) { + var ( + labels []string + keys []string + vals []string + ) + + if selector == nil { + return labels, keys, vals + } + + if len(selector.MatchLabels) == 0 && len(selector.MatchExpressions) == 0 { + labels = append(labels, "") + keys = append(keys, "") + vals = append(vals, "") + + return labels, keys, vals + } + + sortedKeys, sortedVals := util.SortMap(&selector.MatchLabels) + + for i := range sortedKeys { + labels = append(labels, sortedKeys[i]+":"+sortedVals[i]) + } + keys = append(keys, sortedKeys...) + vals = append(vals, sortedVals...) + + for _, req := range selector.MatchExpressions { + var k string + switch op := req.Operator; op { + case metav1.LabelSelectorOpIn: + for _, v := range req.Values { + k = req.Key + keys = append(keys, k) + vals = append(vals, v) + labels = append(labels, k+":"+v) + } + case metav1.LabelSelectorOpNotIn: + for _, v := range req.Values { + k = util.IptablesNotFlag + req.Key + keys = append(keys, k) + vals = append(vals, v) + labels = append(labels, k+":"+v) + } + // Exists matches pods with req.Key as key + case metav1.LabelSelectorOpExists: + k = req.Key + keys = append(keys, req.Key) + vals = append(vals, "") + labels = append(labels, k) + // DoesNotExist matches pods without req.Key as key + case metav1.LabelSelectorOpDoesNotExist: + k = util.IptablesNotFlag + req.Key + keys = append(keys, k) + vals = append(vals, "") + labels = append(labels, k) + default: + log.Errorf("Invalid operator [%s] for selector [%v] requirement", op, *selector) + } + } + + return labels, keys, vals +} diff --git a/npm/parseSelector_test.go b/npm/parseSelector_test.go new file mode 100644 index 0000000000..877530fa9b --- /dev/null +++ b/npm/parseSelector_test.go @@ -0,0 +1,528 @@ +package npm + +import ( + "reflect" + "testing" + "container/heap" + + "github.com/Azure/azure-container-networking/npm/util" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestParseLabel(t *testing.T) { + label, isComplementSet := ParseLabel("test:frontend") + expectedLabel := "test:frontend" + if isComplementSet || label != expectedLabel { + t.Errorf("TestParseLabel failed @ label %s", label) + } + + label, isComplementSet = ParseLabel("!test:frontend") + expectedLabel = "test:frontend" + if !isComplementSet || label != expectedLabel { + t.Errorf("TestParseLabel failed @ label %s", label) + } + + label, isComplementSet = ParseLabel("test") + expectedLabel = "test" + if isComplementSet || label != expectedLabel { + t.Errorf("TestParseLabel failed @ label %s", label) + } + + label, isComplementSet = ParseLabel("!test") + expectedLabel = "test" + if !isComplementSet || label != expectedLabel { + t.Errorf("TestParseLabel failed @ label %s", label) + } + + label, isComplementSet = ParseLabel("!!test") + expectedLabel = "!test" + if !isComplementSet || label != expectedLabel { + t.Errorf("TestParseLabel failed @ label %s", label) + } + + label, isComplementSet = ParseLabel("test:!frontend") + expectedLabel = "test:!frontend" + if isComplementSet || label != expectedLabel { + t.Errorf("TestParseLabel failed @ label %s", label) + } + + label, isComplementSet = ParseLabel("!test:!frontend") + expectedLabel = "test:!frontend" + if !isComplementSet || label != expectedLabel { + t.Errorf("TestParseLabel failed @ label %s", label) + } +} + +func TestGetOperatorAndLabel(t *testing.T) { + testLabels := []string{ + "a", + "k:v", + "", + "!a:b", + "!a", + } + + resultOperators, resultLabels := []string{}, []string{} + for _, testLabel := range testLabels { + resultOperator, resultLabel := GetOperatorAndLabel(testLabel) + resultOperators = append(resultOperators, resultOperator) + resultLabels = append(resultLabels, resultLabel) + } + + expectedOperators := []string{ + "", + "", + "", + util.IptablesNotFlag, + util.IptablesNotFlag, + } + + expectedLabels := []string{ + "a", + "k:v", + "", + "a:b", + "a", + } + + if !reflect.DeepEqual(resultOperators, expectedOperators) { + t.Errorf("TestGetOperatorAndLabel failed @ operator comparison") + } + + + if !reflect.DeepEqual(resultLabels, expectedLabels) { + t.Errorf("TestGetOperatorAndLabel failed @ label comparison") + } +} + +func TestGetOperatorsAndLabels(t *testing.T) { + testLabels := []string{ + "k:v", + "", + "!a:b", + } + + resultOps, resultLabels := GetOperatorsAndLabels(testLabels) + expectedOps := []string{ + "", + "", + "!", + } + expectedLabels := []string{ + "k:v", + "", + "a:b", + } + + if !reflect.DeepEqual(resultOps, expectedOps) { + t.Errorf("TestGetOperatorsAndLabels failed @ op comparision") + } + + if !reflect.DeepEqual(resultLabels, expectedLabels) { + t.Errorf("TestGetOperatorsAndLabels failed @ label comparision") + } +} + +func TestReqHeap(t *testing.T) { + reqHeap := &ReqHeap{ + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "frontend", + "backend", + }, + }, + metav1.LabelSelectorRequirement{ + Key: "a", + Operator: metav1.LabelSelectorOpIn, + Values: []string{}, + }, + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "b", + "a", + }, + }, + } + + heap.Init(reqHeap) + heap.Push( + reqHeap, + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "a", + }, + }, + ) + + expectedReqHeap := &ReqHeap{ + metav1.LabelSelectorRequirement{ + Key: "a", + Operator: metav1.LabelSelectorOpIn, + Values: []string{}, + }, + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "a", + }, + }, + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "a", + "b", + }, + }, + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "backend", + "frontend", + }, + }, + } + + if !reflect.DeepEqual(reqHeap, expectedReqHeap) { + t.Errorf("TestReqHeap failed @ heap comparison") + t.Errorf("reqHeap: %v", reqHeap) + t.Errorf("expectedReqHeap: %v", expectedReqHeap) + } +} + +func TestSortSelector(t *testing.T) { + selector := &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "frontend", + "backend", + }, + }, + metav1.LabelSelectorRequirement{ + Key: "a", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "b", + }, + }, + }, + MatchLabels: map[string]string{ + "c": "d", + "a": "b", + }, + } + + sortSelector(selector) + expectedSelector := &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "a", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "b", + }, + }, + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "backend", + "frontend", + }, + }, + }, + MatchLabels: map[string]string{ + "a": "b", + "c": "d", + }, + } + + if !reflect.DeepEqual(selector, expectedSelector) { + t.Errorf("TestSortSelector failed @ sort selector comparison") + t.Errorf("selector: %v", selector) + t.Errorf("expectedSelector: %v", expectedSelector) + } +} + +func TestHashSelector(t *testing.T) { + firstSelector := &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "frontend", + "backend", + }, + }, + }, + MatchLabels: map[string]string{ + "a": "b", + "c": "d", + }, + } + + secondSelector := &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "backend", + "frontend", + }, + }, + }, + MatchLabels: map[string]string{ + "c": "d", + "a": "b", + }, + } + + hashedFirstSelector := HashSelector(firstSelector) + hashedSecondSelector := HashSelector(secondSelector) + if hashedFirstSelector != hashedSecondSelector { + t.Errorf("TestHashSelector failed @ hashed selector comparison") + t.Errorf("hashedFirstSelector: %v", hashedFirstSelector) + t.Errorf("hashedSecondSelector: %v", hashedSecondSelector) + } +} + +func TestParseSelector(t *testing.T) { + var selector, expectedSelector *metav1.LabelSelector + selector, expectedSelector = nil, nil + labels, keys, vals := parseSelector(selector) + expectedLabels, expectedKeys, expectedVals := []string{}, []string{}, []string{} + + if len(labels) != len(expectedLabels) { + t.Errorf("TestparseSelector failed @ labels length comparison") + } + + if len(keys) != len(expectedKeys) { + t.Errorf("TestparseSelector failed @ keys length comparison") + } + + if len(vals) != len(expectedVals) { + t.Errorf("TestparseSelector failed @ vals length comparison") + } + + if selector != expectedSelector { + t.Errorf("TestparseSelector failed @ vals length comparison") + } + + selector = &metav1.LabelSelector{} + labels, keys, vals = parseSelector(selector) + expectedLabels, expectedKeys, expectedVals = []string{""}, []string{""}, []string{""} + if len(labels) != len(expectedLabels) { + t.Errorf("TestparseSelector failed @ labels length comparison") + } + + if len(keys) != len(expectedKeys) { + t.Errorf("TestparseSelector failed @ keys length comparison") + } + + if len(vals) != len(expectedVals) { + t.Errorf("TestparseSelector failed @ vals length comparison") + } + + selector = &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "frontend", + "backend", + }, + }, + }, + } + + labels, keys, vals = parseSelector(selector) + expectedLabels = []string{ + "testIn:frontend", + "testIn:backend", + } + expectedKeys = []string{ + "testIn", + "testIn", + } + expectedVals = []string{ + "frontend", + "backend", + } + + if len(labels) != len(expectedLabels) { + t.Errorf("TestparseSelector failed @ labels length comparison") + } + + if len(keys) != len(expectedKeys) { + t.Errorf("TestparseSelector failed @ keys length comparison") + } + + if len(vals) != len(vals) { + t.Errorf("TestparseSelector failed @ vals length comparison") + } + + if !reflect.DeepEqual(labels, expectedLabels) { + t.Errorf("TestparseSelector failed @ label comparison") + } + if !reflect.DeepEqual(keys, expectedKeys) { + t.Errorf("TestparseSelector failed @ key comparison") + } + if !reflect.DeepEqual(vals, expectedVals) { + t.Errorf("TestparseSelector failed @ value comparison") + } + + notIn := metav1.LabelSelectorRequirement{ + Key: "testNotIn", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{ + "frontend", + "backend", + }, + } + + me := &selector.MatchExpressions + *me = append(*me, notIn) + + labels, keys, vals = parseSelector(selector) + addedLabels := []string{ + "!testNotIn:frontend", + "!testNotIn:backend", + } + addedKeys := []string{ + "!testNotIn", + "!testNotIn", + } + addedVals := []string{ + "frontend", + "backend", + } + expectedLabels = append(expectedLabels, addedLabels...) + expectedKeys = append(expectedKeys, addedKeys...) + expectedVals = append(expectedVals, addedVals...) + + if len(labels) != len(expectedLabels) { + t.Errorf("TestparseSelector failed @ labels length comparison") + } + + if len(keys) != len(expectedKeys) { + t.Errorf("TestparseSelector failed @ keys length comparison") + } + + if len(vals) != len(vals) { + t.Errorf("TestparseSelector failed @ vals length comparison") + } + + if !reflect.DeepEqual(labels, expectedLabels) { + t.Errorf("TestparseSelector failed @ label comparison") + } + if !reflect.DeepEqual(keys, expectedKeys) { + t.Errorf("TestparseSelector failed @ key comparison") + } + if !reflect.DeepEqual(vals, expectedVals) { + t.Errorf("TestparseSelector failed @ value comparison") + } + + exists := metav1.LabelSelectorRequirement{ + Key: "testExists", + Operator: metav1.LabelSelectorOpExists, + Values: []string{}, + } + + *me = append(*me, exists) + + labels, keys, vals = parseSelector(selector) + addedLabels = []string{ + "testExists", + } + addedKeys = []string{ + "testExists", + } + addedVals = []string{ + "", + } + expectedLabels = append(expectedLabels, addedLabels...) + expectedKeys = append(expectedKeys, addedKeys...) + expectedVals = append(expectedVals, addedVals...) + + if len(labels) != len(expectedLabels) { + t.Errorf("TestparseSelector failed @ labels length comparison") + } + + if len(keys) != len(expectedKeys) { + t.Errorf("TestparseSelector failed @ keys length comparison") + } + + if len(vals) != len(vals) { + t.Errorf("TestparseSelector failed @ vals length comparison") + } + + if !reflect.DeepEqual(labels, expectedLabels) { + t.Errorf("TestparseSelector failed @ label comparison") + } + if !reflect.DeepEqual(keys, expectedKeys) { + t.Errorf("TestparseSelector failed @ key comparison") + } + if !reflect.DeepEqual(vals, expectedVals) { + t.Errorf("TestparseSelector failed @ value comparison") + } + + doesNotExist := metav1.LabelSelectorRequirement{ + Key: "testDoesNotExist", + Operator: metav1.LabelSelectorOpDoesNotExist, + Values: []string{}, + } + + *me = append(*me, doesNotExist) + + labels, keys, vals = parseSelector(selector) + addedLabels = []string{ + "!testDoesNotExist", + } + addedKeys = []string{ + "!testDoesNotExist", + } + addedVals = []string{ + "", + } + expectedLabels = append(expectedLabels, addedLabels...) + expectedKeys = append(expectedKeys, addedKeys...) + expectedVals = append(expectedVals, addedVals...) + + if len(labels) != len(expectedLabels) { + t.Errorf("TestparseSelector failed @ labels length comparison") + } + + if len(keys) != len(expectedKeys) { + t.Errorf("TestparseSelector failed @ keys length comparison") + } + + if len(vals) != len(vals) { + t.Errorf("TestparseSelector failed @ vals length comparison") + } + + if !reflect.DeepEqual(labels, expectedLabels) { + t.Errorf("TestparseSelector failed @ label comparison") + } + + if !reflect.DeepEqual(keys, expectedKeys) { + t.Errorf("TestparseSelector failed @ key comparison") + } + + if !reflect.DeepEqual(vals, expectedVals) { + t.Errorf("TestparseSelector failed @ value comparison") + } +} diff --git a/npm/plugin/main.go b/npm/plugin/main.go index 1f24059bc7..501b271354 100644 --- a/npm/plugin/main.go +++ b/npm/plugin/main.go @@ -64,8 +64,7 @@ func main() { time.Sleep(time.Second * waitForTelemetryInSeconds) - err = npMgr.Start(wait.NeverStop) - if err != nil { + if err = npMgr.Start(wait.NeverStop); err != nil { log.Logf("npm failed with error %v.", err) panic(err.Error) } diff --git a/npm/pod.go b/npm/pod.go index af35897a9a..427e01d44a 100644 --- a/npm/pod.go +++ b/npm/pod.go @@ -3,8 +3,6 @@ package npm import ( - "strings" - "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/npm/util" @@ -33,7 +31,7 @@ func (npMgr *NetworkPolicyManager) AddPod(podObj *corev1.Pod) error { var err error - podNs := podObj.ObjectMeta.Namespace + podNs := "ns-" + podObj.ObjectMeta.Namespace podName := podObj.ObjectMeta.Name podNodeName := podObj.Spec.NodeName podLabels := podObj.ObjectMeta.Labels @@ -50,20 +48,19 @@ func (npMgr *NetworkPolicyManager) AddPod(podObj *corev1.Pod) error { } // Add the pod to its label's ipset. - var labelKeys []string for podLabelKey, podLabelVal := range podLabels { - //Ignore pod-template-hash label. - if strings.Contains(podLabelKey, util.KubePodTemplateHashFlag) { - continue + log.Printf("Adding pod %s to ipset %s", podIP, podLabelKey) + if err = ipsMgr.AddToSet(podLabelKey, podIP); err != nil { + log.Errorf("Error: failed to add pod to label ipset.") + return err } - labelKey := util.KubeAllNamespacesFlag + "-" + podLabelKey + ":" + podLabelVal - log.Printf("Adding pod %s to ipset %s", podIP, labelKey) - if err = ipsMgr.AddToSet(labelKey, podIP); err != nil { + label := podLabelKey + ":" + podLabelVal + log.Printf("Adding pod %s to ipset %s", podIP, label) + if err = ipsMgr.AddToSet(label, podIP); err != nil { log.Errorf("Error: failed to add pod to label ipset.") return err } - labelKeys = append(labelKeys, labelKey) } ns, err := newNs(podNs) @@ -125,7 +122,7 @@ func (npMgr *NetworkPolicyManager) DeletePod(podObj *corev1.Pod) error { var err error - podNs := podObj.ObjectMeta.Namespace + podNs := "ns-" + podObj.ObjectMeta.Namespace podName := podObj.ObjectMeta.Name podNodeName := podObj.Spec.NodeName podLabels := podObj.ObjectMeta.Labels @@ -141,13 +138,15 @@ func (npMgr *NetworkPolicyManager) DeletePod(podObj *corev1.Pod) error { } // Delete the pod from its label's ipset. for podLabelKey, podLabelVal := range podLabels { - //Ignore pod-template-hash label. - if strings.Contains(podLabelKey, "pod-template-hash") { - continue - } + log.Printf("Deleting pod %s from ipset %s", podIP, podLabelKey) + if err = ipsMgr.DeleteFromSet(podLabelKey, podIP); err != nil { + log.Errorf("Error: failed to delete pod from label ipset.") + return err + } - labelKey := util.KubeAllNamespacesFlag + "-" + podLabelKey + ":" + podLabelVal - if err = ipsMgr.DeleteFromSet(labelKey, podIP); err != nil { + label := podLabelKey + ":" + podLabelVal + log.Printf("Deleting pod %s from ipset %s", podIP, label) + if err = ipsMgr.DeleteFromSet(label, podIP); err != nil { log.Errorf("Error: failed to delete pod from label ipset.") return err } diff --git a/npm/translatePolicy.go b/npm/translatePolicy.go new file mode 100644 index 0000000000..fc636ea47f --- /dev/null +++ b/npm/translatePolicy.go @@ -0,0 +1,1021 @@ +// Copyright 2018 Microsoft. All rights reserved. +// MIT License +package npm + +import ( + "github.com/Azure/azure-container-networking/log" + "github.com/Azure/azure-container-networking/npm/iptm" + "github.com/Azure/azure-container-networking/npm/util" + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type portsInfo struct { + protocol string + port string +} + +func craftPartialIptEntrySpecFromPort(portRule networkingv1.NetworkPolicyPort, sPortOrDPortFlag string) []string { + partialSpec := []string{} + if portRule.Protocol != nil { + partialSpec = append( + partialSpec, + util.IptablesProtFlag, + string(*portRule.Protocol), + ) + } + + if portRule.Port != nil { + partialSpec = append( + partialSpec, + sPortOrDPortFlag, + portRule.Port.String(), + ) + } + + return partialSpec +} + +func craftPartialIptablesCommentFromPort(portRule networkingv1.NetworkPolicyPort, sPortOrDPortFlag string) string { + partialComment := "" + if portRule.Protocol != nil { + partialComment += string(*portRule.Protocol) + if portRule.Port != nil { + partialComment += "-" + } + } + + if portRule.Port != nil { + partialComment += "PORT-" + partialComment += portRule.Port.String() + } + + if portRule.Protocol != nil || portRule.Port != nil { + partialComment += "-OF-" + } + + return partialComment +} + +func craftPartialIptEntrySpecFromOpAndLabel(op, label, srcOrDstFlag string, isNamespaceSelector bool) []string { + if isNamespaceSelector { + label = "ns-" + label + } + partialSpec := []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + op, + util.IptablesMatchSetFlag, + util.GetHashedName(label), + srcOrDstFlag, + } + + return util.DropEmptyFields(partialSpec) +} + +func craftPartialIptEntrySpecFromOpsAndLabels(ns string, ops, labels []string, srcOrDstFlag string, isNamespaceSelector bool) []string { + var spec []string + + if len(ops) == 1 && len(labels) == 1 { + if ops[0] == "" && labels[0] == "" { + if !isNamespaceSelector { + // This is an empty podSelector, + // selecting all the pods within its namespace. + spec = []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-" + ns), + srcOrDstFlag, + } + } else { + // This is an empty namespaceSelector, + // selecting all namespaces. + spec = []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName(util.KubeAllNamespacesFlag), + srcOrDstFlag, + } + } + + return spec + } + } + + for i, _ := range ops { + spec = append(spec, craftPartialIptEntrySpecFromOpAndLabel(ops[i], labels[i], srcOrDstFlag, isNamespaceSelector)...) + } + + return spec +} + +func craftPartialIptEntrySpecFromSelector(ns string, selector *metav1.LabelSelector, srcOrDstFlag string, isNamespaceSelector bool) []string { + labelsWithOps, _, _ := parseSelector(selector) + ops, labels := GetOperatorsAndLabels(labelsWithOps) + return craftPartialIptEntrySpecFromOpsAndLabels(ns, ops, labels, srcOrDstFlag, isNamespaceSelector) +} + +func craftPartialIptablesCommentFromSelector(ns string, selector *metav1.LabelSelector, isNamespaceSelector bool) string { + if selector == nil { + return "none" + } + + if len(selector.MatchExpressions) == 0 && len(selector.MatchLabels) == 0 { + if isNamespaceSelector { + return util.KubeAllNamespacesFlag + } + + return "ns-" + ns + } + + labelsWithOps, _, _ := parseSelector(selector) + ops, labelsWithoutOps := GetOperatorsAndLabels(labelsWithOps) + + var comment, prefix string + if isNamespaceSelector { + prefix = "ns-" + } + + for i, _ := range labelsWithoutOps { + comment += prefix + ops[i] + labelsWithoutOps[i] + comment += "-AND-" + } + + return comment[:len(comment)-len("-AND-")] +} + +func translateIngress(ns string, targetSelector metav1.LabelSelector, rules []networkingv1.NetworkPolicyIngressRule) ([]string, []string, []*iptm.IptEntry) { + var ( + sets []string // ipsets with type: net:hash + lists []string // ipsets with type: list:set + entries []*iptm.IptEntry + ) + + log.Printf("started parsing ingress rule") + + labelsWithOps, _, _ := parseSelector(&targetSelector) + ops, labels := GetOperatorsAndLabels(labelsWithOps) + if len(ops) == 1 && len(labels) == 1 { + if ops[0] == "" && labels[0] == "" { + // targetSelector is empty. Select all pods within the namespace + labels[0] = "ns-" + ns + } + } + sets = append(sets, labels...) + + targetSelectorIptEntrySpec := craftPartialIptEntrySpecFromOpsAndLabels(ns, ops, labels, util.IptablesDstFlag, false) + targetSelectorComment := craftPartialIptablesCommentFromSelector(ns, &targetSelector, false) + + for _, rule := range rules { + allowExternal, portRuleExists, fromRuleExists := false, false, false + + if len(rule.Ports) > 0 { + portRuleExists = true + } + + if rule.From != nil { + if len(rule.From) == 0 { + fromRuleExists = true + allowExternal = true + } + + for _, fromRule := range rule.From { + if fromRule.PodSelector != nil || + fromRule.NamespaceSelector != nil || + fromRule.IPBlock != nil { + fromRuleExists = true + break + } + } + } + + if !portRuleExists && !fromRuleExists && !allowExternal { + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + } + entry.Specs = append( + entry.Specs, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName(util.KubeAllNamespacesFlag), + util.IptablesSrcFlag, + ) + entry.Specs = append(entry.Specs, targetSelectorIptEntrySpec...) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-" + targetSelectorComment + + "-FROM-" +util.KubeAllNamespacesFlag, + ) + + entries = append(entries, entry) + lists = append(lists, util.KubeAllNamespacesFlag) + continue + } + + // Only Ports rules exist + if portRuleExists && !fromRuleExists && !allowExternal { + for _, portRule := range rule.Ports { + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: craftPartialIptEntrySpecFromPort(portRule, util.IptablesDstPortFlag), + } + entry.Specs = append(entry.Specs, targetSelectorIptEntrySpec...) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-" + + craftPartialIptablesCommentFromPort(portRule, util.IptablesDstPortFlag) + + targetSelectorComment, + ) + entries = append(entries, entry) + } + continue + } + + // fromRuleExists + if portRuleExists { + for _, portRule := range rule.Ports { + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: craftPartialIptEntrySpecFromPort(portRule, util.IptablesDstPortFlag), + } + entry.Specs = append(entry.Specs, targetSelectorIptEntrySpec...) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAzureIngressFromChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-" + + craftPartialIptablesCommentFromPort(portRule, util.IptablesDstPortFlag) + + targetSelectorComment + + "-TO-JUMP-TO-" + util.IptablesAzureIngressFromChain, + ) + entries = append(entries, entry) + } + } else { + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + } + entry.Specs = append(entry.Specs, targetSelectorIptEntrySpec...) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAzureIngressFromChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-" + + targetSelectorComment + + "-TO-JUMP-TO-" + util.IptablesAzureIngressFromChain, + ) + entries = append(entries, entry) + } + + if allowExternal { + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + } + entry.Specs = append(entry.Specs, targetSelectorIptEntrySpec...) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-" + + targetSelectorComment, + ) + entries = append(entries, entry) + + continue + } + + for _, fromRule := range rule.From { + // Handle IPBlock field of NetworkPolicyPeer + if fromRule.IPBlock != nil { + if len(fromRule.IPBlock.CIDR) > 0 { + cidrEntry := &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + } + cidrEntry.Specs = append( + cidrEntry.Specs, + util.IptablesSFlag, + fromRule.IPBlock.CIDR, + ) + cidrEntry.Specs = append(cidrEntry.Specs, targetSelectorIptEntrySpec...) + cidrEntry.Specs = append( + cidrEntry.Specs, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-" + fromRule.IPBlock.CIDR + + "-TO-" + targetSelectorComment, + ) + entries = append(entries, cidrEntry) + } + if len(fromRule.IPBlock.Except) > 0 { + for _, except := range fromRule.IPBlock.Except { + exceptEntry := &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + } + exceptEntry.Specs = append( + exceptEntry.Specs, + util.IptablesSFlag, + except, + ) + exceptEntry.Specs = append(exceptEntry.Specs, targetSelectorIptEntrySpec...) + exceptEntry.Specs = append( + exceptEntry.Specs, + util.IptablesJumpFlag, + util.IptablesDrop, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "DROP-" + except + + "-TO-" + targetSelectorComment, + ) + entries = append(entries, exceptEntry) + } + } + continue + } + + // Handle podSelector and namespaceSelector. + // For PodSelector, use hash:net in ipset. + // For NamespaceSelector, use set:list in ipset. + if fromRule.PodSelector == nil && fromRule.NamespaceSelector == nil { + continue + } + + if fromRule.PodSelector == nil && fromRule.NamespaceSelector != nil { + nsLabelsWithOps, _, _ := parseSelector(fromRule.NamespaceSelector) + _, nsLabelsWithoutOps := GetOperatorsAndLabels(nsLabelsWithOps) + if len(nsLabelsWithoutOps) == 1 && nsLabelsWithoutOps[0] == "" { + // Empty namespaceSelector. This selects all namespaces + nsLabelsWithoutOps[0] = util.KubeAllNamespacesFlag + } else { + for i, _ := range nsLabelsWithoutOps { + // Add namespaces prefix to distinguish namespace ipset lists and pod ipsets + nsLabelsWithoutOps[i] = "ns-" + nsLabelsWithoutOps[i] + } + } + lists = append(lists, nsLabelsWithoutOps...) + + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + } + entry.Specs = append( + entry.Specs, + craftPartialIptEntrySpecFromSelector( + ns, + fromRule.NamespaceSelector, + util.IptablesSrcFlag, + true, + )..., + ) + entry.Specs = append(entry.Specs, targetSelectorIptEntrySpec...) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-" + craftPartialIptablesCommentFromSelector(ns, fromRule.NamespaceSelector, true) + + "-TO-" + targetSelectorComment, + ) + entries = append(entries, entry) + continue + } + + if fromRule.PodSelector != nil && fromRule.NamespaceSelector == nil { + podLabelsWithOps, _, _ := parseSelector(fromRule.PodSelector) + _, podLabelsWithoutOps := GetOperatorsAndLabels(podLabelsWithOps) + if len(podLabelsWithoutOps) == 1 { + if podLabelsWithoutOps[0] == "" { + podLabelsWithoutOps[0] = "ns-" + ns + } + } + sets = append(sets, podLabelsWithoutOps...) + + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + } + entry.Specs = append( + entry.Specs, + craftPartialIptEntrySpecFromSelector( + ns, + fromRule.PodSelector, + util.IptablesSrcFlag, + false, + )..., + ) + entry.Specs = append(entry.Specs, targetSelectorIptEntrySpec...) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-" + craftPartialIptablesCommentFromSelector(ns, fromRule.PodSelector, false) + + "-TO-" + targetSelectorComment, + ) + entries = append(entries, entry) + continue + } + + // fromRule has both namespaceSelector and podSelector set. + // We should match the selected pods in the selected namespaces. + // This allows traffic from podSelector intersects namespaceSelector + // This is only supported in kubernetes version >= 1.11 + if !util.IsNewNwPolicyVerFlag { + continue + } + + nsLabelsWithOps, _, _ := parseSelector(fromRule.NamespaceSelector) + _, nsLabelsWithoutOps := GetOperatorsAndLabels(nsLabelsWithOps) + // Add namespaces prefix to distinguish namespace ipsets and pod ipsets + for i, _ := range nsLabelsWithoutOps { + nsLabelsWithoutOps[i] = "ns-" + nsLabelsWithoutOps[i] + } + lists = append(lists, nsLabelsWithoutOps...) + + podLabelsWithOps, _, _ := parseSelector(fromRule.PodSelector) + _, podLabelsWithoutOps := GetOperatorsAndLabels(podLabelsWithOps) + sets = append(sets, podLabelsWithoutOps...) + + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + } + entry.Specs = append( + entry.Specs, + craftPartialIptEntrySpecFromSelector( + ns, + fromRule.NamespaceSelector, + util.IptablesSrcFlag, + true, + )..., + ) + entry.Specs = append( + entry.Specs, + craftPartialIptEntrySpecFromSelector( + ns, + fromRule.PodSelector, + util.IptablesSrcFlag, + false, + )..., + ) + entry.Specs = append(entry.Specs, targetSelectorIptEntrySpec...) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-" + craftPartialIptablesCommentFromSelector(ns, fromRule.NamespaceSelector, true) + + "-AND-" + craftPartialIptablesCommentFromSelector(ns, fromRule.PodSelector, false) + + "-TO-" + targetSelectorComment, + ) + entries = append(entries, entry) + } + } + + log.Printf("finished parsing ingress rule") + return util.DropEmptyFields(sets), util.DropEmptyFields(lists), entries +} + +func translateEgress(ns string, targetSelector metav1.LabelSelector, rules []networkingv1.NetworkPolicyEgressRule) ([]string, []string, []*iptm.IptEntry) { + var ( + sets []string // ipsets with type: net:hash + lists []string // ipsets with type: list:set + entries []*iptm.IptEntry + ) + + log.Printf("started parsing egress rule") + + labelsWithOps, _, _ := parseSelector(&targetSelector) + ops, labels := GetOperatorsAndLabels(labelsWithOps) + if len(ops) == 1 && len(labels) == 1 { + if ops[0] == "" && labels[0] == "" { + // targetSelector is empty. Select all pods within the namespace + labels[0] = "ns-" + ns + } + } + sets = append(sets, labels...) + targetSelectorIptEntrySpec := craftPartialIptEntrySpecFromOpsAndLabels(ns, ops, labels, util.IptablesSrcFlag, false) + targetSelectorComment := craftPartialIptablesCommentFromSelector(ns, &targetSelector, false) + for _, rule := range rules { + allowExternal, portRuleExists, toRuleExists := false, false, false + + if len(rule.Ports) > 0 { + portRuleExists = true + } + + if rule.To != nil { + if len(rule.To) == 0 { + toRuleExists = true + allowExternal = true + } + + for _, toRule := range rule.To { + if toRule.PodSelector != nil || + toRule.NamespaceSelector != nil || + toRule.IPBlock != nil { + toRuleExists = true + break + } + } + } + + if !portRuleExists && !toRuleExists && !allowExternal { + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureEgressPortChain, + Specs: targetSelectorIptEntrySpec, + } + entry.Specs = append( + entry.Specs, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName(util.KubeAllNamespacesFlag), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-FROM-" + targetSelectorComment + + "-TO-" + util.KubeAllNamespacesFlag, + ) + + entries = append(entries, entry) + lists = append(lists, util.KubeAllNamespacesFlag) + continue + } + + // Only Ports rules exist + if portRuleExists && !toRuleExists && !allowExternal { + for _, portRule := range rule.Ports { + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureEgressPortChain, + Specs: craftPartialIptEntrySpecFromPort(portRule, util.IptablesDstPortFlag), + } + entry.Specs = append(entry.Specs, targetSelectorIptEntrySpec...) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-FROM-" + + craftPartialIptablesCommentFromPort(portRule, util.IptablesDstPortFlag) + + targetSelectorComment, + ) + entries = append(entries, entry) + } + continue + } + + // toRuleExists + if portRuleExists { + for _, portRule := range rule.Ports { + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureEgressPortChain, + Specs: craftPartialIptEntrySpecFromPort(portRule, util.IptablesDstPortFlag), + } + entry.Specs = append(entry.Specs, targetSelectorIptEntrySpec...) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAzureEgressToChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-FROM-" + + craftPartialIptablesCommentFromPort(portRule, util.IptablesDstPortFlag) + + targetSelectorComment + + "-TO-JUMP-TO-" + util.IptablesAzureEgressToChain, + ) + entries = append(entries, entry) + } + } else { + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureEgressPortChain, + } + entry.Specs = append(entry.Specs, targetSelectorIptEntrySpec...) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAzureEgressToChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-FROM-" + + targetSelectorComment + + "-TO-JUMP-TO-" + util.IptablesAzureEgressToChain, + ) + entries = append(entries, entry) + } + + if allowExternal { + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureEgressToChain, + } + entry.Specs = append(entry.Specs, targetSelectorIptEntrySpec...) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-FROM-" + + targetSelectorComment, + ) + entries = append(entries, entry) + + continue + } + + for _, toRule := range rule.To { + // Handle IPBlock field of NetworkPolicyPeer + if toRule.IPBlock != nil { + if len(toRule.IPBlock.CIDR) > 0 { + cidrEntry := &iptm.IptEntry{ + Chain: util.IptablesAzureEgressToChain, + Specs: targetSelectorIptEntrySpec, + } + cidrEntry.Specs = append( + cidrEntry.Specs, + util.IptablesDFlag, + toRule.IPBlock.CIDR, + ) + cidrEntry.Specs = append( + cidrEntry.Specs, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-" + toRule.IPBlock.CIDR + + "-FROM-" + targetSelectorComment, + ) + entries = append(entries, cidrEntry) + } + if len(toRule.IPBlock.Except) > 0 { + for _, except := range toRule.IPBlock.Except { + exceptEntry := &iptm.IptEntry{ + Chain: util.IptablesAzureEgressToChain, + Specs: targetSelectorIptEntrySpec, + } + exceptEntry.Specs = append( + exceptEntry.Specs, + util.IptablesDFlag, + except, + ) + exceptEntry.Specs = append( + exceptEntry.Specs, + util.IptablesJumpFlag, + util.IptablesDrop, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "DROP-" + except + + "-FROM-" + targetSelectorComment, + ) + entries = append(entries, exceptEntry) + } + } + continue + } + + // Handle podSelector and namespaceSelector. + // For PodSelector, use hash:net in ipset. + // For NamespaceSelector, use set:list in ipset. + if toRule.PodSelector == nil && toRule.NamespaceSelector == nil { + continue + } + + if toRule.PodSelector == nil && toRule.NamespaceSelector != nil { + nsLabelsWithOps, _, _ := parseSelector(toRule.NamespaceSelector) + _, nsLabelsWithoutOps := GetOperatorsAndLabels(nsLabelsWithOps) + if len(nsLabelsWithoutOps) == 1 && nsLabelsWithoutOps[0] == "" { + // Empty namespaceSelector. This selects all namespaces + nsLabelsWithoutOps[0] = util.KubeAllNamespacesFlag + } else { + for i, _ := range nsLabelsWithoutOps { + // Add namespaces prefix to distinguish namespace ipset lists and pod ipsets + nsLabelsWithoutOps[i] = "ns-" + nsLabelsWithoutOps[i] + } + } + lists = append(lists, nsLabelsWithoutOps...) + + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureEgressToChain, + Specs: targetSelectorIptEntrySpec, + } + entry.Specs = append( + entry.Specs, + craftPartialIptEntrySpecFromSelector( + ns, + toRule.NamespaceSelector, + util.IptablesDstFlag, + true, + )..., + ) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-" + targetSelectorComment + + "-TO-" + craftPartialIptablesCommentFromSelector(ns, toRule.NamespaceSelector, true), + ) + entries = append(entries, entry) + continue + } + + if toRule.PodSelector != nil && toRule.NamespaceSelector == nil { + podLabelsWithOps, _, _ := parseSelector(toRule.PodSelector) + _, podLabelsWithoutOps := GetOperatorsAndLabels(podLabelsWithOps) + if len(podLabelsWithoutOps) == 1 { + if podLabelsWithoutOps[0] == "" { + podLabelsWithoutOps[0] = "ns-" + ns + } + } + sets = append(sets, podLabelsWithoutOps...) + + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureEgressToChain, + Specs: targetSelectorIptEntrySpec, + } + entry.Specs = append( + entry.Specs, + craftPartialIptEntrySpecFromSelector( + ns, + toRule.PodSelector, + util.IptablesDstFlag, + false, + )..., + ) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-" + targetSelectorComment + + "-TO-" + craftPartialIptablesCommentFromSelector(ns, toRule.PodSelector, false), + ) + entries = append(entries, entry) + continue + } + + // toRule has both namespaceSelector and podSelector set. + // We should match the selected pods in the selected namespaces. + // This allows traffic from podSelector intersects namespaceSelector + // This is only supported in kubernetes version >= 1.11 + if !util.IsNewNwPolicyVerFlag { + continue + } + + nsLabelsWithOps, _, _ := parseSelector(toRule.NamespaceSelector) + _, nsLabelsWithoutOps := GetOperatorsAndLabels(nsLabelsWithOps) + // Add namespaces prefix to distinguish namespace ipsets and pod ipsets + for i, _ := range nsLabelsWithoutOps { + nsLabelsWithoutOps[i] = "ns-" + nsLabelsWithoutOps[i] + } + lists = append(lists, nsLabelsWithoutOps...) + + podLabelsWithOps, _, _ := parseSelector(toRule.PodSelector) + _, podLabelsWithoutOps := GetOperatorsAndLabels(podLabelsWithOps) + sets = append(sets, podLabelsWithoutOps...) + + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureEgressToChain, + Specs: targetSelectorIptEntrySpec, + } + entry.Specs = append( + entry.Specs, + craftPartialIptEntrySpecFromSelector( + ns, + toRule.NamespaceSelector, + util.IptablesDstFlag, + true, + )..., + ) + entry.Specs = append( + entry.Specs, + craftPartialIptEntrySpecFromSelector( + ns, + toRule.PodSelector, + util.IptablesDstFlag, + false, + )..., + ) + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-" + targetSelectorComment + + "-TO-" + craftPartialIptablesCommentFromSelector(ns, toRule.NamespaceSelector, true) + + "-AND-" + craftPartialIptablesCommentFromSelector(ns, toRule.PodSelector, false), + ) + entries = append(entries, entry) + } + } + + log.Printf("finished parsing egress rule") + return util.DropEmptyFields(sets), util.DropEmptyFields(lists), entries +} + +// Drop all non-whitelisted packets. +func getDefaultDropEntries(ns string, targetSelector metav1.LabelSelector) []*iptm.IptEntry { + var entries []*iptm.IptEntry + + labelsWithOps, _, _ := parseSelector(&targetSelector) + ops, labels := GetOperatorsAndLabels(labelsWithOps) + if len(ops) == 1 && len(labels) == 1 { + if ops[0] == "" && labels[0] == "" { + // targetSelector is empty. Select all pods within the namespace + labels[0] = "ns-" + ns + } + } + + targetSelectorIngressIptEntrySpec := craftPartialIptEntrySpecFromOpsAndLabels(ns, ops, labels, util.IptablesDstFlag, false) + targetSelectorEgressIptEntrySpec := craftPartialIptEntrySpecFromOpsAndLabels(ns, ops, labels, util.IptablesSrcFlag, false) + targetSelectorComment := craftPartialIptablesCommentFromSelector(ns, &targetSelector, false) + + entry := &iptm.IptEntry{ + Chain: util.IptablesAzureTargetSetsChain, + Specs: targetSelectorIngressIptEntrySpec, + } + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesDrop, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "DROP-ALL-TO-" + targetSelectorComment, + ) + entries = append(entries, entry) + + entry = &iptm.IptEntry{ + Chain: util.IptablesAzureTargetSetsChain, + Specs: targetSelectorEgressIptEntrySpec, + } + entry.Specs = append( + entry.Specs, + util.IptablesJumpFlag, + util.IptablesDrop, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "DROP-ALL-FROM-" + targetSelectorComment, + ) + entries = append(entries, entry) + + return entries +} + +// Allow traffic from/to kube-system pods +func getAllowKubeSystemEntries(ns string, targetSelector metav1.LabelSelector) []*iptm.IptEntry { + var entries []*iptm.IptEntry + hashedKubeSystemSet := util.GetHashedName("ns-" + util.KubeSystemFlag) + targetSelectorComment := craftPartialIptablesCommentFromSelector(ns, &targetSelector, false) + allowKubeSystemIngress := &iptm.IptEntry{ + Chain: util.IptablesAzureKubeSystemChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + hashedKubeSystemSet, + util.IptablesSrcFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-" + "ns-" + util.KubeSystemFlag + + "-TO-" + targetSelectorComment, + }, + } + entries = append(entries, allowKubeSystemIngress) + + allowKubeSystemEgress := &iptm.IptEntry{ + Chain: util.IptablesAzureKubeSystemChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + hashedKubeSystemSet, + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-" + targetSelectorComment + + "-TO-" + "ns-" + util.KubeSystemFlag, + }, + } + entries = append(entries, allowKubeSystemEgress) + + return entries +} + +// translatePolicy translates network policy object into a set of iptables rules. +// input: +// kubernetes network policy project +// output: +// 1. ipset set names generated from all podSelectors +// 2. ipset list names generated from all namespaceSelectors +// 3. iptables entries generated from the input network policy object. +func translatePolicy(npObj *networkingv1.NetworkPolicy) ([]string, []string, []*iptm.IptEntry) { + var ( + resultSets []string + resultLists []string + entries []*iptm.IptEntry + ) + + log.Printf("Translating network policy:\n %+v", npObj) + + defer func() { + log.Printf("Finished translatePolicy") + log.Printf("sets: %v", resultSets) + log.Printf("lists: %v", resultLists) + log.Printf("entries: ") + for _, entry := range entries { + log.Printf("entry: %+v", entry) + } + }() + + npNs := npObj.ObjectMeta.Namespace + // Allow kube-system pods + entries = append(entries, getAllowKubeSystemEntries(npNs, npObj.Spec.PodSelector)...) + + if len(npObj.Spec.PolicyTypes) == 0 { + ingressSets, ingressLists, ingressEntries := translateIngress(npNs, npObj.Spec.PodSelector, npObj.Spec.Ingress) + resultSets = append(resultSets, ingressSets...) + resultLists = append(resultLists, ingressLists...) + entries = append(entries, ingressEntries...) + + egressSets, egressLists, egressEntries := translateEgress(npNs, npObj.Spec.PodSelector, npObj.Spec.Egress) + resultSets = append(resultSets, egressSets...) + resultLists = append(resultLists, egressLists...) + entries = append(entries, egressEntries...) + + entries = append(entries, getDefaultDropEntries(npNs, npObj.Spec.PodSelector)...) + + return util.UniqueStrSlice(resultSets), util.UniqueStrSlice(resultLists), entries + } + + for _, ptype := range npObj.Spec.PolicyTypes { + if ptype == networkingv1.PolicyTypeIngress { + ingressSets, ingressLists, ingressEntries := translateIngress(npNs, npObj.Spec.PodSelector, npObj.Spec.Ingress) + resultSets = append(resultSets, ingressSets...) + resultLists = append(resultLists, ingressLists...) + entries = append(entries, ingressEntries...) + } + + if ptype == networkingv1.PolicyTypeEgress { + egressSets, egressLists, egressEntries := translateEgress(npNs, npObj.Spec.PodSelector, npObj.Spec.Egress) + resultSets = append(resultSets, egressSets...) + resultLists = append(resultLists, egressLists...) + entries = append(entries, egressEntries...) + } + } + + entries = append(entries, getDefaultDropEntries(npNs, npObj.Spec.PodSelector)...) + + resultSets, resultLists = util.UniqueStrSlice(resultSets), util.UniqueStrSlice(resultLists) + + return resultSets, resultLists, entries +} diff --git a/npm/translatePolicy_test.go b/npm/translatePolicy_test.go new file mode 100644 index 0000000000..5951ddc928 --- /dev/null +++ b/npm/translatePolicy_test.go @@ -0,0 +1,2751 @@ +package npm + +import ( + "testing" + "reflect" + "encoding/json" + + "github.com/Azure/azure-container-networking/npm/iptm" + "github.com/Azure/azure-container-networking/npm/util" + "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func TestCraftPartialIptEntrySpecFromPort(t *testing.T) { + portRule := networkingv1.NetworkPolicyPort{} + + iptEntrySpec := craftPartialIptEntrySpecFromPort(portRule, util.IptablesDstPortFlag) + expectedIptEntrySpec := []string{} + + if !reflect.DeepEqual(iptEntrySpec, expectedIptEntrySpec) { + t.Errorf("TestCraftPartialIptEntrySpecFromPort failed @ empty iptEntrySpec comparison") + t.Errorf("iptEntrySpec:\n%v", iptEntrySpec) + t.Errorf("expectedIptEntrySpec:\n%v", expectedIptEntrySpec) + } + + tcp := v1.ProtocolTCP + portRule = networkingv1.NetworkPolicyPort{ + Protocol: &tcp, + } + + iptEntrySpec = craftPartialIptEntrySpecFromPort(portRule, util.IptablesDstPortFlag) + expectedIptEntrySpec = []string{ + util.IptablesProtFlag, + "TCP", + } + + if !reflect.DeepEqual(iptEntrySpec, expectedIptEntrySpec) { + t.Errorf("TestCraftPartialIptEntrySpecFromPort failed @ tcp iptEntrySpec comparison") + t.Errorf("iptEntrySpec:\n%v", iptEntrySpec) + t.Errorf("expectedIptEntrySpec:\n%v", expectedIptEntrySpec) + } + + port8000 := intstr.FromInt(8000) + portRule = networkingv1.NetworkPolicyPort{ + Port: &port8000, + } + + iptEntrySpec = craftPartialIptEntrySpecFromPort(portRule, util.IptablesDstPortFlag) + expectedIptEntrySpec = []string{ + util.IptablesDstPortFlag, + "8000", + } + + if !reflect.DeepEqual(iptEntrySpec, expectedIptEntrySpec) { + t.Errorf("TestCraftPartialIptEntrySpecFromPort failed @ port 8000 iptEntrySpec comparison") + t.Errorf("iptEntrySpec:\n%v", iptEntrySpec) + t.Errorf("expectedIptEntrySpec:\n%v", expectedIptEntrySpec) + } + + portRule = networkingv1.NetworkPolicyPort{ + Protocol: &tcp, + Port: &port8000, + } + + iptEntrySpec = craftPartialIptEntrySpecFromPort(portRule, util.IptablesDstPortFlag) + expectedIptEntrySpec = []string{ + util.IptablesProtFlag, + "TCP", + util.IptablesDstPortFlag, + "8000", + } + + if !reflect.DeepEqual(iptEntrySpec, expectedIptEntrySpec) { + t.Errorf("TestCraftPartialIptEntrySpecFromPort failed @ tcp port 8000 iptEntrySpec comparison") + t.Errorf("iptEntrySpec:\n%v", iptEntrySpec) + t.Errorf("expectedIptEntrySpec:\n%v", expectedIptEntrySpec) + } +} + +func TestCraftPartialIptablesCommentFromPort(t *testing.T) { + portRule := networkingv1.NetworkPolicyPort{} + + comment := craftPartialIptablesCommentFromPort(portRule, util.IptablesDstPortFlag) + expectedComment := "" + + if !reflect.DeepEqual(comment, expectedComment) { + t.Errorf("TestCraftPartialIptablesCommentFromPort failed @ empty comment comparison") + t.Errorf("comment:\n%v", comment) + t.Errorf("expectedComment:\n%v", expectedComment) + } + + tcp := v1.ProtocolTCP + portRule = networkingv1.NetworkPolicyPort{ + Protocol: &tcp, + } + + comment = craftPartialIptablesCommentFromPort(portRule, util.IptablesDstPortFlag) + expectedComment = "TCP-OF-" + + if !reflect.DeepEqual(comment, expectedComment) { + t.Errorf("TestCraftPartialIptablesCommentFromPort failed @ tcp comment comparison") + t.Errorf("comment:\n%v", comment) + t.Errorf("expectedComment:\n%v", expectedComment) + } + + port8000 := intstr.FromInt(8000) + portRule = networkingv1.NetworkPolicyPort{ + Port: &port8000, + } + + comment = craftPartialIptablesCommentFromPort(portRule, util.IptablesDstPortFlag) + expectedComment = "PORT-8000-OF-" + + if !reflect.DeepEqual(comment, expectedComment) { + t.Errorf("TestCraftPartialIptablesCommentFromPort failed @ port 8000 comment comparison") + t.Errorf("comment:\n%v", comment) + t.Errorf("expectedIptEntrySpec:\n%v", expectedComment) + } + + portRule = networkingv1.NetworkPolicyPort{ + Protocol: &tcp, + Port: &port8000, + } + + comment = craftPartialIptablesCommentFromPort(portRule, util.IptablesDstPortFlag) + expectedComment = "TCP-PORT-8000-OF-" + + if !reflect.DeepEqual(comment, expectedComment) { + t.Errorf("TestCraftPartialIptablesCommentFromPort failed @ tcp port 8000 comment comparison") + t.Errorf("comment:\n%v", comment) + t.Errorf("expectedIptEntrySpec:\n%v", expectedComment) + } +} + +func TestCraftPartialIptEntrySpecFromOpAndLabel(t *testing.T) { + srcOp, srcLabel := "", "src" + iptEntrySpec := craftPartialIptEntrySpecFromOpAndLabel(srcOp, srcLabel, util.IptablesSrcFlag, false) + expectedIptEntrySpec := []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName(srcLabel), + util.IptablesSrcFlag, + } + + if !reflect.DeepEqual(iptEntrySpec, expectedIptEntrySpec) { + t.Errorf("TestCraftIptEntrySpecFromOpAndLabel failed @ src iptEntrySpec comparison") + t.Errorf("iptEntrySpec:\n%v", iptEntrySpec) + t.Errorf("expectedIptEntrySpec:\n%v", expectedIptEntrySpec) + } + + dstOp, dstLabel := "!", "dst" + iptEntrySpec = craftPartialIptEntrySpecFromOpAndLabel(dstOp, dstLabel, util.IptablesDstFlag, false) + expectedIptEntrySpec = []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName(dstLabel), + util.IptablesDstFlag, + } + + if !reflect.DeepEqual(iptEntrySpec, expectedIptEntrySpec) { + t.Errorf("TestCraftIptEntrySpecFromOpAndLabel failed @ dst iptEntrySpec comparison") + t.Errorf("iptEntrySpec:\n%v", iptEntrySpec) + t.Errorf("expectedIptEntrySpec:\n%v", expectedIptEntrySpec) + } +} + +func TestCraftPartialIptEntrySpecFromOpsAndLabels(t *testing.T) { + srcOps := []string{ + "", + "", + "!", + } + srcLabels := []string{ + "src", + "src:firstLabel", + "src:secondLabel", + } + + dstOps := []string{ + "!", + "!", + "", + } + dstLabels := []string{ + "dst", + "dst:firstLabel", + "dst:secondLabel", + } + + + srcIptEntry := craftPartialIptEntrySpecFromOpsAndLabels("testnamespace", srcOps, srcLabels, util.IptablesSrcFlag, false) + dstIptEntry := craftPartialIptEntrySpecFromOpsAndLabels("testnamespace", dstOps, dstLabels, util.IptablesDstFlag, false) + iptEntrySpec := append(srcIptEntry, dstIptEntry...) + expectedIptEntrySpec := []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("src"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("src:firstLabel"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("src:secondLabel"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("dst"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("dst:firstLabel"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("dst:secondLabel"), + util.IptablesDstFlag, + } + + if !reflect.DeepEqual(iptEntrySpec, expectedIptEntrySpec) { + t.Errorf("TestCraftIptEntrySpecFromOpsAndLabels failed @ iptEntrySpec comparison") + t.Errorf("iptEntrySpec:\n%v", iptEntrySpec) + t.Errorf("expectedIptEntrySpec:\n%v", expectedIptEntrySpec) + } +} + +func TestCraftPartialIptEntryFromSelector(t *testing.T) { + srcSelector := &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "label": "src", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "labelNotIn", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{ + "src", + }, + }, + }, + } + + iptEntrySpec := craftPartialIptEntrySpecFromSelector("testnamespace", srcSelector, util.IptablesSrcFlag, false) + expectedIptEntrySpec := []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("label:src"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("labelNotIn:src"), + util.IptablesSrcFlag, + } + + if !reflect.DeepEqual(iptEntrySpec, expectedIptEntrySpec) { + t.Errorf("TestCraftPartialIptEntryFromSelector failed @ iptEntrySpec comparison") + t.Errorf("iptEntrySpec:\n%v", iptEntrySpec) + t.Errorf("expectedIptEntrySpec:\n%v", expectedIptEntrySpec) + } +} + +func TestCraftPartialIptablesCommentFromSelector(t *testing.T) { + var selector *metav1.LabelSelector + selector = nil + comment := craftPartialIptablesCommentFromSelector("testnamespace", selector, false) + expectedComment := "none" + if comment != expectedComment { + t.Errorf("TestCraftPartialIptablesCommentFromSelector failed @ nil selector comparison") + t.Errorf("comment:\n%v", comment) + t.Errorf("expectedComment:\n%v", expectedComment) + } + + selector = &metav1.LabelSelector{} + comment = craftPartialIptablesCommentFromSelector("testnamespace", selector, false) + expectedComment = "ns-testnamespace" + if comment != expectedComment { + t.Errorf("TestCraftPartialIptablesCommentFromSelector failed @ empty podSelector comparison") + t.Errorf("comment:\n%v", comment) + t.Errorf("expectedComment:\n%v", expectedComment) + } + + comment = craftPartialIptablesCommentFromSelector("testnamespace", selector, true) + expectedComment = util.KubeAllNamespacesFlag + if comment != expectedComment { + t.Errorf("TestCraftPartialIptablesCommentFromSelector failed @ empty namespaceSelector comparison") + t.Errorf("comment:\n%v", comment) + t.Errorf("expectedComment:\n%v", expectedComment) + } + + selector = &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "k0": "v0", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "k1", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "v10", + "v11", + }, + }, + metav1.LabelSelectorRequirement{ + Key: "k2", + Operator: metav1.LabelSelectorOpDoesNotExist, + Values: []string{}, + }, + }, + } + comment = craftPartialIptablesCommentFromSelector("testnamespace", selector, false) + expectedComment = "k0:v0-AND-k1:v10-AND-k1:v11-AND-!k2" + if comment != expectedComment { + t.Errorf("TestCraftPartialIptablesCommentFromSelector failed @ normal selector comparison") + t.Errorf("comment:\n%v", comment) + t.Errorf("expectedComment:\n%v", expectedComment) + } + + nsSelector := &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "k0": "v0", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "k1", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "v10", + "v11", + }, + }, + metav1.LabelSelectorRequirement{ + Key: "k2", + Operator: metav1.LabelSelectorOpDoesNotExist, + Values: []string{}, + }, + }, + } + comment = craftPartialIptablesCommentFromSelector("testnamespace", nsSelector, true) + expectedComment = "ns-k0:v0-AND-ns-k1:v10-AND-ns-k1:v11-AND-ns-!k2" + if comment != expectedComment { + t.Errorf("TestCraftPartialIptablesCommentFromSelector failed @ namespace selector comparison") + t.Errorf("comment:\n%v", comment) + t.Errorf("expectedComment:\n%v", expectedComment) + } + +} + +func TestGetDefaultDropEntries(t *testing.T) { + ns := "testnamespace" + + targetSelector := metav1.LabelSelector{ + MatchLabels: map[string]string{ + "context": "dev", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testNotIn", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{ + "frontend", + }, + }, + }, + } + + iptEntries := getDefaultDropEntries(ns, targetSelector) + + expectedIptEntries := []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureTargetSetsChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("context:dev"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("testNotIn:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesDrop, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "DROP-ALL-TO-context:dev-AND-!testNotIn:frontend", + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureTargetSetsChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("context:dev"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("testNotIn:frontend"), + util.IptablesSrcFlag, + util.IptablesJumpFlag, + util.IptablesDrop, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "DROP-ALL-FROM-context:dev-AND-!testNotIn:frontend", + }, + }, + } + + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("TestGetDefaultDropEntries failed @ iptEntries comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } +} + +func TestTranslateIngress(t *testing.T) { + ns := "testnamespace" + + targetSelector := metav1.LabelSelector{ + MatchLabels: map[string]string{ + "context": "dev", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testNotIn", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{ + "frontend", + }, + }, + }, + } + + tcp := v1.ProtocolTCP + port6783 := intstr.FromInt(6783) + ingressPodSelector := &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "db", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "frontend", + }, + }, + }, + } + ingressNamespaceSelector := &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "ns": "dev", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "frontendns", + }, + }, + }, + } + + compositeNetworkPolicyPeer := networkingv1.NetworkPolicyPeer{ + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "region": "northpole", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "k", + Operator: metav1.LabelSelectorOpDoesNotExist, + }, + }, + }, + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "planet": "earth", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "keyExists", + Operator: metav1.LabelSelectorOpExists, + }, + }, + }, + } + + rules := []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Protocol: &tcp, + Port: &port6783, + }, + }, + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: ingressPodSelector, + }, + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: ingressNamespaceSelector, + }, + compositeNetworkPolicyPeer, + }, + }, + } + + util.IsNewNwPolicyVerFlag = true + sets, lists, iptEntries := translateIngress(ns, targetSelector, rules) + expectedSets := []string{ + "context:dev", + "testNotIn:frontend", + "app:db", + "testIn:frontend", + "region:northpole", + "k", + } + + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedIngress failed @ sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists := []string{ + "ns-ns:dev", + "ns-testIn:frontendns", + "ns-planet:earth", + "ns-keyExists", + } + + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedIngress failed @ lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries := []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: []string{ + util.IptablesProtFlag, + string(v1.ProtocolTCP), + util.IptablesDstPortFlag, + "6783", + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("context:dev"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("testNotIn:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAzureIngressFromChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-TCP-PORT-6783-OF-context:dev-AND-!testNotIn:frontend-TO-JUMP-TO-" + + util.IptablesAzureIngressFromChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:db"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("testIn:frontend"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("context:dev"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("testNotIn:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-app:db-AND-testIn:frontend-TO-context:dev-AND-!testNotIn:frontend", + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-ns:dev"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-testIn:frontendns"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("context:dev"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("testNotIn:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ns-ns:dev-AND-ns-testIn:frontendns-TO-context:dev-AND-!testNotIn:frontend", + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-planet:earth"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-keyExists"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("region:northpole"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("k"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("context:dev"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("testNotIn:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ns-planet:earth-AND-ns-keyExists-AND-region:northpole-AND-!k-TO-context:dev-AND-!testNotIn:frontend", + }, + }, + } + + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedIngress failed @ composite ingress rule comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } +} + +func TestTranslateEgress(t *testing.T) { + ns := "testnamespace" + + targetSelector := metav1.LabelSelector{ + MatchLabels: map[string]string{ + "context": "dev", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testNotIn", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{ + "frontend", + }, + }, + }, + } + + tcp := v1.ProtocolTCP + port6783 := intstr.FromInt(6783) + egressPodSelector := &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "db", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "frontend", + }, + }, + }, + } + egressNamespaceSelector := &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "ns": "dev", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "testIn", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "frontendns", + }, + }, + }, + } + + compositeNetworkPolicyPeer := networkingv1.NetworkPolicyPeer{ + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "region": "northpole", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "k", + Operator: metav1.LabelSelectorOpDoesNotExist, + }, + }, + }, + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "planet": "earth", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "keyExists", + Operator: metav1.LabelSelectorOpExists, + }, + }, + }, + } + + rules := []networkingv1.NetworkPolicyEgressRule{ + networkingv1.NetworkPolicyEgressRule{ + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Protocol: &tcp, + Port: &port6783, + }, + }, + To: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: egressPodSelector, + }, + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: egressNamespaceSelector, + }, + compositeNetworkPolicyPeer, + }, + }, + } + + util.IsNewNwPolicyVerFlag = true + sets, lists, iptEntries := translateEgress(ns, targetSelector, rules) + expectedSets := []string{ + "context:dev", + "testNotIn:frontend", + "app:db", + "testIn:frontend", + "region:northpole", + "k", + } + + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedEgress failed @ sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists := []string{ + "ns-ns:dev", + "ns-testIn:frontendns", + "ns-planet:earth", + "ns-keyExists", + } + + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedEgress failed @ lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries := []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureEgressPortChain, + Specs: []string{ + util.IptablesProtFlag, + string(v1.ProtocolTCP), + util.IptablesDstPortFlag, + "6783", + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("context:dev"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("testNotIn:frontend"), + util.IptablesSrcFlag, + util.IptablesJumpFlag, + util.IptablesAzureEgressToChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-FROM-TCP-PORT-6783-OF-context:dev-AND-!testNotIn:frontend-TO-JUMP-TO-" + + util.IptablesAzureEgressToChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureEgressToChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("context:dev"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("testNotIn:frontend"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:db"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("testIn:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-context:dev-AND-!testNotIn:frontend-TO-app:db-AND-testIn:frontend", + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureEgressToChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("context:dev"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("testNotIn:frontend"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-ns:dev"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-testIn:frontendns"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-context:dev-AND-!testNotIn:frontend-TO-ns-ns:dev-AND-ns-testIn:frontendns", + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureEgressToChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("context:dev"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("testNotIn:frontend"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-planet:earth"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-keyExists"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("region:northpole"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("k"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-context:dev-AND-!testNotIn:frontend-TO-ns-planet:earth-AND-ns-keyExists-AND-region:northpole-AND-!k", + }, + }, + } + + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedEgress failed @ composite egress rule comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } +} + +func TestTranslatePolicy(t *testing.T) { + targetSelector := metav1.LabelSelector{} + denyAllPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deny-all-policy", + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{}, + }, + } + + sets, lists, iptEntries := translatePolicy(denyAllPolicy) + + expectedSets := []string{"ns-testnamespace"} + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ deny-all-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists := []string{} + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ deny-all-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries := []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("testnamespace", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ deny-all-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "backend", + }, + } + allowBackendToFrontendPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ALLOW-app:backend-TO-app:frontend-policy", + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "frontend", + }, + }, + }, + }, + }, + }, + }, + } + + sets, lists, iptEntries = translatePolicy(allowBackendToFrontendPolicy) + + expectedSets = []string{ + "app:backend", + "app:frontend", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ ALLOW-app:backend-TO-app:frontend-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{} + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ ALLOW-app:backend-TO-app:frontend-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + + nonKubeSystemEntries := []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:backend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAzureIngressFromChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-app:backend-TO-JUMP-TO-" + + util.IptablesAzureIngressFromChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:backend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-app:frontend-TO-app:backend", + }, + }, + } + expectedIptEntries = append(expectedIptEntries, nonKubeSystemEntries...) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("testnamespace", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ ALLOW-app:frontend-TO-app:backend-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "frontend", + }, + } + allowToFrontendPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ALLOW-all-TO-app:frontend-FROM-all-namespaces-policy", + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{}, + }, + }, + } + + sets, lists, iptEntries = translatePolicy(allowToFrontendPolicy) + + expectedSets = []string{ + "app:frontend", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ ALLOW-all-TO-app:frontend-FROM-all-namespaces-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{ + util.KubeAllNamespacesFlag, + } + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ ALLOW-all-TO-app:frontend-FROM-all-namespaces-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + + nonKubeSystemEntries = []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName(util.KubeAllNamespacesFlag), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-app:frontend-FROM-all-namespaces", + }, + }, + } + expectedIptEntries = append(expectedIptEntries, nonKubeSystemEntries...) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("testnamespace", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ ALLOW-all-TO-app:frontend-FROM-all-namespaces-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "frontend", + }, + } + denyAllToFrontendPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ALLOW-none-TO-app:frontend-policy", + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{}, + }, + } + + sets, lists, iptEntries = translatePolicy(denyAllToFrontendPolicy) + + expectedSets = []string{ + "app:frontend", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ ALLOW-none-TO-app:frontend-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{} + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ ALLOW-none-TO-app:frontend-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("testnamespace", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ ALLOW-none-TO-app:frontend-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "frontend", + }, + } + allowNsTestNamespaceToFrontendPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ALLOW-ns-testnamespace-TO-app:frontend-policy", + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + }, + } + + sets, lists, iptEntries = translatePolicy(allowNsTestNamespaceToFrontendPolicy) + + expectedSets = []string{ + "app:frontend", + "ns-testnamespace", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ ALLOW-ns-testnamespace-TO-app:frontend-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{} + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ ALLOW-ns-testnamespace-TO-app:frontend-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + + nonKubeSystemEntries = []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAzureIngressFromChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-app:frontend-TO-JUMP-TO-" + + util.IptablesAzureIngressFromChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-testnamespace"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ns-testnamespace-TO-app:frontend", + }, + }, + } + expectedIptEntries = append(expectedIptEntries, nonKubeSystemEntries...) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("testnamespace", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ ALLOW-ns-testnamespace-TO-app:frontend-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "frontend", + }, + } + allowAllNsToFrontendPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ALLOW-all-namespaces-TO-app:frontend-policy", + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + }, + } + + sets, lists, iptEntries = translatePolicy(allowAllNsToFrontendPolicy) + expectedSets = []string{ + "app:frontend", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ ALLOW-all-namespaces-TO-app:frontend-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{ + util.KubeAllNamespacesFlag, + } + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ ALLOW-all-namespaces-TO-app:frontend-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + + nonKubeSystemEntries = []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAzureIngressFromChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-app:frontend-TO-JUMP-TO-" + + util.IptablesAzureIngressFromChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName(util.KubeAllNamespacesFlag), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-all-namespaces-TO-app:frontend", + }, + }, + } + expectedIptEntries = append(expectedIptEntries, nonKubeSystemEntries...) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("testnamespace", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ ALLOW-all-namespaces-TO-app:frontend-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "frontend", + }, + } + allowNsDevToFrontendPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ALLOW-ns-namespace:dev-AND-!ns-namespace:test0-AND-!ns-namespace:test1-TO-app:frontend-policy", + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "namespace": "dev", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "namespace", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{ + "test0", + "test1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + sets, lists, iptEntries = translatePolicy(allowNsDevToFrontendPolicy) + + expectedSets = []string{ + "app:frontend", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ ALLOW-ns-namespace:dev-AND-!ns-namespace:test0-AND-!ns-namespace:test1-TO-app:frontend-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{ + "ns-namespace:dev", + "ns-namespace:test0", + "ns-namespace:test1", + } + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ ALLOW-ns-namespace:dev-AND-!ns-namespace:test0-AND-!ns-namespace:test1-TO-app:frontend-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + + nonKubeSystemEntries = []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAzureIngressFromChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-app:frontend-TO-JUMP-TO-" + + util.IptablesAzureIngressFromChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-namespace:dev"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-namespace:test0"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-namespace:test1"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ns-namespace:dev-AND-ns-!namespace:test0-AND-ns-!namespace:test1-TO-app:frontend", + }, + }, + } + + expectedIptEntries = append(expectedIptEntries, nonKubeSystemEntries...) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("testnamespace", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ ALLOW-ns-namespace:dev-AND-!ns-namespace:test0-AND-!ns-namespace:test1-TO-app:frontend-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "k0", + Operator: metav1.LabelSelectorOpDoesNotExist, + Values: []string{}, + }, + metav1.LabelSelectorRequirement{ + Key: "k1", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"v0", "v1"}, + }, + }, + MatchLabels: map[string]string{ + "app": "frontend", + }, + } + allowAllToFrontendPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "AllOW-ALL-TO-k0-AND-k1:v0-AND-k1:v1-AND-app:frontend-policy", + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + }, + } + + sets, lists, iptEntries = translatePolicy(allowAllToFrontendPolicy) + + expectedSets = []string{ + "app:frontend", + "k0", + "k1:v0", + "k1:v1", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ AllOW-ALL-TO-k0-AND-k1:v0-AND-k1:v1-AND-app:frontend-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{util.KubeAllNamespacesFlag} + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ AllOW-ALL-TO-k0-AND-k1:v0-AND-k1:v1-AND-app:frontend-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + nonKubeSystemEntries = []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("k0"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("k1:v0"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("k1:v1"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAzureIngressFromChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-app:frontend-AND-!k0-AND-k1:v0-AND-k1:v1-TO-JUMP-TO-" + + util.IptablesAzureIngressFromChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName(util.KubeAllNamespacesFlag), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesNotFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("k0"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("k1:v0"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("k1:v1"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-all-namespaces-TO-app:frontend-AND-!k0-AND-k1:v0-AND-k1:v1", + }, + }, + } + + expectedIptEntries = append(expectedIptEntries, nonKubeSystemEntries...) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("testnamespace", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ AllOW-all-TO-k0-AND-k1:v0-AND-k1:v1-AND-app:frontend-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app":"frontend", + }, + } + allowNsDevAndBackendToFrontendPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ALLOW-ns-ns:dev-AND-app:backend-TO-app:frontend", + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app":"backend", + }, + }, + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "ns": "dev", + }, + }, + }, + }, + }, + }, + }, + } + + util.IsNewNwPolicyVerFlag = true + sets, lists, iptEntries = translatePolicy(allowNsDevAndBackendToFrontendPolicy) + + expectedSets = []string{ + "app:frontend", + "app:backend", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ ALLOW-ns-ns:dev-AND-app:backend-TO-app:frontend sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{ + "ns-ns:dev", + } + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ ALLOW-ns-ns:dev-AND-app:backend-TO-app:frontend lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + nonKubeSystemEntries = []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAzureIngressFromChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-app:frontend-TO-JUMP-TO-" + + util.IptablesAzureIngressFromChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-ns:dev"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:backend"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ns-ns:dev-AND-app:backend-TO-app:frontend", + }, + }, + } + + expectedIptEntries = append(expectedIptEntries, nonKubeSystemEntries...) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("testnamespace", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ ALLOW-ns-ns:dev-AND-app:backend-TO-app:frontend policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "backdoor", + }, + } + allowInternalAndExternalPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ALLOW-ALL-TO-app:backdoor-policy", + Namespace: "dangerous", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{}, + }, + }, + }, + } + + sets, lists, iptEntries = translatePolicy(allowInternalAndExternalPolicy) + + expectedSets = []string{ + "app:backdoor", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ ALLOW-ALL-TO-app:backdoor-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{} + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ ALLOW-ALL-TO-app:backdoor-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + + nonKubeSystemEntries = []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:backdoor"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAzureIngressFromChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-app:backdoor-TO-JUMP-TO-" + + util.IptablesAzureIngressFromChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:backdoor"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-app:backdoor", + }, + }, + } + + expectedIptEntries = append(expectedIptEntries, nonKubeSystemEntries...) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("dangerous", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ ALLOW-ALL-TO-app:backdoor-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "frontend", + }, + } + + port8000 := intstr.FromInt(8000) + allowBackendToFrontendPort8000Policy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ALLOW-app:backend-TO-app:frontend-port-8000-policy", + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Port: &port8000, + }, + }, + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "backend", + }, + }, + }, + }, + }, + }, + }, + } + + sets, lists, iptEntries = translatePolicy(allowBackendToFrontendPort8000Policy) + + expectedSets = []string{ + "app:frontend", + "app:backend", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ ALLOW-app:backend-TO-app:frontend-port-8000-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{} + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ ALLOW-app:backend-TO-app:frontend-port-8000-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + + nonKubeSystemEntries = []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: []string{ + util.IptablesDstPortFlag, + "8000", + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAzureIngressFromChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-PORT-8000-OF-app:frontend-TO-JUMP-TO-" + + util.IptablesAzureIngressFromChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:backend"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-app:backend-TO-app:frontend", + }, + }, + } + + expectedIptEntries = append(expectedIptEntries, nonKubeSystemEntries...) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("dangerous", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ ALLOW-ALL-TO-app:backdoor-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "k8s", + "team": "aks", + }, + } + allowCniOrCnsToK8sPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ALLOW-program:cni-AND-team:acn-OR-binary:cns-AND-group:container-TO-app:k8s-AND-team:aks-policy", + Namespace: "acn", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "program": "cni", + "team": "acn", + }, + }, + }, + networkingv1.NetworkPolicyPeer{ + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "binary": "cns", + "group": "container", + }, + }, + }, + }, + }, + }, + }, + } + + sets, lists, iptEntries = translatePolicy(allowCniOrCnsToK8sPolicy) + + expectedSets = []string{ + "app:k8s", + "team:aks", + "program:cni", + "team:acn", + "binary:cns", + "group:container", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ ALLOW-program:cni-AND-team:acn-OR-binary:cns-AND-group:container-TO-app:k8s-AND-team:aks-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{} + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ ALLOW-program:cni-AND-team:acn-OR-binary:cns-AND-group:container-TO-app:k8s-AND-team:aks-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("acn", targetSelector)..., + ) + + nonKubeSystemEntries = []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:k8s"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("team:aks"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAzureIngressFromChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-app:k8s-AND-team:aks-TO-JUMP-TO-" + + util.IptablesAzureIngressFromChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("program:cni"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("team:acn"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:k8s"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("team:aks"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-program:cni-AND-team:acn-TO-app:k8s-AND-team:aks", + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("binary:cns"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("group:container"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:k8s"), + util.IptablesDstFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("team:aks"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-binary:cns-AND-group:container-TO-app:k8s-AND-team:aks", + }, + }, + } + + expectedIptEntries = append(expectedIptEntries, nonKubeSystemEntries...) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("acn", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ ALLOW-program:cni-AND-team:acn-OR-binary:cns-AND-group:container-TO-app:k8s-AND-team:aks-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "backend", + }, + } + denyAllFromBackendPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ALLOW-none-FROM-app:backend-policy", + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeEgress, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{}, + }, + } + + sets, lists, iptEntries = translatePolicy(denyAllFromBackendPolicy) + + expectedSets = []string{ + "app:backend", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ ALLOW-none-FROM-app:backend-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{} + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ ALLOW-none-FROM-app:backend-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("testnamespace", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ ALLOW-none-FROM-app:backend-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + + targetSelector = metav1.LabelSelector{} + denyAllFromNsUnsafePolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ALLOW-none-FROM-ns-unsafe-policy", + Namespace: "unsafe", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeEgress, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{}, + }, + } + + sets, lists, iptEntries = translatePolicy(denyAllFromNsUnsafePolicy) + + expectedSets = []string{ + "ns-unsafe", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ ALLOW-none-FROM-ns-unsafe-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + expectedLists = []string{} + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ ALLOW-none-FROM-app:backend-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("unsafe", targetSelector)..., + ) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("unsafe", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ ALLOW-none-FROM-app:backend-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "frontend", + }, + } + + tcp, udp := v1.ProtocolTCP, v1.ProtocolUDP + port53 := intstr.FromInt(53) + allowFrontendToTCPPort80UDPPOrt443Policy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ALLOW-ALL-FROM-app:frontend-TCP-PORT-53-OR-UDP-PORT-53-policy", + Namespace: "testnamespace", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeEgress, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{ + networkingv1.NetworkPolicyEgressRule{ + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Protocol: &tcp, + Port: &port53, + }, + networkingv1.NetworkPolicyPort{ + Protocol: &udp, + Port: &port53, + }, + }, + }, + networkingv1.NetworkPolicyEgressRule{ + To: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + }, + } + + sets, lists, iptEntries = translatePolicy(allowFrontendToTCPPort80UDPPOrt443Policy) + + expectedSets = []string{ + "app:frontend", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ ALLOW-ALL-FROM-app:frontend-TCP-PORT-53-OR-UDP-PORT-53-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{ + util.KubeAllNamespacesFlag, + } + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ ALLOW-ALL-FROM-app:frontend-TCP-PORT-53-OR-UDP-PORT-53-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + + nonKubeSystemEntries = []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureEgressPortChain, + Specs: []string{ + util.IptablesProtFlag, + "TCP", + util.IptablesDstPortFlag, + "53", + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesSrcFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-FROM-TCP-PORT-53-OF-app:frontend", + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureEgressPortChain, + Specs: []string{ + util.IptablesProtFlag, + "UDP", + util.IptablesDstPortFlag, + "53", + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesSrcFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-FROM-UDP-PORT-53-OF-app:frontend", + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureEgressPortChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesSrcFlag, + util.IptablesJumpFlag, + util.IptablesAzureEgressToChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-FROM-app:frontend-TO-JUMP-TO-" + + util.IptablesAzureEgressToChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureEgressToChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("app:frontend"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName(util.KubeAllNamespacesFlag), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-app:frontend-TO-" + + util.KubeAllNamespacesFlag, + }, + }, + } + expectedIptEntries = append(expectedIptEntries, nonKubeSystemEntries...) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("testnamespace", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ ALLOW-ALL-FROM-app:frontend-TCP-PORT-53-OR-UDP-PORT-53-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } + + targetSelector = metav1.LabelSelector{ + MatchLabels: map[string]string{ + "role": "db", + }, + } + + tcp = v1.ProtocolTCP + port6379, port5978 := intstr.FromInt(6379), intstr.FromInt(5978) + k8sExamplePolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "k8s-example-policy", + Namespace: "default", + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: targetSelector, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + networkingv1.PolicyTypeEgress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + IPBlock: &networkingv1.IPBlock{ + CIDR: "172.17.0.0/16", + Except: []string{ + "172.17.1.0/24", + }, + }, + }, + networkingv1.NetworkPolicyPeer{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "project": "myproject", + }, + }, + }, + networkingv1.NetworkPolicyPeer{ + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "role": "frontend", + }, + }, + }, + }, + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Protocol: &tcp, + Port: &port6379, + }, + }, + }, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{ + networkingv1.NetworkPolicyEgressRule{ + To: []networkingv1.NetworkPolicyPeer{ + networkingv1.NetworkPolicyPeer{ + IPBlock: &networkingv1.IPBlock{ + CIDR: "10.0.0.0/24", + }, + }, + }, + Ports: []networkingv1.NetworkPolicyPort{ + networkingv1.NetworkPolicyPort{ + Protocol: &tcp, + Port: &port5978, + }, + }, + }, + }, + }, + } + + sets, lists, iptEntries = translatePolicy(k8sExamplePolicy) + + expectedSets = []string{ + "role:db", + "role:frontend", + } + if !reflect.DeepEqual(sets, expectedSets) { + t.Errorf("translatedPolicy failed @ k8s-example-policy sets comparison") + t.Errorf("sets: %v", sets) + t.Errorf("expectedSets: %v", expectedSets) + } + + expectedLists = []string{ + "ns-project:myproject", + } + if !reflect.DeepEqual(lists, expectedLists) { + t.Errorf("translatedPolicy failed @ k8s-example-policy lists comparison") + t.Errorf("lists: %v", lists) + t.Errorf("expectedLists: %v", expectedLists) + } + + expectedIptEntries = []*iptm.IptEntry{} + expectedIptEntries = append( + expectedIptEntries, + getAllowKubeSystemEntries("testnamespace", targetSelector)..., + ) + + nonKubeSystemEntries = []*iptm.IptEntry{ + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressPortChain, + Specs: []string{ + util.IptablesProtFlag, + "TCP", + util.IptablesDstPortFlag, + "6379", + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("role:db"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAzureIngressFromChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-TO-TCP-PORT-6379-OF-role:db-TO-JUMP-TO-" + + util.IptablesAzureIngressFromChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesSFlag, + "172.17.0.0/16", + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("role:db"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-172.17.0.0/16-TO-role:db", + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesSFlag, + "172.17.1.0/24", + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("role:db"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesDrop, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "DROP-172.17.1.0/24-TO-role:db", + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("ns-project:myproject"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("role:db"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ns-project:myproject-TO-role:db", + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureIngressFromChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("role:frontend"), + util.IptablesSrcFlag, + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("role:db"), + util.IptablesDstFlag, + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-role:frontend-TO-role:db", + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureEgressPortChain, + Specs: []string{ + util.IptablesProtFlag, + "TCP", + util.IptablesDstPortFlag, + "5978", + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("role:db"), + util.IptablesSrcFlag, + util.IptablesJumpFlag, + util.IptablesAzureEgressToChain, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-ALL-FROM-TCP-PORT-5978-OF-role:db-TO-JUMP-TO-" + + util.IptablesAzureEgressToChain, + }, + }, + &iptm.IptEntry{ + Chain: util.IptablesAzureEgressToChain, + Specs: []string{ + util.IptablesModuleFlag, + util.IptablesSetModuleFlag, + util.IptablesMatchSetFlag, + util.GetHashedName("role:db"), + util.IptablesSrcFlag, + util.IptablesDFlag, + "10.0.0.0/24", + util.IptablesJumpFlag, + util.IptablesAccept, + util.IptablesModuleFlag, + util.IptablesCommentModuleFlag, + util.IptablesCommentFlag, + "ALLOW-10.0.0.0/24-FROM-role:db", + }, + }, + } + expectedIptEntries = append(expectedIptEntries, nonKubeSystemEntries...) + expectedIptEntries = append(expectedIptEntries, getDefaultDropEntries("testnamespace", targetSelector)...) + if !reflect.DeepEqual(iptEntries, expectedIptEntries) { + t.Errorf("translatedPolicy failed @ k8s-example-policy policy comparison") + marshalledIptEntries, _ := json.Marshal(iptEntries) + marshalledExpectedIptEntries, _ := json.Marshal(expectedIptEntries) + t.Errorf("iptEntries: %s", marshalledIptEntries) + t.Errorf("expectedIptEntries: %s", marshalledExpectedIptEntries) + } +} diff --git a/npm/util/const.go b/npm/util/const.go index 040ab48fba..b1910f56ca 100644 --- a/npm/util/const.go +++ b/npm/util/const.go @@ -7,7 +7,7 @@ const ( KubeSystemFlag string = "kube-system" KubePodTemplateHashFlag string = "pod-template-hash" KubeAllPodsFlag string = "all-pod" - KubeAllNamespacesFlag string = "all-namespace" + KubeAllNamespacesFlag string = "all-namespaces" KubeAppFlag string = "k8s-app" KubeProxyFlag string = "kube-proxy" KubePodStatusFailedFlag string = "Failed" @@ -21,50 +21,51 @@ const ( //iptables related constants. const ( - Iptables string = "iptables" - Ip6tables string = "ip6tables" - IptablesSave string = "iptables-save" - IptablesRestore string = "iptables-restore" - IptablesConfigFile string = "/var/log/iptables.conf" - IptablesTestConfigFile string = "/var/log/iptables-test.conf" - IptablesLockFile string = "/run/xtables.lock" - IptablesChainCreationFlag string = "-N" - IptablesInsertionFlag string = "-I" - IptablesAppendFlag string = "-A" - IptablesDeletionFlag string = "-D" - IptablesFlushFlag string = "-F" - IptablesCheckFlag string = "-C" - IptablesDestroyFlag string = "-X" - IptablesJumpFlag string = "-j" - IptablesWaitFlag string = "-w" - IptablesAccept string = "ACCEPT" - IptablesReject string = "REJECT" - IptablesDrop string = "DROP" - IptablesSrcFlag string = "src" - IptablesDstFlag string = "dst" - IptablesProtFlag string = "-p" - IptablesSFlag string = "-s" - IptablesDFlag string = "-d" - IptablesDstPortFlag string = "--dport" - IptablesMatchFlag string = "-m" - IptablesSetFlag string = "set" - IptablesMatchSetFlag string = "--match-set" - IptablesStateFlag string = "state" - IptablesMatchStateFlag string = "--state" - IptablesMultiportFlag string = "multiport" - IptablesMultiDestportFlag string = "--dports" - IptablesRelatedState string = "RELATED" - IptablesEstablishedState string = "ESTABLISHED" - IptablesFilterTable string = "filter" + Iptables string = "iptables" + Ip6tables string = "ip6tables" + IptablesSave string = "iptables-save" + IptablesRestore string = "iptables-restore" + IptablesConfigFile string = "/var/log/iptables.conf" + IptablesTestConfigFile string = "/var/log/iptables-test.conf" + IptablesLockFile string = "/run/xtables.lock" + IptablesChainCreationFlag string = "-N" + IptablesInsertionFlag string = "-I" + IptablesAppendFlag string = "-A" + IptablesDeletionFlag string = "-D" + IptablesFlushFlag string = "-F" + IptablesCheckFlag string = "-C" + IptablesDestroyFlag string = "-X" + IptablesJumpFlag string = "-j" + IptablesWaitFlag string = "-w" + IptablesAccept string = "ACCEPT" + IptablesReject string = "REJECT" + IptablesDrop string = "DROP" + IptablesSrcFlag string = "src" + IptablesDstFlag string = "dst" + IptablesNotFlag string = "!" + IptablesProtFlag string = "-p" + IptablesSFlag string = "-s" + IptablesDFlag string = "-d" + IptablesDstPortFlag string = "--dport" + IptablesModuleFlag string = "-m" + IptablesSetModuleFlag string = "set" + IptablesMatchSetFlag string = "--match-set" + IptablesStateModuleFlag string = "state" + IptablesStateFlag string = "--state" + IptablesMultiportFlag string = "multiport" + IptablesMultiDestportFlag string = "--dports" + IptablesRelatedState string = "RELATED" + IptablesEstablishedState string = "ESTABLISHED" + IptablesFilterTable string = "filter" + IptablesCommentModuleFlag string = "comment" + IptablesCommentFlag string = "--comment" + IptablesAddCommentFlag IptablesAzureChain string = "AZURE-NPM" + IptablesAzureKubeSystemChain string = "AZURE-NPM-KUBE-SYSTEM" IptablesAzureIngressPortChain string = "AZURE-NPM-INGRESS-PORT" IptablesAzureIngressFromChain string = "AZURE-NPM-INGRESS-FROM" - IptablesAzureIngressFromNsChain string = "AZURE-NPM-INGRESS-FROM-NS" - IptablesAzureIngressFromPodChain string = "AZURE-NPM-INGRESS-FROM-POD" IptablesAzureEgressPortChain string = "AZURE-NPM-EGRESS-PORT" IptablesAzureEgressToChain string = "AZURE-NPM-EGRESS-TO" - IptablesAzureEgressToNsChain string = "AZURE-NPM-EGRESS-TO-NS" - IptablesAzureEgressToPodChain string = "AZURE-NPM-EGRESS-TO-POD" IptablesAzureTargetSetsChain string = "AZURE-NPM-TARGET-SETS" IptablesForwardChain string = "FORWARD" IptablesInputChain string = "INPUT" diff --git a/npm/util/util.go b/npm/util/util.go index 0494273164..70c7909ca1 100644 --- a/npm/util/util.go +++ b/npm/util/util.go @@ -8,6 +8,7 @@ import ( "os" "strconv" "strings" + "sort" "k8s.io/apimachinery/pkg/version" ) @@ -36,11 +37,6 @@ func GetClusterID(nodeName string) string { return s[2] } -// GetNsIpsetName returns ipset name from namespaceSelector. -func GetNsIpsetName(k, v string) string { - return "ns-" + k + ":" + v -} - // Hash hashes a string to another string with length <= 32. func Hash(s string) string { h := fnv.New32a() @@ -48,6 +44,27 @@ func Hash(s string) string { return fmt.Sprint(h.Sum32()) } +// SortMap sorts the map by key in alphabetical order. +// Note: even though the map is sorted, accessing it through range will still result in random order. +func SortMap(m *map[string]string) ([]string, []string) { + var sortedKeys, sortedVals []string + for k := range *m { + sortedKeys = append(sortedKeys, k) + } + sort.Strings(sortedKeys) + + sortedMap := &map[string]string{} + for _, k := range sortedKeys { + v := (*m)[k] + (*sortedMap)[k] = v + sortedVals = append(sortedVals, v) + } + + m = sortedMap + + return sortedKeys, sortedVals +} + // UniqueStrSlice removes duplicate elements from the input string. func UniqueStrSlice(s []string) []string { m, unique := map[string]bool{}, []string{} @@ -152,3 +169,51 @@ func SetIsNewNwPolicyVerFlag(ver *version.Info) error { return nil } + +// GetOperatorAndLabel returns the operator associated with the label and the label without operator. +func GetOperatorAndLabel(label string) (string, string) { + if len(label) == 0 { + return "", "" + } + + if string(label[0]) == IptablesNotFlag { + return IptablesNotFlag, label[1:] + } + + return "", label +} + +// GetLabelsWithoutOperators returns labels without operators. +func GetLabelsWithoutOperators(labels []string) []string { + var res []string + for _, label := range labels { + if len(label) > 0 { + if string(label[0]) == IptablesNotFlag { + res = append(res, label[1:]) + } else { + res = append(res, label) + } + } + } + + return res +} + +// DropEmptyFields deletes empty entries from a slice. +func DropEmptyFields(s []string) []string { + i := 0 + for { + if i == len(s) { + break + } + + if s[i] == "" { + s = append(s[:i], s[i+1:]...) + continue + } + + i++ + } + + return s +} \ No newline at end of file diff --git a/npm/util/util_test.go b/npm/util/util_test.go index 2192efec40..0a7d16d690 100644 --- a/npm/util/util_test.go +++ b/npm/util/util_test.go @@ -2,10 +2,45 @@ package util import ( "testing" + "reflect" "k8s.io/apimachinery/pkg/version" ) +func TestSortMap(t *testing.T) { + m := &map[string]string{ + "e": "f", + "c": "d", + "a": "b", + } + + sortedKeys, sortedVals := SortMap(m) + + expectedKeys := []string{ + "a", + "c", + "e", + } + + expectedVals := []string{ + "b", + "d", + "f", + } + + if !reflect.DeepEqual(sortedKeys, expectedKeys) { + t.Errorf("TestSortMap failed @ key comparison") + t.Errorf("sortedKeys: %v", sortedKeys) + t.Errorf("expectedKeys: %v", expectedKeys) + } + + if !reflect.DeepEqual(sortedVals, expectedVals) { + t.Errorf("TestSortMap failed @ val comparison") + t.Errorf("sortedVals: %v", sortedVals) + t.Errorf("expectedVals: %v", expectedVals) + } +} + func TestCompareK8sVer(t *testing.T) { firstVer := &version.Info{ Major: "!", @@ -105,3 +140,35 @@ func TestIsNewNwPolicyVer(t *testing.T) { t.Errorf("TestIsNewNwPolicyVer failed @ newer version test") } } + +func TestDropEmptyFields(t *testing.T) { + testSlice := []string{ + "", + "a:b", + "", + "!", + "-m", + "--match-set", + "", + } + + resultSlice := DropEmptyFields(testSlice) + expectedSlice := []string{ + "a:b", + "!", + "-m", + "--match-set", + } + + if !reflect.DeepEqual(resultSlice, expectedSlice) { + t.Errorf("TestDropEmptyFields failed @ slice comparison") + } + + testSlice = []string{""} + resultSlice = DropEmptyFields(testSlice) + expectedSlice = []string{} + + if !reflect.DeepEqual(resultSlice, expectedSlice) { + t.Errorf("TestDropEmptyFields failed @ slice comparison") + } +} \ No newline at end of file