node-vault-client 1.0.0 → 1.0.1

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,187 +1,187 @@
1
- 'use strict';
2
-
3
- const VaultBaseAuth = require('./VaultBaseAuth');
4
- const aws4 = require('aws4');
5
- const { fromNodeProviderChain } = require('@aws-sdk/credential-providers');
6
- const _ = require('lodash');
7
- const errors = require('../errors');
8
-
9
- /**
10
- * Implementation of AWS Auth Backend :: IAM Authentication Method
11
- * @link https://www.vaultproject.io/docs/auth/aws.html#iam-authentication-method
12
- *
13
- * @usage
14
- *
15
- * ```bash
16
- * vault write auth/aws/config/client secret_key=AWS_SECRET_KEY access_key=AWS_ACCESS_KEY
17
- * vault write auth/aws/config/client iam_server_id_header_value=VAULT_ADDR
18
- * vault write auth/aws/role/iam_name_of_role auth_type=iam bound_iam_principal_arn=arn:aws:iam::.... max_ttl=500h
19
- * ```
20
- *
21
- * ```js
22
- *
23
- * VaultClient.boot('main', {
24
- * api: { url: VAULT_ADDR },
25
- * auth: {
26
- * type: 'iam',
27
- * mount: 'some_other_aws_mount_point', // Optional
28
- * config: {
29
- * role: 'my_iam_role',
30
- * iam_server_id_header_value: VAULT_ADDR, // Optional
31
- * namespace: 'some_namespace', // Optional
32
- * credentials: { // Optional
33
- * accessKeyId: AWS_ACCESS_KEY,
34
- * secretAccessKey: AWS_SECRET_KEY,
35
- * },
36
- * }
37
- * }
38
- * })
39
- * ```
40
- *
41
- */
42
- class VaultIAMAuth extends VaultBaseAuth {
43
- /**
44
- * @typedef AWSCredentials
45
- * @property {String} accessKeyId
46
- * @property {String} secretAccessKey
47
- *
48
- * @typedef {Object} VaultIAMAuthConfig
49
- * @property {String} role - Role name of the auth/{mount}/role/{name} backend.
50
- * @property [AWSCredentials] [credentials] - Optional. AWS Credentials
51
- * @property {String} [iam_server_id_header_value] - Optional. Header's value X-Vault-AWS-IAM-Server-ID.
52
- *
53
- * @param {VaultApiClient} api - see {@link VaultBaseAuth#constructor}
54
- * @param {Object} logger
55
- * @param {VaultIAMAuthConfig} config
56
- * @param {String} mount - Vault's AWS Auth Backend mount point ("aws" by default)
57
- */
58
- constructor(api, logger, config, mount) {
59
- super(api, logger, mount || 'aws');
60
-
61
- this.__role = config.role;
62
- this.__iam_server_id_header_value = config.iam_server_id_header_value;
63
- this.__namespace = config.namespace;
64
-
65
-
66
- const { credentials } = config;
67
- this._validateCredentialsConfig(credentials);
68
-
69
- this.__credentialChain = credentials ? () => Promise.resolve(credentials) : fromNodeProviderChain();
70
- }
71
-
72
- /**
73
- * @inheritDoc
74
- */
75
- _authenticate() {
76
- this._log.info(
77
- 'making authentication request: role=%s',
78
- this.__role
79
- );
80
-
81
- var headers = {};
82
-
83
- if (this.__namespace) {
84
- headers = {
85
- 'X-Vault-Namespace': this.__namespace,
86
- }
87
- }
88
-
89
-
90
- return Promise.resolve()
91
- .then(() => this.__getCredentials())
92
- .then((credentials) => {
93
- return this.__apiClient.makeRequest(
94
- 'POST',
95
- `/auth/${this._mount}/login`,
96
- this.__getVaultAuthRequestBody(this.__getStsRequest(credentials)),
97
- headers
98
- );
99
- })
100
- .then((response) => {
101
- this._log.debug(
102
- 'receive token: %s',
103
- response.auth.client_token
104
- );
105
- return this._getTokenEntity(response.auth.client_token)
106
- })
107
- }
108
-
109
- /**
110
- * Credentials resolved by {@see @aws-sdk/credential-providers}
111
- *
112
- * @returns {Promise<AwsCredentialIdentity>}
113
- * @private
114
- */
115
- __getCredentials() {
116
- return this.__credentialChain();
117
- }
118
-
119
- /**
120
- * Prepare vault auth request body
121
- *
122
- * @param stsRequest
123
- * @returns {Object} {@link https://www.vaultproject.io/docs/auth/aws.html#via-the-api}
124
- * @private
125
- */
126
- __getVaultAuthRequestBody(stsRequest) {
127
- return {
128
- iam_http_request_method: stsRequest.method,
129
- iam_request_headers: this.__base64encode(
130
- JSON.stringify(this.__headersLikeGolangStyle(stsRequest.headers))
131
- ),
132
- iam_request_body: this.__base64encode(stsRequest.body),
133
- iam_request_url: this.__base64encode(`https://${stsRequest.hostname}${stsRequest.path}`),
134
- role: this.__role
135
- }
136
- }
137
-
138
- /**
139
- * Prepare signed request to AWS STS :: GetCallerIdentity
140
- *
141
- * @param credentials
142
- * @private
143
- */
144
- __getStsRequest(credentials) {
145
- return aws4.sign({
146
- service: 'sts',
147
- method: 'POST',
148
- body: 'Action=GetCallerIdentity&Version=2011-06-15',
149
- headers: this.__iam_server_id_header_value ? {
150
- 'X-Vault-AWS-IAM-Server-ID': this.__iam_server_id_header_value,
151
- } : {}
152
- }, credentials);
153
- }
154
-
155
- /**
156
- * @param string
157
- * @private
158
- */
159
- __base64encode(string) {
160
- return new Buffer(string).toString('base64')
161
- }
162
-
163
- /**
164
- * @link https://github.com/hashicorp/vault/issues/2810
165
- * @link https://golang.org/pkg/net/http/#Header
166
- *
167
- * @param {Object} headers
168
- * @returns {Object}
169
- * @private
170
- */
171
- __headersLikeGolangStyle(headers) {
172
- return _.mapValues(headers, (value) => [`${value}`]);
173
- }
174
-
175
- _validateCredentialsConfig(credentials) {
176
- if (Array.isArray(credentials)) {
177
- throw new errors.InvalidAWSCredentialsError('Invalid AWS credentials provided in config. See CHANGELOG if migating from 0.x.x');
178
- }
179
- if (credentials && typeof credentials === 'object') {
180
- if (!credentials.secretAccessKey || !credentials.accessKeyId) {
181
- throw new errors.InvalidAWSCredentialsError('Invalid AWS credentials provided in config: accessKeyId and secretAccessKey are required.');
182
- }
183
- }
184
- }
185
- }
186
-
187
- module.exports = VaultIAMAuth;
1
+ 'use strict';
2
+
3
+ const VaultBaseAuth = require('./VaultBaseAuth');
4
+ const aws4 = require('aws4');
5
+ const { fromNodeProviderChain } = require('@aws-sdk/credential-providers');
6
+ const _ = require('lodash');
7
+ const errors = require('../errors');
8
+
9
+ /**
10
+ * Implementation of AWS Auth Backend :: IAM Authentication Method
11
+ * @link https://www.vaultproject.io/docs/auth/aws.html#iam-authentication-method
12
+ *
13
+ * @usage
14
+ *
15
+ * ```bash
16
+ * vault write auth/aws/config/client secret_key=AWS_SECRET_KEY access_key=AWS_ACCESS_KEY
17
+ * vault write auth/aws/config/client iam_server_id_header_value=VAULT_ADDR
18
+ * vault write auth/aws/role/iam_name_of_role auth_type=iam bound_iam_principal_arn=arn:aws:iam::.... max_ttl=500h
19
+ * ```
20
+ *
21
+ * ```js
22
+ *
23
+ * VaultClient.boot('main', {
24
+ * api: { url: VAULT_ADDR },
25
+ * auth: {
26
+ * type: 'iam',
27
+ * mount: 'some_other_aws_mount_point', // Optional
28
+ * config: {
29
+ * role: 'my_iam_role',
30
+ * iam_server_id_header_value: VAULT_ADDR, // Optional
31
+ * namespace: 'some_namespace', // Optional
32
+ * credentials: { // Optional
33
+ * accessKeyId: AWS_ACCESS_KEY,
34
+ * secretAccessKey: AWS_SECRET_KEY,
35
+ * },
36
+ * }
37
+ * }
38
+ * })
39
+ * ```
40
+ *
41
+ */
42
+ class VaultIAMAuth extends VaultBaseAuth {
43
+ /**
44
+ * @typedef AWSCredentials
45
+ * @property {String} accessKeyId
46
+ * @property {String} secretAccessKey
47
+ *
48
+ * @typedef {Object} VaultIAMAuthConfig
49
+ * @property {String} role - Role name of the auth/{mount}/role/{name} backend.
50
+ * @property [AWSCredentials] [credentials] - Optional. AWS Credentials
51
+ * @property {String} [iam_server_id_header_value] - Optional. Header's value X-Vault-AWS-IAM-Server-ID.
52
+ *
53
+ * @param {VaultApiClient} api - see {@link VaultBaseAuth#constructor}
54
+ * @param {Object} logger
55
+ * @param {VaultIAMAuthConfig} config
56
+ * @param {String} mount - Vault's AWS Auth Backend mount point ("aws" by default)
57
+ */
58
+ constructor(api, logger, config, mount) {
59
+ super(api, logger, mount || 'aws');
60
+
61
+ this.__role = config.role;
62
+ this.__iam_server_id_header_value = config.iam_server_id_header_value;
63
+ this.__namespace = config.namespace;
64
+
65
+
66
+ const { credentials } = config;
67
+ this._validateCredentialsConfig(credentials);
68
+
69
+ this.__credentialChain = credentials ? () => Promise.resolve(credentials) : fromNodeProviderChain();
70
+ }
71
+
72
+ /**
73
+ * @inheritDoc
74
+ */
75
+ _authenticate() {
76
+ this._log.info(
77
+ 'making authentication request: role=%s',
78
+ this.__role
79
+ );
80
+
81
+ var headers = {};
82
+
83
+ if (this.__namespace) {
84
+ headers = {
85
+ 'X-Vault-Namespace': this.__namespace,
86
+ }
87
+ }
88
+
89
+
90
+ return Promise.resolve()
91
+ .then(() => this.__getCredentials())
92
+ .then((credentials) => {
93
+ return this.__apiClient.makeRequest(
94
+ 'POST',
95
+ `/auth/${this._mount}/login`,
96
+ this.__getVaultAuthRequestBody(this.__getStsRequest(credentials)),
97
+ headers
98
+ );
99
+ })
100
+ .then((response) => {
101
+ this._log.debug(
102
+ 'receive token: %s',
103
+ response.auth.client_token
104
+ );
105
+ return this._getTokenEntity(response.auth.client_token)
106
+ })
107
+ }
108
+
109
+ /**
110
+ * Credentials resolved by {@see @aws-sdk/credential-providers}
111
+ *
112
+ * @returns {Promise<AwsCredentialIdentity>}
113
+ * @private
114
+ */
115
+ __getCredentials() {
116
+ return this.__credentialChain();
117
+ }
118
+
119
+ /**
120
+ * Prepare vault auth request body
121
+ *
122
+ * @param stsRequest
123
+ * @returns {Object} {@link https://www.vaultproject.io/docs/auth/aws.html#via-the-api}
124
+ * @private
125
+ */
126
+ __getVaultAuthRequestBody(stsRequest) {
127
+ return {
128
+ iam_http_request_method: stsRequest.method,
129
+ iam_request_headers: this.__base64encode(
130
+ JSON.stringify(this.__headersLikeGolangStyle(stsRequest.headers))
131
+ ),
132
+ iam_request_body: this.__base64encode(stsRequest.body),
133
+ iam_request_url: this.__base64encode(`https://${stsRequest.hostname}${stsRequest.path}`),
134
+ role: this.__role
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Prepare signed request to AWS STS :: GetCallerIdentity
140
+ *
141
+ * @param credentials
142
+ * @private
143
+ */
144
+ __getStsRequest(credentials) {
145
+ return aws4.sign({
146
+ service: 'sts',
147
+ method: 'POST',
148
+ body: 'Action=GetCallerIdentity&Version=2011-06-15',
149
+ headers: this.__iam_server_id_header_value ? {
150
+ 'X-Vault-AWS-IAM-Server-ID': this.__iam_server_id_header_value,
151
+ } : {}
152
+ }, credentials);
153
+ }
154
+
155
+ /**
156
+ * @param string
157
+ * @private
158
+ */
159
+ __base64encode(string) {
160
+ return new Buffer(string).toString('base64')
161
+ }
162
+
163
+ /**
164
+ * @link https://github.com/hashicorp/vault/issues/2810
165
+ * @link https://golang.org/pkg/net/http/#Header
166
+ *
167
+ * @param {Object} headers
168
+ * @returns {Object}
169
+ * @private
170
+ */
171
+ __headersLikeGolangStyle(headers) {
172
+ return _.mapValues(headers, (value) => [`${value}`]);
173
+ }
174
+
175
+ _validateCredentialsConfig(credentials) {
176
+ if (Array.isArray(credentials)) {
177
+ throw new errors.InvalidAWSCredentialsError('Invalid AWS credentials provided in config. See CHANGELOG if migating from 0.x.x');
178
+ }
179
+ if (credentials && typeof credentials === 'object') {
180
+ if (!credentials.secretAccessKey || !credentials.accessKeyId) {
181
+ throw new errors.InvalidAWSCredentialsError('Invalid AWS credentials provided in config: accessKeyId and secretAccessKey are required.');
182
+ }
183
+ }
184
+ }
185
+ }
186
+
187
+ module.exports = VaultIAMAuth;