Skip to content

Commit 7a84eec

Browse files
author
lenny
committed
refactor: Refactor to be proper abstraction of a SecretStore
No longer leak that Vault is the only implementation Add Type to the configuration and factory method, so it will error if not set Move SecretStore client from edgex-go in to this abstraction Now have to interfaces that the vault wrapper implements, which are SecretsClient & SecretStoreClient closes #87 BREAKING CHANGE: All existing SecretStore configuration must add `Type = 'vault'` Signed-off-by: lenny <[email protected]>
1 parent f21943c commit 7a84eec

26 files changed

+2820
-799
lines changed

internal/pkg/vault/client.go

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*******************************************************************************
2+
* Copyright 2019 Dell Inc.
3+
* Copyright 2021 Intel Corp.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License. You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software distributed under the License
11+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12+
* or implied. See the License for the specific language governing permissions and limitations under
13+
* the License.
14+
*******************************************************************************/
15+
16+
package vault
17+
18+
import (
19+
"context"
20+
"crypto/tls"
21+
"crypto/x509"
22+
"io/ioutil"
23+
"net/http"
24+
"time"
25+
26+
"github.com/edgexfoundry/go-mod-secrets/v2/pkg"
27+
"github.com/edgexfoundry/go-mod-secrets/v2/pkg/types"
28+
29+
"github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger"
30+
)
31+
32+
// *Client defines the behavior for interacting with the Vault REST secret key/value store via HTTP(S).
33+
type Client struct {
34+
Config types.SecretConfig
35+
HttpCaller pkg.Caller
36+
lc logger.LoggingClient
37+
context context.Context
38+
}
39+
40+
// NewVaultClient constructs a Vault *Client which communicates with Vault via HTTP(S)
41+
//
42+
// lc is any logging client that implements the loggingClient interface;
43+
// today EdgeX's logger.LoggingClient from go-mod-core-contracts satisfies this implementation
44+
//
45+
func NewClient(config types.SecretConfig, requester pkg.Caller, forSecrets bool, lc logger.LoggingClient) (*Client, error) {
46+
if forSecrets && config.Authentication.AuthToken == "" {
47+
return nil, pkg.NewErrSecretStore("AuthToken is required in config")
48+
}
49+
50+
var err error
51+
if requester == nil {
52+
requester, err = createHTTPClient(config)
53+
if err != nil {
54+
return nil, err
55+
}
56+
}
57+
58+
if config.RetryWaitPeriod != "" {
59+
retryTimeDuration, err := time.ParseDuration(config.RetryWaitPeriod)
60+
if err != nil {
61+
return nil, err
62+
}
63+
config.RetryWaitPeriodTime = retryTimeDuration
64+
}
65+
66+
vaultClient := Client{
67+
Config: config,
68+
HttpCaller: requester,
69+
lc: lc,
70+
}
71+
72+
return &vaultClient, err
73+
}
74+
75+
func createHTTPClient(config types.SecretConfig) (pkg.Caller, error) {
76+
77+
if config.RootCaCertPath == "" {
78+
return http.DefaultClient, nil
79+
}
80+
81+
// Read and load the CA Root certificate so the client will be able to use TLS without skipping the verification of
82+
// the cert received by the server.
83+
caCert, err := ioutil.ReadFile(config.RootCaCertPath)
84+
if err != nil {
85+
return nil, ErrCaRootCert{
86+
path: config.RootCaCertPath,
87+
description: err.Error(),
88+
}
89+
}
90+
caCertPool := x509.NewCertPool()
91+
caCertPool.AppendCertsFromPEM(caCert)
92+
93+
return &http.Client{
94+
Transport: &http.Transport{
95+
TLSClientConfig: &tls.Config{
96+
RootCAs: caCertPool,
97+
ServerName: config.ServerName,
98+
},
99+
},
100+
}, nil
101+
}

internal/pkg/vault/client_test.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// Copyright (c) 2021 Intel Corporation
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
// in compliance with the License. You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software distributed under the License
10+
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
// or implied. See the License for the specific language governing permissions and limitations under
12+
// the License.
13+
//
14+
15+
package vault
16+
17+
import (
18+
"testing"
19+
20+
"github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger"
21+
"github.com/stretchr/testify/require"
22+
23+
"github.com/edgexfoundry/go-mod-secrets/v2/pkg/types"
24+
)
25+
26+
func TestNewClient(t *testing.T) {
27+
mockLogger := logger.NewMockClient()
28+
29+
validConfig := types.SecretConfig{
30+
RootCaCertPath: "", // Leave empty so it uses default HTTP Client
31+
Authentication: types.AuthenticationInfo{
32+
AuthToken: "my-unit-test-token",
33+
},
34+
RetryWaitPeriod: "1ms",
35+
}
36+
noToken := validConfig
37+
noToken.Authentication.AuthToken = ""
38+
badWaitPeriod := validConfig
39+
badWaitPeriod.RetryWaitPeriod = "n/a"
40+
41+
tests := []struct {
42+
Name string
43+
Config types.SecretConfig
44+
ExpectError bool
45+
}{
46+
{"Valid", validConfig, false},
47+
{"Invalid - no token", noToken, true},
48+
{"Invalid - bad wait period", badWaitPeriod, true},
49+
}
50+
51+
for _, test := range tests {
52+
t.Run(test.Name, func(t *testing.T) {
53+
client, err := NewClient(test.Config, nil, true, mockLogger)
54+
if test.ExpectError {
55+
require.Error(t, err)
56+
return
57+
}
58+
59+
require.NoError(t, err)
60+
require.NotNil(t, client)
61+
})
62+
}
63+
}

internal/pkg/vault/constants.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*******************************************************************************
2+
* Copyright 2019 Dell Inc.
3+
* Copyright 2021 Intel Corp.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License. You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software distributed under the License
11+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12+
* or implied. See the License for the specific language governing permissions and limitations under
13+
* the License.
14+
*******************************************************************************/
15+
16+
package vault
17+
18+
const (
19+
// NamespaceHeader specifies the header name to use when including Namespace information in a request.
20+
NamespaceHeader = "X-Vault-Namespace"
21+
AuthTypeHeader = "X-Vault-Token"
22+
23+
HealthAPI = "/v1/sys/health"
24+
InitAPI = "/v1/sys/init"
25+
UnsealAPI = "/v1/sys/unseal"
26+
CreatePolicyPath = "/v1/sys/policies/acl/%s"
27+
CreateTokenAPI = "/v1/auth/token/create"
28+
ListAccessorsAPI = "/v1/auth/token/accessors"
29+
RevokeAccessorAPI = "/v1/auth/token/revoke-accessor"
30+
LookupAccessorAPI = "/v1/auth/token/lookup-accessor"
31+
LookupSelfAPI = "/v1/auth/token/lookup-self"
32+
RevokeSelfAPI = "/v1/auth/token/revoke-self"
33+
RootTokenControlAPI = "/v1/sys/generate-root/attempt"
34+
RootTokenRetrievalAPI = "/v1/sys/generate-root/update"
35+
MountsAPI = "/v1/sys/mounts"
36+
37+
lookupSelfVaultAPI = "/v1/auth/token/lookup-self"
38+
renewSelfVaultAPI = "/v1/auth/token/renew-self"
39+
)

pkg/providers/vault/errors.go internal/pkg/vault/errors.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*******************************************************************************
22
* Copyright 2019 Dell Inc.
3-
* Copyright 2020 Intel Corp.
3+
* Copyright 2021 Intel Corp.
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
66
* in compliance with the License. You may obtain a copy of the License at

0 commit comments

Comments
 (0)