Browse Source

tls: share tls tickets key between cluster workers

fix #5871
v0.11.5-release
Fedor Indutny 12 years ago
parent
commit
d62c2d975a
  1. 17
      lib/_tls_wrap.js
  2. 43
      src/node_crypto.cc
  3. 2
      src/node_crypto.h
  4. 125
      test/simple/test-tls-ticket-cluster.js

17
lib/_tls_wrap.js

@ -352,6 +352,7 @@ function Server(/* [options], listener */) {
crl: self.crl,
sessionIdContext: self.sessionIdContext
});
this._sharedCreds = sharedCreds;
var timeout = options.handshakeTimeout || (120 * 1000);
@ -363,6 +364,10 @@ function Server(/* [options], listener */) {
sharedCreds.context.setSessionTimeout(self.sessionTimeout);
}
if (self.ticketKeys) {
sharedCreds.context.setTicketKeys(self.ticketKeys);
}
// constructor call
net.Server.call(this, function(raw_socket) {
var socket = new TLSSocket(raw_socket, {
@ -422,6 +427,18 @@ exports.createServer = function(options, listener) {
};
Server.prototype._getServerData = function() {
return {
ticketKeys: this._sharedCreds.context.getTicketKeys().toString('hex')
};
};
Server.prototype._setServerData = function(data) {
this._sharedCreds.context.setTicketKeys(new Buffer(data.ticketKeys, 'hex'));
};
Server.prototype.setOptions = function(options) {
if (IS_BOOLEAN(options.requestCert)) {
this.requestCert = options.requestCert;

43
src/node_crypto.cc

@ -193,6 +193,8 @@ void SecureContext::Initialize(Handle<Object> target) {
SecureContext::SetSessionTimeout);
NODE_SET_PROTOTYPE_METHOD(t, "close", SecureContext::Close);
NODE_SET_PROTOTYPE_METHOD(t, "loadPKCS12", SecureContext::LoadPKCS12);
NODE_SET_PROTOTYPE_METHOD(t, "getTicketKeys", SecureContext::GetTicketKeys);
NODE_SET_PROTOTYPE_METHOD(t, "setTicketKeys", SecureContext::SetTicketKeys);
target->Set(String::NewSymbol("SecureContext"), t->GetFunction());
}
@ -750,6 +752,47 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) {
}
void SecureContext::GetTicketKeys(const FunctionCallbackInfo<Value>& args) {
#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_get_tlsext_ticket_keys)
HandleScope scope(node_isolate);
UNWRAP(SecureContext);
Local<Object> buff = Buffer::New(48);
if (SSL_CTX_get_tlsext_ticket_keys(wrap->ctx_,
Buffer::Data(buff),
Buffer::Length(buff)) != 1) {
return ThrowError("Failed to fetch tls ticket keys");
}
args.GetReturnValue().Set(buff);
#endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
}
void SecureContext::SetTicketKeys(const FunctionCallbackInfo<Value>& args) {
#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_get_tlsext_ticket_keys)
HandleScope scope(node_isolate);
if (args.Length() < 1 ||
!Buffer::HasInstance(args[0]) ||
Buffer::Length(args[0]) != 48) {
return ThrowTypeError("Bad argument");
}
UNWRAP(SecureContext);
if (SSL_CTX_set_tlsext_ticket_keys(wrap->ctx_,
Buffer::Data(args[0]),
Buffer::Length(args[0])) != 1) {
return ThrowError("Failed to fetch tls ticket keys");
}
args.GetReturnValue().Set(true);
#endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
}
size_t ClientHelloParser::Write(const uint8_t* data, size_t len) {
HandleScope scope(node_isolate);

2
src/node_crypto.h

@ -79,6 +79,8 @@ class SecureContext : ObjectWrap {
const v8::FunctionCallbackInfo<v8::Value>& args);
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
static void LoadPKCS12(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetTicketKeys(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetTicketKeys(const v8::FunctionCallbackInfo<v8::Value>& args);
static SSL_SESSION* GetSessionCallback(SSL* s,
unsigned char* key,

125
test/simple/test-tls-ticket-cluster.js

@ -0,0 +1,125 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
if (!process.versions.openssl) {
console.error('Skipping because node compiled without OpenSSL.');
process.exit(0);
}
var common = require('../common');
var assert = require('assert');
var cluster = require('cluster');
var tls = require('tls');
var fs = require('fs');
var join = require('path').join;
var workerCount = 4;
var expectedReqCount = 16;
if (cluster.isMaster) {
var reusedCount = 0;
var reqCount = 0;
var lastSession = null;
var shootOnce = false;
function shoot() {
console.error('[master] connecting');
var c = tls.connect(common.PORT, {
session: lastSession,
rejectUnauthorized: false
}, function() {
lastSession = c.getSession();
c.end();
if (++reqCount === expectedReqCount) {
Object.keys(cluster.workers).forEach(function(id) {
cluster.workers[id].send('die');
});
} else {
shoot();
}
});
}
function fork() {
var worker = cluster.fork();
var workerReqCount = 0;
worker.on('message', function(msg) {
console.error('[master] got %j', msg);
if (msg === 'reused') {
++reusedCount;
} else if (msg === 'listening' && !shootOnce) {
shootOnce = true;
shoot();
}
});
worker.on('exit', function() {
console.error('[master] worker died');
});
}
for (var i = 0; i < workerCount; i++) {
fork();
}
process.on('exit', function() {
assert.equal(reqCount, expectedReqCount);
assert.equal(reusedCount + 1, reqCount);
});
return;
}
var keyFile = join(common.fixturesDir, 'agent.key');
var certFile = join(common.fixturesDir, 'agent.crt');
var key = fs.readFileSync(keyFile);
var cert = fs.readFileSync(certFile);
var options = {
key: key,
cert: cert
};
var server = tls.createServer(options, function(c) {
if (c.isSessionReused()) {
process.send('reused');
} else {
process.send('not-reused');
}
c.end();
});
server.listen(common.PORT, function() {
process.send('listening');
});
process.on('message', function listener(msg) {
console.error('[worker] got %j', msg);
if (msg === 'die') {
server.close(function() {
console.error('[worker] server close');
process.exit();
});
}
});
process.on('exit', function() {
console.error('[worker] exit');
});
Loading…
Cancel
Save