The main difference between new and legacy (pre-1.7) netpol semantics
are the following:
* The DenyDefault ingress isolation annotation is obsoleted.
* Traffic is allowed to a pod if there are no netpol selecting
the pod.
See https://kubernetes.io/docs/api-reference/v1.6/#networkpolicyspec-v1beta1-extensions
vs https://kubernetes.io/docs/api-reference/v1.7/#networkpolicyspec-v1-networking
for more details.
weave-npc can operate either in the legacy or the new mode (default). To
use the legacy, pass "--use-legacy-netpol" arg when starting the npc.
In non-legacy mode we create for each Namespace a default-allow ipset
which stores IP addrs of such Pods which are not selected by any
NetworkPolicy.The ipsets are dst's in rules of the WEAVE-NPC-DEFAULT chain.
175 lines
4.8 KiB
Go
175 lines
4.8 KiB
Go
package npc
|
|
|
|
import (
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
|
|
"github.com/weaveworks/weave/common"
|
|
"github.com/weaveworks/weave/npc/ipset"
|
|
)
|
|
|
|
type selectorSpec struct {
|
|
key string // string representation (for hash keying/equality comparison)
|
|
selector labels.Selector // k8s Selector object (for matching)
|
|
dst bool // destination selector
|
|
|
|
ipsetType ipset.Type // type of ipset to provision
|
|
ipsetName ipset.Name // generated ipset name
|
|
nsName string // Namespace name
|
|
}
|
|
|
|
func newSelectorSpec(json *metav1.LabelSelector, dst bool, nsName string, ipsetType ipset.Type) (*selectorSpec, error) {
|
|
selector, err := metav1.LabelSelectorAsSelector(json)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
key := selector.String()
|
|
return &selectorSpec{
|
|
key: key,
|
|
selector: selector,
|
|
dst: dst,
|
|
// We prefix the selector string with the namespace name when generating
|
|
// the shortname because you can specify the same selector in multiple
|
|
// namespaces - we need those to map to distinct ipsets
|
|
ipsetName: ipset.Name(IpsetNamePrefix + shortName(nsName+":"+key)),
|
|
ipsetType: ipsetType,
|
|
nsName: nsName}, nil
|
|
}
|
|
|
|
type selector struct {
|
|
ips ipset.Interface
|
|
spec *selectorSpec
|
|
}
|
|
|
|
func (s *selector) matches(labelMap map[string]string) bool {
|
|
return s.spec.selector.Matches(labels.Set(labelMap))
|
|
}
|
|
|
|
func (s *selector) addEntry(entry string, comment string) error {
|
|
return s.ips.AddEntry(s.spec.ipsetName, entry, comment)
|
|
}
|
|
|
|
func (s *selector) delEntry(entry string) error {
|
|
return s.ips.DelEntry(s.spec.ipsetName, entry)
|
|
}
|
|
|
|
type selectorFn func(selector *selector) error
|
|
|
|
type selectorSet struct {
|
|
ips ipset.Interface
|
|
onNewSelector selectorFn
|
|
|
|
// invoked after dst selector has been provisioned for the first time
|
|
onNewDstSelector selectorFn
|
|
// invoked after the last instance of dst selector has been deprovisioned
|
|
onDestroyDstSelector selectorFn
|
|
|
|
users map[string]map[types.UID]struct{} // list of users per selector
|
|
entries map[string]*selector
|
|
|
|
// We need to keep track of dst selector instances to be able to invoke
|
|
// onNewDstSelector and onDestroyDstSelector callbacks at the right time;
|
|
// selectorSpec.Key -> count
|
|
dstSelectorsCount map[string]int
|
|
}
|
|
|
|
func newSelectorSet(ips ipset.Interface, onNewSelector, onNewDstSelector selectorFn, onDestroyDstSelector selectorFn) *selectorSet {
|
|
return &selectorSet{
|
|
ips: ips,
|
|
onNewSelector: onNewSelector,
|
|
onNewDstSelector: onNewDstSelector,
|
|
onDestroyDstSelector: onDestroyDstSelector,
|
|
users: make(map[string]map[types.UID]struct{}),
|
|
entries: make(map[string]*selector),
|
|
dstSelectorsCount: make(map[string]int)}
|
|
}
|
|
|
|
func (ss *selectorSet) addToMatching(labelMap map[string]string, entry string, comment string) (bool, error) {
|
|
found := false
|
|
for _, s := range ss.entries {
|
|
if s.matches(labelMap) {
|
|
if ss.dstSelectorExist(s) {
|
|
found = true
|
|
}
|
|
if err := s.addEntry(entry, comment); err != nil {
|
|
return found, err
|
|
}
|
|
}
|
|
}
|
|
return found, nil
|
|
}
|
|
|
|
func (ss *selectorSet) delFromMatching(labelMap map[string]string, entry string) error {
|
|
for _, s := range ss.entries {
|
|
if s.matches(labelMap) {
|
|
if err := s.delEntry(entry); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ss *selectorSet) dstSelectorExist(s *selector) bool {
|
|
return ss.dstSelectorsCount[s.spec.key] > 0
|
|
}
|
|
|
|
func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*selectorSpec) error {
|
|
for key, spec := range current {
|
|
if _, found := desired[key]; !found {
|
|
delete(ss.users[key], user)
|
|
if len(ss.users[key]) == 0 {
|
|
common.Log.Infof("destroying ipset: %#v", spec)
|
|
if err := ss.ips.Destroy(spec.ipsetName); err != nil {
|
|
return err
|
|
}
|
|
|
|
delete(ss.entries, key)
|
|
delete(ss.users, key)
|
|
}
|
|
|
|
if spec.dst {
|
|
ss.dstSelectorsCount[key]--
|
|
if ss.dstSelectorsCount[key] == 0 {
|
|
if err := ss.onDestroyDstSelector(&selector{ss.ips, spec}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ss *selectorSet) provision(user types.UID, current, desired map[string]*selectorSpec) error {
|
|
for key, spec := range desired {
|
|
if _, found := current[key]; !found {
|
|
selector := &selector{ss.ips, spec}
|
|
|
|
if _, found := ss.users[key]; !found {
|
|
common.Log.Infof("creating ipset: %#v", spec)
|
|
if err := ss.ips.Create(spec.ipsetName, spec.ipsetType); err != nil {
|
|
return err
|
|
}
|
|
if err := ss.onNewSelector(selector); err != nil {
|
|
return err
|
|
}
|
|
ss.users[key] = make(map[types.UID]struct{})
|
|
ss.entries[key] = selector
|
|
}
|
|
ss.users[key][user] = struct{}{}
|
|
|
|
if spec.dst {
|
|
ss.dstSelectorsCount[key]++
|
|
if ss.dstSelectorsCount[key] == 1 {
|
|
if err := ss.onNewDstSelector(selector); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|