Skip to content

Commit

Permalink
feat: allow TLS when using Sentinel
Browse files Browse the repository at this point in the history
Two options added: `enableTLSForSentinelMode` & `sentinelTLS`
  • Loading branch information
fenichelar authored and luin committed Jan 7, 2019
1 parent 2d241ac commit ebef8f5
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 2 deletions.
12 changes: 10 additions & 2 deletions lib/connectors/SentinelConnector/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {createConnection, Socket} from 'net'
import {CONNECTION_CLOSED_ERROR_MSG, packObject, sample} from '../../utils'
import {TLSSocket} from 'tls'
import {connect as createTLSConnection, TLSSocket, SecureContextOptions} from 'tls'
import {ITcpConnectionOptions, isIIpcConnectionOptions} from '../StandaloneConnector'
import SentinelIterator from './SentinelIterator'
import {ISentinelAddress} from './types';
Expand Down Expand Up @@ -28,6 +28,8 @@ interface ISentinelConnectionOptions extends ITcpConnectionOptions {
sentinelRetryStrategy?: (retryAttempts: number) => number
preferredSlaves?: PreferredSlaves
connectTimeout?: number
enableTLSForSentinelMode?: boolean
sentinelTLS?: SecureContextOptions
}

export default class SentinelConnector extends AbstractConnector {
Expand Down Expand Up @@ -104,7 +106,12 @@ export default class SentinelConnector extends AbstractConnector {
}
if (resolved) {
debug('resolved: %s:%s', resolved.host, resolved.port)
_this.stream = createConnection(resolved)
if (_this.options.enableTLSForSentinelMode && _this.options.tls) {
Object.assign(resolved, _this.options.tls)
_this.stream = createTLSConnection(resolved)
} else {
_this.stream = createConnection(resolved)
}
_this.sentinelIterator.reset(true)
callback(null, _this.stream)
} else {
Expand Down Expand Up @@ -193,6 +200,7 @@ export default class SentinelConnector extends AbstractConnector {
port: endpoint.port || 26379,
host: endpoint.host,
family: endpoint.family || (isIIpcConnectionOptions(this.options) ? undefined : this.options.family),
tls: this.options.sentinelTLS,
retryStrategy: null,
enableReadyCheck: false,
connectTimeout: this.options.connectTimeout,
Expand Down
6 changes: 6 additions & 0 deletions lib/redis.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ var PromiseContainer = require('./promiseContainer');
* Only available for cluster mode.
* @param {boolean} [options.stringNumbers=false] - Force numbers to be always returned as JavaScript
* strings. This option is necessary when dealing with big numbers (exceed the [-2^53, +2^53] range).
* @param {boolean} [options.enableTLSForSentinelMode=false] - Whether to support the `tls` option
* when connecting to Redis via sentinel mode.
* @extends [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)
* @extends Commander
* @example
Expand Down Expand Up @@ -168,6 +170,7 @@ Redis.defaultOptions = {
sentinelRetryStrategy: function (times) {
return Math.min(times * 10, 1000);
},
enableTLSForSentinelMode: false,
// Status
password: null,
db: 0,
Expand Down Expand Up @@ -273,6 +276,9 @@ Redis.prototype.connect = function (callback) {
return;
}
var CONNECT_EVENT = options.tls ? 'secureConnect' : 'connect';
if (options.sentinels && !options.enableTLSForSentinelMode) {
CONNECT_EVENT = 'connect';
}

_this.stream = stream;
if (typeof options.keepAlive === 'number') {
Expand Down
96 changes: 96 additions & 0 deletions test/functional/tls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use strict'
import * as tls from 'tls'
import * as net from 'net'

describe('tls option', () => {
describe('Standalone', () => {
it('supports tls', (done) => {
let redis

stub(tls, 'connect', (op) => {
expect(op.ca).to.eql('123')
expect(op.port).to.eql(6379)
const stream = net.createConnection(op)
stream.on('connect', data => {
stream.emit('secureConnect', data)
})
return stream
})

redis = new Redis({tls: {ca: '123'}})
redis.on('ready', () => {
redis.disconnect()
tls.connect.restore()
redis.on('end', () => done())
})
})
})

describe('Sentinel', () => {
it('does not use tls option by default', (done) => {
new MockServer(27379, function (argv) {
if (argv[0] === 'sentinel' && argv[1] === 'get-master-addr-by-name') {
return ['127.0.0.1', '6379']
}
});

stub(tls, 'connect', () => {
throw new Error('called')
})

const redis = new Redis({sentinels: [{port: 27379}], name: 'my', tls: {ca: '123'}})
redis.on('ready', () => {
redis.disconnect()
tls.connect.restore()
done()
})
})

it('can be enabled by `enableTLSForSentinelMode`', (done) => {
new MockServer(27379, function (argv) {
if (argv[0] === 'sentinel' && argv[1] === 'get-master-addr-by-name') {
return ['127.0.0.1', '6379']
}
});

let redis

stub(tls, 'connect', (op) => {
expect(op.ca).to.eql('123')
redis.disconnect()
tls.connect.restore()
process.nextTick(done)
return tls.connect(op)
})

redis = new Redis({sentinels: [{port: 27379}], name: 'my', tls: {ca: '123'}, enableTLSForSentinelMode: true})
})

it('supports sentinelTLS', (done) => {
new MockServer(27379, function (argv) {
if (argv[0] === 'sentinel' && argv[1] === 'get-master-addr-by-name') {
return ['127.0.0.1', '6379']
}
});

let redis

stub(tls, 'connect', (op) => {
expect(op.ca).to.eql('123')
expect(op.port).to.eql(27379)
const stream = net.createConnection(op)
stream.on('connect', data => {
stream.emit('secureConnect', data)
})
return stream
})

redis = new Redis({sentinels: [{port: 27379}], name: 'my', sentinelTLS: {ca: '123'}})
redis.on('ready', () => {
redis.disconnect()
tls.connect.restore()
redis.on('end', () => done())
})
})
})
})

0 comments on commit ebef8f5

Please sign in to comment.