From ae75c7e6e388365bed714d9ba50f033ff3146020 Mon Sep 17 00:00:00 2001 From: Violet Hynes Date: Thu, 21 Jul 2022 13:40:19 -0400 Subject: [PATCH] VAULT-6727 Role resolution for K8S Auth (#156) * VAULT-6727 Role resolution for K8S Auth * VAULT-6727 use the correct file * VAULT-6727 Add extra test --- go.mod | 4 +-- go.sum | 7 ++-- path_login.go | 22 ++++++++++++ path_login_test.go | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 5f51623e..0e738319 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/hashicorp/go-uuid v1.0.2 github.com/hashicorp/go-version v1.2.0 github.com/hashicorp/vault/api v1.5.0 - github.com/hashicorp/vault/sdk v0.4.1 + github.com/hashicorp/vault/sdk v0.5.3 github.com/mitchellh/mapstructure v1.5.0 gopkg.in/square/go-jose.v2 v2.6.0 k8s.io/api v0.0.0-20190409092523-d687e77c8ae9 @@ -40,7 +40,7 @@ require ( github.com/hashicorp/go-retryablehttp v0.6.6 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.1.4 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect diff --git a/go.sum b/go.sum index 45c93469..b4a354e0 100644 --- a/go.sum +++ b/go.sum @@ -220,8 +220,8 @@ github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PU github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc= github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.4 h1:hrIH/qrOTHfG9a1Jz6Z2jQf7Xe77AaD464W1fCFLwPQ= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.4/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= @@ -242,8 +242,9 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/vault/api v1.5.0 h1:Bp6yc2bn7CWkOrVIzFT/Qurzx528bdavF3nz590eu28= github.com/hashicorp/vault/api v1.5.0/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM= -github.com/hashicorp/vault/sdk v0.4.1 h1:3SaHOJY687jY1fnB61PtL0cOkKItphrbLmux7T92HBo= github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0= +github.com/hashicorp/vault/sdk v0.5.3 h1:PWY8sq/9pRrK9vUIy75qCH2Jd8oeENAgkaa/qbhzFrs= +github.com/hashicorp/vault/sdk v0.5.3/go.mod h1:DoGraE9kKGNcVgPmTuX357Fm6WAx1Okvde8Vp3dPDoU= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= diff --git a/path_login.go b/path_login.go index a1fb47e8..83055335 100644 --- a/path_login.go +++ b/path_login.go @@ -44,6 +44,7 @@ func pathLogin(b *kubeAuthBackend) *framework.Path { Callbacks: map[logical.Operation]framework.OperationFunc{ logical.UpdateOperation: b.pathLogin, logical.AliasLookaheadOperation: b.aliasLookahead, + logical.ResolveRoleOperation: b.pathResolveRole, }, HelpSynopsis: pathLoginHelpSyn, @@ -51,6 +52,27 @@ func pathLogin(b *kubeAuthBackend) *framework.Path { } } +// pathLogin is used to resolve the role to be used from a login request +func (b *kubeAuthBackend) pathResolveRole(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { + roleName, resp := b.getFieldValueStr(data, "role") + if resp != nil { + return resp, nil + } + + b.l.RLock() + defer b.l.RUnlock() + + role, err := b.role(ctx, req.Storage, roleName) + if err != nil { + return nil, err + } + if role == nil { + return logical.ErrorResponse(fmt.Sprintf("invalid role name %q", roleName)), nil + } + + return logical.ResolveRoleResponse(roleName) +} + // pathLogin is used to authenticate to this backend func (b *kubeAuthBackend) pathLogin(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { roleName, resp := b.getFieldValueStr(data, "role") diff --git a/path_login_test.go b/path_login_test.go index 8ef1c201..cc8005da 100644 --- a/path_login_test.go +++ b/path_login_test.go @@ -12,6 +12,7 @@ import ( "encoding/pem" "errors" "fmt" + "github.com/hashicorp/vault/sdk/helper/tokenutil" "net/http" "reflect" "strings" @@ -1474,3 +1475,90 @@ func requireErrorCode(t *testing.T, err error, expectedCode int) { t.Fatalf("wrong error code, expected %d, got %d", expectedCode, codedErr.Code()) } } + +func TestResolveRole(t *testing.T) { + b, storage := getBackend(t) + role := "testrole" + + validRoleStorageEntry := &roleStorageEntry{ + TokenParams: tokenutil.TokenParams{ + TokenPolicies: []string{"test"}, + TokenPeriod: 3 * time.Second, + TokenTTL: 1 * time.Second, + TokenMaxTTL: 5 * time.Second, + TokenNumUses: 12, + TokenBoundCIDRs: nil, + }, + Policies: []string{"test"}, + Period: 3 * time.Second, + ServiceAccountNames: []string{"name"}, + ServiceAccountNamespaces: []string{"namespace"}, + TTL: 1 * time.Second, + MaxTTL: 5 * time.Second, + NumUses: 12, + BoundCIDRs: nil, + AliasNameSource: aliasNameSourceDefault, + } + + entry, err := logical.StorageEntryJSON("role/"+role, validRoleStorageEntry) + if err != nil { + t.Fatal(err) + } + if err := storage.Put(context.Background(), entry); err != nil { + t.Fatal(err) + } + + loginData := map[string]interface{}{ + "role": role, + } + loginReq := &logical.Request{ + Operation: logical.ResolveRoleOperation, + Path: "login", + Storage: storage, + Data: loginData, + Connection: &logical.Connection{ + RemoteAddr: "127.0.0.1", + }, + } + + resp, err := b.HandleRequest(context.Background(), loginReq) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("err:%v resp:%#v", err, resp) + } + + if resp.Data["role"] != role { + t.Fatalf("Role was not as expected. Expected %s, received %s", role, resp.Data["role"]) + } +} + +func TestResolveRole_RoleDoesNotExist(t *testing.T) { + b, storage := getBackend(t) + role := "testrole" + + loginData := map[string]interface{}{ + "role": role, + } + loginReq := &logical.Request{ + Operation: logical.ResolveRoleOperation, + Path: "login", + Storage: storage, + Data: loginData, + Connection: &logical.Connection{ + RemoteAddr: "127.0.0.1", + }, + } + + resp, err := b.HandleRequest(context.Background(), loginReq) + if resp == nil && !resp.IsError() { + t.Fatalf("Response was not an error: err:%v resp:%#v", err, resp) + } + + errString, ok := resp.Data["error"].(string) + if !ok { + t.Fatal("Error not part of response.") + } + + if !strings.Contains(errString, "invalid role name") { + t.Fatalf("Error was not due to invalid role name. Error: %s", errString) + } +}