From db21ead31cbb58c6abf0a62e2205e21233fcc5c6 Mon Sep 17 00:00:00 2001 From: Irena Rindos Date: Wed, 31 Aug 2022 12:40:35 -0400 Subject: [PATCH] Vault proxy supporting code (#2415) * Vault proxy supporting methods --- internal/credential/vault/credential_store.go | 4 +- .../credential/vault/private_credential.go | 4 +- internal/credential/vault/private_library.go | 4 +- internal/credential/vault/private_store.go | 4 +- .../vault/repository_credential_store_test.go | 67 +++---------------- internal/credential/vault/testing.go | 18 ++++- internal/credential/vault/testing_test.go | 6 +- internal/credential/vault/vault.go | 42 +++++++++--- internal/credential/vault/vault_test.go | 12 ++-- internal/daemon/controller/controller.go | 7 ++ internal/server/repository_workerauth.go | 20 ++++++ 11 files changed, 104 insertions(+), 84 deletions(-) diff --git a/internal/credential/vault/credential_store.go b/internal/credential/vault/credential_store.go index 52592e20e9..ac549cbc21 100644 --- a/internal/credential/vault/credential_store.go +++ b/internal/credential/vault/credential_store.go @@ -169,7 +169,7 @@ func (cs *CredentialStore) ClientCertificate() *ClientCertificate { return cs.clientCert } -func (cs *CredentialStore) client(ctx context.Context) (*client, error) { +func (cs *CredentialStore) client(ctx context.Context) (vaultClient, error) { const op = "vault.(CredentialStore).client" clientConfig := &clientConfig{ Addr: cs.VaultAddress, @@ -184,7 +184,7 @@ func (cs *CredentialStore) client(ctx context.Context) (*client, error) { clientConfig.ClientKey = cs.clientCert.GetCertificateKey() } - c, err := newClient(ctx, clientConfig) + c, err := vaultClientFactoryFn(ctx, clientConfig, WithWorkerFilter(cs.WorkerFilter)) if err != nil { return nil, errors.Wrap(ctx, err, op) } diff --git a/internal/credential/vault/private_credential.go b/internal/credential/vault/private_credential.go index be01e74020..bf447fd3c5 100644 --- a/internal/credential/vault/private_credential.go +++ b/internal/credential/vault/private_credential.go @@ -79,7 +79,7 @@ func (pc *privateCredential) decrypt(ctx context.Context, cipher wrapping.Wrappe return nil } -func (pc *privateCredential) client(ctx context.Context) (*client, error) { +func (pc *privateCredential) client(ctx context.Context) (vaultClient, error) { const op = "vault.(privateCredential).client" clientConfig := &clientConfig{ Addr: pc.VaultAddress, @@ -95,7 +95,7 @@ func (pc *privateCredential) client(ctx context.Context) (*client, error) { clientConfig.ClientKey = pc.ClientKey } - client, err := newClient(ctx, clientConfig) + client, err := vaultClientFactoryFn(ctx, clientConfig, WithWorkerFilter(pc.WorkerFilter)) if err != nil { return nil, errors.WrapDeprecated(err, op, errors.WithMsg("unable to create vault client")) } diff --git a/internal/credential/vault/private_library.go b/internal/credential/vault/private_library.go index 6b9840f7cf..f143381d36 100644 --- a/internal/credential/vault/private_library.go +++ b/internal/credential/vault/private_library.go @@ -257,7 +257,7 @@ func (pl *privateLibrary) decrypt(ctx context.Context, cipher wrapping.Wrapper) return nil } -func (pl *privateLibrary) client(ctx context.Context) (*client, error) { +func (pl *privateLibrary) client(ctx context.Context) (vaultClient, error) { const op = "vault.(privateLibrary).client" clientConfig := &clientConfig{ Addr: pl.VaultAddress, @@ -273,7 +273,7 @@ func (pl *privateLibrary) client(ctx context.Context) (*client, error) { clientConfig.ClientKey = pl.ClientKey } - client, err := newClient(ctx, clientConfig) + client, err := vaultClientFactoryFn(ctx, clientConfig, WithWorkerFilter(pl.WorkerFilter)) if err != nil { return nil, errors.WrapDeprecated(err, op, errors.WithMsg("unable to create vault client")) } diff --git a/internal/credential/vault/private_store.go b/internal/credential/vault/private_store.go index 857d776b0c..d89cd7d07a 100644 --- a/internal/credential/vault/private_store.go +++ b/internal/credential/vault/private_store.go @@ -182,7 +182,7 @@ func (ps *privateStore) decrypt(ctx context.Context, cipher wrapping.Wrapper) er return nil } -func (ps *privateStore) client(ctx context.Context) (*client, error) { +func (ps *privateStore) client(ctx context.Context) (vaultClient, error) { const op = "vault.(privateStore).client" clientConfig := &clientConfig{ Addr: ps.VaultAddress, @@ -198,7 +198,7 @@ func (ps *privateStore) client(ctx context.Context) (*client, error) { clientConfig.ClientKey = ps.ClientKey } - client, err := newClient(ctx, clientConfig) + client, err := vaultClientFactoryFn(ctx, clientConfig, WithWorkerFilter(ps.WorkerFilter)) if err != nil { return nil, errors.WrapDeprecated(err, op, errors.WithMsg("unable to create vault client")) } diff --git a/internal/credential/vault/repository_credential_store_test.go b/internal/credential/vault/repository_credential_store_test.go index eddfb78584..548b8dbb15 100644 --- a/internal/credential/vault/repository_credential_store_test.go +++ b/internal/credential/vault/repository_credential_store_test.go @@ -359,13 +359,6 @@ func TestRepository_UpdateCredentialStore_Attributes(t *testing.T) { } } - changeWorkerFilter := func(wf string) func(*CredentialStore) *CredentialStore { - return func(cs *CredentialStore) *CredentialStore { - cs.WorkerFilter = wf - return cs - } - } - makeNil := func() func(*CredentialStore) *CredentialStore { return func(cs *CredentialStore) *CredentialStore { return nil @@ -513,37 +506,19 @@ func TestRepository_UpdateCredentialStore_Attributes(t *testing.T) { wantCount: 1, }, { - name: "change-worker-filter", + name: "change-name-and-description", orig: &CredentialStore{ CredentialStore: &store.CredentialStore{ - WorkerFilter: "test-workerfilter", - }, - }, - chgFn: changeWorkerFilter("test-update-worker-filter"), - masks: []string{"WorkerFilter"}, - want: &CredentialStore{ - CredentialStore: &store.CredentialStore{ - WorkerFilter: "test-update-worker-filter", - }, - }, - wantCount: 1, - }, - { - name: "change-name-and-description-and-workerfilter", - orig: &CredentialStore{ - CredentialStore: &store.CredentialStore{ - Name: "test-name-repo", - Description: "test-description-repo", - WorkerFilter: "test-workerfilter", + Name: "test-name-repo", + Description: "test-description-repo", }, }, - chgFn: combine(changeDescription("test-update-description-repo"), changeName("test-update-name-repo"), changeWorkerFilter("test-update-worker-filter")), + chgFn: combine(changeDescription("test-update-description-repo"), changeName("test-update-name-repo")), masks: []string{"Name", "Description"}, want: &CredentialStore{ CredentialStore: &store.CredentialStore{ - Name: "test-update-name-repo", - Description: "test-update-description-repo", - WorkerFilter: "test-update-worker-filter", + Name: "test-update-name-repo", + Description: "test-update-description-repo", }, }, wantCount: 1, @@ -552,9 +527,8 @@ func TestRepository_UpdateCredentialStore_Attributes(t *testing.T) { name: "delete-name", orig: &CredentialStore{ CredentialStore: &store.CredentialStore{ - Name: "test-name-repo", - Description: "test-description-repo", - WorkerFilter: "test-workerfilter", + Name: "test-name-repo", + Description: "test-description-repo", }, }, masks: []string{"Name"}, @@ -570,9 +544,8 @@ func TestRepository_UpdateCredentialStore_Attributes(t *testing.T) { name: "delete-description", orig: &CredentialStore{ CredentialStore: &store.CredentialStore{ - Name: "test-name-repo", - Description: "test-description-repo", - WorkerFilter: "test-workerfilter", + Name: "test-name-repo", + Description: "test-description-repo", }, }, masks: []string{"Description"}, @@ -584,26 +557,6 @@ func TestRepository_UpdateCredentialStore_Attributes(t *testing.T) { }, wantCount: 1, }, - { - name: "delete-workerfilter", - orig: &CredentialStore{ - CredentialStore: &store.CredentialStore{ - Name: "test-name-repo", - Description: "test-description-repo", - WorkerFilter: "test-workerfilter", - }, - }, - masks: []string{"WorkerFilter"}, - chgFn: combine(changeDescription("test-update-description-repo"), changeName("test-update-name-repo"), - changeWorkerFilter("")), - want: &CredentialStore{ - CredentialStore: &store.CredentialStore{ - Name: "test-name-repo", - Description: "test-description-repo", - }, - }, - wantCount: 1, - }, { name: "do-not-delete-name", orig: &CredentialStore{ diff --git a/internal/credential/vault/testing.go b/internal/credential/vault/testing.go index 432fe6f704..33408b3dca 100644 --- a/internal/credential/vault/testing.go +++ b/internal/credential/vault/testing.go @@ -620,12 +620,26 @@ func TestRenewableToken(b bool) TestOption { } } +// TestClientConfig returns a client config, using the +// provided Vault Server and token +func TestClientConfig(v *TestVaultServer, token string) *clientConfig { + clientConfig := &clientConfig{ + Addr: v.Addr, + Token: TokenSecret(token), + CaCert: v.CaCert, + ClientCert: v.ClientCert, + ClientKey: v.ClientKey, + } + + return clientConfig +} + func (v *TestVaultServer) client(t testing.TB) *client { t.Helper() - return v.clientUsingToken(t, v.RootToken) + return v.ClientUsingToken(t, v.RootToken) } -func (v *TestVaultServer) clientUsingToken(t testing.TB, token string) *client { +func (v *TestVaultServer) ClientUsingToken(t testing.TB, token string) *client { t.Helper() ctx := context.Background() require := require.New(t) diff --git a/internal/credential/vault/testing_test.go b/internal/credential/vault/testing_test.go index df04cc99d3..6b10c9d6c0 100644 --- a/internal/credential/vault/testing_test.go +++ b/internal/credential/vault/testing_test.go @@ -507,7 +507,7 @@ func TestTestVaultServer_VerifyTokenInvalid(t *testing.T) { v := NewTestVaultServer(t, WithDockerNetwork(true)) _, token := v.CreateToken(t) - client := v.clientUsingToken(t, token) + client := v.ClientUsingToken(t, token) err := client.revokeToken(ctx) require.NoError(err) v.VerifyTokenInvalid(t, token) @@ -559,7 +559,7 @@ func TestTestVaultServer_AddKVPolicy(t *testing.T) { _, token := v.CreateToken(t, WithPolicies([]string{"default", "secret"})) require.NotNil(token) - client := v.clientUsingToken(t, token) + client := v.ClientUsingToken(t, token) _, err := client.get(ctx, "/secret/data/my-secret") assert.Error(err) @@ -574,7 +574,7 @@ func TestTestVaultServer_AddKVPolicy(t *testing.T) { v.AddKVPolicy(t) _, token = v.CreateToken(t, WithPolicies([]string{"default", "secret"})) require.NotNil(token) - client = v.clientUsingToken(t, token) + client = v.ClientUsingToken(t, token) _, err = client.get(ctx, "/secret/data/my-secret") assert.NoError(err) diff --git a/internal/credential/vault/vault.go b/internal/credential/vault/vault.go index ba87aa130f..6a7e210773 100644 --- a/internal/credential/vault/vault.go +++ b/internal/credential/vault/vault.go @@ -14,15 +14,39 @@ import ( "github.com/mitchellh/mapstructure" ) +type vaultClient interface { + ping(context.Context) error + renewToken(context.Context) (*vault.Secret, error) + revokeToken(context.Context) error + renewLease(context.Context, string, time.Duration) (*vault.Secret, error) + revokeLease(context.Context, string) error + lookupToken(context.Context) (*vault.Secret, error) + swapToken(context.Context, TokenSecret) (old TokenSecret) + get(context.Context, string) (*vault.Secret, error) + post(context.Context, string, []byte) (*vault.Secret, error) + capabilities(context.Context, []string) (pathCapabilities, error) +} + +var vaultClientFactoryFn = vaultClientFactory + +func vaultClientFactory(ctx context.Context, c *clientConfig, opt ...Option) (vaultClient, error) { + const op = "vault.vaultClientFactory" + nc, err := newClient(ctx, c) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + return nc, nil +} + type clientConfig struct { - Addr string - Token TokenSecret - CaCert []byte - ClientCert []byte - ClientKey KeySecret - TlsServerName string - TlsSkipVerify bool - Namespace string + Addr string `json:"addr"` + Token []byte `json:"token"` + CaCert []byte `json:"ca_cert"` + ClientCert []byte `json:"client_cert"` + ClientKey []byte `json:"client_key"` + TlsServerName string `json:"tls_server_name"` + TlsSkipVerify bool `json:"tls_skip_verify"` + Namespace string `json:"namespace"` } func (c *clientConfig) isValid() bool { @@ -32,6 +56,8 @@ func (c *clientConfig) isValid() bool { return true } +var _ vaultClient = (*client)(nil) + func (c *clientConfig) isClientTLS() bool { if len(c.ClientCert) > 0 && len(c.ClientKey) > 0 { return true diff --git a/internal/credential/vault/vault_test.go b/internal/credential/vault/vault_test.go index 2ce6eceb05..5b199d61c9 100644 --- a/internal/credential/vault/vault_test.go +++ b/internal/credential/vault/vault_test.go @@ -78,7 +78,7 @@ func TestClient_RenewToken(t *testing.T) { // need to sleep so the expiration times will be different time.Sleep(100 * time.Millisecond) - client := v.clientUsingToken(t, token) + client := v.ClientUsingToken(t, token) renewedToken, err := client.renewToken(ctx) require.NoError(t, err) assert.NotNil(renewedToken) @@ -112,7 +112,7 @@ func TestClient_LookupToken(t *testing.T) { _, token := v.CreateToken(t) secretLookup := v.LookupToken(t, token) - client := v.clientUsingToken(t, token) + client := v.ClientUsingToken(t, token) tokenLookup, err := client.lookupToken(ctx) assert.NoError(err) require.NotNil(tokenLookup) @@ -129,7 +129,7 @@ func TestClient_RevokeToken(t *testing.T) { _, token := v.CreateToken(t) - client := v.clientUsingToken(t, token) + client := v.ClientUsingToken(t, token) tokenLookup, err := client.lookupToken(ctx) assert.NoError(err) assert.NotNil(tokenLookup) @@ -192,7 +192,7 @@ func TestClient_RenewLease(t *testing.T) { v.MountDatabase(t) _, token := v.CreateToken(t, WithPolicies([]string{"boundary-controller", "database"})) - client := v.clientUsingToken(t, token) + client := v.ClientUsingToken(t, token) // Create secret cred, err := client.get(ctx, path.Join("database", "creds", "opened")) @@ -252,7 +252,7 @@ func TestClient_capabilities(t *testing.T) { t.Run(tt.name, func(t *testing.T) { assert := assert.New(t) _, token := v.CreateToken(t, WithPolicies(tt.polices)) - client := v.clientUsingToken(t, token) + client := v.ClientUsingToken(t, token) have, err := client.capabilities(ctx, tt.require.paths()) assert.NoError(err) @@ -270,7 +270,7 @@ func TestClient_revokeLease(t *testing.T) { testDatabase := v.MountDatabase(t) _, token := v.CreateToken(t, WithPolicies([]string{"boundary-controller", "database"})) - client := v.clientUsingToken(t, token) + client := v.ClientUsingToken(t, token) cred, err := client.get(ctx, path.Join("database", "creds", "opened")) assert.NoError(err) diff --git a/internal/daemon/controller/controller.go b/internal/daemon/controller/controller.go index 3da8a9b9d3..637c6ec12b 100644 --- a/internal/daemon/controller/controller.go +++ b/internal/daemon/controller/controller.go @@ -72,6 +72,7 @@ var ( downstreamersFactory func(context.Context, string) (downstreamers, error) downstreamWorkersTickerFactory func(context.Context, string, downstreamers, downstreamRouter) (downstreamWorkersTicker, error) + commandClientFactory func(context.Context, *Controller) error ) type Controller struct { @@ -361,6 +362,12 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { if err != nil { return nil, fmt.Errorf("unable to initialize downstream workers graph: %w", err) } + if commandClientFactory != nil { + err := commandClientFactory(ctx, c) + if err != nil { + return nil, fmt.Errorf("unable to initialize issue command factory: %w", err) + } + } } return c, nil diff --git a/internal/server/repository_workerauth.go b/internal/server/repository_workerauth.go index 8001716506..9bdac5dfa3 100644 --- a/internal/server/repository_workerauth.go +++ b/internal/server/repository_workerauth.go @@ -779,3 +779,23 @@ func decrypt(ctx context.Context, value []byte, wrapper wrapping.Wrapper) ([]byt return marshaledInfo, nil } + +// FindWorkerAuthByWorkerId takes a workerId and returns the WorkerAuth record associated with that worker. +func (r *WorkerAuthRepositoryStorage) FindWorkerAuthByWorkerId(ctx context.Context, workerId string) (*WorkerAuth, error) { + const op = "server.(WorkerAuthRepositoryStorage).FindWorkerAuthByWorkerId" + if len(workerId) == 0 { + return nil, errors.New(ctx, errors.InvalidParameter, op, "empty worker ID") + } + + worker := allocWorkerAuth() + worker.WorkerId = workerId + err := r.reader.SearchWhere(ctx, &worker, "worker_id = ?", []interface{}{workerId}) + if err != nil { + if errors.Is(err, dbw.ErrRecordNotFound) { + return nil, nil + } + return nil, errors.Wrap(ctx, err, op) + } + + return worker, nil +}