Skip to content

Commit 6681879

Browse files
authored
Merge pull request #279 from nodevault/copilot/fix-error-in-vault-usage
Document requestOptions for SSL/TLS configuration; fix broken rpOptions example
2 parents b27d395 + 7fc1321 commit 6681879

3 files changed

Lines changed: 191 additions & 7 deletions

File tree

README.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,50 @@ const SocksProxyAgent = require('socks-proxy-agent');
120120
const agent = new SocksProxyAgent(`socks://127.0.0.1:${socks4Port}`, true);
121121
const options = {
122122
apiVersion: 'v1',
123-
rpOptions: {
123+
requestOptions: {
124124
agent,
125125
},
126126
};
127127

128128
const vault = require('node-vault')(options);
129129
```
130130

131+
## Custom SSL/TLS Configuration
132+
133+
If you encounter SSL errors after upgrading to Node 18+ (e.g., `EPROTO` errors related to
134+
`unsafe legacy renegotiation disabled`), you can pass SSL/TLS options via `requestOptions`
135+
or `rpDefaults` when initializing the client:
136+
137+
```javascript
138+
const vault = require('node-vault')({
139+
apiVersion: 'v1',
140+
endpoint: 'https://vault.example.com:8200',
141+
token: 'MY_TOKEN',
142+
requestOptions: {
143+
agentOptions: {
144+
securityOptions: 'SSL_OP_LEGACY_SERVER_CONNECT',
145+
},
146+
},
147+
});
148+
```
149+
150+
The `requestOptions` object is passed through to the underlying HTTP library
151+
([postman-request](https://www.npmjs.com/package/postman-request)) for every request. You can
152+
use it to configure any supported request option, including `agentOptions`, custom `headers`,
153+
or a custom `agent`.
154+
155+
You can also pass request options per-call to any method:
156+
157+
```javascript
158+
vault.read('secret/hello', {
159+
agentOptions: {
160+
securityOptions: 'SSL_OP_LEGACY_SERVER_CONNECT',
161+
},
162+
});
163+
```
164+
165+
See [example/pass_request_options.js](example/pass_request_options.js) for more examples.
166+
131167
[![Backers](https://opencollective.com/node-vault/tiers/backers.svg?avatarHeight=80&width=600)](https://opencollective.com/node-vault/contribute)
132168

133169
[examples]: https://github.com/nodevault/node-vault/tree/master/example

example/pass_request_options.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,32 @@
22

33
process.env.DEBUG = 'node-vault'; // switch on debug mode
44

5-
const vault = require('./../src/index')();
5+
// Pass request options at initialization time.
6+
// These options are forwarded to postman-request for every request.
7+
const vault = require('./../src/index')({
8+
requestOptions: {
9+
agentOptions: {
10+
cert: '<path-to-cert>',
11+
key: '<path-to-key>',
12+
passphrase: '<your-passphrase>',
13+
securityOptions: 'SSL_OP_NO_SSLv3',
14+
},
15+
},
16+
});
617

7-
const options = {
18+
// You can also pass (or override) request options per-call.
19+
const perCallOptions = {
820
headers: {
921
'X-HELLO': 'world',
1022
},
1123
agentOptions: {
12-
cert: 'mycert',
13-
key: 'mykey',
14-
passphrase: 'password',
24+
cert: '<path-to-cert>',
25+
key: '<path-to-key>',
26+
passphrase: '<your-passphrase>',
1527
securityOptions: 'SSL_OP_NO_SSLv3',
1628
},
1729
};
1830

19-
vault.help('sys/policy', options)
31+
vault.help('sys/policy', perCallOptions)
2032
.then(() => vault.help('sys/mounts'))
2133
.catch((err) => console.error(err.message));

test/unit.js

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,142 @@ describe('node-vault', () => {
295295
});
296296
});
297297

298+
describe('config.requestOptions forwarding', () => {
299+
let requestWithOpts = null;
300+
let vaultWithOpts = null;
301+
302+
const agentOpts = {
303+
securityOptions: 'SSL_OP_LEGACY_SERVER_CONNECT',
304+
};
305+
306+
function getURIWithOpts(path) {
307+
return [vaultWithOpts.endpoint, vaultWithOpts.apiVersion, path].join('/');
308+
}
309+
310+
function assertRequestWithOpts(thisRequest, params, done) {
311+
return () => {
312+
thisRequest.should.have.calledOnce();
313+
thisRequest.calledWithMatch(params).should.be.ok();
314+
return done();
315+
};
316+
}
317+
318+
beforeEach(() => {
319+
requestWithOpts = sinon.stub();
320+
const resp = sinon.stub();
321+
resp.statusCode = 200;
322+
323+
requestWithOpts.returns({
324+
then(fn) {
325+
return fn(resp);
326+
},
327+
catch(fn) {
328+
return fn();
329+
},
330+
});
331+
332+
vaultWithOpts = index({
333+
endpoint: 'http://localhost:8200',
334+
token: '123',
335+
'request-promise': {
336+
defaults: () => requestWithOpts,
337+
},
338+
requestOptions: {
339+
agentOptions: agentOpts,
340+
},
341+
});
342+
});
343+
344+
it('should forward agentOptions from config.requestOptions in help()', (done) => {
345+
const path = 'sys/policy';
346+
const params = {
347+
method: 'GET',
348+
uri: `${getURIWithOpts(path)}?help=1`,
349+
agentOptions: agentOpts,
350+
};
351+
vaultWithOpts.help(path)
352+
.then(assertRequestWithOpts(requestWithOpts, params, done))
353+
.catch(done);
354+
});
355+
356+
it('should forward agentOptions from config.requestOptions in read()', (done) => {
357+
const path = 'secret/hello';
358+
const params = {
359+
method: 'GET',
360+
uri: getURIWithOpts(path),
361+
agentOptions: agentOpts,
362+
};
363+
vaultWithOpts.read(path)
364+
.then(assertRequestWithOpts(requestWithOpts, params, done))
365+
.catch(done);
366+
});
367+
368+
it('should forward agentOptions from config.requestOptions in write()', (done) => {
369+
const path = 'secret/hello';
370+
const params = {
371+
method: 'POST',
372+
uri: getURIWithOpts(path),
373+
agentOptions: agentOpts,
374+
};
375+
vaultWithOpts.write(path, { value: 'world' })
376+
.then(assertRequestWithOpts(requestWithOpts, params, done))
377+
.catch(done);
378+
});
379+
380+
it('should forward agentOptions from config.requestOptions in delete()', (done) => {
381+
const path = 'secret/hello';
382+
const params = {
383+
method: 'DELETE',
384+
uri: getURIWithOpts(path),
385+
agentOptions: agentOpts,
386+
};
387+
vaultWithOpts.delete(path)
388+
.then(assertRequestWithOpts(requestWithOpts, params, done))
389+
.catch(done);
390+
});
391+
392+
it('should forward agentOptions from config.requestOptions in list()', (done) => {
393+
const path = 'secret/hello';
394+
const params = {
395+
method: 'LIST',
396+
uri: getURIWithOpts(path),
397+
agentOptions: agentOpts,
398+
};
399+
vaultWithOpts.list(path)
400+
.then(assertRequestWithOpts(requestWithOpts, params, done))
401+
.catch(done);
402+
});
403+
404+
it('should forward agentOptions from config.requestOptions in generated functions', (done) => {
405+
const name = 'myTestFunction';
406+
vaultWithOpts.generateFunction(name, {
407+
method: 'GET',
408+
path: '/myroute',
409+
});
410+
const params = {
411+
agentOptions: agentOpts,
412+
};
413+
vaultWithOpts[name]()
414+
.then(assertRequestWithOpts(requestWithOpts, params, done))
415+
.catch(done);
416+
});
417+
418+
it('should allow per-call requestOptions to override config.requestOptions', (done) => {
419+
const path = 'secret/hello';
420+
const overrideOpts = {
421+
securityOptions: 'SSL_OP_NO_SSLv3',
422+
};
423+
const params = {
424+
method: 'GET',
425+
uri: getURIWithOpts(path),
426+
agentOptions: overrideOpts,
427+
};
428+
vaultWithOpts.read(path, { agentOptions: overrideOpts })
429+
.then(assertRequestWithOpts(requestWithOpts, params, done))
430+
.catch(done);
431+
});
432+
});
433+
298434
describe('unwrap(options)', () => {
299435
it('should return original response', (done) => {
300436
const path = 'sys/wrapping/unwrap';

0 commit comments

Comments
 (0)