node-vault-client 0.5.5 → 0.6.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 +153 -153
- package/package.json +43 -43
- package/src/Lease.js +57 -57
- package/src/VaultApiClient.js +55 -55
- package/src/VaultClient.js +266 -253
- package/src/VaultNodeConfig.js +105 -105
- package/src/auth/AuthToken.js +94 -94
- package/src/auth/VaultAppRoleAuth.js +40 -40
- package/src/auth/VaultBaseAuth.js +140 -140
- package/src/auth/VaultIAMAuth.js +181 -169
- package/src/auth/VaultKubernetesAuth.js +48 -48
- package/src/auth/VaultTokenAuth.js +34 -34
- package/src/errors.js +21 -21
|
@@ -1,140 +1,140 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const lt = require('long-timeout');
|
|
4
|
-
const prettyMs = require('pretty-ms');
|
|
5
|
-
const AuthToken = require('./AuthToken');
|
|
6
|
-
const errors = require('../errors');
|
|
7
|
-
|
|
8
|
-
class VaultBaseAuth {
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @param {VaultApiClient} apiClient
|
|
12
|
-
* @param {Object} logger
|
|
13
|
-
* @param {String} mount - Vault's mount point
|
|
14
|
-
*/
|
|
15
|
-
constructor(apiClient, logger, mount) {
|
|
16
|
-
this.__apiClient = apiClient;
|
|
17
|
-
/** @protected */
|
|
18
|
-
this._log = logger;
|
|
19
|
-
this._mount = mount;
|
|
20
|
-
|
|
21
|
-
/** @type AuthToken */
|
|
22
|
-
this.__authToken = null;
|
|
23
|
-
this.__refreshTimeout = null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @protected
|
|
28
|
-
* @returns {Promise<AuthToken>}
|
|
29
|
-
*/
|
|
30
|
-
_authenticate() {
|
|
31
|
-
throw new Error('Method should be overridden');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
getAuthToken() {
|
|
35
|
-
if (this.__authToken === null || (this.__authToken instanceof AuthToken && this.__authToken.isExpired() && this._reauthenticationAllowed())) {
|
|
36
|
-
if (this.__authToken !== null && this.__authToken.isExpired() && !this._reauthenticationAllowed()) {
|
|
37
|
-
throw new errors.AuthTokenExpiredError('Auth token has expired & cannot be refreshed since auth method doesn\'t support this.');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
this._log.info('getting auth token (mount=%s)', this._mount);
|
|
41
|
-
|
|
42
|
-
const tokenPromise = this._authenticate().then(authToken => {
|
|
43
|
-
this.__authToken = authToken;
|
|
44
|
-
|
|
45
|
-
if (this.__authToken.isRenewable()) {
|
|
46
|
-
this._log.debug(
|
|
47
|
-
'setting refresh timer for token %s',
|
|
48
|
-
authToken.getId()
|
|
49
|
-
);
|
|
50
|
-
this.__setupTokenRefreshTimer(this.__authToken);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return this.__authToken;
|
|
54
|
-
}).catch(e => {
|
|
55
|
-
this.__authToken = null;
|
|
56
|
-
throw e;
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
this.__authToken = tokenPromise;
|
|
60
|
-
return tokenPromise;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
this._log.debug('token already exist');
|
|
64
|
-
return Promise.resolve(this.__authToken);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* @protected
|
|
69
|
-
* @returns {Promise<AuthToken>}
|
|
70
|
-
*/
|
|
71
|
-
_getTokenEntity(tokenId) {
|
|
72
|
-
return this.__apiClient.makeRequest('GET', '/auth/token/lookup-self', null, {'X-Vault-Token': tokenId})
|
|
73
|
-
.then(res => {
|
|
74
|
-
return AuthToken.fromResponse(res);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* @protected
|
|
80
|
-
* @returns {boolean}
|
|
81
|
-
*/
|
|
82
|
-
_reauthenticationAllowed() {
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* @param {AuthToken} authToken
|
|
88
|
-
* @private
|
|
89
|
-
*/
|
|
90
|
-
__setupTokenRefreshTimer(authToken) {
|
|
91
|
-
if (this.__refreshTimeout !== null) {
|
|
92
|
-
lt.clearTimeout(this.__refreshTimeout);
|
|
93
|
-
this.__refreshTimeout = null;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (!authToken.isRenewable() || authToken.isExpired()) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const timer = Math.max((authToken.getExpiresAt() - Math.floor(Date.now() / 1000)) / 2, 1) * 1000;
|
|
101
|
-
|
|
102
|
-
this.__refreshTimeout = lt.setTimeout(() => {
|
|
103
|
-
this.__renewToken(authToken).then(authToken => {
|
|
104
|
-
this.__authToken = authToken;
|
|
105
|
-
this.__setupTokenRefreshTimer(authToken);
|
|
106
|
-
}).catch(err => {
|
|
107
|
-
this.__setupTokenRefreshTimer(authToken);
|
|
108
|
-
|
|
109
|
-
this._log.error(`Cannot refresh auth token with "${authToken.getAccessor()}" accessor. Error: ${err.message}`);
|
|
110
|
-
this._log.error(err);
|
|
111
|
-
});
|
|
112
|
-
}, timer);
|
|
113
|
-
|
|
114
|
-
this._log.debug(
|
|
115
|
-
'sleeping for %s',
|
|
116
|
-
prettyMs(timer)
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* @param {AuthToken} authToken
|
|
122
|
-
* @returns {Promise.<AuthToken>}
|
|
123
|
-
* @private
|
|
124
|
-
*/
|
|
125
|
-
__renewToken(authToken) {
|
|
126
|
-
this._log.debug('renewing vault token');
|
|
127
|
-
|
|
128
|
-
return this.__apiClient.makeRequest('POST', '/auth/token/renew-self', null, {'X-Vault-Token': authToken.getId()})
|
|
129
|
-
.then(() => {
|
|
130
|
-
this._log.info('successfully renewed token');
|
|
131
|
-
return this._getTokenEntity(authToken.getId());
|
|
132
|
-
})
|
|
133
|
-
.catch((reason) => {
|
|
134
|
-
this._log.error('token renew failed: %s', reason.message);
|
|
135
|
-
throw reason;
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
module.exports = VaultBaseAuth;
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const lt = require('long-timeout');
|
|
4
|
+
const prettyMs = require('pretty-ms');
|
|
5
|
+
const AuthToken = require('./AuthToken');
|
|
6
|
+
const errors = require('../errors');
|
|
7
|
+
|
|
8
|
+
class VaultBaseAuth {
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {VaultApiClient} apiClient
|
|
12
|
+
* @param {Object} logger
|
|
13
|
+
* @param {String} mount - Vault's mount point
|
|
14
|
+
*/
|
|
15
|
+
constructor(apiClient, logger, mount) {
|
|
16
|
+
this.__apiClient = apiClient;
|
|
17
|
+
/** @protected */
|
|
18
|
+
this._log = logger;
|
|
19
|
+
this._mount = mount;
|
|
20
|
+
|
|
21
|
+
/** @type AuthToken */
|
|
22
|
+
this.__authToken = null;
|
|
23
|
+
this.__refreshTimeout = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @protected
|
|
28
|
+
* @returns {Promise<AuthToken>}
|
|
29
|
+
*/
|
|
30
|
+
_authenticate() {
|
|
31
|
+
throw new Error('Method should be overridden');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getAuthToken() {
|
|
35
|
+
if (this.__authToken === null || (this.__authToken instanceof AuthToken && this.__authToken.isExpired() && this._reauthenticationAllowed())) {
|
|
36
|
+
if (this.__authToken !== null && this.__authToken.isExpired() && !this._reauthenticationAllowed()) {
|
|
37
|
+
throw new errors.AuthTokenExpiredError('Auth token has expired & cannot be refreshed since auth method doesn\'t support this.');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this._log.info('getting auth token (mount=%s)', this._mount);
|
|
41
|
+
|
|
42
|
+
const tokenPromise = this._authenticate().then(authToken => {
|
|
43
|
+
this.__authToken = authToken;
|
|
44
|
+
|
|
45
|
+
if (this.__authToken.isRenewable()) {
|
|
46
|
+
this._log.debug(
|
|
47
|
+
'setting refresh timer for token %s',
|
|
48
|
+
authToken.getId()
|
|
49
|
+
);
|
|
50
|
+
this.__setupTokenRefreshTimer(this.__authToken);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return this.__authToken;
|
|
54
|
+
}).catch(e => {
|
|
55
|
+
this.__authToken = null;
|
|
56
|
+
throw e;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
this.__authToken = tokenPromise;
|
|
60
|
+
return tokenPromise;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this._log.debug('token already exist');
|
|
64
|
+
return Promise.resolve(this.__authToken);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @protected
|
|
69
|
+
* @returns {Promise<AuthToken>}
|
|
70
|
+
*/
|
|
71
|
+
_getTokenEntity(tokenId) {
|
|
72
|
+
return this.__apiClient.makeRequest('GET', '/auth/token/lookup-self', null, {'X-Vault-Token': tokenId})
|
|
73
|
+
.then(res => {
|
|
74
|
+
return AuthToken.fromResponse(res);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @protected
|
|
80
|
+
* @returns {boolean}
|
|
81
|
+
*/
|
|
82
|
+
_reauthenticationAllowed() {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @param {AuthToken} authToken
|
|
88
|
+
* @private
|
|
89
|
+
*/
|
|
90
|
+
__setupTokenRefreshTimer(authToken) {
|
|
91
|
+
if (this.__refreshTimeout !== null) {
|
|
92
|
+
lt.clearTimeout(this.__refreshTimeout);
|
|
93
|
+
this.__refreshTimeout = null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!authToken.isRenewable() || authToken.isExpired()) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const timer = Math.max((authToken.getExpiresAt() - Math.floor(Date.now() / 1000)) / 2, 1) * 1000;
|
|
101
|
+
|
|
102
|
+
this.__refreshTimeout = lt.setTimeout(() => {
|
|
103
|
+
this.__renewToken(authToken).then(authToken => {
|
|
104
|
+
this.__authToken = authToken;
|
|
105
|
+
this.__setupTokenRefreshTimer(authToken);
|
|
106
|
+
}).catch(err => {
|
|
107
|
+
this.__setupTokenRefreshTimer(authToken);
|
|
108
|
+
|
|
109
|
+
this._log.error(`Cannot refresh auth token with "${authToken.getAccessor()}" accessor. Error: ${err.message}`);
|
|
110
|
+
this._log.error(err);
|
|
111
|
+
});
|
|
112
|
+
}, timer);
|
|
113
|
+
|
|
114
|
+
this._log.debug(
|
|
115
|
+
'sleeping for %s',
|
|
116
|
+
prettyMs(timer)
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {AuthToken} authToken
|
|
122
|
+
* @returns {Promise.<AuthToken>}
|
|
123
|
+
* @private
|
|
124
|
+
*/
|
|
125
|
+
__renewToken(authToken) {
|
|
126
|
+
this._log.debug('renewing vault token');
|
|
127
|
+
|
|
128
|
+
return this.__apiClient.makeRequest('POST', '/auth/token/renew-self', null, {'X-Vault-Token': authToken.getId()})
|
|
129
|
+
.then(() => {
|
|
130
|
+
this._log.info('successfully renewed token');
|
|
131
|
+
return this._getTokenEntity(authToken.getId());
|
|
132
|
+
})
|
|
133
|
+
.catch((reason) => {
|
|
134
|
+
this._log.error('token renew failed: %s', reason.message);
|
|
135
|
+
throw reason;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = VaultBaseAuth;
|
package/src/auth/VaultIAMAuth.js
CHANGED
|
@@ -1,170 +1,182 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const VaultBaseAuth = require('./VaultBaseAuth');
|
|
4
|
-
const aws4 = require('aws4');
|
|
5
|
-
const _ = require('lodash');
|
|
6
|
-
const errors = require('../errors');
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Implementation of AWS Auth Backend :: IAM Authentication Method
|
|
10
|
-
* @link https://www.vaultproject.io/docs/auth/aws.html#iam-authentication-method
|
|
11
|
-
*
|
|
12
|
-
* @usage
|
|
13
|
-
*
|
|
14
|
-
* ```bash
|
|
15
|
-
* vault write auth/aws/config/client secret_key=AWS_SECRET_KEY access_key=AWS_ACCESS_KEY
|
|
16
|
-
* vault write auth/aws/config/client iam_server_id_header_value=VAULT_ADDR
|
|
17
|
-
* vault write auth/aws/role/iam_name_of_role auth_type=iam bound_iam_principal_arn=arn:aws:iam::.... max_ttl=500h
|
|
18
|
-
* ```
|
|
19
|
-
*
|
|
20
|
-
* ```js
|
|
21
|
-
*
|
|
22
|
-
* VaultClient.boot('main', {
|
|
23
|
-
* api: { url: VAULT_ADDR },
|
|
24
|
-
* auth: {
|
|
25
|
-
* type: 'iam',
|
|
26
|
-
* mount: 'some_other_aws_mount_point', // Optional
|
|
27
|
-
* config: {
|
|
28
|
-
* role: 'my_iam_role',
|
|
29
|
-
* iam_server_id_header_value: VAULT_ADDR, // Optional
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
* @param {
|
|
45
|
-
* @param {Object}
|
|
46
|
-
* @param {
|
|
47
|
-
* @param {
|
|
48
|
-
* @param {
|
|
49
|
-
* @param {String}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.
|
|
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
|
-
return this.
|
|
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
|
-
* @private
|
|
164
|
-
*/
|
|
165
|
-
|
|
166
|
-
return
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const VaultBaseAuth = require('./VaultBaseAuth');
|
|
4
|
+
const aws4 = require('aws4');
|
|
5
|
+
const _ = require('lodash');
|
|
6
|
+
const errors = require('../errors');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Implementation of AWS Auth Backend :: IAM Authentication Method
|
|
10
|
+
* @link https://www.vaultproject.io/docs/auth/aws.html#iam-authentication-method
|
|
11
|
+
*
|
|
12
|
+
* @usage
|
|
13
|
+
*
|
|
14
|
+
* ```bash
|
|
15
|
+
* vault write auth/aws/config/client secret_key=AWS_SECRET_KEY access_key=AWS_ACCESS_KEY
|
|
16
|
+
* vault write auth/aws/config/client iam_server_id_header_value=VAULT_ADDR
|
|
17
|
+
* vault write auth/aws/role/iam_name_of_role auth_type=iam bound_iam_principal_arn=arn:aws:iam::.... max_ttl=500h
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* ```js
|
|
21
|
+
*
|
|
22
|
+
* VaultClient.boot('main', {
|
|
23
|
+
* api: { url: VAULT_ADDR },
|
|
24
|
+
* auth: {
|
|
25
|
+
* type: 'iam',
|
|
26
|
+
* mount: 'some_other_aws_mount_point', // Optional
|
|
27
|
+
* config: {
|
|
28
|
+
* role: 'my_iam_role',
|
|
29
|
+
* iam_server_id_header_value: VAULT_ADDR, // Optional
|
|
30
|
+
* namespace: 'some_namespace', // Optional
|
|
31
|
+
* credentials: new AWS.Credentials({
|
|
32
|
+
* accessKeyId: AWS_ACCESS_KEY,
|
|
33
|
+
* secretAccessKey: AWS_SECRET_KEY,
|
|
34
|
+
* })
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
* })
|
|
38
|
+
*
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
*/
|
|
42
|
+
class VaultIAMAuth extends VaultBaseAuth {
|
|
43
|
+
/**
|
|
44
|
+
* @param {VaultApiClient} api - see {@link VaultBaseAuth#constructor}
|
|
45
|
+
* @param {Object} logger
|
|
46
|
+
* @param {Object} config
|
|
47
|
+
* @param {String} config.role - Role name of the auth/{mount}/role/{name} backend.
|
|
48
|
+
* @param {AWS.Credentials|AWS.Credentials[]} config.credentials {@see AWS.CredentialProviderChain providers}
|
|
49
|
+
* @param {String} [config.iam_server_id_header_value] - Optional. Header's value X-Vault-AWS-IAM-Server-ID.
|
|
50
|
+
* @param {String} mount - Vault's AWS Auth Backend mount point ("aws" by default)
|
|
51
|
+
*/
|
|
52
|
+
constructor(api, logger, config, mount) {
|
|
53
|
+
super(api, logger, mount || 'aws');
|
|
54
|
+
|
|
55
|
+
const AWS = require('aws-sdk');
|
|
56
|
+
|
|
57
|
+
this.__role = config.role;
|
|
58
|
+
this.__iam_server_id_header_value = config.iam_server_id_header_value;
|
|
59
|
+
this.__namespace = config.namespace;
|
|
60
|
+
|
|
61
|
+
if (!(config.credentials instanceof AWS.Credentials) && !_.isArray(config.credentials)) {
|
|
62
|
+
throw new errors.InvalidAWSCredentialsError('Credentials must be provided. {AWS.Credentials|AWS.Credentials[]} or function-providers, which return them.')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const credentialsProviders = _.isArray(config.credentials)
|
|
66
|
+
? config.credentials
|
|
67
|
+
: [config.credentials];
|
|
68
|
+
|
|
69
|
+
this.__credentialChain = new AWS.CredentialProviderChain(
|
|
70
|
+
credentialsProviders
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @inheritDoc
|
|
76
|
+
*/
|
|
77
|
+
_authenticate() {
|
|
78
|
+
this._log.info(
|
|
79
|
+
'making authentication request: role=%s',
|
|
80
|
+
this.__role
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
var headers = {};
|
|
84
|
+
|
|
85
|
+
if (this.__namespace) {
|
|
86
|
+
headers = {
|
|
87
|
+
'X-Vault-Namespace': this.__namespace,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
return Promise.resolve()
|
|
93
|
+
.then(() => this.__getCredentials())
|
|
94
|
+
.then((credentials) => {
|
|
95
|
+
return this.__apiClient.makeRequest(
|
|
96
|
+
'POST',
|
|
97
|
+
`/auth/${this._mount}/login`,
|
|
98
|
+
this.__getVaultAuthRequestBody(this.__getStsRequest(credentials)),
|
|
99
|
+
headers
|
|
100
|
+
);
|
|
101
|
+
})
|
|
102
|
+
.then((response) => {
|
|
103
|
+
this._log.debug(
|
|
104
|
+
'receive token: %s',
|
|
105
|
+
response.auth.client_token
|
|
106
|
+
);
|
|
107
|
+
return this._getTokenEntity(response.auth.client_token)
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Credentials resolved by {@see AWS.CredentialProviderChain}
|
|
113
|
+
*
|
|
114
|
+
* @returns {Promise<AWS.Credentials>}
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
__getCredentials() {
|
|
118
|
+
return new Promise((resolve, reject) =>
|
|
119
|
+
this.__credentialChain.resolve((err, credentials) =>
|
|
120
|
+
err ? reject(err) : resolve(credentials)
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Prepare vault auth request body
|
|
127
|
+
*
|
|
128
|
+
* @param stsRequest
|
|
129
|
+
* @returns {Object} {@link https://www.vaultproject.io/docs/auth/aws.html#via-the-api}
|
|
130
|
+
* @private
|
|
131
|
+
*/
|
|
132
|
+
__getVaultAuthRequestBody(stsRequest) {
|
|
133
|
+
return {
|
|
134
|
+
iam_http_request_method: stsRequest.method,
|
|
135
|
+
iam_request_headers: this.__base64encode(
|
|
136
|
+
JSON.stringify(this.__headersLikeGolangStyle(stsRequest.headers))
|
|
137
|
+
),
|
|
138
|
+
iam_request_body: this.__base64encode(stsRequest.body),
|
|
139
|
+
iam_request_url: this.__base64encode(`https://${stsRequest.hostname}${stsRequest.path}`),
|
|
140
|
+
role: this.__role
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Prepare signed request to AWS STS :: GetCallerIdentity
|
|
146
|
+
*
|
|
147
|
+
* @param credentials
|
|
148
|
+
* @private
|
|
149
|
+
*/
|
|
150
|
+
__getStsRequest(credentials) {
|
|
151
|
+
return aws4.sign({
|
|
152
|
+
service: 'sts',
|
|
153
|
+
method: 'POST',
|
|
154
|
+
body: 'Action=GetCallerIdentity&Version=2011-06-15',
|
|
155
|
+
headers: this.__iam_server_id_header_value ? {
|
|
156
|
+
'X-Vault-AWS-IAM-Server-ID': this.__iam_server_id_header_value,
|
|
157
|
+
} : {}
|
|
158
|
+
}, credentials);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @param string
|
|
163
|
+
* @private
|
|
164
|
+
*/
|
|
165
|
+
__base64encode(string) {
|
|
166
|
+
return new Buffer(string).toString('base64')
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @link https://github.com/hashicorp/vault/issues/2810
|
|
171
|
+
* @link https://golang.org/pkg/net/http/#Header
|
|
172
|
+
*
|
|
173
|
+
* @param {Object} headers
|
|
174
|
+
* @returns {Object}
|
|
175
|
+
* @private
|
|
176
|
+
*/
|
|
177
|
+
__headersLikeGolangStyle(headers) {
|
|
178
|
+
return _.mapValues(headers, (value) => [`${value}`]);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
170
182
|
module.exports = VaultIAMAuth;
|