From 6d7b06dd1bcca57a84317d5eb9bcf1812812f07b Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 25 Jun 2014 18:24:26 -0600 Subject: [PATCH 1/7] Add au --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index 386656c..508dc65 100644 --- a/README.md +++ b/README.md @@ -119,3 +119,27 @@ The same dissolution from the terminal would be export NODE_TLS_REJECT_UNAUTHORIZED="0" node my-service.js ``` + +# Index + +Other information you might want to know while you're here. + +## Generating an SSL Cert + +Just in case you didn't know, here's how you do it: + +``` +openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr +``` + +**DO NOT FILL OUT** email address, challenge password, or optional company name + +However, you *should* fill out country name, FULL state name, locality name, organization name. + +*organizational unit* is optional. + +``` +cat server.csr +``` + +That creates a sha-1 hash. From b68c15e962a4ab4ef13a0f54ea99dd5d80aa12b1 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 25 Jun 2014 19:08:17 -0600 Subject: [PATCH 2/7] Update README.md --- README.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/README.md b/README.md index 508dc65..a11da18 100644 --- a/README.md +++ b/README.md @@ -143,3 +143,43 @@ cat server.csr ``` That creates a sha-1 hash. + +When you submit that to the likes of RapidSSL you'll get back an X.509 that you should call `server.crt` (at least for the purposes of this mini-tutorial). + +You cannot use "bundled" certificates (`.pem`) with node.js. + +### the server + +Here's a complete working example: + +```javascript +'use strict'; + +var https = require('https') + , fs = require('fs') + , connect = require('connect') + , app = connect() + , sslOptions + , server + , port = 4080 + ; + +require('ssl-root-cas/latest') + .inject() + .addFile(__dirname + '/ssl/Geotrust Cross Root CA.txt') + .addFile(__dirname + '/ssl/Rapid SSL CA.txt') + ; + +sslOptions = { + key: fs.readFileSync('./ssl/server.key') +, cert: fs.readFileSync('./ssl/server.crt') +}; + +app.use('/', function (req, res) { + res.end('

Hello World

'); +}); + +server = https.createServer(sslOptions, app).listen(port, function(){ + console.log("Listening on " + server.address().port); +}); +``` From 4944799e2794e5d1879d65db6cc210e15481a5f2 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 25 Jun 2014 19:09:42 -0600 Subject: [PATCH 3/7] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a11da18..8643387 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,6 @@ app.use('/', function (req, res) { }); server = https.createServer(sslOptions, app).listen(port, function(){ - console.log("Listening on " + server.address().port); + console.log('Listening on https://' + server.address().address + ':' + server.address().port); }); ``` From 5275511071b557fa64ebf943c1ea7fa73fc47f30 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 25 Jun 2014 19:50:42 -0600 Subject: [PATCH 4/7] Added HTTPS w/ SNI example --- README.md | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8643387..9294b16 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ When you submit that to the likes of RapidSSL you'll get back an X.509 that you You cannot use "bundled" certificates (`.pem`) with node.js. -### the server +### A single HTTPS server Here's a complete working example: @@ -183,3 +183,79 @@ server = https.createServer(sslOptions, app).listen(port, function(){ console.log('Listening on https://' + server.address().address + ':' + server.address().port); }); ``` + +### Multiple HTTPS servers using SNI + +I know this works - because I just bought two SSL certs from RapidSSL (through name.com), +a Digital Ocean VPS, +and played around for an hour until it did. + +:-) + +```javascript +'use strict'; + +var https = require('https') + , fs = require('fs') + , crypto = require('crypto') + , connect = require('connect') + , vhost = require('vhost') + , app = connect() + , secureContexts = {} + , sslOptions + , server + , port = 4080 + ; + +require('ssl-root-cas/latest') + .inject() + .addFile(__dirname + '/ssl/Geotrust Cross Root CA.txt') + .addFile(__dirname + '/ssl/Rapid SSL CA.txt') + ; + +function getAppContext(domain) { + // Really you'd want to do this: + // return require(__dirname + '/' + domain + '/app.js'); + + // But for this demo we'll do this: + return connect().use('/', function (req, res) { + res.end('

Welcome to ' + domain + '!

'); + }); +} + +function getSecureContext(domain) { + return crypto.createCredentials({ + key: fs.readFileSync(__dirname + '/' + domain + '/ssl/server.key') + , cert: fs.readFileSync(__dirname + '/' + domain + '/ssl/server.crt') + }).context; +} + +[ 'aj.the.dj' +, 'ballprovo.com' +].forEach(function (domain) { + secureContexts[domain] = getSecureContext(domain); + app.use(vhost('*.' + domain, getAppContext(domain))); + app.use(vhost(domain, getAppContext(domain))); +}); + +// fallback / default domain +app.use('/', function (req, res) { + res.end('

Hello World

