From 12058026adbd080d13e7a8ded028b37e1ed3a840 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sat, 30 Mar 2019 01:40:26 -0600 Subject: [PATCH] v2.6.9: fix and update vhost example --- examples/vhost.js | 118 ++++++++++++++++++++++------------------------ package.json | 15 ++---- 2 files changed, 61 insertions(+), 72 deletions(-) diff --git a/examples/vhost.js b/examples/vhost.js index 977b763..92b91a6 100644 --- a/examples/vhost.js +++ b/examples/vhost.js @@ -11,22 +11,22 @@ // The prefix where sites go by name. // For example: whatever.com may live in /srv/www/whatever.com, thus /srv/www is our path -var srv = '/srv/www/'; +var srv = process.argv[3] || '/srv/www/'; var path = require('path'); -var fs = require('fs'); +var fs = require('fs').promises; var finalhandler = require('finalhandler'); var serveStatic = require('serve-static'); //var glx = require('greenlock-express') -var glx = require('../').create({ +var glx = require('./').create({ version: 'draft-11' // Let's Encrypt v2 is ACME draft 11 , server: 'https://acme-v02.api.letsencrypt.org/directory' // If at first you don't succeed, stop and switch to staging // https://acme-staging-v02.api.letsencrypt.org/directory -, configDir: '~/.config/acme/' // You MUST have access to write to directory where certs +, configDir: process.argv[4] || '~/.config/acme/' // You MUST have access to write to directory where certs // are saved. ex: /home/foouser/.config/acme , approveDomains: myApproveDomains // Greenlock's wraps around tls.SNICallback. Check the @@ -35,9 +35,9 @@ var glx = require('../').create({ , app: myVhostApp // Any node-style http app (i.e. express, koa, hapi, rill) /* CHANGE TO A VALID EMAIL */ -, email:'jon@example.com' // Email for Let's Encrypt account and Greenlock Security +, email: process.argv[2] || 'jon.doe@example.com' // Email for Let's Encrypt account and Greenlock Security , agreeTos: true // Accept Let's Encrypt ToS -, communityMember: true // Join Greenlock to get important updates, no spam +//, communityMember: true // Join Greenlock to get important updates, no spam //, debug: true @@ -48,67 +48,61 @@ server.on('listening', function () { console.info(server.type + " listening on", server.address()); }); -// [SECURITY] -// Since v2.4.0+ Greenlock proactively protects against -// SQL injection and timing attacks by rejecting invalid domain names, -// but it's up to you to make sure that you accept opts.domain BEFORE -// an attempt is made to issue a certificate for it. function myApproveDomains(opts, certs, cb) { - + console.log(opts.domains); // In this example the filesystem is our "database". - // We check in /srv/www/ for opts.domain (i.e. "example.com") and only proceed if it exists. - console.log(opts.domain); + // We check in /srv/www for whatever.com and if it exists, it's allowed - // Check that the hosting domain exists on the file system. - var hostdir = path.join(srv, opts.domain); - fs.readdir(hostdir, function (err, nodes) { - var e; - if (err || !nodes) { - e = new Error("rejecting '" + opts.domains[0] + "' because '" + hostdir + "' could not be read"); - console.error(e); - console.error(err); - cb(e); - return; - } + // SECURITY Greenlock validates opts.domains ahead-of-time so you don't have to + return checkWwws(opts.domains[0]).then(function () { + //opts.email = email; + opts.agreeTos = true; + cb(null, { options: opts, certs: certs }); + }).catch(cb); +} - // You could put a variety of configuration details alongside the vhost folder - // For example, /srv/www/example.com.json could describe the following: - - // If you have multiple domains grouped together, you can list them on the same certificate - // opts.domains = [ 'example.com', 'www.example.com', 'api.example.com', 'sso.example.com' ] - - // You can also change other options on-the-fly - // (approveDomains is called after the in-memory certificates cache is checked, but before any ACME requests) - - // opts.email = "jon@example.com" - // opts.agreeTos = true; +function checkWwws(_hostname) { + var hostname = _hostname; + var hostdir = path.join(srv, hostname); + // TODO could test for www/no-www both in directory + return fs.readdir(hostdir).then(function () { + // TODO check for some sort of htaccess.json and use email in that + // NOTE: you can also change other options such as `challengeType` and `challenge` // opts.challengeType = 'http-01'; // opts.challenge = require('le-challenge-fs').create({}); - cb(null, { options: opts, certs: certs }); - }); - -} - -// [SECURITY] -// Since v2.4.0+ Greenlock Express will proactively protect against -// SQL injection and timing attacks by rejecting invalid domain names -// in Host headers. -// It will also make them lowercase and protect against "domain fronting". -// However, it's up to you to make sure you actually have a domain to serve :) -var servers = {}; -function myVhostApp(req, res) { - var hostname = req.headers.host; - var srvpath = path.join(srv, hostname); - console.log('vhost for', req.headers.host); - - if (!servers[hostname]) { - try { - fs.accessSync(srvpath); - servers[hostname] = serveStatic(srvpath, { redirect: true }); - } catch(e) { - finalhandler(req, res); + return hostname; + }).catch(function () { + if ('www.' === hostname.slice(0, 4)) { + // Assume we'll redirect to non-www if it's available. + hostname = hostname.slice(4); + hostdir = path.join(srv, hostname); + return fs.readdir(hostdir).then(function () { + return hostname; + }); + } else { + // Or check and see if perhaps we should redirect non-www to www + hostname = 'www.' + hostname; + hostdir = path.join(srv, hostname); + return fs.readdir(hostdir).then(function () { + return hostname; + }); } - } - - servers[hostname](req, res, finalhandler(req, res)); + }).catch(function () { + throw new Error("rejecting '" + _hostname + "' because '" + hostdir + "' could not be read"); + }); +} + +function myVhostApp(req, res) { + // SECURITY greenlock pre-sanitizes hostnames to prevent unauthorized fs access so you don't have to + // (also: only domains approved above will get here) + console.log(req.headers.host); + + // We could cache wether or not a host exists for some amount of time + var fin = finalhandler(req, res); + return checkWwws(req.headers.host).then(function (hostname) { + var serve = serveStatic(path.join(srv, hostname), { redirect: true }); + serve(req, res, fin); + }).catch(function () { + fin(); + }); } diff --git a/package.json b/package.json index eafcb97..82ef22b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "greenlock-express", - "version": "2.6.8", + "version": "2.6.9", "description": "Free SSL and managed or automatic HTTPS for node.js with Express, Koa, Connect, Hapi, and all other middleware systems.", "main": "index.js", "homepage": "https://git.coolaj86.com/coolaj86/greenlock-express.js", @@ -36,19 +36,14 @@ "url": "https://git.coolaj86.com/coolaj86/greenlock-express.js.git" }, "keywords": [ - "acme", - "cloud", - "cluster", "free", + "ssl", + "letsencrypt", + "acme", "greenlock", "https", + "cloud", "le", - "letsencrypt", - "multi-core", - "node", - "node.js", - "scale", - "ssl", "tls" ], "author": "AJ ONeal (https://coolaj86.com/)",