diff options
author | Mike Crute <mike@crute.us> | 2022-05-21 13:04:49 -0700 |
---|---|---|
committer | Mike Crute <mike@crute.us> | 2022-05-21 13:04:49 -0700 |
commit | 5bf75fb5b7e88153e34d2c7133315b654dbe1642 (patch) | |
tree | 5e02e16f485f9826dcdba419d0b40d42debbaf93 | |
parent | f4f23715cf22f06b7fb3b7663d054c20d220ce13 (diff) | |
download | golib-5bf75fb5b7e88153e34d2c7133315b654dbe1642.tar.bz2 golib-5bf75fb5b7e88153e34d2c7133315b654dbe1642.tar.xz golib-5bf75fb5b7e88153e34d2c7133315b654dbe1642.zip |
vault: add full client with renewalvault/v0.2.0
-rw-r--r-- | vault/client.go | 330 | ||||
-rw-r--r-- | vault/go.mod | 7 | ||||
-rw-r--r-- | vault/go.sum | 11 | ||||
-rw-r--r-- | vault/simple_client.go | 21 |
4 files changed, 363 insertions, 6 deletions
diff --git a/vault/client.go b/vault/client.go new file mode 100644 index 0000000..2f645d4 --- /dev/null +++ b/vault/client.go | |||
@@ -0,0 +1,330 @@ | |||
1 | package vault | ||
2 | |||
3 | import ( | ||
4 | "context" | ||
5 | "fmt" | ||
6 | "os" | ||
7 | "path" | ||
8 | "sync" | ||
9 | "time" | ||
10 | |||
11 | "github.com/hashicorp/vault/api" | ||
12 | "github.com/hashicorp/vault/api/auth/approle" | ||
13 | "github.com/mitchellh/mapstructure" | ||
14 | ) | ||
15 | |||
16 | type VaultClient interface { | ||
17 | LoginApprole(c context.Context, roleId string, secretId string) error | ||
18 | |||
19 | DbStaticCredential(c context.Context, suffix string) (*VaultUsernamePassword, error) | ||
20 | DbCredential(c context.Context, suffix string) (*VaultUsernamePassword, error) | ||
21 | |||
22 | KV(c context.Context, suffix string, out interface{}) (*VaultSecret, error) | ||
23 | KVApiKey(c context.Context, suffix string) (*VaultApiKey, error) | ||
24 | KVCredential(c context.Context, suffix string) (*VaultUsernamePassword, error) | ||
25 | |||
26 | Destroy(HasSecret) | ||
27 | Run(ctx context.Context, wg *sync.WaitGroup) error | ||
28 | } | ||
29 | |||
30 | type HasSecret interface { | ||
31 | VaultSecret() *VaultSecret | ||
32 | } | ||
33 | |||
34 | // VaultSecret is an opaque reference to a secret from Vault. It is | ||
35 | // meant to be given to the Destroy function to check-in and destroy | ||
36 | // unneeded credentials. Everything returned from the client has a | ||
37 | // VaultSecret and implements HasSecret for that purpose. If the | ||
38 | // credential is not renewable then destroying it is a no-op. | ||
39 | type VaultSecret struct { | ||
40 | s *api.Secret | ||
41 | n string | ||
42 | } | ||
43 | |||
44 | func (s *VaultSecret) VaultSecret() *VaultSecret { | ||
45 | return s | ||
46 | } | ||
47 | |||
48 | type VaultApiKey struct { | ||
49 | Key string `json:"key"` | ||
50 | s *VaultSecret | ||
51 | } | ||
52 | |||
53 | func (k *VaultApiKey) VaultSecret() *VaultSecret { | ||
54 | return k.s | ||
55 | } | ||
56 | |||
57 | type VaultUsernamePassword struct { | ||
58 | Username string `json:"username"` | ||
59 | Password string `json:"password"` | ||
60 | s *VaultSecret | ||
61 | } | ||
62 | |||
63 | func (k *VaultUsernamePassword) VaultSecret() *VaultSecret { | ||
64 | return k.s | ||
65 | } | ||
66 | |||
67 | type Renewal struct { | ||
68 | RenewedAt time.Time | ||
69 | Name string | ||
70 | } | ||
71 | |||
72 | type vaultClient struct { | ||
73 | sync.Mutex | ||
74 | c *api.Client | ||
75 | lc *api.Logical | ||
76 | wg *sync.WaitGroup | ||
77 | watcherDone chan error | ||
78 | watchers map[string]*api.LifetimeWatcher | ||
79 | renewInfo chan *Renewal | ||
80 | } | ||
81 | |||
82 | // NewApproleClientEnv is a convenience function to create a new | ||
83 | // VaultClient based on the environment, start it, and login using | ||
84 | // Approle authentication. | ||
85 | // | ||
86 | // The following environment variables are used and must be present: | ||
87 | // | ||
88 | // VAULT_ADDR - URL to Vault server (of form https://host:port/) | ||
89 | // VAULT_ROLE_ID - Role ID used for Approle authentication | ||
90 | // VAULT_SECRET_ID - Secret ID used for Approle authentication | ||
91 | // | ||
92 | func NewApproleClientEnv(ctx context.Context, wg *sync.WaitGroup, renewInfo chan *Renewal) (VaultClient, error) { | ||
93 | vaultHost := os.Getenv("VAULT_ADDR") | ||
94 | if vaultHost == "" { | ||
95 | return nil, fmt.Errorf("NewApproleClientEnv: VAULT_ADDR is not set in environment") | ||
96 | } | ||
97 | |||
98 | roleId := os.Getenv("VAULT_ROLE_ID") | ||
99 | if roleId == "" { | ||
100 | return nil, fmt.Errorf("NewApproleClientEnv: VAULT_ROLE_ID is not set in environment") | ||
101 | } | ||
102 | |||
103 | secretId := os.Getenv("VAULT_SECRET_ID") | ||
104 | if secretId == "" { | ||
105 | return nil, fmt.Errorf("NewApproleClientEnv: VAULT_SECRET_ID is not set in environment") | ||
106 | } | ||
107 | |||
108 | vc, err := NewVaultClient(vaultHost, renewInfo) | ||
109 | if err != nil { | ||
110 | return nil, fmt.Errorf("NewApproleClientEnv: error creating client %w", err) | ||
111 | } | ||
112 | |||
113 | go vc.Run(ctx, wg) | ||
114 | |||
115 | if err = vc.LoginApprole(ctx, roleId, secretId); err != nil { | ||
116 | return nil, fmt.Errorf("NewApproleClientEnv: error logging in to vault %w", err) | ||
117 | } | ||
118 | |||
119 | return vc, nil | ||
120 | } | ||
121 | |||
122 | func NewVaultClient(host string, renewInfo chan *Renewal) (VaultClient, error) { | ||
123 | cfg := api.DefaultConfig() | ||
124 | cfg.Address = host | ||
125 | |||
126 | c, err := api.NewClient(cfg) | ||
127 | if err != nil { | ||
128 | return nil, err | ||
129 | } | ||
130 | |||
131 | return &vaultClient{ | ||
132 | c: c, | ||
133 | lc: c.Logical(), | ||
134 | renewInfo: renewInfo, | ||
135 | watcherDone: make(chan error, 10), | ||
136 | watchers: map[string]*api.LifetimeWatcher{}, | ||
137 | }, nil | ||
138 | } | ||
139 | |||
140 | func (c *vaultClient) watchWatcher(w *api.LifetimeWatcher, name string) { | ||
141 | c.wg.Add(1) | ||
142 | defer c.wg.Done() | ||
143 | |||
144 | for { | ||
145 | select { | ||
146 | case err := <-w.DoneCh(): | ||
147 | if err != nil { | ||
148 | c.watcherDone <- err | ||
149 | } | ||
150 | return | ||
151 | case r := <-w.RenewCh(): | ||
152 | // Report this so consumers can do their own reporting, if not | ||
153 | // provided we just read this to drain the chan and throw it away. | ||
154 | if c.renewInfo != nil { | ||
155 | c.renewInfo <- &Renewal{ | ||
156 | Name: name, | ||
157 | RenewedAt: r.RenewedAt, | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | |||
164 | func (c *vaultClient) addWatcher(name string, s *api.Secret) error { | ||
165 | w, err := c.c.NewLifetimeWatcher(&api.LifetimeWatcherInput{ | ||
166 | Secret: s, | ||
167 | }) | ||
168 | if err != nil { | ||
169 | return err | ||
170 | } | ||
171 | |||
172 | c.Lock() | ||
173 | c.watchers[name] = w | ||
174 | c.Unlock() | ||
175 | |||
176 | go w.Start() | ||
177 | go c.watchWatcher(w, name) | ||
178 | |||
179 | return nil | ||
180 | } | ||
181 | |||
182 | func (c *vaultClient) read(ctx context.Context, prefix, suffix string) (*api.Secret, string, error) { | ||
183 | key := path.Join(prefix, suffix) | ||
184 | |||
185 | s, err := c.lc.ReadWithContext(ctx, key) | ||
186 | if err != nil { | ||
187 | return nil, "", err | ||
188 | } | ||
189 | |||
190 | if s.Renewable { | ||
191 | return s, key, c.addWatcher(key, s) | ||
192 | } | ||
193 | |||
194 | return s, key, nil | ||
195 | } | ||
196 | |||
197 | func (c *vaultClient) stop() { | ||
198 | c.Lock() | ||
199 | defer c.Unlock() | ||
200 | |||
201 | for _, w := range c.watchers { | ||
202 | w.Stop() | ||
203 | } | ||
204 | } | ||
205 | |||
206 | func (c *vaultClient) Run(ctx context.Context, wg *sync.WaitGroup) error { | ||
207 | c.Lock() | ||
208 | c.wg = wg | ||
209 | c.Unlock() | ||
210 | |||
211 | c.wg.Add(1) | ||
212 | defer c.wg.Done() | ||
213 | |||
214 | for { | ||
215 | select { | ||
216 | case <-ctx.Done(): | ||
217 | c.stop() | ||
218 | return nil | ||
219 | case err := <-c.watcherDone: | ||
220 | c.stop() | ||
221 | return err | ||
222 | } | ||
223 | } | ||
224 | } | ||
225 | |||
226 | func (c *vaultClient) Destroy(s HasSecret) { | ||
227 | vs := s.VaultSecret() | ||
228 | if vs == nil || vs.n == "" || vs.s == nil { | ||
229 | return | ||
230 | } | ||
231 | |||
232 | c.Lock() | ||
233 | defer c.Unlock() | ||
234 | |||
235 | if w, ok := c.watchers[vs.n]; ok { | ||
236 | delete(c.watchers, vs.n) | ||
237 | w.Stop() | ||
238 | } | ||
239 | |||
240 | // TODO: Delete dynamic credentials like DB sessions from Vault | ||
241 | |||
242 | // Drop references to the secret so that even if the client holds on to | ||
243 | // it we free the RAM. | ||
244 | vs.s = nil | ||
245 | vs.n = "" | ||
246 | } | ||
247 | |||
248 | func (c *vaultClient) LoginApprole(ctx context.Context, roleId string, secretId string) error { | ||
249 | a, err := approle.NewAppRoleAuth(roleId, &approle.SecretID{FromString: secretId}) | ||
250 | if err != nil { | ||
251 | return err | ||
252 | } | ||
253 | |||
254 | s, err := c.c.Auth().Login(ctx, a) | ||
255 | if err != nil { | ||
256 | return err | ||
257 | } | ||
258 | |||
259 | // This credential can not be destroyed like the others | ||
260 | return c.addWatcher("login", s) | ||
261 | } | ||
262 | |||
263 | func (c *vaultClient) DbStaticCredential(ctx context.Context, suffix string) (*VaultUsernamePassword, error) { | ||
264 | s, k, err := c.read(ctx, "database/static-creds", suffix) | ||
265 | if err != nil { | ||
266 | return nil, err | ||
267 | } | ||
268 | |||
269 | var d VaultUsernamePassword | ||
270 | if err = mapstructure.Decode(s.Data, &d); err != nil { | ||
271 | return nil, err | ||
272 | } | ||
273 | |||
274 | d.s = &VaultSecret{s: s, n: k} | ||
275 | |||
276 | return &d, nil | ||
277 | } | ||
278 | |||
279 | func (c *vaultClient) DbCredential(ctx context.Context, suffix string) (*VaultUsernamePassword, error) { | ||
280 | s, k, err := c.read(ctx, "database/creds", suffix) | ||
281 | if err != nil { | ||
282 | return nil, err | ||
283 | } | ||
284 | |||
285 | var d VaultUsernamePassword | ||
286 | if err = mapstructure.Decode(s.Data, &d); err != nil { | ||
287 | return nil, err | ||
288 | } | ||
289 | |||
290 | d.s = &VaultSecret{s: s, n: k} | ||
291 | |||
292 | return &d, nil | ||
293 | } | ||
294 | |||
295 | func (c *vaultClient) KV(ctx context.Context, suffix string, out interface{}) (*VaultSecret, error) { | ||
296 | s, k, err := c.read(ctx, "kv/data", suffix) | ||
297 | if err != nil { | ||
298 | return nil, err | ||
299 | } | ||
300 | |||
301 | if err = mapstructure.Decode(s.Data["data"], out); err != nil { | ||
302 | return nil, err | ||
303 | } | ||
304 | |||
305 | return &VaultSecret{s: s, n: k}, nil | ||
306 | } | ||
307 | |||
308 | func (c *vaultClient) KVApiKey(ctx context.Context, suffix string) (*VaultApiKey, error) { | ||
309 | var ak VaultApiKey | ||
310 | s, err := c.KV(ctx, suffix, &ak) | ||
311 | if err != nil { | ||
312 | return nil, err | ||
313 | } | ||
314 | |||
315 | ak.s = s | ||
316 | |||
317 | return &ak, nil | ||
318 | } | ||
319 | |||
320 | func (c *vaultClient) KVCredential(ctx context.Context, suffix string) (*VaultUsernamePassword, error) { | ||
321 | var ak VaultUsernamePassword | ||
322 | s, err := c.KV(ctx, suffix, &ak) | ||
323 | if err != nil { | ||
324 | return nil, err | ||
325 | } | ||
326 | |||
327 | ak.s = s | ||
328 | |||
329 | return &ak, nil | ||
330 | } | ||
diff --git a/vault/go.mod b/vault/go.mod index 73fcdbe..05d59a0 100644 --- a/vault/go.mod +++ b/vault/go.mod | |||
@@ -3,7 +3,8 @@ module code.crute.us/mcrute/golib/vault | |||
3 | go 1.17 | 3 | go 1.17 |
4 | 4 | ||
5 | require ( | 5 | require ( |
6 | github.com/hashicorp/vault/api v1.3.0 | 6 | github.com/hashicorp/vault/api v1.5.0 |
7 | github.com/hashicorp/vault/api/auth/approle v0.1.1 | ||
7 | github.com/mitchellh/mapstructure v1.4.2 | 8 | github.com/mitchellh/mapstructure v1.4.2 |
8 | ) | 9 | ) |
9 | 10 | ||
@@ -15,7 +16,7 @@ require ( | |||
15 | github.com/golang/protobuf v1.5.2 // indirect | 16 | github.com/golang/protobuf v1.5.2 // indirect |
16 | github.com/golang/snappy v0.0.4 // indirect | 17 | github.com/golang/snappy v0.0.4 // indirect |
17 | github.com/hashicorp/errwrap v1.1.0 // indirect | 18 | github.com/hashicorp/errwrap v1.1.0 // indirect |
18 | github.com/hashicorp/go-cleanhttp v0.5.1 // indirect | 19 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect |
19 | github.com/hashicorp/go-hclog v0.16.2 // indirect | 20 | github.com/hashicorp/go-hclog v0.16.2 // indirect |
20 | github.com/hashicorp/go-immutable-radix v1.3.1 // indirect | 21 | github.com/hashicorp/go-immutable-radix v1.3.1 // indirect |
21 | github.com/hashicorp/go-multierror v1.1.1 // indirect | 22 | github.com/hashicorp/go-multierror v1.1.1 // indirect |
@@ -30,7 +31,7 @@ require ( | |||
30 | github.com/hashicorp/go-version v1.2.0 // indirect | 31 | github.com/hashicorp/go-version v1.2.0 // indirect |
31 | github.com/hashicorp/golang-lru v0.5.4 // indirect | 32 | github.com/hashicorp/golang-lru v0.5.4 // indirect |
32 | github.com/hashicorp/hcl v1.0.0 // indirect | 33 | github.com/hashicorp/hcl v1.0.0 // indirect |
33 | github.com/hashicorp/vault/sdk v0.3.0 // indirect | 34 | github.com/hashicorp/vault/sdk v0.4.1 // indirect |
34 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect | 35 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect |
35 | github.com/mattn/go-colorable v0.1.6 // indirect | 36 | github.com/mattn/go-colorable v0.1.6 // indirect |
36 | github.com/mattn/go-isatty v0.0.12 // indirect | 37 | github.com/mattn/go-isatty v0.0.12 // indirect |
diff --git a/vault/go.sum b/vault/go.sum index 7bbb973..c034144 100644 --- a/vault/go.sum +++ b/vault/go.sum | |||
@@ -89,8 +89,9 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv | |||
89 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= | 89 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= |
90 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | 90 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= |
91 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= | 91 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= |
92 | github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= | ||
93 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= | 92 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= |
93 | github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= | ||
94 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= | ||
94 | github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= | 95 | github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= |
95 | github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= | 96 | github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= |
96 | github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= | 97 | github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= |
@@ -130,10 +131,14 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l | |||
130 | github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= | 131 | github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= |
131 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= | 132 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= |
132 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= | 133 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= |
133 | github.com/hashicorp/vault/api v1.3.0 h1:uDy39PLSvy6gtKyjOCRPizy2QdFiIYSWBR2pxCEzYL8= | ||
134 | github.com/hashicorp/vault/api v1.3.0/go.mod h1:EabNQLI0VWbWoGlA+oBLC8PXmR9D60aUVgQGvangFWQ= | 134 | github.com/hashicorp/vault/api v1.3.0/go.mod h1:EabNQLI0VWbWoGlA+oBLC8PXmR9D60aUVgQGvangFWQ= |
135 | github.com/hashicorp/vault/sdk v0.3.0 h1:kR3dpxNkhh/wr6ycaJYqp6AFT/i2xaftbfnwZduTKEY= | 135 | github.com/hashicorp/vault/api v1.5.0 h1:Bp6yc2bn7CWkOrVIzFT/Qurzx528bdavF3nz590eu28= |
136 | github.com/hashicorp/vault/api v1.5.0/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM= | ||
137 | github.com/hashicorp/vault/api/auth/approle v0.1.1 h1:R5yA+xcNvw1ix6bDuWOaLOq2L4L77zDCVsethNw97xQ= | ||
138 | github.com/hashicorp/vault/api/auth/approle v0.1.1/go.mod h1:mHOLgh//xDx4dpqXoq6tS8Ob0FoCFWLU2ibJ26Lfmag= | ||
136 | github.com/hashicorp/vault/sdk v0.3.0/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0= | 139 | github.com/hashicorp/vault/sdk v0.3.0/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0= |
140 | github.com/hashicorp/vault/sdk v0.4.1 h1:3SaHOJY687jY1fnB61PtL0cOkKItphrbLmux7T92HBo= | ||
141 | github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0= | ||
137 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= | 142 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= |
138 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= | 143 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= |
139 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= | 144 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= |
diff --git a/vault/simple_client.go b/vault/simple_client.go index 4ceb4b5..2bef0a6 100644 --- a/vault/simple_client.go +++ b/vault/simple_client.go | |||
@@ -55,6 +55,27 @@ func GetVaultKeyStruct(path string, out interface{}) error { | |||
55 | return nil | 55 | return nil |
56 | } | 56 | } |
57 | 57 | ||
58 | // GetVaultApiKey fetches a JSON k/v value from Vault. The JSON document | ||
59 | // must have the format: { "key": "value" } | ||
60 | func GetVaultApiKey(path string) (string, error) { | ||
61 | s, err := loginAndRead(fmt.Sprintf("kv/data/%s", path)) | ||
62 | if err != nil { | ||
63 | return "", err | ||
64 | } | ||
65 | |||
66 | ret := struct { | ||
67 | Key string `json:"key"` | ||
68 | }{} | ||
69 | if err = mapstructure.Decode(s.Data["data"], &ret); err != nil { | ||
70 | return "", err | ||
71 | } | ||
72 | |||
73 | return ret.Key, nil | ||
74 | } | ||
75 | |||
76 | // GetVaultKey fetches a JSON k/v value from vault. The JSON document | ||
77 | // must have the format: | ||
78 | // { "username": "username", "password": "password" } | ||
58 | func GetVaultKey(path string) (Credential, error) { | 79 | func GetVaultKey(path string) (Credential, error) { |
59 | s, err := loginAndRead(fmt.Sprintf("kv/data/%s", path)) | 80 | s, err := loginAndRead(fmt.Sprintf("kv/data/%s", path)) |
60 | if err != nil { | 81 | if err != nil { |