'); +}); + +//provide a SNICallback when you create the options for the https server +sslOptions = { + //SNICallback is passed the domain name, see NodeJS docs on TLS + SNICallback: function (domain) { + console.log(domain); + return secureContexts[domain]; + } + // fallback / default domain + , key: fs.readFileSync(__dirname + '/aj.the.dj/ssl/server.key') + , cert: fs.readFileSync(__dirname + '/aj.the.dj/ssl/server.crt') +}; + +server = https.createServer(sslOptions, app).listen(port, function(){ + console.log("Listening on " + server.address().port); +}); +``` From 98db5e1d0497fe17b29a499dc46e8b4322163980 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 25 Jun 2014 19:56:04 -0600 Subject: [PATCH 5/7] Update README.md --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9294b16..88ec3c5 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,27 @@ and played around for an hour until it did. :-) +File hierarchy: + +``` +webapps/ +└── vhosts + ├── aj.the.dj + │   └── ssl + │   ├── server.crt + │   └── server.key + ├── ballprovo.com + │   └── ssl + │   ├── server.crt + │   └── server.key + ├── server.js + └── ssl + ├── Geotrust Cross Root CA.txt + └── Rapid SSL CA.txt +``` + + +`server.js`: ```javascript 'use strict'; @@ -247,7 +268,7 @@ app.use('/', function (req, res) { sslOptions = { //SNICallback is passed the domain name, see NodeJS docs on TLS SNICallback: function (domain) { - console.log(domain); + console.log('SNI:', domain); return secureContexts[domain]; } // fallback / default domain From 160bc1ff0f2ec50acffe29cea4f6753f712fbd29 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 25 Jun 2014 20:04:52 -0600 Subject: [PATCH 6/7] Update README.md --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 88ec3c5..e8c3717 100644 --- a/README.md +++ b/README.md @@ -240,21 +240,19 @@ function getAppContext(domain) { // But for this demo we'll do this: return connect().use('/', function (req, res) { + console.log('req.vhost', JSON.stringify(req.vhost)); res.end('

Welcome to ' + domain + '!

'); }); } -function getSecureContext(domain) { - return crypto.createCredentials({ - key: fs.readFileSync(__dirname + '/' + domain + '/ssl/server.key') - , cert: fs.readFileSync(__dirname + '/' + domain + '/ssl/server.crt') - }).context; -} - [ 'aj.the.dj' , 'ballprovo.com' ].forEach(function (domain) { - secureContexts[domain] = getSecureContext(domain); + secureContexts[domain] = crypto.createCredentials({ + key: fs.readFileSync(__dirname + '/' + domain + '/ssl/server.key') + , cert: fs.readFileSync(__dirname + '/' + domain + '/ssl/server.crt') + }).context; + app.use(vhost('*.' + domain, getAppContext(domain))); app.use(vhost(domain, getAppContext(domain))); }); From 10db8662caf0c700445c86442e0b0e1949617067 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 25 Jun 2014 20:17:04 -0600 Subject: [PATCH 7/7] added http -> https redirect --- README.md | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e8c3717..ec56f26 100644 --- a/README.md +++ b/README.md @@ -212,20 +212,33 @@ webapps/ ``` -`server.js`: +#### `server.js` + ```javascript 'use strict'; var https = require('https') + , http = require('http') , fs = require('fs') , crypto = require('crypto') , connect = require('connect') , vhost = require('vhost') + + // connect / express app , app = connect() + + // SSL Server , secureContexts = {} - , sslOptions + , secureOpts + , secureServer + , securePort = 4443 + + // force SSL upgrade server , server , port = 4080 + + // the ssl domains I have + , domains = ['aj.the.dj', 'ballprovo.com'] ; require('ssl-root-cas/latest') @@ -245,9 +258,7 @@ function getAppContext(domain) { }); } -[ 'aj.the.dj' -, 'ballprovo.com' -].forEach(function (domain) { +domains.forEach(function (domain) { secureContexts[domain] = crypto.createCredentials({ key: fs.readFileSync(__dirname + '/' + domain + '/ssl/server.key') , cert: fs.readFileSync(__dirname + '/' + domain + '/ssl/server.crt') @@ -263,7 +274,7 @@ app.use('/', function (req, res) { }); //provide a SNICallback when you create the options for the https server -sslOptions = { +secureOpts = { //SNICallback is passed the domain name, see NodeJS docs on TLS SNICallback: function (domain) { console.log('SNI:', domain); @@ -274,7 +285,18 @@ sslOptions = { , cert: fs.readFileSync(__dirname + '/aj.the.dj/ssl/server.crt') }; -server = https.createServer(sslOptions, app).listen(port, function(){ - console.log("Listening on " + server.address().port); +secureServer = https.createServer(secureOpts, app).listen(securePort, function(){ + console.log("Listening on https://localhost:" + secureServer.address().port); +}); + +server = http.createServer(function (req, res) { + res.setHeader( + 'Location' + , 'https://' + req.headers.host.replace(/:\d+/, ':' + securePort) + ); + res.statusCode = 302; + res.end(); +}).listen(port, function(){ + console.log("Listening on http://localhost:" + server.address().port); }); ```