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.
@@ -1,187 +1,212 @@
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
+ * region: 'eu-central-1', // Optional. AWS STS region.
33
+ * credentials: { // Optional
34
+ * accessKeyId: AWS_ACCESS_KEY,
35
+ * secretAccessKey: AWS_SECRET_KEY,
36
+ * },
37
+ * }
38
+ * }
39
+ * })
40
+ * ```
41
+ *
42
+ */
43
+ class VaultIAMAuth extends VaultBaseAuth {
44
+ /**
45
+ * @typedef AWSCredentials
46
+ * @property {String} accessKeyId
47
+ * @property {String} secretAccessKey
48
+ *
49
+ * @typedef {Object} VaultIAMAuthConfig
50
+ * @property {String} role - Role name of the auth/{mount}/role/{name} backend.
51
+ * @property {AWSCredentials} [credentials] - Optional. AWS Credentials
52
+ * @property {String} [namespace] - Optional. Vault namespace, sent as the X-Vault-Namespace header.
53
+ * @property {String} [iam_server_id_header_value] - Optional. Header's value X-Vault-AWS-IAM-Server-ID.
54
+ * @property {String} [region] - Optional. AWS region used to sign the STS GetCallerIdentity
55
+ * request. When set, the request is signed against the regional STS endpoint
56
+ * (`sts.<region>.amazonaws.com`) and the SigV4 credential scope is bound to that region.
57
+ * When omitted, the global endpoint (`sts.amazonaws.com`, scope `us-east-1`) is used,
58
+ * preserving the previous default behavior.
59
+ *
60
+ * @param {VaultApiClient} api - see {@link VaultBaseAuth#constructor}
61
+ * @param {Object} logger
62
+ * @param {VaultIAMAuthConfig} config
63
+ * @param {String} mount - Vault's AWS Auth Backend mount point ("aws" by default)
64
+ */
65
+ constructor(api, logger, config, mount) {
66
+ super(api, logger, mount || 'aws');
67
+
68
+ this.__role = config.role;
69
+ this.__iam_server_id_header_value = config.iam_server_id_header_value;
70
+ this.__namespace = config.namespace;
71
+ this.__region = config.region;
72
+
73
+
74
+ const { credentials } = config;
75
+ this._validateCredentialsConfig(credentials);
76
+
77
+ this.__credentialChain = credentials ? () => Promise.resolve(credentials) : fromNodeProviderChain();
78
+ }
79
+
80
+ /**
81
+ * @inheritDoc
82
+ */
83
+ _authenticate() {
84
+ this._log.info(
85
+ 'making authentication request: role=%s',
86
+ this.__role
87
+ );
88
+
89
+ var headers = {};
90
+
91
+ if (this.__namespace) {
92
+ headers = {
93
+ 'X-Vault-Namespace': this.__namespace,
94
+ }
95
+ }
96
+
97
+
98
+ return Promise.resolve()
99
+ .then(() => this.__getCredentials())
100
+ .then((credentials) => {
101
+ return this.__apiClient.makeRequest(
102
+ 'POST',
103
+ `/auth/${this._mount}/login`,
104
+ this.__getVaultAuthRequestBody(this.__getStsRequest(credentials)),
105
+ headers
106
+ );
107
+ })
108
+ .then((response) => {
109
+ this._log.debug(
110
+ 'receive token: %s',
111
+ response.auth.client_token
112
+ );
113
+ return this._getTokenEntity(response.auth.client_token)
114
+ })
115
+ }
116
+
117
+ /**
118
+ * Credentials resolved by {@see @aws-sdk/credential-providers}
119
+ *
120
+ * @returns {Promise<AwsCredentialIdentity>}
121
+ * @private
122
+ */
123
+ __getCredentials() {
124
+ return this.__credentialChain();
125
+ }
126
+
127
+ /**
128
+ * Prepare vault auth request body
129
+ *
130
+ * @param stsRequest
131
+ * @returns {Object} {@link https://www.vaultproject.io/docs/auth/aws.html#via-the-api}
132
+ * @private
133
+ */
134
+ __getVaultAuthRequestBody(stsRequest) {
135
+ return {
136
+ iam_http_request_method: stsRequest.method,
137
+ iam_request_headers: this.__base64encode(
138
+ JSON.stringify(this.__headersLikeGolangStyle(stsRequest.headers))
139
+ ),
140
+ iam_request_body: this.__base64encode(stsRequest.body),
141
+ // `aws4` populates `hostname` for the default (global) endpoint, but
142
+ // `host` when an explicit host is supplied (regional endpoint). Use
143
+ // whichever is present so the URL sent to Vault matches the signed
144
+ // Host header.
145
+ iam_request_url: this.__base64encode(`https://${stsRequest.host || stsRequest.hostname}${stsRequest.path}`),
146
+ role: this.__role
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Prepare signed request to AWS STS :: GetCallerIdentity
152
+ *
153
+ * @param credentials
154
+ * @private
155
+ */
156
+ __getStsRequest(credentials) {
157
+ const request = {
158
+ service: 'sts',
159
+ method: 'POST',
160
+ body: 'Action=GetCallerIdentity&Version=2011-06-15',
161
+ headers: this.__iam_server_id_header_value ? {
162
+ 'X-Vault-AWS-IAM-Server-ID': this.__iam_server_id_header_value,
163
+ } : {}
164
+ };
165
+
166
+ // When a region is configured, sign the request against the regional STS
167
+ // endpoint. Both the SigV4 credential scope (via `region`) and the signed
168
+ // Host header / request URL (via `host`) must reference the same region,
169
+ // otherwise Vault's STS replay fails with `SignatureDoesNotMatch`.
170
+ // When omitted, `aws4` defaults to the global endpoint (`sts.amazonaws.com`,
171
+ // scope `us-east-1`), preserving the previous behavior.
172
+ if (this.__region) {
173
+ request.region = this.__region;
174
+ request.host = `sts.${this.__region}.amazonaws.com`;
175
+ }
176
+
177
+ return aws4.sign(request, credentials);
178
+ }
179
+
180
+ /**
181
+ * @param string
182
+ * @private
183
+ */
184
+ __base64encode(string) {
185
+ return Buffer.from(string).toString('base64')
186
+ }
187
+
188
+ /**
189
+ * @link https://github.com/hashicorp/vault/issues/2810
190
+ * @link https://golang.org/pkg/net/http/#Header
191
+ *
192
+ * @param {Object} headers
193
+ * @returns {Object}
194
+ * @private
195
+ */
196
+ __headersLikeGolangStyle(headers) {
197
+ return _.mapValues(headers, (value) => [`${value}`]);
198
+ }
199
+
200
+ _validateCredentialsConfig(credentials) {
201
+ if (Array.isArray(credentials)) {
202
+ throw new errors.InvalidAWSCredentialsError('Invalid AWS credentials provided in config. See CHANGELOG if migating from 0.x.x');
203
+ }
204
+ if (credentials && typeof credentials === 'object') {
205
+ if (!credentials.secretAccessKey || !credentials.accessKeyId) {
206
+ throw new errors.InvalidAWSCredentialsError('Invalid AWS credentials provided in config: accessKeyId and secretAccessKey are required.');
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ module.exports = VaultIAMAuth;
@@ -1,48 +1,49 @@
1
- const fs = require('fs');
2
- const VaultBaseAuth = require('./VaultBaseAuth');
3
-
4
- class VaultKubernetesAuth extends VaultBaseAuth {
5
- /**
6
- * @param {VaultApiClient} apiClient - see {@link VaultBaseAuth#constructor}
7
- * @param {Object} config
8
- * @param {String} config.role - Role configured in Vault Kubernetes Auth backend under which we want to issue Vault token.
9
- * @param {String} [config.tokenPath] - Path to the Kube JWT token. If omitted - default will be used.
10
- * @param {String} mount - Vault's mount point ("kubernetes" by default)
11
- */
12
- constructor(apiClient, logger, config, mount) {
13
- super(apiClient, logger, mount || 'kubernetes');
14
-
15
- this.__role = config.role;
16
- this.__tokenPath = '/var/run/secrets/kubernetes.io/serviceaccount/token';
17
- if (config.tokenPath !== undefined) {
18
- this.__tokenPath = config.tokenPath;
19
- }
20
- }
21
-
22
- _authenticate() {
23
- this._log.info(
24
- 'making authentication request: Vault role: "%s"; K8s token path: "%s"',
25
- this.__role, this.__tokenPath
26
- );
27
-
28
- const k8sJwtToken = fs.readFileSync(this.__tokenPath).toString();
29
- this._log.debug(
30
- 'receive K8s token: %s',
31
- k8sJwtToken
32
- );
33
-
34
- return this.__apiClient.makeRequest('POST', `/auth/${this._mount}/login`, {
35
- role: this.__role,
36
- jwt: k8sJwtToken,
37
- }).then((res) => {
38
- this._log.debug(
39
- 'receive token: %s',
40
- res.auth.client_token
41
- );
42
-
43
- return this._getTokenEntity(res.auth.client_token);
44
- });
45
- }
46
- }
47
-
48
- module.exports = VaultKubernetesAuth;
1
+ const fs = require('fs');
2
+ const VaultBaseAuth = require('./VaultBaseAuth');
3
+
4
+ class VaultKubernetesAuth extends VaultBaseAuth {
5
+ /**
6
+ * @param {VaultApiClient} apiClient - see {@link VaultBaseAuth#constructor}
7
+ * @param {Object} logger
8
+ * @param {Object} config
9
+ * @param {String} config.role - Role configured in Vault Kubernetes Auth backend under which we want to issue Vault token.
10
+ * @param {String} [config.tokenPath] - Path to the Kube JWT token. If omitted - default will be used.
11
+ * @param {String} mount - Vault's mount point ("kubernetes" by default)
12
+ */
13
+ constructor(apiClient, logger, config, mount) {
14
+ super(apiClient, logger, mount || 'kubernetes');
15
+
16
+ this.__role = config.role;
17
+ this.__tokenPath = '/var/run/secrets/kubernetes.io/serviceaccount/token';
18
+ if (config.tokenPath !== undefined) {
19
+ this.__tokenPath = config.tokenPath;
20
+ }
21
+ }
22
+
23
+ _authenticate() {
24
+ this._log.info(
25
+ 'making authentication request: Vault role: "%s"; K8s token path: "%s"',
26
+ this.__role, this.__tokenPath
27
+ );
28
+
29
+ const k8sJwtToken = fs.readFileSync(this.__tokenPath).toString();
30
+ this._log.debug(
31
+ 'receive K8s token: %s',
32
+ k8sJwtToken
33
+ );
34
+
35
+ return this.__apiClient.makeRequest('POST', `/auth/${this._mount}/login`, {
36
+ role: this.__role,
37
+ jwt: k8sJwtToken,
38
+ }).then((res) => {
39
+ this._log.debug(
40
+ 'receive token: %s',
41
+ res.auth.client_token
42
+ );
43
+
44
+ return this._getTokenEntity(res.auth.client_token);
45
+ });
46
+ }
47
+ }
48
+
49
+ module.exports = VaultKubernetesAuth;
@@ -1,34 +1,35 @@
1
- 'use strict';
2
-
3
- const VaultBaseAuth = require('./VaultBaseAuth');
4
- const errors = require('../errors');
5
-
6
- class VaultTokenAuth extends VaultBaseAuth {
7
-
8
- /**
9
- * @param {Object} connConfig - see {@link VaultBaseAuth#constructor}
10
- * @param {Object} config
11
- * @param {String} config.token
12
- * @param {String} mount - Vault's mount point ("token" by default)
13
- */
14
- constructor(connConfig, logger, config, mount) {
15
- super(connConfig, logger, mount || 'token');
16
-
17
- if (!config.token) {
18
- throw new errors.InvalidArgumentsError('Auth token should be provided for VaultTokenAuth');
19
- }
20
-
21
- this.__token = config.token;
22
- }
23
-
24
- _authenticate() {
25
- return this._getTokenEntity(this.__token);
26
- }
27
-
28
- _reauthenticationAllowed() {
29
- return false;
30
- }
31
-
32
- }
33
-
34
- module.exports = VaultTokenAuth;
1
+ 'use strict';
2
+
3
+ const VaultBaseAuth = require('./VaultBaseAuth');
4
+ const errors = require('../errors');
5
+
6
+ class VaultTokenAuth extends VaultBaseAuth {
7
+
8
+ /**
9
+ * @param {VaultApiClient} apiClient - see {@link VaultBaseAuth#constructor}
10
+ * @param {Object} logger
11
+ * @param {Object} config
12
+ * @param {String} config.token
13
+ * @param {String} mount - Vault's mount point ("token" by default)
14
+ */
15
+ constructor(apiClient, logger, config, mount) {
16
+ super(apiClient, logger, mount || 'token');
17
+
18
+ if (!config.token) {
19
+ throw new errors.InvalidArgumentsError('Auth token should be provided for VaultTokenAuth');
20
+ }
21
+
22
+ this.__token = config.token;
23
+ }
24
+
25
+ _authenticate() {
26
+ return this._getTokenEntity(this.__token);
27
+ }
28
+
29
+ _reauthenticationAllowed() {
30
+ return false;
31
+ }
32
+
33
+ }
34
+
35
+ module.exports = VaultTokenAuth;
package/src/errors.js CHANGED
@@ -1,21 +1,21 @@
1
- 'use strict';
2
-
3
- class VaultError extends Error {
4
- constructor(message, error) {
5
- super(message);
6
- this.name = this.constructor.name;
7
- this.message = message;
8
-
9
- Error.captureStackTrace(this, this.constructor.name);
10
- }
11
- }
12
- class InvalidArgumentsError extends VaultError {}
13
- class InvalidAWSCredentialsError extends InvalidArgumentsError {}
14
- class AuthTokenExpiredError extends VaultError {}
15
-
16
- module.exports = {
17
- VaultError,
18
- InvalidArgumentsError,
19
- InvalidAWSCredentialsError,
20
- AuthTokenExpiredError,
21
- };
1
+ 'use strict';
2
+
3
+ class VaultError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = this.constructor.name;
7
+ this.message = message;
8
+
9
+ Error.captureStackTrace(this, this.constructor);
10
+ }
11
+ }
12
+ class InvalidArgumentsError extends VaultError {}
13
+ class InvalidAWSCredentialsError extends InvalidArgumentsError {}
14
+ class AuthTokenExpiredError extends VaultError {}
15
+
16
+ module.exports = {
17
+ VaultError,
18
+ InvalidArgumentsError,
19
+ InvalidAWSCredentialsError,
20
+ AuthTokenExpiredError,
21
+ };