node-vault-client 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +200 -200
- package/README.md +337 -154
- package/package.json +64 -44
- package/src/Lease.js +57 -57
- package/src/VaultApiClient.js +94 -55
- package/src/VaultClient.js +288 -263
- package/src/VaultNodeConfig.js +104 -105
- package/src/auth/AuthToken.js +141 -94
- package/src/auth/VaultAppRoleAuth.js +49 -40
- package/src/auth/VaultBaseAuth.js +162 -140
- package/src/auth/VaultIAMAuth.js +212 -187
- package/src/auth/VaultKubernetesAuth.js +49 -48
- package/src/auth/VaultTokenAuth.js +35 -34
- package/src/errors.js +21 -21
package/src/VaultClient.js
CHANGED
|
@@ -1,263 +1,288 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const _ = require('lodash');
|
|
4
|
-
const Lease = require('./Lease');
|
|
5
|
-
const errors = require('./errors');
|
|
6
|
-
const VaultApiClient = require('./VaultApiClient');
|
|
7
|
-
const VaultAppRoleAuth = require('./auth/VaultAppRoleAuth');
|
|
8
|
-
const VaultTokenAuth = require('./auth/VaultTokenAuth');
|
|
9
|
-
const VaultIAMAuth = require('./auth/VaultIAMAuth');
|
|
10
|
-
const VaultNodeConfig = require('./VaultNodeConfig');
|
|
11
|
-
const VaultKubernetesAuth = require('./auth/VaultKubernetesAuth');
|
|
12
|
-
const vaultInstances = {};
|
|
13
|
-
|
|
14
|
-
class VaultClient {
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Client constructor function.
|
|
18
|
-
*
|
|
19
|
-
* @param {Object} options
|
|
20
|
-
* @param {Object} options.api
|
|
21
|
-
* @param {String} options.api.url - the url of the vault server
|
|
22
|
-
* @param {String} [options.api.apiVersion='v1']
|
|
23
|
-
* @param {Object} options.
|
|
24
|
-
*
|
|
25
|
-
* @
|
|
26
|
-
* @param {Object
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
.
|
|
231
|
-
this.__log.
|
|
232
|
-
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const Lease = require('./Lease');
|
|
5
|
+
const errors = require('./errors');
|
|
6
|
+
const VaultApiClient = require('./VaultApiClient');
|
|
7
|
+
const VaultAppRoleAuth = require('./auth/VaultAppRoleAuth');
|
|
8
|
+
const VaultTokenAuth = require('./auth/VaultTokenAuth');
|
|
9
|
+
const VaultIAMAuth = require('./auth/VaultIAMAuth');
|
|
10
|
+
const VaultNodeConfig = require('./VaultNodeConfig');
|
|
11
|
+
const VaultKubernetesAuth = require('./auth/VaultKubernetesAuth');
|
|
12
|
+
const vaultInstances = {};
|
|
13
|
+
|
|
14
|
+
class VaultClient {
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Client constructor function.
|
|
18
|
+
*
|
|
19
|
+
* @param {Object} options
|
|
20
|
+
* @param {Object} options.api
|
|
21
|
+
* @param {String} options.api.url - the url of the vault server
|
|
22
|
+
* @param {String} [options.api.apiVersion='v1']
|
|
23
|
+
* @param {Object} [options.api.requestOptions] - extra options merged into every HTTP
|
|
24
|
+
* request (e.g. an undici `dispatcher` for a proxy/SOCKS agent or custom TLS/CA trust).
|
|
25
|
+
* See {@link VaultApiClient#constructor}.
|
|
26
|
+
* @param {Object} options.auth
|
|
27
|
+
* @param {String} options.auth.type
|
|
28
|
+
* @param {Object} options.auth.config - auth configuration variables
|
|
29
|
+
* @param {Object|false} options.logger - Logger that supports "error", "info", "warn", "trace", "debug" methods. Uses `console` by default. Pass `false` to disable logging.
|
|
30
|
+
*/
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this.__log = this.__setupLogger(options.logger);
|
|
33
|
+
|
|
34
|
+
this.__api = new VaultApiClient(
|
|
35
|
+
options.api,
|
|
36
|
+
this.__log
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @type {VaultBaseAuth}
|
|
41
|
+
* @private
|
|
42
|
+
*/
|
|
43
|
+
this.__auth = this.__getAuthProvider(
|
|
44
|
+
options.auth,
|
|
45
|
+
this.__api
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
this.__namespace = options.auth.config.namespace;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Boot an instance of Vault
|
|
53
|
+
*
|
|
54
|
+
* The instance will be stored in a local hash. Calling Vault.boot multiple
|
|
55
|
+
* times with the same name will return the same instance.
|
|
56
|
+
*
|
|
57
|
+
* @param {String} name - Vault instance name
|
|
58
|
+
* @param {Object} [options] - options for {@link VaultClient#constructor}.
|
|
59
|
+
* @return {VaultClient}
|
|
60
|
+
*/
|
|
61
|
+
static boot(name, options) {
|
|
62
|
+
if (options === undefined) {
|
|
63
|
+
throw new errors.InvalidArgumentsError('Options should be provided');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let instance = vaultInstances[name];
|
|
67
|
+
if (instance === undefined) {
|
|
68
|
+
vaultInstances[name] = instance = new VaultClient(options);
|
|
69
|
+
}
|
|
70
|
+
return instance;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get an instance of Vault
|
|
75
|
+
*
|
|
76
|
+
* The instance will be stored in a local hash. Calling Vault.pop multiple
|
|
77
|
+
* times with the same name will return the same instance.
|
|
78
|
+
*
|
|
79
|
+
* @param {String} name - Vault instance name
|
|
80
|
+
* @return {VaultClient}
|
|
81
|
+
*/
|
|
82
|
+
static get(name) {
|
|
83
|
+
let instance = vaultInstances[name];
|
|
84
|
+
|
|
85
|
+
if (instance === undefined) {
|
|
86
|
+
throw new errors.InvalidArgumentsError('Invalid instance name');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return instance;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Clear named Vault instance
|
|
94
|
+
*
|
|
95
|
+
* If no name passed all named instances will be cleared.
|
|
96
|
+
*
|
|
97
|
+
* @param {String} [name] - Vault instance name, all instances will be cleared if no name were passed
|
|
98
|
+
*/
|
|
99
|
+
static clear(name) {
|
|
100
|
+
if (typeof name === 'string') {
|
|
101
|
+
const instance = vaultInstances[name];
|
|
102
|
+
if (instance !== undefined) {
|
|
103
|
+
instance.close();
|
|
104
|
+
delete vaultInstances[name];
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
for (let k in vaultInstances) {
|
|
108
|
+
if (Object.hasOwn(vaultInstances, k)) {
|
|
109
|
+
vaultInstances[k].close();
|
|
110
|
+
delete vaultInstances[k];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Release resources held by this client.
|
|
118
|
+
*
|
|
119
|
+
* Cancels the background auth-token refresh timer that keeps the Node.js event loop
|
|
120
|
+
* alive for renewable tokens. Call this once you are done with the client so a
|
|
121
|
+
* short-lived script can exit on its own. Safe to call when no timer is armed and
|
|
122
|
+
* safe to call multiple times. After calling `close()` the client may still be used;
|
|
123
|
+
* the next operation that fetches a renewable token will arm a new refresh timer.
|
|
124
|
+
*
|
|
125
|
+
* @returns {void}
|
|
126
|
+
*/
|
|
127
|
+
close() {
|
|
128
|
+
if (this.__auth && typeof this.__auth.cancelTokenRefresh === 'function') {
|
|
129
|
+
this.__auth.cancelTokenRefresh();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @protected
|
|
135
|
+
*
|
|
136
|
+
* @param {Object} authConfig
|
|
137
|
+
* @param {string} authConfig.type
|
|
138
|
+
* @param {string} authConfig.mount
|
|
139
|
+
* @param {Object} authConfig.config
|
|
140
|
+
* @param {VaultApiClient} api
|
|
141
|
+
* @return {VaultBaseAuth}
|
|
142
|
+
* @private
|
|
143
|
+
*/
|
|
144
|
+
__getAuthProvider(authConfig, api) {
|
|
145
|
+
this.__log.debug('creating vault auth method: "%s"', authConfig.type);
|
|
146
|
+
|
|
147
|
+
switch (authConfig.type) {
|
|
148
|
+
case 'iam':
|
|
149
|
+
return new VaultIAMAuth(
|
|
150
|
+
api,
|
|
151
|
+
this.__log,
|
|
152
|
+
authConfig.config,
|
|
153
|
+
authConfig.mount
|
|
154
|
+
);
|
|
155
|
+
case 'appRole':
|
|
156
|
+
return new VaultAppRoleAuth(
|
|
157
|
+
api,
|
|
158
|
+
this.__log,
|
|
159
|
+
authConfig.config,
|
|
160
|
+
authConfig.mount
|
|
161
|
+
);
|
|
162
|
+
case 'token':
|
|
163
|
+
return new VaultTokenAuth(
|
|
164
|
+
api,
|
|
165
|
+
this.__log,
|
|
166
|
+
authConfig.config,
|
|
167
|
+
authConfig.mount
|
|
168
|
+
);
|
|
169
|
+
case 'kubernetes':
|
|
170
|
+
return new VaultKubernetesAuth(
|
|
171
|
+
api,
|
|
172
|
+
this.__log,
|
|
173
|
+
authConfig.config,
|
|
174
|
+
authConfig.mount
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
throw new errors.InvalidArgumentsError('Unsupported auth method')
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Populates Vault's values to NPM "config" module
|
|
183
|
+
*/
|
|
184
|
+
fillNodeConfig() {
|
|
185
|
+
const vaultConf = new VaultNodeConfig(this);
|
|
186
|
+
|
|
187
|
+
return vaultConf.populate();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
getHeaders(token) {
|
|
191
|
+
if (this.__namespace) {
|
|
192
|
+
return {
|
|
193
|
+
'X-Vault-Token': token.getId(),
|
|
194
|
+
'X-Vault-Namespace': this.__namespace
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {'X-Vault-Token': token.getId()}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Read secret from Vault
|
|
203
|
+
* @param {string} path - path to the secret
|
|
204
|
+
* @returns {Promise<Lease>}
|
|
205
|
+
*/
|
|
206
|
+
read(path) {
|
|
207
|
+
this.__log.debug('read secret %s', path);
|
|
208
|
+
return this.__auth.getAuthToken()
|
|
209
|
+
.then(token => this.__api.makeRequest('GET', path, null, this.getHeaders(token)))
|
|
210
|
+
.then(res => {
|
|
211
|
+
this.__log.debug('receive secret %s', path);
|
|
212
|
+
return Lease.fromResponse(res);
|
|
213
|
+
})
|
|
214
|
+
.catch((reason) => {
|
|
215
|
+
this.__log.error('read secret failed: %s', reason.message);
|
|
216
|
+
throw reason;
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Retrieves secrets list
|
|
222
|
+
*
|
|
223
|
+
* @param {string} path - path to the secret
|
|
224
|
+
* @returns {Promise<Lease>}
|
|
225
|
+
*/
|
|
226
|
+
list(path) {
|
|
227
|
+
this.__log.debug('list secrets %s', path);
|
|
228
|
+
return this.__auth.getAuthToken()
|
|
229
|
+
.then(token => this.__api.makeRequest('LIST', path, null, this.getHeaders(token)))
|
|
230
|
+
.then(res => {
|
|
231
|
+
this.__log.debug('got secrets list %s', path);
|
|
232
|
+
return Lease.fromResponse(res);
|
|
233
|
+
})
|
|
234
|
+
.catch((reason) => {
|
|
235
|
+
this.__log.error('list secrets failed: %s', reason.message);
|
|
236
|
+
throw reason;
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Writes data to Vault
|
|
242
|
+
*
|
|
243
|
+
* @param {string} path - path used to write data
|
|
244
|
+
* @param {object} data - data to write
|
|
245
|
+
* @returns {Promise<Object>} the parsed Vault response body
|
|
246
|
+
*/
|
|
247
|
+
write(path, data) {
|
|
248
|
+
this.__log.debug('write secret %s', path);
|
|
249
|
+
return this.__auth.getAuthToken()
|
|
250
|
+
.then((token) => this.__api.makeRequest('POST', path, data, this.getHeaders(token)))
|
|
251
|
+
.then((response) => {
|
|
252
|
+
this.__log.debug('secret %s was written', path);
|
|
253
|
+
return response;
|
|
254
|
+
})
|
|
255
|
+
.catch((reason) => {
|
|
256
|
+
this.__log.error('write secret failed: %s', reason.message);
|
|
257
|
+
throw reason;
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* @private
|
|
263
|
+
*/
|
|
264
|
+
__setupLogger(logger) {
|
|
265
|
+
if (logger === false) {
|
|
266
|
+
return {
|
|
267
|
+
error: _.noop,
|
|
268
|
+
warn: _.noop,
|
|
269
|
+
info: _.noop,
|
|
270
|
+
debug: _.noop,
|
|
271
|
+
trace: _.noop,
|
|
272
|
+
}
|
|
273
|
+
} else if (_.intersection(_.functionsIn(logger), ['error', 'warn', 'info', 'debug', 'trace']).length >= 5) {
|
|
274
|
+
return logger
|
|
275
|
+
} else {
|
|
276
|
+
return {
|
|
277
|
+
error: console.error,
|
|
278
|
+
warn: console.warn,
|
|
279
|
+
info: console.info,
|
|
280
|
+
trace: console.trace,
|
|
281
|
+
// avoid output sensitive information
|
|
282
|
+
debug: _.noop
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
module.exports = VaultClient;
|