From f068d84490e15ea661346b1200162adbf979065c Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 8 Jul 2015 00:43:46 -0600 Subject: [PATCH] custom https certs, http-to-https redirects --- README.md | 49 +++++++++++++++++++++++++++---- package.json | 1 + serve.js | 81 +++++++++++++++++++++++++++++++++++++++++++++++---- test-chain.sh | 18 ++++++++++++ 4 files changed, 138 insertions(+), 11 deletions(-) create mode 100755 test-chain.sh diff --git a/README.md b/README.md index 7642e15..f91b492 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ -localhost.daplie.com-server -=========================== +serve-https +=========== -Serves HTTPS using TLS (SSL) certs for localhost.daplie.com - great for testing and development. +Serves HTTPS using TLS (SSL) certs. + +Bundles a valid certificate for localhost.daplie.com - great for testing and development. + +Also great for testing ACME certs from letsencrypt.org. Install ------- @@ -20,7 +24,16 @@ Usage * `-p ` - i.e. `sudo serve-https -p 443` * `-d ` - i.e. `serve-https -d /tmp/` -* `-c ` - i.e. `server-https -c 'Hello, World!'` +* `-c ` - i.e. `server-https -c 'Hello, World! '` +* `--insecure-port ` - run an http server that redirects to https + +Specifying a custom HTTPS certificate: + +* `--key /path/to/privkey.pem` +* `--cert /path/to/cert.pem` +* `--chain /path/to/chain.pem` + +Note: you may specify a file containing all certificate authorities or use even `--chain` multiple times such as `--chain /path/to/intermediate-ca-1.pem --chain /path/to/intermediate-ca-2.pem` Examples -------- @@ -28,7 +41,7 @@ Examples ```bash serve-https -p 1443 -c 'Hello from 1443' & serve-https -p 2443 -c 'Hello from 2443' & -serve-https -p 3443 -d /tmp & +serve-https -p 3443 -d /tmp --insecure-port 4080 & curl https://localhost.daplie.com:1443 > Hello from 1443 @@ -39,3 +52,29 @@ curl --insecure https://localhost:2443 curl https://localhost.daplie.com:3443 > [html index listing of /tmp] ``` + +And if you tested in a browser, +it would redirect to . + +(in curl it would just show an error message) + +### Testing ACME Let's Encrypt certs + +You can get free https certificates from letsencrypt.org (ACME letsencrypt) +and even a free domain from https://freedns.afraid.org. + +```bash +serve-https -p 8443 \ + --key /etc/letsencrypt/live/test.mooo.com/privkey.pem \ + --cert /etc/letsencrypt/live/test.mooo.com/cert.pem \ + --chain /etc/letsencrypt/live/test.mooo.com/chain.pem \ + -c "$(cat '/etc/letsencrypt/live/test.mooo.com/chain.pem')" +``` + +```bash +curl --insecure https://test.mooo.com:8443 > ./chain.pem +curl https://test.mooo.com:8843 --cacert ./chain.pem +``` + +* [QuickStart Guide for Let's Encrypt](https://coolaj86.com/articles/lets-encrypt-on-raspberry-pi/) +* [QuickStart Guide for FreeDNS](https://coolaj86.com/articles/free-dns-hosting-with-freedns-afraid-org.html) diff --git a/package.json b/package.json index f62de52..d8a2d47 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "finalhandler": "^0.4.0", "localhost.daplie.com-certificates": "^1.0.2", "minimist": "^1.1.1", + "redirect-https": "^1.1.0", "serve-index": "^1.7.0", "serve-static": "^1.10.0" } diff --git a/serve.js b/serve.js index 34ab234..addebc5 100755 --- a/serve.js +++ b/serve.js @@ -2,21 +2,48 @@ 'use strict'; var https = require('https'); +var http = require('http'); +var fs = require('fs'); var path = require('path'); -function createServer(port, pubdir, content) { - var options = require('localhost.daplie.com-certificates'); - var server = https.createServer(options); +function createInsecureServer(port, pubdir, opts) { + var server = http.createServer(); + + server.on('error', function (err) { + console.error(err); + process.exit(1); + }); + + server.on('request', require('redirect-https')({ + port: port + })); + + server.listen(opts.insecurePort, function () { + var msg = 'Serving ' + pubdir + ' at http://' + opts.servername; + var p = server.address().port; + if (80 !== p) { + msg += ':' + p; + } + console.info(msg); + }); +} + +function createServer(port, pubdir, content, opts) { + var server = https.createServer(opts); var app = require('./app'); var directive = { public: pubdir, content: content }; + if (opts.insecurePort) { + createInsecureServer(port, pubdir, opts); + } + server.on('error', function (err) { console.error(err); process.exit(1); }); server.listen(port, function () { - var msg = 'Serving ' + pubdir + ' at https://localhost.daplie.com'; + var msg = 'Serving ' + pubdir + ' at https://' + opts.servername; var p = server.address().port; if (443 !== p) { msg += ':' + p; @@ -40,10 +67,52 @@ module.exports.createServer = createServer; function run() { var minimist = require('minimist'); var argv = minimist(process.argv.slice(2)); - var port = argv.p || argv._[0] || 1443; + var port = argv.p || argv.port || argv._[0] || 1443; var pubdir = path.resolve(argv.d || argv._[1] || process.cwd()); var content = argv.c; - createServer(port, pubdir, content); + + var cert = require('localhost.daplie.com-certificates'); + var opts = { + key: cert.key + , cert: cert.cert + , ca: cert.ca + , SNICallback: function (servername, cb) { + cb(null, require('tls').createSecureContext(opts)); + return; + } + }; + + if (argv.key || argv.cert || argv.chain) { + if (!argv.key || !argv.cert || !argv.chain) { + console.error("You must specify each of --key --cert and --chain (chain may be empty)"); + return; + } + + if (!Array.isArray(argv.chain)) { + argv.chain = [argv.chain]; + } + + opts.key = fs.readFileSync(argv.key); + opts.cert = fs.readFileSync(argv.cert); + // turn multiple-cert pemfile into array of cert strings + opts.ca = argv.chain.reduce(function (chain, fullpath) { + return chain.concat(fs.readFileSync(fullpath, 'ascii') + .split('-----END CERTIFICATE-----') + .filter(function (ca) { + return ca.trim(); + }).map(function (ca) { + return (ca + '-----END CERTIFICATE-----').trim(); + })); + }, []); + } + + opts.servername = 'localhost.daplie.com'; + if (argv.servername) { + opts.servername = argv.servername; + } + opts.insecurePort = argv.i || argv['insecure-port']; + + createServer(port, pubdir, content, opts); } if (require.main === module) { diff --git a/test-chain.sh b/test-chain.sh new file mode 100755 index 0000000..a5fb322 --- /dev/null +++ b/test-chain.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +node serve.js \ + --port 8443 \ + --key node_modules/localhost.daplie.com-certificates/certs/server/my-server.key.pem \ + --cert node_modules/localhost.daplie.com-certificates/certs/server/my-server.crt.pem \ + --chain node_modules/localhost.daplie.com-certificates/certs/ca/intermediate.crt.pem \ + --chain node_modules/localhost.daplie.com-certificates/certs/ca/root.crt.pem \ + -c "$(cat node_modules/localhost.daplie.com-certificates/certs/ca/root.crt.pem)" & + +PID=$! + +sleep 1 +curl -s --insecure http://localhost.daplie.com:8443 > ./root.pem +curl -s https://localhost.daplie.com:8443 --cacert ./root.pem + +rm ./root.pem +kill $PID 2>/dev/null