Otherwise, the same ipBlock ipset can be used among different namespaces which results in panic as refcounting of ipsets happens only per namespace, so npc will try to create the same ipset twice.
198 lines
6.1 KiB
Go
198 lines
6.1 KiB
Go
package npc
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
apiv1 "k8s.io/api/core/v1"
|
|
networkingv1 "k8s.io/api/networking/v1"
|
|
"k8s.io/apimachinery/pkg/util/intstr"
|
|
|
|
"github.com/weaveworks/weave/net/ipset"
|
|
)
|
|
|
|
func (ns *ns) analysePolicy(policy *networkingv1.NetworkPolicy) (
|
|
rules map[string]*ruleSpec,
|
|
nsSelectors, podSelectors map[string]*selectorSpec,
|
|
ipBlocks map[string]*ipBlockSpec,
|
|
err error) {
|
|
|
|
nsSelectors = make(map[string]*selectorSpec)
|
|
podSelectors = make(map[string]*selectorSpec)
|
|
ipBlocks = make(map[string]*ipBlockSpec)
|
|
rules = make(map[string]*ruleSpec)
|
|
policyTypes := make([]policyType, 0)
|
|
|
|
for _, pt := range policy.Spec.PolicyTypes {
|
|
if pt == networkingv1.PolicyTypeIngress {
|
|
policyTypes = append(policyTypes, policyTypeIngress)
|
|
}
|
|
if pt == networkingv1.PolicyTypeEgress {
|
|
policyTypes = append(policyTypes, policyTypeEgress)
|
|
}
|
|
}
|
|
// If empty, matches all pods in a namespace
|
|
targetSelector, err := newSelectorSpec(&policy.Spec.PodSelector, policyTypes, ns.name, ipset.HashIP)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, err
|
|
}
|
|
|
|
// To prevent targetSelector being overwritten by a subsequent selector with
|
|
// the same key, addIfNotExist MUST be used when adding to podSelectors.
|
|
// Otherwise, policyTypes of the selector might be lost resulting in
|
|
// an invalid content in any "default-allow" ipset.
|
|
addIfNotExist(targetSelector, podSelectors)
|
|
|
|
// If ingress is empty then this NetworkPolicy does not allow any ingress traffic
|
|
if policy.Spec.Ingress != nil && len(policy.Spec.Ingress) != 0 {
|
|
for _, ingressRule := range policy.Spec.Ingress {
|
|
// If Ports is empty or missing, this rule matches all ports
|
|
allPorts := ingressRule.Ports == nil || len(ingressRule.Ports) == 0
|
|
// If From is empty or missing, this rule matches all sources
|
|
allSources := ingressRule.From == nil || len(ingressRule.From) == 0
|
|
|
|
if allSources {
|
|
if allPorts {
|
|
rule := newRuleSpec(policyTypeIngress, nil, nil, targetSelector, nil)
|
|
rules[rule.key] = rule
|
|
} else {
|
|
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
|
rule := newRuleSpec(policyTypeIngress, &proto, nil, targetSelector, &port)
|
|
rules[rule.key] = rule
|
|
})
|
|
}
|
|
} else {
|
|
for _, peer := range ingressRule.From {
|
|
var srcSelector *selectorSpec
|
|
|
|
// NetworkPolicyPeer describes a peer to allow traffic from.
|
|
// Exactly one of its fields must be specified.
|
|
if peer.PodSelector != nil {
|
|
srcSelector, err = newSelectorSpec(peer.PodSelector, nil, ns.name, ipset.HashIP)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, err
|
|
}
|
|
addIfNotExist(srcSelector, podSelectors)
|
|
|
|
} else if peer.NamespaceSelector != nil {
|
|
srcSelector, err = newSelectorSpec(peer.NamespaceSelector, nil, "", ipset.ListSet)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, err
|
|
}
|
|
nsSelectors[srcSelector.key] = srcSelector
|
|
}
|
|
|
|
if allPorts {
|
|
rule := newRuleSpec(policyTypeIngress, nil, srcSelector, targetSelector, nil)
|
|
rules[rule.key] = rule
|
|
} else {
|
|
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
|
rule := newRuleSpec(policyTypeIngress, &proto, srcSelector, targetSelector, &port)
|
|
rules[rule.key] = rule
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If egress is empty then this NetworkPolicy does not allow any egress traffic
|
|
if policy.Spec.Egress != nil && len(policy.Spec.Egress) != 0 {
|
|
for _, egressRule := range policy.Spec.Egress {
|
|
// If Ports is empty or missing, this rule matches all ports
|
|
allPorts := egressRule.Ports == nil || len(egressRule.Ports) == 0
|
|
// If To is empty or missing, this rule matches all destinations
|
|
allDestinations := egressRule.To == nil || len(egressRule.To) == 0
|
|
|
|
if allDestinations {
|
|
if allPorts {
|
|
rule := newRuleSpec(policyTypeEgress, nil, targetSelector, nil, nil)
|
|
rules[rule.key] = rule
|
|
} else {
|
|
withNormalisedProtoAndPort(egressRule.Ports, func(proto, port string) {
|
|
rule := newRuleSpec(policyTypeEgress, &proto, targetSelector, nil, &port)
|
|
rules[rule.key] = rule
|
|
})
|
|
}
|
|
} else {
|
|
for _, peer := range egressRule.To {
|
|
var dstSelector *selectorSpec
|
|
var dstRuleHost ruleHost
|
|
|
|
// NetworkPolicyPeer describes a peer to allow traffic to.
|
|
// Exactly one of its fields must be specified.
|
|
if peer.PodSelector != nil {
|
|
dstSelector, err = newSelectorSpec(peer.PodSelector, nil, ns.name, ipset.HashIP)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, err
|
|
}
|
|
addIfNotExist(dstSelector, podSelectors)
|
|
dstRuleHost = dstSelector
|
|
|
|
} else if peer.NamespaceSelector != nil {
|
|
dstSelector, err = newSelectorSpec(peer.NamespaceSelector, nil, "", ipset.ListSet)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, err
|
|
}
|
|
nsSelectors[dstSelector.key] = dstSelector
|
|
dstRuleHost = dstSelector
|
|
} else if peer.IPBlock != nil {
|
|
ipBlock := newIPBlockSpec(peer.IPBlock, ns.name)
|
|
ipBlocks[ipBlock.key] = ipBlock
|
|
dstRuleHost = ipBlock
|
|
}
|
|
|
|
if allPorts {
|
|
rule := newRuleSpec(policyTypeEgress, nil, targetSelector, dstRuleHost, nil)
|
|
rules[rule.key] = rule
|
|
} else {
|
|
withNormalisedProtoAndPort(egressRule.Ports, func(proto, port string) {
|
|
rule := newRuleSpec(policyTypeEgress, &proto, targetSelector, dstRuleHost, &port)
|
|
rules[rule.key] = rule
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rules, nsSelectors, podSelectors, ipBlocks, nil
|
|
}
|
|
|
|
func addIfNotExist(s *selectorSpec, ss map[string]*selectorSpec) {
|
|
if _, ok := ss[s.key]; !ok {
|
|
ss[s.key] = s
|
|
}
|
|
}
|
|
|
|
func withNormalisedProtoAndPort(npps []networkingv1.NetworkPolicyPort, f func(proto, port string)) {
|
|
for _, npp := range npps {
|
|
f(proto(npp.Protocol), port(npp.Port))
|
|
}
|
|
}
|
|
|
|
func proto(p *apiv1.Protocol) string {
|
|
// If no proto is specified, default to TCP
|
|
proto := string(apiv1.ProtocolTCP)
|
|
if p != nil {
|
|
proto = string(*p)
|
|
}
|
|
|
|
return proto
|
|
}
|
|
|
|
func port(p *intstr.IntOrString) string {
|
|
// If no port is specified, match any port. Let iptables executable handle
|
|
// service name resolution
|
|
port := "0:65535"
|
|
if p != nil {
|
|
switch p.Type {
|
|
case intstr.Int:
|
|
port = fmt.Sprintf("%d", p.IntVal)
|
|
case intstr.String:
|
|
port = p.StrVal
|
|
}
|
|
}
|
|
|
|
return port
|
|
}
|