Skip to content

Commit ebef8f5

Browse files
fenichelarluin
authored andcommitted
feat: allow TLS when using Sentinel
Two options added: `enableTLSForSentinelMode` & `sentinelTLS`
1 parent 2d241ac commit ebef8f5

File tree

3 files changed

+112
-2
lines changed

3 files changed

+112
-2
lines changed

lib/connectors/SentinelConnector/index.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {createConnection, Socket} from 'net'
22
import {CONNECTION_CLOSED_ERROR_MSG, packObject, sample} from '../../utils'
3-
import {TLSSocket} from 'tls'
3+
import {connect as createTLSConnection, TLSSocket, SecureContextOptions} from 'tls'
44
import {ITcpConnectionOptions, isIIpcConnectionOptions} from '../StandaloneConnector'
55
import SentinelIterator from './SentinelIterator'
66
import {ISentinelAddress} from './types';
@@ -28,6 +28,8 @@ interface ISentinelConnectionOptions extends ITcpConnectionOptions {
2828
sentinelRetryStrategy?: (retryAttempts: number) => number
2929
preferredSlaves?: PreferredSlaves
3030
connectTimeout?: number
31+
enableTLSForSentinelMode?: boolean
32+
sentinelTLS?: SecureContextOptions
3133
}
3234

3335
export default class SentinelConnector extends AbstractConnector {
@@ -104,7 +106,12 @@ export default class SentinelConnector extends AbstractConnector {
104106
}
105107
if (resolved) {
106108
debug('resolved: %s:%s', resolved.host, resolved.port)
107-
_this.stream = createConnection(resolved)
109+
if (_this.options.enableTLSForSentinelMode && _this.options.tls) {
110+
Object.assign(resolved, _this.options.tls)
111+
_this.stream = createTLSConnection(resolved)
112+
} else {
113+
_this.stream = createConnection(resolved)
114+
}
108115
_this.sentinelIterator.reset(true)
109116
callback(null, _this.stream)
110117
} else {
@@ -193,6 +200,7 @@ export default class SentinelConnector extends AbstractConnector {
193200
port: endpoint.port || 26379,
194201
host: endpoint.host,
195202
family: endpoint.family || (isIIpcConnectionOptions(this.options) ? undefined : this.options.family),
203+
tls: this.options.sentinelTLS,
196204
retryStrategy: null,
197205
enableReadyCheck: false,
198206
connectTimeout: this.options.connectTimeout,

lib/redis.js

+6
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ var PromiseContainer = require('./promiseContainer');
8484
* Only available for cluster mode.
8585
* @param {boolean} [options.stringNumbers=false] - Force numbers to be always returned as JavaScript
8686
* strings. This option is necessary when dealing with big numbers (exceed the [-2^53, +2^53] range).
87+
* @param {boolean} [options.enableTLSForSentinelMode=false] - Whether to support the `tls` option
88+
* when connecting to Redis via sentinel mode.
8789
* @extends [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)
8890
* @extends Commander
8991
* @example
@@ -168,6 +170,7 @@ Redis.defaultOptions = {
168170
sentinelRetryStrategy: function (times) {
169171
return Math.min(times * 10, 1000);
170172
},
173+
enableTLSForSentinelMode: false,
171174
// Status
172175
password: null,
173176
db: 0,
@@ -273,6 +276,9 @@ Redis.prototype.connect = function (callback) {
273276
return;
274277
}
275278
var CONNECT_EVENT = options.tls ? 'secureConnect' : 'connect';
279+
if (options.sentinels && !options.enableTLSForSentinelMode) {
280+
CONNECT_EVENT = 'connect';
281+
}
276282

277283
_this.stream = stream;
278284
if (typeof options.keepAlive === 'number') {

test/functional/tls.js

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
'use strict'
2+
import * as tls from 'tls'
3+
import * as net from 'net'
4+
5+
describe('tls option', () => {
6+
describe('Standalone', () => {
7+
it('supports tls', (done) => {
8+
let redis
9+
10+
stub(tls, 'connect', (op) => {
11+
expect(op.ca).to.eql('123')
12+
expect(op.port).to.eql(6379)
13+
const stream = net.createConnection(op)
14+
stream.on('connect', data => {
15+
stream.emit('secureConnect', data)
16+
})
17+
return stream
18+
})
19+
20+
redis = new Redis({tls: {ca: '123'}})
21+
redis.on('ready', () => {
22+
redis.disconnect()
23+
tls.connect.restore()
24+
redis.on('end', () => done())
25+
})
26+
})
27+
})
28+
29+
describe('Sentinel', () => {
30+
it('does not use tls option by default', (done) => {
31+
new MockServer(27379, function (argv) {
32+
if (argv[0] === 'sentinel' && argv[1] === 'get-master-addr-by-name') {
33+
return ['127.0.0.1', '6379']
34+
}
35+
});
36+
37+
stub(tls, 'connect', () => {
38+
throw new Error('called')
39+
})
40+
41+
const redis = new Redis({sentinels: [{port: 27379}], name: 'my', tls: {ca: '123'}})
42+
redis.on('ready', () => {
43+
redis.disconnect()
44+
tls.connect.restore()
45+
done()
46+
})
47+
})
48+
49+
it('can be enabled by `enableTLSForSentinelMode`', (done) => {
50+
new MockServer(27379, function (argv) {
51+
if (argv[0] === 'sentinel' && argv[1] === 'get-master-addr-by-name') {
52+
return ['127.0.0.1', '6379']
53+
}
54+
});
55+
56+
let redis
57+
58+
stub(tls, 'connect', (op) => {
59+
expect(op.ca).to.eql('123')
60+
redis.disconnect()
61+
tls.connect.restore()
62+
process.nextTick(done)
63+
return tls.connect(op)
64+
})
65+
66+
redis = new Redis({sentinels: [{port: 27379}], name: 'my', tls: {ca: '123'}, enableTLSForSentinelMode: true})
67+
})
68+
69+
it('supports sentinelTLS', (done) => {
70+
new MockServer(27379, function (argv) {
71+
if (argv[0] === 'sentinel' && argv[1] === 'get-master-addr-by-name') {
72+
return ['127.0.0.1', '6379']
73+
}
74+
});
75+
76+
let redis
77+
78+
stub(tls, 'connect', (op) => {
79+
expect(op.ca).to.eql('123')
80+
expect(op.port).to.eql(27379)
81+
const stream = net.createConnection(op)
82+
stream.on('connect', data => {
83+
stream.emit('secureConnect', data)
84+
})
85+
return stream
86+
})
87+
88+
redis = new Redis({sentinels: [{port: 27379}], name: 'my', sentinelTLS: {ca: '123'}})
89+
redis.on('ready', () => {
90+
redis.disconnect()
91+
tls.connect.restore()
92+
redis.on('end', () => done())
93+
})
94+
})
95+
})
96+
})

0 commit comments

Comments
 (0)