From 63af1f93ec8882254d5b069308aeb7399794500e Mon Sep 17 00:00:00 2001 From: VioletHynes Date: Mon, 18 Jul 2022 09:49:10 -0400 Subject: [PATCH 1/3] VAULT-6727 Role resolution for K8S Auth --- go.mod | 4 ++-- go.sum | 7 +++--- path_config_test.go | 56 +++++++++++++++++++++++++++++++++++++++++++++ path_login.go | 22 ++++++++++++++++++ 4 files changed, 84 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_config_test.go b/path_config_test.go index da03a39f..1f6a906c 100644 --- a/path_config_test.go +++ b/path_config_test.go @@ -3,6 +3,7 @@ package kubeauth import ( "context" "crypto" + "github.com/hashicorp/vault/sdk/helper/tokenutil" "io/ioutil" "os" "reflect" @@ -536,6 +537,61 @@ func TestConfig_LocalJWTRenewal(t *testing.T) { } } +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.TODO(), 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"]) + } +} + var testLocalCACert string = `-----BEGIN CERTIFICATE----- MIIDVDCCAjwCCQDFiyFY1M6afTANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJV UzETMBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEgMB4GA1UE 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") From a19921b4e94a35daf3748feb52350eb02d87769c Mon Sep 17 00:00:00 2001 From: VioletHynes Date: Mon, 18 Jul 2022 10:05:00 -0400 Subject: [PATCH 2/3] VAULT-6727 use the correct file --- path_config_test.go | 56 --------------------------------------------- path_login_test.go | 56 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/path_config_test.go b/path_config_test.go index 1f6a906c..da03a39f 100644 --- a/path_config_test.go +++ b/path_config_test.go @@ -3,7 +3,6 @@ package kubeauth import ( "context" "crypto" - "github.com/hashicorp/vault/sdk/helper/tokenutil" "io/ioutil" "os" "reflect" @@ -537,61 +536,6 @@ func TestConfig_LocalJWTRenewal(t *testing.T) { } } -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.TODO(), 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"]) - } -} - var testLocalCACert string = `-----BEGIN CERTIFICATE----- MIIDVDCCAjwCCQDFiyFY1M6afTANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJV UzETMBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEgMB4GA1UE diff --git a/path_login_test.go b/path_login_test.go index 8ef1c201..1dede3b8 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,58 @@ 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.TODO(), 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"]) + } +} From a8a7a69761baa9fe971c1f650d40367df2433b4d Mon Sep 17 00:00:00 2001 From: VioletHynes Date: Mon, 18 Jul 2022 11:23:18 -0400 Subject: [PATCH 3/3] VAULT-6727 Add extra test --- path_login_test.go | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/path_login_test.go b/path_login_test.go index 1dede3b8..cc8005da 100644 --- a/path_login_test.go +++ b/path_login_test.go @@ -1504,7 +1504,7 @@ func TestResolveRole(t *testing.T) { if err != nil { t.Fatal(err) } - if err := storage.Put(context.TODO(), entry); err != nil { + if err := storage.Put(context.Background(), entry); err != nil { t.Fatal(err) } @@ -1530,3 +1530,35 @@ func TestResolveRole(t *testing.T) { 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) + } +}