Skip to content

Commit

Permalink
test: validate direct JWT passing and acceptance
Browse files Browse the repository at this point in the history
  • Loading branch information
mcrute committed Jul 23, 2024
1 parent cb6f099 commit 3201b9d
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 1 deletion.
25 changes: 24 additions & 1 deletion tests/common/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ import (

clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/tests/v3/framework/config"
"go.etcd.io/etcd/tests/v3/framework/e2e"
"go.etcd.io/etcd/tests/v3/framework/testutils"
)

var tokenTTL = time.Second
var defaultKeyPath = mustAbsPath("../fixtures/server.key.insecure")
var defaultAuthToken = fmt.Sprintf("jwt,pub-key=%s,priv-key=%s,sign-method=RS256,ttl=%s",
mustAbsPath("../fixtures/server.crt"), mustAbsPath("../fixtures/server.key.insecure"), tokenTTL)
mustAbsPath("../fixtures/server.crt"), defaultKeyPath, tokenTTL)
var verifyJWTOnlyAuth = fmt.Sprintf("jwt,pub-key=%s,sign-method=RS256,ttl=%s",
mustAbsPath("../fixtures/server.crt"), tokenTTL)

const (
PermissionDenied = "etcdserver: permission denied"
Expand Down Expand Up @@ -758,6 +762,25 @@ func TestAuthJWTExpire(t *testing.T) {
})
}

func TestAuthJWTOnly(t *testing.T) {
testRunner.BeforeTest(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1, AuthToken: verifyJWTOnlyAuth}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
authRev, err := setupAuthAndGetRevision(cc, []authRole{testRole}, []authUser{rootUser, testUser})
require.NoErrorf(t, err, "failed to enable auth")

token, err := createSignedJWT(defaultKeyPath, testUserName, authRev)
require.NoErrorf(t, err, "failed to create test user JWT")

testUserAuthClient := testutils.MustClient(clus.Client(e2e.WithAuthToken(token)))
require.NoError(t, testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{}))
})
}

// TestAuthRevisionConsistency ensures auth revision is the same after member restarts
func TestAuthRevisionConsistency(t *testing.T) {
testRunner.BeforeTest(t)
Expand Down
49 changes: 49 additions & 0 deletions tests/common/auth_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ package common
import (
"context"
"fmt"
"os"
"testing"
"time"

"github.com/golang-jwt/jwt"
"github.com/stretchr/testify/require"

"go.etcd.io/etcd/api/v3/authpb"
Expand Down Expand Up @@ -93,6 +96,29 @@ func createUsers(c interfaces.Client, users []authUser) error {
return nil
}

func createSignedJWT(keyPath, username string, authRevision uint64) (string, error) {
signMethod := jwt.GetSigningMethod("RS256")

keyBytes, err := os.ReadFile(keyPath)
if err != nil {
return "", err
}

key, err := jwt.ParseRSAPrivateKeyFromPEM(keyBytes)
if err != nil {
return "", err
}

tk := jwt.NewWithClaims(signMethod,
jwt.MapClaims{
"username": username,
"revision": authRevision,
"exp": time.Now().Add(time.Minute).Unix(),
})

return tk.SignedString(key)
}

func setupAuth(c interfaces.Client, roles []authRole, users []authUser) error {
// create roles
if err := createRoles(c, roles); err != nil {
Expand All @@ -107,6 +133,29 @@ func setupAuth(c interfaces.Client, roles []authRole, users []authUser) error {
return c.AuthEnable(context.TODO())
}

func setupAuthAndGetRevision(c interfaces.Client, roles []authRole, users []authUser) (uint64, error) {
// create roles
if err := createRoles(c, roles); err != nil {
return 0, err
}

if err := createUsers(c, users); err != nil {
return 0, err
}

// This needs to happen before enabling auth for the TestAuthJWTOnly
// test case because once auth is enabled we can no longer mint a valid
// auth token without the revision, which we won't be able to obtain
// without a valid auth token.
authrev, err := c.AuthStatus(context.TODO())
if err != nil {
return 0, err
}

// enable auth
return authrev.AuthRevision, c.AuthEnable(context.TODO())
}

func requireRolePermissionEqual(t *testing.T, expectRole authRole, actual []*authpb.Permission) {
require.Equal(t, 1, len(actual))
require.Equal(t, expectRole.permission, clientv3.PermissionType(actual[0].PermType))
Expand Down
11 changes: 11 additions & 0 deletions tests/framework/e2e/etcdctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type EtcdctlV3 struct {
cfg ClientConfig
endpoints []string
authConfig clientv3.AuthConfig
authToken string
}

func NewEtcdctl(cfg ClientConfig, endpoints []string, opts ...config.ClientOption) (*EtcdctlV3, error) {
Expand Down Expand Up @@ -73,6 +74,13 @@ func WithAuth(userName, password string) config.ClientOption {
}
}

func WithAuthToken(token string) config.ClientOption {
return func(c any) {
ctl := c.(*EtcdctlV3)
ctl.authToken = token
}
}

func WithEndpoints(endpoints []string) config.ClientOption {
return func(c any) {
ctl := c.(*EtcdctlV3)
Expand Down Expand Up @@ -347,6 +355,9 @@ func (ctl *EtcdctlV3) flags() map[string]string {
if !ctl.authConfig.Empty() {
fmap["user"] = ctl.authConfig.Username + ":" + ctl.authConfig.Password
}
if ctl.authToken != "" {
fmap["auth-token"] = ctl.authToken
}
return fmap
}

Expand Down
1 change: 1 addition & 0 deletions tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ replace (
require (
github.com/anishathalye/porcupine v0.1.4
github.com/coreos/go-semver v0.3.1
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang/protobuf v1.5.4
github.com/google/go-cmp v0.6.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
Expand Down
2 changes: 2 additions & 0 deletions tests/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
Expand Down

0 comments on commit 3201b9d

Please sign in to comment.