Skip to content

Commit

Permalink
policy: Remove requirement of single shared label root prefix
Browse files Browse the repository at this point in the history
Up to now, we required all policy node names to share a single
prefix which defaulted to "io.cilium". This was configurable
but required a specific label addressing schema.

This commits changes this and puts all labels under a single root
label called "root". Rules attached to the root label applied to
all endpoints. The root node can have children, e.g.

          root
       io       org
  k8s     foo       corp
                 foo    bar

Signed-off-by: Thomas Graf <thomas@cilium.io>
  • Loading branch information
tgraf committed Mar 17, 2017
1 parent 1c6052f commit 7a21fa4
Show file tree
Hide file tree
Showing 33 changed files with 754 additions and 572 deletions.
7 changes: 2 additions & 5 deletions common/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,14 @@ const (

// Miscellaneous dedicated constants

// GlobalLabelPrefix is the default root path for the policy.
GlobalLabelPrefix = "io.cilium"
// CiliumLabelSource is the default label source for the labels read from containers.
CiliumLabelSource = "cilium"
// K8sLabelSource is the default label source for the labels read from kubernetes.
K8sLabelSource = "k8s"
// K8sAnnotationName is the annotation name used for the cilium policy name in the
// kubernetes network policy.
K8sAnnotationName = "io.cilium.name"
// K8sLabelPrefix is the default prefix used when parsing labels that don't have
// the GlobalLabelPrefix in kubernetes.
// K8sLabelPrefix is the default prefix used to represent kubernetes labels
K8sLabelPrefix = "io.cilium.k8s."
// K8sDefaultParent is the default prefix for network policies received from
// kubernetes.
Expand All @@ -76,7 +73,7 @@ const (
// Label source for reserved types
ReservedLabelSource = "reserved"
// Label used to represent the reserved source
ReservedLabelKey = GlobalLabelPrefix + "." + ReservedLabelSource
ReservedLabelKey = "io.cilium." + ReservedLabelSource
// EndpointsPerHost is the maximum number of endpoints allowed per host. It should
// represent the same number of IPv6 addresses supported on each node.
EndpointsPerHost = 0xFFFF
Expand Down
3 changes: 1 addition & 2 deletions daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,10 +471,9 @@ func NewDaemon(c *Config) (*Daemon, error) {
events: make(chan events.Event, 512),
loadBalancer: lb,
consumableCache: policy.NewConsumableCache(),
policy: policy.Tree{Root: policy.NewNode(common.GlobalLabelPrefix, nil)},
policy: policy.Tree{},
ignoredContainers: make(map[string]int),
}
d.policy.Root.Path()

d.listenForCiliumEvents()

Expand Down
174 changes: 13 additions & 161 deletions daemon/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"bytes"
"encoding/json"
"fmt"
"strings"

"github.com/cilium/cilium/api/v1/models"
. "github.com/cilium/cilium/api/v1/server/restapi/policy"
Expand All @@ -34,37 +33,6 @@ import (
"github.com/op/go-logging"
)

func validPath(path string) bool {
return strings.HasPrefix(path, common.GlobalLabelPrefix)
}

// findNode returns node and its parent or an error
func (d *Daemon) findNode(path string) (*policy.Node, *policy.Node) {
var parent *policy.Node

newPath := strings.Replace(path, common.GlobalLabelPrefix, "", 1)
if newPath == "" {
return d.policy.Root, nil
}

current := d.policy.Root
parent = nil

for _, nodeName := range strings.Split(newPath, ".") {
if nodeName == "" {
continue
}
if child, ok := current.Children[nodeName]; ok {
parent = current
current = child
} else {
return nil, nil
}
}

return current, parent
}

func (d *Daemon) GetCachedLabelList(ID policy.NumericIdentity) ([]labels.Label, error) {
// Check if we have the source security context in our local
// consumable cache
Expand Down Expand Up @@ -176,73 +144,6 @@ func (h *getPolicyResolve) Handle(params GetPolicyResolveParams) middleware.Resp
return NewGetPolicyResolveOK().WithPayload(&result)
}

func (d *Daemon) policyAddNode(path string, node *policy.Node) (bool, error) {
var (
currNode, parentNode *policy.Node
policyModified bool
err error
)

if node.Name == "" {
path, node.Name = policy.SplitNodePath(path)
} else if strings.Contains(node.Name, ".") && node.Name != common.GlobalLabelPrefix {
path, node.Name = policy.SplitNodePath(path + "." + node.Name)
}

currNode, parentNode = d.findNode(path)
log.Debugf("Policy currNode %+v, parentNode %+v", currNode, parentNode)

// eg. path = io.cilium.lizards.foo.db and io.cilium.lizards doesn't exist
if (currNode == nil && parentNode == nil) ||
// eg. path = io.cilium.lizards.foo and io.cilium.lizards.foo doesn't exist
(currNode == nil && parentNode != nil) {

pn := policy.NewNode("", nil)
policyModified, err = d.policyAddNode(path, pn)
if err != nil {
return false, err
}
currNode, parentNode = d.findNode(path)
log.Debugf("Policy currNode %+v, parentNode %+v", currNode, parentNode)
}
// eg. path = io.cilium
if currNode != nil && parentNode == nil {
if currNode.Name == node.Name {
node.Path()
policyModified, err = currNode.Merge(node)
if err != nil {
return false, err
}
} else {
policyModified, err = currNode.AddChild(node.Name, node)
if err != nil {
return false, err
}
}
} else if currNode != nil && parentNode != nil {
// eg. path = io.cilium.lizards.db exists
policyModified, err = currNode.AddChild(node.Name, node)
if err != nil {
return false, err
}
}

return policyModified, nil
}

func (d *Daemon) policyAdd(path string, node *policy.Node) (bool, error) {
d.policy.Mutex.Lock()
defer d.policy.Mutex.Unlock()

if modified, err := d.policyAddNode(path, node); err != nil {
return false, err
} else if modified {
return modified, node.ResolveTree()
}

return false, nil
}

func (d *Daemon) enablePolicyEnforcement() {
d.conf.Opts.Set(endpoint.OptionPolicy, true)

Expand All @@ -261,17 +162,12 @@ func (d *Daemon) enablePolicyEnforcement() {
func (d *Daemon) PolicyAdd(path string, node *policy.Node) *apierror.ApiError {
log.Debugf("Policy Add Request: %s %+v", path, node)

if !strings.HasPrefix(path, common.GlobalLabelPrefix) {
return apierror.New(PutPolicyPathInvalidPathCode,
"Invalid path %s: must start with %s", path, common.GlobalLabelPrefix)
}

// Enable policy if not already enabled
if !d.conf.Opts.IsEnabled(endpoint.OptionPolicy) {
d.enablePolicyEnforcement()
}

if policyModified, err := d.policyAdd(path, node); err != nil {
if policyModified, err := d.policy.Add(path, node); err != nil {
return apierror.Error(PutPolicyPathFailureCode, err)
} else if policyModified {
log.Info("New policy imported, regenerating...")
Expand All @@ -281,60 +177,22 @@ func (d *Daemon) PolicyAdd(path string, node *policy.Node) *apierror.ApiError {
return nil
}

func (d *Daemon) deleteNode(node *policy.Node, parent *policy.Node) {
if node == d.policy.Root {
d.policy.Root = policy.NewNode(common.GlobalLabelPrefix, nil)
d.policy.Root.Path()
} else {
delete(parent.Children, node.Name)
}
}

// PolicyDelete deletes the policy set in the given path from the policy tree. If
// cover256Sum is set it finds the rule with the respective coverage that rule from the
// node. If the path's node becomes ruleless it is removed from the tree.
// PolicyDelete deletes the policy set in the given path from the policy tree.
// If cover256Sum is set it finds the rule with the respective coverage that
// rule from the node. If the path's node becomes ruleless it is removed from
// the tree.
func (d *Daemon) PolicyDelete(path, cover256Sum string) *apierror.ApiError {
log.Debugf("Policy Delete Request: %s, cover256Sum %s", path, cover256Sum)

d.policy.Mutex.Lock()
node, parent := d.findNode(path)
if node == nil {
d.policy.Mutex.Unlock()
return apierror.New(DeletePolicyPathNotFoundCode, "Policy node not found")
if cover256Sum != "" && len(cover256Sum) != policy.CoverageSHASize {
return apierror.New(DeletePolicyPathInvalidCode,
"invalid length of hash, must be %d", policy.CoverageSHASize)
}

// Deletion request of a specific rule of a node
if cover256Sum != "" {
if len(cover256Sum) != policy.CoverageSHASize {
d.policy.Mutex.Unlock()
return apierror.New(DeletePolicyPathInvalidCode,
"Invalid length of hash, must be %d", policy.CoverageSHASize)
}

for i, pr := range node.Rules {
if prCover256Sum, err := pr.CoverageSHA256Sum(); err == nil &&
prCover256Sum == cover256Sum {
node.Rules = append(node.Rules[:i], node.Rules[i+1:]...)

// If the rule was the last remaining, delete the node
if !node.HasRules() {
d.deleteNode(node, parent)
}

d.policy.Mutex.Unlock()
d.triggerPolicyUpdates([]policy.NumericIdentity{})
return nil
}
}

d.policy.Mutex.Unlock()
if !d.policy.Delete(path, cover256Sum) {
return apierror.New(DeletePolicyPathNotFoundCode, "policy not found")
}

// Deletion request for entire node
d.deleteNode(node, parent)
d.policy.Mutex.Unlock()

d.triggerPolicyUpdates([]policy.NumericIdentity{})
return nil
}
Expand Down Expand Up @@ -366,10 +224,6 @@ func NewPutPolicyPathHandler(d *Daemon) PutPolicyPathHandler {

func (h *putPolicyPath) Handle(params PutPolicyPathParams) middleware.Responder {
d := h.daemon
if !validPath(params.Path) {
return apierror.New(PutPolicyPathInvalidPathCode,
"path must have prefix %s", common.GlobalLabelPrefix)
}

var node policy.Node
if err := json.Unmarshal([]byte(*params.Policy), &node); err != nil {
Expand Down Expand Up @@ -397,6 +251,9 @@ func (h *getPolicy) Handle(params GetPolicyParams) middleware.Responder {
d.policy.Mutex.RLock()
defer d.policy.Mutex.RUnlock()
node := d.policy.Root
if node == nil {
node = &policy.Node{}
}
return NewGetPolicyOK().WithPayload(models.PolicyTree(node.JSONMarshal()))
}

Expand All @@ -413,12 +270,7 @@ func (h *getPolicyPath) Handle(params GetPolicyPathParams) middleware.Responder
d.policy.Mutex.RLock()
defer d.policy.Mutex.RUnlock()

if !validPath(params.Path) {
return apierror.New(GetPolicyPathInvalidCode,
"path must have prefix %s", common.GlobalLabelPrefix)
}

if node, _ := d.findNode(params.Path); node == nil {
if node, _ := d.policy.Lookup(params.Path); node == nil {
return NewGetPolicyPathNotFound()
} else {
return NewGetPolicyPathOK().WithPayload(models.PolicyTree(node.JSONMarshal()))
Expand Down
73 changes: 9 additions & 64 deletions daemon/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,71 +36,16 @@ var (
IPv4Addr, _ = addressing.NewCiliumIPv4("10.11.12.13")
)

func (ds *DaemonSuite) TestFindNode(c *C) {
var nullPtr *policy.Node

pn := policy.Node{
Name: "io.cilium",
Children: map[string]*policy.Node{
"foo": {},
"bar": {},
},
}

err := ds.d.PolicyAdd("io.cilium", &pn)
c.Assert(err, Equals, nilApiError)

n, p := ds.d.findNode("io.cilium")
c.Assert(n, Not(Equals), nil)
c.Assert(p, Equals, nullPtr)

n, p = ds.d.findNode("io.cilium.foo")
c.Assert(n, Not(Equals), nil)
c.Assert(p, Not(Equals), nil)

n, p = ds.d.findNode("io.cilium.baz")
c.Assert(n, Equals, nullPtr)
c.Assert(p, Equals, nullPtr)

n, p = ds.d.findNode("io.cilium..foo")
c.Assert(n, Not(Equals), nullPtr)
c.Assert(p, Not(Equals), nullPtr)

err = ds.d.PolicyDelete("io.cilium", "")
c.Assert(err, IsNil)
}

func (ds *DaemonSuite) TestPolicyGet(c *C) {
pn := policy.Node{
Name: "io.cilium",
Children: map[string]*policy.Node{
"foo": {
Name: "magic",
},
},
}

err := ds.d.PolicyAdd("io.cilium", &pn)
c.Assert(err, Equals, nilApiError)

n, p := ds.d.findNode("io.cilium.foo")
c.Assert(n, Not(Equals), nil)
c.Assert(p, Not(Equals), nil)

err = ds.d.PolicyDelete("io.cilium.foo", "")
c.Assert(err, IsNil)
}

func (ds *DaemonSuite) TestUpdateConsumerMap(c *C) {
lblProd := labels.NewLabel("io.cilium.Prod", "", common.CiliumLabelSource)
lblQA := labels.NewLabel("io.cilium.QA", "", common.CiliumLabelSource)
lblFoo := labels.NewLabel("io.cilium.foo", "", common.CiliumLabelSource)
lblBar := labels.NewLabel("io.cilium.bar", "", common.CiliumLabelSource)
lblJoe := labels.NewLabel("io.cilium.user", "joe", common.CiliumLabelSource)
lblPete := labels.NewLabel("io.cilium.user", "pete", common.CiliumLabelSource)
lblProd := labels.NewLabel("root.Prod", "", common.CiliumLabelSource)
lblQA := labels.NewLabel("root.QA", "", common.CiliumLabelSource)
lblFoo := labels.NewLabel("root.foo", "", common.CiliumLabelSource)
lblBar := labels.NewLabel("root.bar", "", common.CiliumLabelSource)
lblJoe := labels.NewLabel("root.user", "joe", common.CiliumLabelSource)
lblPete := labels.NewLabel("root.user", "pete", common.CiliumLabelSource)

rootNode := policy.Node{
Name: common.GlobalLabelPrefix,
Name: "root",
Rules: []policy.PolicyRule{
&policy.RuleConsumers{
Coverage: []labels.Label{*lblBar},
Expand Down Expand Up @@ -141,7 +86,7 @@ func (ds *DaemonSuite) TestUpdateConsumerMap(c *C) {

c.Assert(rootNode.ResolveTree(), IsNil)

err3 := ds.d.PolicyAdd("io.cilium", &rootNode)
err3 := ds.d.PolicyAdd("root", &rootNode)
c.Assert(err3, Equals, nilApiError)

qaBarLbls := labels.Labels{lblBar.Key: lblBar, lblQA.Key: lblQA}
Expand Down Expand Up @@ -212,6 +157,6 @@ func (ds *DaemonSuite) TestUpdateConsumerMap(c *C) {
c.Assert(e.Allows(prodFooSecLblsCtx.ID), Equals, true)
c.Assert(e.Allows(prodFooJoeSecLblsCtx.ID), Equals, true)

err = ds.d.PolicyDelete("io.cilium", "")
err = ds.d.PolicyDelete("root", "")
c.Assert(err, IsNil)
}
2 changes: 1 addition & 1 deletion doc/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ ip -6 route add default via beef::1
The following tests connectivity from a container to the outside world:

```
$ sudo docker run --rm -ti --net cilium -l io.cilium noironetworks/nettools ping6 www.google.com
$ sudo docker run --rm -ti --net cilium -l client noironetworks/nettools ping6 www.google.com
PING www.google.com(zrh04s07-in-x04.1e100.net) 56 data bytes
64 bytes from zrh04s07-in-x04.1e100.net: icmp_seq=1 ttl=56 time=7.84 ms
64 bytes from zrh04s07-in-x04.1e100.net: icmp_seq=2 ttl=56 time=8.63 ms
Expand Down
2 changes: 1 addition & 1 deletion examples/demo/demo1.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
. $(dirname ${BASH_SOURCE})/../../contrib/shell/util.sh

NETWORK="cilium"
CLIENT_LABEL="io.cilium.client"
CLIENT_LABEL="client"

function cleanup {
docker rm -f demo1 2> /dev/null || true
Expand Down

0 comments on commit 7a21fa4

Please sign in to comment.