node-vault-client 0.5.7 → 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.
@@ -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;
@@ -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
- * credentials: new AWS.Credentials({
31
- * accessKeyId: AWS_ACCESS_KEY,
32
- * secretAccessKey: AWS_SECRET_KEY,
33
- * })
34
- * }
35
- * }
36
- * })
37
- *
38
- * ```
39
- *
40
- */
41
- class VaultIAMAuth extends VaultBaseAuth {
42
- /**
43
- * @param {VaultApiClient} api - see {@link VaultBaseAuth#constructor}
44
- * @param {Object} logger
45
- * @param {Object} config
46
- * @param {String} config.role - Role name of the auth/{mount}/role/{name} backend.
47
- * @param {AWS.Credentials|AWS.Credentials[]} config.credentials {@see AWS.CredentialProviderChain providers}
48
- * @param {String} [config.iam_server_id_header_value] - Optional. Header's value X-Vault-AWS-IAM-Server-ID.
49
- * @param {String} mount - Vault's AWS Auth Backend mount point ("aws" by default)
50
- */
51
- constructor(api, logger, config, mount) {
52
- super(api, logger, mount || 'aws');
53
-
54
- const AWS = require('aws-sdk');
55
-
56
- this.__role = config.role;
57
- this.__iam_server_id_header_value = config.iam_server_id_header_value;
58
-
59
- if (!(config.credentials instanceof AWS.Credentials) && !_.isArray(config.credentials)) {
60
- throw new errors.InvalidAWSCredentialsError('Credentials must be provided. {AWS.Credentials|AWS.Credentials[]} or function-providers, which return them.')
61
- }
62
-
63
- const credentialsProviders = _.isArray(config.credentials)
64
- ? config.credentials
65
- : [config.credentials];
66
-
67
- this.__credentialChain = new AWS.CredentialProviderChain(
68
- credentialsProviders
69
- );
70
- }
71
-
72
- /**
73
- * @inheritDoc
74
- */
75
- _authenticate() {
76
- this._log.info(
77
- 'making authentication request: role=%s',
78
- this.__role
79
- );
80
-
81
- return Promise.resolve()
82
- .then(() => this.__getCredentials())
83
- .then((credentials) => {
84
- return this.__apiClient.makeRequest(
85
- 'POST',
86
- `/auth/${this._mount}/login`,
87
- this.__getVaultAuthRequestBody(this.__getStsRequest(credentials))
88
- );
89
- })
90
- .then((response) => {
91
- this._log.debug(
92
- 'receive token: %s',
93
- response.auth.client_token
94
- );
95
- return this._getTokenEntity(response.auth.client_token)
96
- })
97
- }
98
-
99
- /**
100
- * Credentials resolved by {@see AWS.CredentialProviderChain}
101
- *
102
- * @returns {Promise<AWS.Credentials>}
103
- * @private
104
- */
105
- __getCredentials() {
106
- return new Promise((resolve, reject) =>
107
- this.__credentialChain.resolve((err, credentials) =>
108
- err ? reject(err) : resolve(credentials)
109
- )
110
- );
111
- }
112
-
113
- /**
114
- * Prepare vault auth request body
115
- *
116
- * @param stsRequest
117
- * @returns {Object} {@link https://www.vaultproject.io/docs/auth/aws.html#via-the-api}
118
- * @private
119
- */
120
- __getVaultAuthRequestBody(stsRequest) {
121
- return {
122
- iam_http_request_method: stsRequest.method,
123
- iam_request_headers: this.__base64encode(
124
- JSON.stringify(this.__headersLikeGolangStyle(stsRequest.headers))
125
- ),
126
- iam_request_body: this.__base64encode(stsRequest.body),
127
- iam_request_url: this.__base64encode(`https://${stsRequest.hostname}${stsRequest.path}`),
128
- role: this.__role
129
- }
130
- }
131
-
132
- /**
133
- * Prepare signed request to AWS STS :: GetCallerIdentity
134
- *
135
- * @param credentials
136
- * @private
137
- */
138
- __getStsRequest(credentials) {
139
- return aws4.sign({
140
- service: 'sts',
141
- method: 'POST',
142
- body: 'Action=GetCallerIdentity&Version=2011-06-15',
143
- headers: this.__iam_server_id_header_value ? {
144
- 'X-Vault-AWS-IAM-Server-ID': this.__iam_server_id_header_value,
145
- } : {}
146
- }, credentials);
147
- }
148
-
149
- /**
150
- * @param string
151
- * @private
152
- */
153
- __base64encode(string) {
154
- return new Buffer(string).toString('base64')
155
- }
156
-
157
- /**
158
- * @link https://github.com/hashicorp/vault/issues/2810
159
- * @link https://golang.org/pkg/net/http/#Header
160
- *
161
- * @param {Object} headers
162
- * @returns {Object}
163
- * @private
164
- */
165
- __headersLikeGolangStyle(headers) {
166
- return _.mapValues(headers, (value) => [`${value}`]);
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;