npm/pkg/dataplane/policies/policy_linux.go (148 lines of code) (raw):

package policies import ( "fmt" "strconv" "strings" "github.com/Azure/azure-container-networking/npm/metrics" "github.com/Azure/azure-container-networking/npm/pkg/dataplane/ipsets" "github.com/Azure/azure-container-networking/npm/util" ) type UniqueDirection bool const ( forIngress UniqueDirection = true forEgress UniqueDirection = false // 5-6 elements depending on Included boolean maxLengthForMatchSetSpecs = 6 ) // the NPMNetworkPolicy ACLPolicyID field is unnused in Linux func aclPolicyID(_, _ string) string { return "" } // returns two booleans indicating whether the network policy has ingress and egress respectively func (networkPolicy *NPMNetworkPolicy) hasIngressAndEgress() (hasIngress, hasEgress bool) { hasIngress = false hasEgress = false for _, aclPolicy := range networkPolicy.ACLs { hasIngress = hasIngress || aclPolicy.hasIngress() hasEgress = hasEgress || aclPolicy.hasEgress() } return } func (networkPolicy *NPMNetworkPolicy) egressChainName() string { return networkPolicy.chainName(util.IptablesAzureEgressPolicyChainPrefix) } func (networkPolicy *NPMNetworkPolicy) ingressChainName() string { return networkPolicy.chainName(util.IptablesAzureIngressPolicyChainPrefix) } func (networkPolicy *NPMNetworkPolicy) chainName(prefix string) string { policyHash := util.Hash(networkPolicy.PolicyKey) return joinWithDash(prefix, policyHash) } func (networkPolicy *NPMNetworkPolicy) commentForJumpToIngress() string { return networkPolicy.commentForJump(forIngress) } func (networkPolicy *NPMNetworkPolicy) commentForJumpToEgress() string { return networkPolicy.commentForJump(forEgress) } func (networkPolicy *NPMNetworkPolicy) commentForJump(direction UniqueDirection) string { prefix := "EGRESS" if direction == forIngress { prefix = "INGRESS" } toFrom := "FROM" if direction == forIngress { toFrom = "TO" } podSelectorComment := "all" if len(networkPolicy.PodSelectorList) > 0 { podSelectorComment = commentForInfos(networkPolicy.PodSelectorList) } return fmt.Sprintf("%s-POLICY-%s-%s-%s-IN-ns-%s", prefix, networkPolicy.PolicyKey, toFrom, podSelectorComment, networkPolicy.Namespace) } func commentForInfos(infos []SetInfo) string { infoComments := make([]string, 0, len(infos)) for _, info := range infos { infoComments = append(infoComments, info.comment()) } return strings.Join(infoComments, "-AND-") } func (info SetInfo) comment() string { name := info.IPSet.GetPrefixName() if info.Included { return name } return "!" + name } func (info SetInfo) matchSetSpecs(matchString string) []string { specs := make([]string, 0, maxLengthForMatchSetSpecs) specs = append(specs, util.IptablesModuleFlag, util.IptablesSetModuleFlag) if !info.Included { specs = append(specs, util.IptablesNotFlag) } hashedSetName := info.IPSet.GetHashedName() specs = append(specs, util.IptablesMatchSetFlag, hashedSetName, matchString) return specs } func (aclPolicy *ACLPolicy) comment() string { // cleanPeerList contains peers that aren't namedports var cleanPeerList []SetInfo // TODO remove the if check and initialize the list like in egress once we replace SrcList and DstList with PeerList if aclPolicy.Direction == Ingress { cleanPeerList = aclPolicy.SrcList } else { cleanPeerList = make([]SetInfo, 0, len(aclPolicy.DstList)) } var namedPortPeer SetInfo foundNamedPortPeer := false for _, info := range aclPolicy.DstList { if info.IPSet.Type == ipsets.NamedPorts { if foundNamedPortPeer { metrics.SendErrorLogAndMetric(util.IptmID, "while creating ACL comment, unexpectedly found more than one namedPort peer for ACL:\n%s", aclPolicy.PrettyString()) } namedPortPeer = info foundNamedPortPeer = true } else if aclPolicy.Direction == Egress { // TODO remove the if check once we replace SrcList and DstList with PeerList cleanPeerList = append(cleanPeerList, info) } } builder := strings.Builder{} if aclPolicy.Target == Allowed { builder.WriteString("ALLOW") } else { builder.WriteString("DROP") } if len(cleanPeerList) == 0 { builder.WriteString("-ALL") } else { if aclPolicy.Direction == Ingress { builder.WriteString("-FROM-") } else { builder.WriteString("-TO-") } builder.WriteString(commentForInfos(cleanPeerList)) } builder.WriteString(aclPolicy.Protocol.comment()) builder.WriteString(aclPolicy.DstPorts.comment()) if foundNamedPortPeer { builder.WriteString("-TO-" + namedPortPeer.comment()) } return builder.String() } func (proto Protocol) comment() string { if proto == UnspecifiedProtocol { return "" } return fmt.Sprintf("-ON-%s", string(proto)) } func (portRange *Ports) comment() string { if portRange.Port == 0 { return "" } if portRange.Port >= portRange.EndPort { return fmt.Sprintf("-TO-PORT-%d", portRange.Port) } return fmt.Sprintf("-TO-PORT-%d:%d", portRange.Port, portRange.EndPort) } func (portRange *Ports) toIPTablesString() string { start := strconv.Itoa(int(portRange.Port)) if portRange.Port == portRange.EndPort { return start } end := strconv.Itoa(int(portRange.EndPort)) return start + ":" + end } /* Notes on commenting v1 overall: "[value]" means include if needed e.g. if a port is specified - prefix: - no to/from rules and not allowing external: - allowed: "ALLOW-ALL" - denied: "DROP-ALL" - otherwise: drop the "ALL" - suffix: - no to/from rules or port rules and not allowing external: - ingress: "-FROM-all-namespaces" - egress: "-TO-all-namespaces" - otherwise: "" (no suffix) - append these to each other to form the whole comment: prefix [-cidrIPSetName] [-AND-nsSelectorComment] [-AND] [-podSelectorComment] [-protocolComment] [-portComment] -TO (or "-FROM" if egress) -targetSelectorComment suffix v2 overall for ACLs: - prefix: - allowed: - no IPSets in SrcList/DstList (i.e. allow external): "ALLOW-ALL" - otherwise: - ingress: "ALLOW-FROM" - egress: "ALLOW-TO" - denied: replace "ALLOW" with "DROP" - similar idea (think there are at most two non-namedPort ipsets e.g. ns selector and pod selector): prefix [-ipset1Name] [-AND] [-ipset2Name] [-ON-protocolComment] [-TO-namedPortIPSetName] [-TO-portComment] NOTE: can have none or only one of namedPort and port (range) v2 for jumps to ingress/egress chains: - prefix: - ingress: "INGRESS" - egress: "EGRESS" - form: INGRESS (or "EGRESS-" if egress) -POLICY -policyKey -TO (or "-FROM" if egress) [-podSelectorComment] (or "all" if there are no pod selectors) -IN-ns -namespaceName strings for protocol, ports, selectors: protocol: just "name" port (range): - v1: - for single port: PORT-x - for namedport: PORT-name - v2: - for single port: PORT-x - with endport: PORT-x:y - in v2, namedports are specified as IPSets selector: "[!]" means optionally include "!" if the label is not included - namespace selectors (only for v1): - if there are no match expressions or labels: all-namespaces - otherwise: ns-[!]label1-AND-ns-[!]label2... - other w/out ns: [!]label1-AND-[!]label2... - other w/ ns: [!]label1-AND-[!]label2...-AND-ns-[!]labelM-IN-ns-name */