From 25855b23622d4836f9422eb1b841782319f98ba7 Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Thu, 14 Aug 2014 19:25:12 -0700 Subject: [PATCH] consul: ACL enforcement for KV updates --- consul/kvs_endpoint.go | 17 +++++++++ consul/kvs_endpoint_test.go | 75 +++++++++++++++++++++++++++++++++++-- 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/consul/kvs_endpoint.go b/consul/kvs_endpoint.go index e271445c60..53ed238be1 100644 --- a/consul/kvs_endpoint.go +++ b/consul/kvs_endpoint.go @@ -26,6 +26,23 @@ func (k *KVS) Apply(args *structs.KVSRequest, reply *bool) error { return fmt.Errorf("Must provide key") } + // Apply the ACL policy if any + acl, err := k.srv.resolveToken(args.Token) + if err != nil { + return err + } else if acl != nil { + switch args.Op { + case structs.KVSDeleteTree: + if !acl.KeyWritePrefix(args.DirEnt.Key) { + return permissionDeniedErr + } + default: + if !acl.KeyWrite(args.DirEnt.Key) { + return permissionDeniedErr + } + } + } + // If this is a lock, we must check for a lock-delay. Since lock-delay // is based on wall-time, each peer expire the lock-delay at a slightly // different time. This means the enforcement of lock-delay cannot be done diff --git a/consul/kvs_endpoint_test.go b/consul/kvs_endpoint_test.go index f4cfd0941e..3a37698257 100644 --- a/consul/kvs_endpoint_test.go +++ b/consul/kvs_endpoint_test.go @@ -1,11 +1,13 @@ package consul import ( - "github.com/hashicorp/consul/consul/structs" - "github.com/hashicorp/consul/testutil" "os" + "strings" "testing" "time" + + "github.com/hashicorp/consul/consul/structs" + "github.com/hashicorp/consul/testutil" ) func TestKVS_Apply(t *testing.T) { @@ -64,6 +66,68 @@ func TestKVS_Apply(t *testing.T) { } } +func TestKVS_Apply_ACLDeny(t *testing.T) { + dir1, s1 := testServerWithConfig(t, func(c *Config) { + c.ACLDatacenter = "dc1" + c.ACLMasterToken = "root" + c.ACLDefaultPolicy = "deny" + }) + defer os.RemoveAll(dir1) + defer s1.Shutdown() + client := rpcClient(t, s1) + defer client.Close() + + testutil.WaitForLeader(t, client.Call, "dc1") + + // Create the ACL + arg := structs.ACLRequest{ + Datacenter: "dc1", + Op: structs.ACLSet, + ACL: structs.ACL{ + Name: "User token", + Type: structs.ACLTypeClient, + Rules: testListRules, + }, + WriteRequest: structs.WriteRequest{Token: "root"}, + } + var out string + if err := client.Call("ACL.Apply", &arg, &out); err != nil { + t.Fatalf("err: %v", err) + } + id := out + + // Try a write + argR := structs.KVSRequest{ + Datacenter: "dc1", + Op: structs.KVSSet, + DirEnt: structs.DirEntry{ + Key: "foo/bar", + Flags: 42, + Value: []byte("test"), + }, + WriteRequest: structs.WriteRequest{Token: id}, + } + var outR bool + err := client.Call("KVS.Apply", &argR, &outR) + if err == nil || !strings.Contains(err.Error(), permissionDenied) { + t.Fatalf("err: %v", err) + } + + // Try a recursive delete + argR = structs.KVSRequest{ + Datacenter: "dc1", + Op: structs.KVSDeleteTree, + DirEnt: structs.DirEntry{ + Key: "test", + }, + WriteRequest: structs.WriteRequest{Token: id}, + } + err = client.Call("KVS.Apply", &argR, &outR) + if err == nil || !strings.Contains(err.Error(), permissionDenied) { + t.Fatalf("err: %v", err) + } +} + func TestKVS_Get(t *testing.T) { dir1, s1 := testServer(t) defer os.RemoveAll(dir1) @@ -128,7 +192,7 @@ func TestKVS_Get_ACLDeny(t *testing.T) { Datacenter: "dc1", Op: structs.KVSSet, DirEnt: structs.DirEntry{ - Key: "test", + Key: "zip", Flags: 42, Value: []byte("test"), }, @@ -141,7 +205,7 @@ func TestKVS_Get_ACLDeny(t *testing.T) { getR := structs.KeyRequest{ Datacenter: "dc1", - Key: "test", + Key: "zip", } var dirent structs.IndexedDirEntries if err := client.Call("KVS.Get", &getR, &dirent); err != nil { @@ -512,4 +576,7 @@ key "foo" { key "test" { policy = "write" } +key "test/priv" { + policy = "read" +} `