Nginx templates

This commit is contained in:
Jamie Curnow 2018-08-17 09:25:59 +10:00
parent 05d974267b
commit 724e89d308
15 changed files with 129 additions and 275 deletions

View File

@ -5,11 +5,11 @@
const logger = require('./logger').global; const logger = require('./logger').global;
function appStart () { function appStart () {
const migrate = require('./migrate'); const migrate = require('./migrate');
const setup = require('./setup'); const setup = require('./setup');
const app = require('./app'); const app = require('./app');
const apiValidator = require('./lib/validator/api'); const apiValidator = require('./lib/validator/api');
const internalSsl = require('./internal/ssl'); const internalCertificate = require('./internal/certificate');
return migrate.latest() return migrate.latest()
.then(() => { .then(() => {
@ -20,7 +20,7 @@ function appStart () {
}) })
.then(() => { .then(() => {
internalSsl.initTimer(); internalCertificate.initTimer();
const server = app.listen(81, () => { const server = app.listen(81, () => {
logger.info('PID ' + process.pid + ' listening on port 81 ...'); logger.info('PID ' + process.pid + ' listening on port 81 ...');

View File

@ -18,7 +18,43 @@ function omissions () {
const internalCertificate = { const internalCertificate = {
allowed_ssl_files: ['certificate', 'certificate_key', 'intermediate_certificate'], allowed_ssl_files: ['certificate', 'certificate_key', 'intermediate_certificate'],
interval_timeout: 1000 * 60 * 60 * 12, // 12 hours
interval: null,
interval_processing: false,
initTimer: () => {
logger.info('Let\'s Encrypt Renewal Timer initialized');
internalCertificate.interval = setInterval(internalCertificate.processExpiringHosts, internalCertificate.interval_timeout);
},
/**
* Triggered by a timer, this will check for expiring hosts and renew their ssl certs if required
*/
processExpiringHosts: () => {
let internalNginx = require('./nginx');
if (!internalCertificate.interval_processing) {
internalCertificate.interval_processing = true;
logger.info('Renewing SSL certs close to expiry...');
return utils.exec(certbot_command + ' renew -q ' + (debug_mode ? '--staging' : ''))
.then(result => {
logger.info(result);
internalCertificate.interval_processing = false;
return internalNginx.reload()
.then(() => {
logger.info('Renew Complete');
return result;
});
})
.catch(err => {
logger.error(err);
internalCertificate.interval_processing = false;
});
}
},
/** /**
* @param {Access} access * @param {Access} access
@ -493,7 +529,7 @@ const internalCertificate = {
* @returns {Promise} * @returns {Promise}
*/ */
requestLetsEncryptSsl: certificate => { requestLetsEncryptSsl: certificate => {
logger.info('Requesting Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); logger.info('Requesting Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
return utils.exec(certbot_command + ' certonly --cert-name "npm-' + certificate.id + '" --agree-tos ' + return utils.exec(certbot_command + ' certonly --cert-name "npm-' + certificate.id + '" --agree-tos ' +
'--email "' + certificate.meta.letsencrypt_email + '" ' + '--email "' + certificate.meta.letsencrypt_email + '" ' +
@ -511,7 +547,7 @@ const internalCertificate = {
* @returns {Promise} * @returns {Promise}
*/ */
renewLetsEncryptSsl: certificate => { renewLetsEncryptSsl: certificate => {
logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
return utils.exec(certbot_command + ' renew -n --force-renewal --disable-hook-validation --cert-name "npm-' + certificate.id + '" ' + (debug_mode ? '--staging' : '')) return utils.exec(certbot_command + ' renew -n --force-renewal --disable-hook-validation --cert-name "npm-' + certificate.id + '" ' + (debug_mode ? '--staging' : ''))
.then(result => { .then(result => {
@ -519,6 +555,16 @@ const internalCertificate = {
return result; return result;
}); });
}, },
/**
* @param {Object} certificate
* @returns {Boolean}
*/
hasLetsEncryptSslCerts: certificate => {
let le_path = '/etc/letsencrypt/live/npm-' + certificate.id;
return fs.existsSync(le_path + '/fullchain.pem') && fs.existsSync(le_path + '/privkey.pem');
}
}; };
module.exports = internalCertificate; module.exports = internalCertificate;

View File

@ -1,13 +1,13 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const fs = require('fs'); const fs = require('fs');
const Liquid = require('liquidjs'); const Liquid = require('liquidjs');
const logger = require('../logger').nginx; const logger = require('../logger').nginx;
const utils = require('../lib/utils'); const utils = require('../lib/utils');
const error = require('../lib/error'); const error = require('../lib/error');
const internalSsl = require('./ssl'); const internalCertificate = require('./certificate');
const debug_mode = process.env.NODE_ENV !== 'production'; const debug_mode = process.env.NODE_ENV !== 'production';
const internalNginx = { const internalNginx = {
@ -32,11 +32,6 @@ const internalNginx = {
// We're deleting this config regardless. // We're deleting this config regardless.
return internalNginx.deleteConfig(host_type, host); // Don't throw errors, as the file may not exist at all return internalNginx.deleteConfig(host_type, host); // Don't throw errors, as the file may not exist at all
}) })
.then(() => {
if (host.ssl && !internalSsl.hasValidSslCerts(host_type, host)) {
return internalSsl.configureSsl(host_type, host);
}
})
.then(() => { .then(() => {
return internalNginx.generateConfig(host_type, host); return internalNginx.generateConfig(host_type, host);
}) })
@ -56,7 +51,6 @@ const internalNginx = {
}); });
}) })
.catch(err => { .catch(err => {
if (debug_mode) { if (debug_mode) {
logger.error('Nginx test failed:', err.message); logger.error('Nginx test failed:', err.message);
} }

View File

@ -1,164 +0,0 @@
'use strict';
const fs = require('fs');
const Liquid = require('liquidjs');
const timestamp = require('unix-timestamp');
const internalNginx = require('./nginx');
const logger = require('../logger').ssl;
const utils = require('../lib/utils');
const error = require('../lib/error');
timestamp.round = true;
const internalSsl = {
interval_timeout: 1000 * 60 * 60 * 12, // 12 hours
interval: null,
interval_processing: false,
initTimer: () => {
logger.info('Let\'s Encrypt Renewal Timer initialized');
internalSsl.interval = setInterval(internalSsl.processExpiringHosts, internalSsl.interval_timeout);
},
/**
* Triggered by a timer, this will check for expiring hosts and renew their ssl certs if required
*/
processExpiringHosts: () => {
if (!internalSsl.interval_processing) {
logger.info('Renewing SSL certs close to expiry...');
return utils.exec('/usr/bin/certbot renew -q')
.then(result => {
logger.info(result);
internalSsl.interval_processing = false;
return internalNginx.reload()
.then(() => {
logger.info('Renew Complete');
return result;
});
})
.catch(err => {
logger.error(err);
internalSsl.interval_processing = false;
});
}
},
/**
* @param {String} host_type
* @param {Object} host
* @returns {Boolean}
*/
hasValidSslCerts: (host_type, host) => {
host_type = host_type.replace(new RegExp('-', 'g'), '_');
let le_path = '/etc/letsencrypt/live/' + host_type + '-' + host.id;
return fs.existsSync(le_path + '/fullchain.pem') && fs.existsSync(le_path + '/privkey.pem');
},
/**
* @param {String} host_type
* @param {Object} host
* @returns {Promise}
*/
requestSsl: (host_type, host) => {
logger.info('Requesting SSL certificates for ' + host_type + ' #' + host.id);
// TODO
return utils.exec('/usr/bin/letsencrypt certonly --agree-tos --email "' + host.letsencrypt_email + '" -n -a webroot -d "' + host.hostname + '"')
.then(result => {
logger.info(result);
return result;
});
},
/**
* @param {String} host_type
* @param {Object} host
* @returns {Promise}
*/
renewSsl: (host_type, host) => {
logger.info('Renewing SSL certificates for ' + host_type + ' #' + host.id);
// TODO
return utils.exec('/usr/bin/certbot renew --force-renewal --disable-hook-validation --cert-name "' + host.hostname + '"')
.then(result => {
logger.info(result);
return result;
});
},
/**
* @param {String} host_type
* @param {Object} host
* @returns {Promise}
*/
deleteCerts: (host_type, host) => {
logger.info('Deleting SSL certificates for ' + host_type + ' #' + host.id);
// TODO
return utils.exec('/usr/bin/certbot delete -n --cert-name "' + host.hostname + '"')
.then(result => {
logger.info(result);
})
.catch(err => {
logger.error(err);
});
},
/**
* @param {String} host_type
* @param {Object} host
* @returns {Promise}
*/
generateSslSetupConfig: (host_type, host) => {
host_type = host_type.replace(new RegExp('-', 'g'), '_');
let renderEngine = Liquid();
let template = null;
let filename = internalNginx.getConfigName(host_type, host);
return new Promise((resolve, reject) => {
try {
template = fs.readFileSync(__dirname + '/../templates/letsencrypt.conf', {encoding: 'utf8'});
} catch (err) {
reject(new error.ConfigurationError(err.message));
return;
}
return renderEngine
.parseAndRender(template, host)
.then(config_text => {
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
return template_data;
})
.catch(err => {
throw new error.ConfigurationError(err.message);
});
});
},
/**
* @param {String} host_type
* @param {Object} host
* @returns {Promise}
*/
configureSsl: (host_type, host) => {
// TODO
return internalSsl.generateSslSetupConfig(host)
.then(data => {
return internalNginx.reload()
.then(() => {
return internalSsl.requestSsl(data);
});
});
}
};
module.exports = internalSsl;

View File

@ -0,0 +1,4 @@
{% if caching_enabled == 1 or caching_enabled == true -%}
# Asset Caching
include conf.d/include/assets.conf;
{%- endif %}

View File

@ -0,0 +1,12 @@
{%- if certificate and certificate_id > 0 -%}
{%- if certificate.provider == "letsencrypt" %}
# Let's Encrypt SSL
include conf.d/include/letsencrypt-acme-challenge.conf;
include conf.d/include/ssl-ciphers.conf;
ssl_certificate /etc/letsencrypt/live/npm-{{ certificate.id }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/npm-{{ certificate.id }}/privkey.pem;
{%- endif -%}
# TODO: Custom SSL paths
{%- endif %}

View File

@ -0,0 +1,4 @@
{% if block_exploits == 1 or block_exploits == true -%}
# Block Exploits
include conf.d/include/block-exploits.conf;
{%- endif -%}

View File

@ -0,0 +1,6 @@
{%- if certificate and certificate_id > 0 -%}
{%- if ssl_forced == 1 or ssl_forced == true -%}
# Force SSL
include conf.d/include/force-ssl.conf;
{%- endif -%}
{%- endif %}

View File

@ -0,0 +1,3 @@
# ------------------------------------------------------------
# {{ domain_names | join: ", " }}
# ------------------------------------------------------------

View File

@ -0,0 +1,5 @@
listen 80;
{%- if certificate -%}
listen 443 ssl;
{%- endif %}
server_name {{ domain_names | join: " " }};

View File

@ -1,21 +1,10 @@
# {{ domain_names | join: ", " }} {% include "_header_comment.conf" %}
server {
listen 80;
{%- if ssl_enabled == 1 or ssl_enabled == true -%}
listen 443 ssl;
{%- endif %}
server_name {{ domain_names | join: " " }};
access_log /data/logs/proxy_host-{{ id }}.log proxy;
{%- if ssl_enabled == 1 or ssl_enabled == true -%} server {
{%- if ssl_provider == "letsencrypt" %} {% include "_listen.conf" %}
# Let's Encrypt SSL {% include "_certificates.conf" %}
include conf.d/include/letsencrypt-acme-challenge.conf;
include conf.d/include/ssl-ciphers.conf; access_log /data/logs/dead_host-{{ id }}.log proxy;
ssl_certificate /etc/letsencrypt/live/proxy_host-{{ id }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/proxy_host-{{ id }}/privkey.pem;
{%- endif -%}
{%- endif %}
# TODO: Advanced config options # TODO: Advanced config options

View File

@ -1,10 +0,0 @@
# Letsencrypt Verification Temporary Host: {{ domain_names | join: ", " }}
server {
listen 80;
server_name {{ domain_names | join: " " }};
access_log /data/logs/letsencrypt.log proxy;
location / {
root /data/letsencrypt-acme-challenge;
}
}

View File

@ -1,33 +1,15 @@
# {{ domain_names | join: ", " }} {% include "_header_comment.conf" %}
server {
listen 80;
{%- if ssl_enabled == 1 or ssl_enabled == true -%}
listen 443 ssl;
{%- endif %}
server_name {{ domain_names | join: " " }};
access_log /data/logs/proxy_host-{{ id }}.log proxy;
server {
set $server {{ forward_ip }}; set $server {{ forward_ip }};
set $port {{ forward_port }}; set $port {{ forward_port }};
{% if caching_enabled == 1 or caching_enabled == true -%} {% include "_listen.conf" %}
# Asset Caching {% include "_certificates.conf" %}
include conf.d/include/assets.conf; {% include "_assets.conf" %}
{%- endif %} {% include "_exploits.conf" %}
{% if block_exploits == 1 or block_exploits == true -%}
# Block Exploits
include conf.d/include/block-exploits.conf;
{%- endif -%}
{%- if ssl_enabled == 1 or ssl_enabled == true -%} access_log /data/logs/proxy_host-{{ id }}.log proxy;
{%- if ssl_provider == "letsencrypt" %}
# Let's Encrypt SSL
include conf.d/include/letsencrypt-acme-challenge.conf;
include conf.d/include/ssl-ciphers.conf;
ssl_certificate /etc/letsencrypt/live/proxy_host-{{ id }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/proxy_host-{{ id }}/privkey.pem;
{%- endif -%}
{%- endif %}
# TODO: Advanced config options # TODO: Advanced config options
@ -38,12 +20,7 @@ server {
auth_basic_user_file /config/access/{{ access_list_id }}; auth_basic_user_file /config/access/{{ access_list_id }};
{%- endif %} {%- endif %}
{%- if ssl_enabled == 1 or ssl_enabled == true -%} {% include "_forced_ssl.conf" %}
{%- if ssl_forced == 1 or ssl_forced == true -%}
# Force SSL
include conf.d/include/force-ssl.conf;
{%- endif -%}
{%- endif %}
# Proxy! # Proxy!
include conf.d/include/proxy.conf; include conf.d/include/proxy.conf;

View File

@ -1,34 +1,20 @@
# {{ domain_names | join: ", " }} {% include "_header_comment.conf" %}
server { server {
listen 80; {% include "_listen.conf" %}
{%- if ssl_enabled == 1 or ssl_enabled == true -%} {% include "_certificates.conf" %}
listen 443 ssl; {% include "_assets.conf" %}
{%- endif %} {% include "_exploits.conf" %}
server_name {{ domain_names | join: " " }};
access_log /data/logs/proxy_host-{{ id }}.log proxy;
{%- if caching_enabled == 1 or caching_enabled == true %} access_log /data/logs/redirection_host-{{ id }}.log proxy;
# Asset Caching
include conf.d/include/assets.conf;
{%- endif %}
{%- if block_exploits == 1 or block_exploits == true %}
# Block Exploits
include conf.d/include/block-exploits.conf;
{%- endif -%}
{%- if ssl_enabled == 1 or ssl_enabled == true -%}
{%- if ssl_provider == "letsencrypt" %}
# Let's Encrypt SSL
include conf.d/include/letsencrypt-acme-challenge.conf;
include conf.d/include/ssl-ciphers.conf;
ssl_certificate /etc/letsencrypt/live/proxy_host-{{ id }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/proxy_host-{{ id }}/privkey.pem;
{%- endif -%}
{%- endif %}
# TODO: Advanced config options # TODO: Advanced config options
# TODO: Preserve Path Option # TODO: Preserve Path Option
return 301 $scheme://{{ forward_domain_name }}$request_uri; location / {
{% include "_forced_ssl.conf" %}
return 301 $scheme://{{ forward_domain_name }}$request_uri;
}
} }

View File

@ -1,14 +1,16 @@
# ------------------------------------------------------------
# {{ incoming_port }} TCP: {{ tcp_forwarding }} UDP: {{ udp_forwarding }} # {{ incoming_port }} TCP: {{ tcp_forwarding }} UDP: {{ udp_forwarding }}
# ------------------------------------------------------------
{% if tcp_forwarding == 1 or tcp_forwarding == true -%} {% if tcp_forwarding == 1 or tcp_forwarding == true -%}
server { server {
listen {{ incoming_port }}; listen {{ incoming_port }};
proxy_pass {{ forward_ip }}:{{ forwarding_port }}; proxy_pass {{ forward_ip }}:{{ forwarding_port }};
} }
{% endif %} {% endif %}
{% if udp_forwarding == 1 or udp_forwarding == true %} {% if udp_forwarding == 1 or udp_forwarding == true %}
server { server {
listen {{ incoming_port }} udp; listen {{ incoming_port }} udp;
proxy_pass {{ forward_ip }}:{{ forwarding_port }}; proxy_pass {{ forward_ip }}:{{ forwarding_port }};
} }
{% endif %} {% endif %}