* Module name changed to `github.com/rajch/weave` * Added environment variable `CHECKPOINT_DISABLE=1` to default manifests, to bypass weaveworks telemetry
239 lines
8.0 KiB
Go
239 lines
8.0 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/rajch/weave/net/ipset"
|
|
)
|
|
|
|
func (ns *ns) analysePolicy(policy *networkingv1.NetworkPolicy) (
|
|
rules map[string]*ruleSpec,
|
|
nsSelectors, podSelectors, namespacedPodSelectors map[string]*selectorSpec,
|
|
ipBlocks map[string]*ipBlockSpec,
|
|
err error) {
|
|
|
|
nsSelectors = make(map[string]*selectorSpec)
|
|
podSelectors = make(map[string]*selectorSpec)
|
|
namespacedPodSelectors = 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, nil, policyTypes, ns.name, ipset.HashIP)
|
|
if err != nil {
|
|
return nil, 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 !allPorts {
|
|
if err = checkForNamedPorts(ingressRule.Ports); err != nil {
|
|
return nil, nil, nil, nil, nil, fmt.Errorf("named ports in network policies is not supported yet. "+
|
|
"Rejecting network policy: %s from further processing. "+err.Error(), policy.Name)
|
|
}
|
|
}
|
|
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
|
|
var srcRuleHost ruleHost
|
|
|
|
// NetworkPolicyPeer describes a peer to allow traffic from.
|
|
if peer.PodSelector != nil && peer.NamespaceSelector != nil {
|
|
srcSelector, err = newSelectorSpec(peer.PodSelector, peer.NamespaceSelector, nil, "", ipset.HashIP)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, nil, err
|
|
}
|
|
addIfNotExist(srcSelector, namespacedPodSelectors)
|
|
srcRuleHost = srcSelector
|
|
} else if peer.PodSelector != nil {
|
|
srcSelector, err = newSelectorSpec(peer.PodSelector, nil, nil, ns.name, ipset.HashIP)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, nil, err
|
|
}
|
|
addIfNotExist(srcSelector, podSelectors)
|
|
srcRuleHost = srcSelector
|
|
} else if peer.NamespaceSelector != nil {
|
|
srcSelector, err = newSelectorSpec(nil, peer.NamespaceSelector, nil, "", ipset.ListSet)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, nil, err
|
|
}
|
|
nsSelectors[srcSelector.key] = srcSelector
|
|
srcRuleHost = srcSelector
|
|
} else if peer.IPBlock != nil {
|
|
ipBlock := newIPBlockSpec(peer.IPBlock, ns.name)
|
|
ipBlocks[ipBlock.key] = ipBlock
|
|
srcRuleHost = ipBlock
|
|
}
|
|
|
|
if allPorts {
|
|
rule := newRuleSpec(policyTypeIngress, nil, srcRuleHost, targetSelector, nil)
|
|
rules[rule.key] = rule
|
|
} else {
|
|
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
|
rule := newRuleSpec(policyTypeIngress, &proto, srcRuleHost, 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 !allPorts {
|
|
if err = checkForNamedPorts(egressRule.Ports); err != nil {
|
|
return nil, nil, nil, nil, nil, fmt.Errorf("named ports in network policies is not supported yet. "+
|
|
"Rejecting network policy: %s from further processing. "+err.Error(), policy.Name)
|
|
}
|
|
}
|
|
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.
|
|
if peer.PodSelector != nil && peer.NamespaceSelector != nil {
|
|
dstSelector, err = newSelectorSpec(peer.PodSelector, peer.NamespaceSelector, nil, "", ipset.HashIP)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, nil, err
|
|
}
|
|
addIfNotExist(dstSelector, namespacedPodSelectors)
|
|
dstRuleHost = dstSelector
|
|
} else if peer.PodSelector != nil {
|
|
dstSelector, err = newSelectorSpec(peer.PodSelector, nil, nil, ns.name, ipset.HashIP)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, nil, err
|
|
}
|
|
addIfNotExist(dstSelector, podSelectors)
|
|
dstRuleHost = dstSelector
|
|
|
|
} else if peer.NamespaceSelector != nil {
|
|
dstSelector, err = newSelectorSpec(nil, peer.NamespaceSelector, nil, "", ipset.ListSet)
|
|
if err != nil {
|
|
return nil, 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, namespacedPodSelectors, 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
|
|
}
|
|
|
|
func checkForNamedPorts(ports []networkingv1.NetworkPolicyPort) error {
|
|
for _, npProtocolPort := range ports {
|
|
if npProtocolPort.Port != nil && npProtocolPort.Port.Type == intstr.String {
|
|
return fmt.Errorf("named port %s in network policy", port(npProtocolPort.Port))
|
|
}
|
|
}
|
|
return nil
|
|
}
|