-
Notifications
You must be signed in to change notification settings - Fork 0
/
hashicorp_vault.go
executable file
·211 lines (166 loc) · 5.17 KB
/
hashicorp_vault.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
package hashicorpvault
import (
"errors"
"fmt"
"github.com/definenulls/komerco-chain/secrets"
"github.com/hashicorp/go-hclog"
vault "github.com/hashicorp/vault/api"
)
// VaultSecretsManager is a SecretsManager that
// stores secrets on a Hashicorp Vault instance
type VaultSecretsManager struct {
// Logger object
logger hclog.Logger
// Token used for Vault instance authentication
token string
// The Server URL of the Vault instance
serverURL string
// The name of the current node, used for prefixing names of secrets
name string
// The base path to store the secrets in the KV-2 Vault storage
basePath string
// The HTTP client used for interacting with the Vault server
client *vault.Client
// The namespace under which the secrets are stored
namespace string
}
// SecretsManagerFactory implements the factory method
func SecretsManagerFactory(
config *secrets.SecretsManagerConfig,
params *secrets.SecretsManagerParams,
) (secrets.SecretsManager, error) {
// Set up the base object
vaultManager := &VaultSecretsManager{
logger: params.Logger.Named(string(secrets.HashicorpVault)),
}
// Check if the token is present
if config.Token == "" {
return nil, errors.New("no token specified for Vault secrets manager")
}
// Grab the token from the config
vaultManager.token = config.Token
// Check if the server URL is present
if config.ServerURL == "" {
return nil, errors.New("no server URL specified for Vault secrets manager")
}
// Grab the server URL from the config
vaultManager.serverURL = config.ServerURL
// Check if the node name is present
if config.Name == "" {
return nil, errors.New("no node name specified for Vault secrets manager")
}
// Grab the node name from the config
vaultManager.name = config.Name
// Grab the namespace from the config
vaultManager.namespace = config.Namespace
// Set the base path to store the secrets in the KV-2 Vault storage
vaultManager.basePath = fmt.Sprintf("secret/data/%s", vaultManager.name)
// Run the initial setup
_ = vaultManager.Setup()
return vaultManager, nil
}
// Setup sets up the Hashicorp Vault secrets manager
func (v *VaultSecretsManager) Setup() error {
config := vault.DefaultConfig()
// Set the server URL
config.Address = v.serverURL
client, err := vault.NewClient(config)
if err != nil {
return fmt.Errorf("unable to initialize Vault client: %w", err)
}
// Set the access token
client.SetToken(v.token)
// Set the namespace
client.SetNamespace(v.namespace)
v.client = client
return nil
}
// constructSecretPath is a helper method for constructing a path to the secret
func (v *VaultSecretsManager) constructSecretPath(name string) string {
return fmt.Sprintf("%s/%s", v.basePath, name)
}
// GetSecret fetches a secret from the Hashicorp Vault server
func (v *VaultSecretsManager) GetSecret(name string) ([]byte, error) {
secret, err := v.client.Logical().Read(v.constructSecretPath(name))
if err != nil {
return nil, fmt.Errorf("unable to read secret from Vault, %w", err)
}
if secret == nil {
return nil, secrets.ErrSecretNotFound
}
// KV-2 (versioned key-value storage) in Vault stores data in the following format:
// {
// "data": {
// key: value
// }
// }
data, ok := secret.Data["data"]
if !ok {
return nil, fmt.Errorf(
"unable to assert type for secret from Vault, %T %#v",
secret.Data["data"],
secret.Data["data"],
)
}
// Check if the data is empty
if data == nil {
return nil, secrets.ErrSecretNotFound
}
// Convert data into a map
secretsMap, ok := data.(map[string]interface{})
if !ok {
return nil, secrets.ErrSecretNotFound
}
// Grab the value
value, ok := secretsMap[name]
if !ok {
return nil, secrets.ErrSecretNotFound
}
stringVal, ok := value.(string)
if !ok {
return nil, errors.New("invalid type assertion for secret value")
}
return []byte(stringVal), nil
}
// SetSecret saves a secret to the Hashicorp Vault server
// Secrets saved in Vault need to have a string value (Base64)
func (v *VaultSecretsManager) SetSecret(name string, value []byte) error {
// Check if overwrite is possible
_, err := v.GetSecret(name)
if err == nil {
// Secret is present
v.logger.Warn(fmt.Sprintf("Overwriting secret: %s", name))
} else if !errors.Is(err, secrets.ErrSecretNotFound) {
// An unrelated error occurred
return err
}
// Construct the data wrapper
data := make(map[string]string)
data[name] = string(value)
_, err = v.client.Logical().Write(v.constructSecretPath(name), map[string]interface{}{
"data": data,
})
if err != nil {
return fmt.Errorf("unable to store secret (%s), %w", name, err)
}
return nil
}
// HasSecret checks if the secret is present on the Hashicorp Vault server
func (v *VaultSecretsManager) HasSecret(name string) bool {
_, err := v.GetSecret(name)
return err == nil
}
// RemoveSecret removes a secret from the Hashicorp Vault server
func (v *VaultSecretsManager) RemoveSecret(name string) error {
// Check if overwrite is possible
_, err := v.GetSecret(name)
if err != nil {
return err
}
// Delete the secret from Vault storage
_, err = v.client.Logical().Delete(v.constructSecretPath(name))
if err != nil {
return fmt.Errorf("unable to delete secret (%s), %w", name, err)
}
return nil
}