From b3a0b0502cd5edec69bc1cc7b399379cd45dce11 Mon Sep 17 00:00:00 2001 From: Anthony Romano Date: Fri, 10 Jun 2016 10:53:40 -0700 Subject: [PATCH 1/2] etcdserver: respect auth on serialized Range --- etcdserver/v3_server.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/etcdserver/v3_server.go b/etcdserver/v3_server.go index 09a6263a7..a06cd89fc 100644 --- a/etcdserver/v3_server.go +++ b/etcdserver/v3_server.go @@ -17,6 +17,7 @@ package etcdserver import ( "time" + "github.com/coreos/etcd/auth" pb "github.com/coreos/etcd/etcdserver/etcdserverpb" "github.com/coreos/etcd/lease" "github.com/coreos/etcd/lease/leasehttp" @@ -74,6 +75,14 @@ type Authenticator interface { func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) { if r.Serializable { + user, err := s.usernameFromCtx(ctx) + if err != nil { + return nil, err + } + hdr := &pb.RequestHeader{Username: user} + if !s.AuthStore().IsRangePermitted(hdr, string(r.Key), string(r.RangeEnd)) { + return nil, auth.ErrPermissionDenied + } return s.applyV3.Range(noTxn, r) } From 1e22137a9acf17a0d8a687709d198afb6047391d Mon Sep 17 00:00:00 2001 From: Anthony Romano Date: Thu, 9 Jun 2016 10:05:26 -0700 Subject: [PATCH 2/2] e2e: test auth is respected for Puts --- e2e/ctl_v3_auth_test.go | 107 +++++++++++++++++++++++++++++++++++++--- e2e/ctl_v3_test.go | 7 +++ 2 files changed, 106 insertions(+), 8 deletions(-) diff --git a/e2e/ctl_v3_auth_test.go b/e2e/ctl_v3_auth_test.go index e0025f175..3158bbda1 100644 --- a/e2e/ctl_v3_auth_test.go +++ b/e2e/ctl_v3_auth_test.go @@ -14,24 +14,33 @@ package e2e -import "testing" +import ( + "fmt" + "testing" +) -func TestCtlV3AuthEnable(t *testing.T) { testCtl(t, authEnableTest) } -func TestCtlV3AuthDisable(t *testing.T) { testCtl(t, authDisableTest) } +func TestCtlV3AuthEnable(t *testing.T) { testCtl(t, authEnableTest) } +func TestCtlV3AuthDisable(t *testing.T) { testCtl(t, authDisableTest) } +func TestCtlV3AuthWriteKey(t *testing.T) { testCtl(t, authCredWriteKeyTest) } func authEnableTest(cx ctlCtx) { + if err := authEnable(cx); err != nil { + cx.t.Fatal(err) + } +} + +func authEnable(cx ctlCtx) error { // create root user with root role if err := ctlV3User(cx, []string{"add", "root", "--interactive=false"}, "User root created", []string{"root"}); err != nil { - cx.t.Fatalf("failed to create root user %v", err) + return fmt.Errorf("failed to create root user %v", err) } - if err := ctlV3User(cx, []string{"grant-role", "root", "root"}, "Role root is granted to user root", nil); err != nil { - cx.t.Fatalf("failed to grant root user root role %v", err) + return fmt.Errorf("failed to grant root user root role %v", err) } - if err := ctlV3AuthEnable(cx); err != nil { - cx.t.Fatalf("authEnableTest ctlV3AuthEnable error (%v)", err) + return fmt.Errorf("authEnableTest ctlV3AuthEnable error (%v)", err) } + return nil } func ctlV3AuthEnable(cx ctlCtx) error { @@ -49,3 +58,85 @@ func ctlV3AuthDisable(cx ctlCtx) error { cmdArgs := append(cx.PrefixArgs(), "auth", "disable") return spawnWithExpect(cmdArgs, "Authentication Disabled") } + +func authCredWriteKeyTest(cx ctlCtx) { + // baseline key to check for failed puts + if err := ctlV3Put(cx, "foo", "a", ""); err != nil { + cx.t.Fatal(err) + } + + if err := authEnable(cx); err != nil { + cx.t.Fatal(err) + } + + cx.user, cx.pass = "root", "root" + authSetupTestUser(cx) + + // confirm root role doesn't grant access to all keys + if err := ctlV3PutFailPerm(cx, "foo", "bar"); err != nil { + cx.t.Fatal(err) + } + if err := ctlV3GetFailPerm(cx, "foo"); err != nil { + cx.t.Fatal(err) + } + + // try invalid user + cx.user, cx.pass = "a", "b" + if err := ctlV3PutFailAuth(cx, "foo", "bar"); err != nil { + cx.t.Fatal(err) + } + // confirm put failed + cx.user, cx.pass = "test-user", "pass" + if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "a"}}...); err != nil { + cx.t.Fatal(err) + } + + // try good user + cx.user, cx.pass = "test-user", "pass" + if err := ctlV3Put(cx, "foo", "bar", ""); err != nil { + cx.t.Fatal(err) + } + // confirm put succeeded + if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar"}}...); err != nil { + cx.t.Fatal(err) + } + + // try bad password + cx.user, cx.pass = "test-user", "badpass" + if err := ctlV3PutFailAuth(cx, "foo", "baz"); err != nil { + cx.t.Fatal(err) + } + // confirm put failed + cx.user, cx.pass = "test-user", "pass" + if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar"}}...); err != nil { + cx.t.Fatal(err) + } +} + +func ctlV3PutFailAuth(cx ctlCtx, key, val string) error { + return spawnWithExpect(append(cx.PrefixArgs(), "put", key, val), "authentication failed") +} + +func ctlV3PutFailPerm(cx ctlCtx, key, val string) error { + return spawnWithExpect(append(cx.PrefixArgs(), "put", key, val), "permission denied") +} + +func ctlV3GetFailPerm(cx ctlCtx, key string) error { + return spawnWithExpect(append(cx.PrefixArgs(), "get", key), "permission denied") +} + +func authSetupTestUser(cx ctlCtx) { + if err := ctlV3User(cx, []string{"add", "test-user", "--interactive=false"}, "User test-user created", []string{"pass"}); err != nil { + cx.t.Fatal(err) + } + if err := spawnWithExpect(append(cx.PrefixArgs(), "role", "add", "test-role"), "Role test-role created"); err != nil { + cx.t.Fatal(err) + } + if err := ctlV3User(cx, []string{"grant-role", "test-user", "test-role"}, "Role test-role is granted to user test-user", nil); err != nil { + cx.t.Fatal(err) + } + cmd := append(cx.PrefixArgs(), "role", "grant-permission", "test-role", "readwrite", "foo") + if err := spawnWithExpect(cmd, "Role test-role updated"); err != nil { + cx.t.Fatal(err) + } +} diff --git a/e2e/ctl_v3_test.go b/e2e/ctl_v3_test.go index aa944e0fa..9ea4ea069 100644 --- a/e2e/ctl_v3_test.go +++ b/e2e/ctl_v3_test.go @@ -48,6 +48,9 @@ type ctlCtx struct { quorum bool // if true, set up 3-node cluster and linearizable read interactive bool + + user string + pass string } type ctlOption func(*ctlCtx) @@ -147,6 +150,10 @@ func (cx *ctlCtx) PrefixArgs() []string { } } + if cx.user != "" { + cmdArgs = append(cmdArgs, "--user="+cx.user+":"+cx.pass) + } + return cmdArgs }