diff --git a/src/backend/index.js b/src/backend/index.js index 5a29476..df3d63b 100644 --- a/src/backend/index.js +++ b/src/backend/index.js @@ -5,11 +5,11 @@ const logger = require('./logger').global; function appStart () { - const migrate = require('./migrate'); - const setup = require('./setup'); - const app = require('./app'); - const apiValidator = require('./lib/validator/api'); - const internalSsl = require('./internal/ssl'); + const migrate = require('./migrate'); + const setup = require('./setup'); + const app = require('./app'); + const apiValidator = require('./lib/validator/api'); + const internalCertificate = require('./internal/certificate'); return migrate.latest() .then(() => { @@ -20,7 +20,7 @@ function appStart () { }) .then(() => { - internalSsl.initTimer(); + internalCertificate.initTimer(); const server = app.listen(81, () => { logger.info('PID ' + process.pid + ' listening on port 81 ...'); diff --git a/src/backend/internal/certificate.js b/src/backend/internal/certificate.js index 70cc990..034a6c4 100644 --- a/src/backend/internal/certificate.js +++ b/src/backend/internal/certificate.js @@ -18,7 +18,43 @@ function omissions () { 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 @@ -493,7 +529,7 @@ const internalCertificate = { * @returns {Promise} */ 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 ' + '--email "' + certificate.meta.letsencrypt_email + '" ' + @@ -511,7 +547,7 @@ const internalCertificate = { * @returns {Promise} */ 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' : '')) .then(result => { @@ -519,6 +555,16 @@ const internalCertificate = { 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; diff --git a/src/backend/internal/nginx.js b/src/backend/internal/nginx.js index c020958..6fce499 100644 --- a/src/backend/internal/nginx.js +++ b/src/backend/internal/nginx.js @@ -1,13 +1,13 @@ 'use strict'; -const _ = require('lodash'); -const fs = require('fs'); -const Liquid = require('liquidjs'); -const logger = require('../logger').nginx; -const utils = require('../lib/utils'); -const error = require('../lib/error'); -const internalSsl = require('./ssl'); -const debug_mode = process.env.NODE_ENV !== 'production'; +const _ = require('lodash'); +const fs = require('fs'); +const Liquid = require('liquidjs'); +const logger = require('../logger').nginx; +const utils = require('../lib/utils'); +const error = require('../lib/error'); +const internalCertificate = require('./certificate'); +const debug_mode = process.env.NODE_ENV !== 'production'; const internalNginx = { @@ -32,11 +32,6 @@ const internalNginx = { // We're deleting this config regardless. 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(() => { return internalNginx.generateConfig(host_type, host); }) @@ -56,7 +51,6 @@ const internalNginx = { }); }) .catch(err => { - if (debug_mode) { logger.error('Nginx test failed:', err.message); } diff --git a/src/backend/internal/ssl.js b/src/backend/internal/ssl.js deleted file mode 100644 index 714feff..0000000 --- a/src/backend/internal/ssl.js +++ /dev/null @@ -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; diff --git a/src/backend/templates/_assets.conf b/src/backend/templates/_assets.conf new file mode 100644 index 0000000..fb86fc2 --- /dev/null +++ b/src/backend/templates/_assets.conf @@ -0,0 +1,4 @@ +{% if caching_enabled == 1 or caching_enabled == true -%} + # Asset Caching + include conf.d/include/assets.conf; +{%- endif %} diff --git a/src/backend/templates/_certificates.conf b/src/backend/templates/_certificates.conf new file mode 100644 index 0000000..832ac4a --- /dev/null +++ b/src/backend/templates/_certificates.conf @@ -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 %} diff --git a/src/backend/templates/_exploits.conf b/src/backend/templates/_exploits.conf new file mode 100644 index 0000000..c46f391 --- /dev/null +++ b/src/backend/templates/_exploits.conf @@ -0,0 +1,4 @@ +{% if block_exploits == 1 or block_exploits == true -%} + # Block Exploits + include conf.d/include/block-exploits.conf; +{%- endif -%} diff --git a/src/backend/templates/_forced_ssl.conf b/src/backend/templates/_forced_ssl.conf new file mode 100644 index 0000000..467faf3 --- /dev/null +++ b/src/backend/templates/_forced_ssl.conf @@ -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 %} diff --git a/src/backend/templates/_header_comment.conf b/src/backend/templates/_header_comment.conf new file mode 100644 index 0000000..481d0b7 --- /dev/null +++ b/src/backend/templates/_header_comment.conf @@ -0,0 +1,3 @@ +# ------------------------------------------------------------ +# {{ domain_names | join: ", " }} +# ------------------------------------------------------------ diff --git a/src/backend/templates/_listen.conf b/src/backend/templates/_listen.conf new file mode 100644 index 0000000..d6861ed --- /dev/null +++ b/src/backend/templates/_listen.conf @@ -0,0 +1,5 @@ + listen 80; +{%- if certificate -%} + listen 443 ssl; +{%- endif %} + server_name {{ domain_names | join: " " }}; diff --git a/src/backend/templates/dead_host.conf b/src/backend/templates/dead_host.conf index 102b757..6ddc846 100644 --- a/src/backend/templates/dead_host.conf +++ b/src/backend/templates/dead_host.conf @@ -1,21 +1,10 @@ -# {{ domain_names | join: ", " }} -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; +{% include "_header_comment.conf" %} - {%- 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 %} +server { + {% include "_listen.conf" %} + {% include "_certificates.conf" %} + + access_log /data/logs/dead_host-{{ id }}.log proxy; # TODO: Advanced config options diff --git a/src/backend/templates/letsencrypt.conf b/src/backend/templates/letsencrypt.conf deleted file mode 100644 index 0434c5b..0000000 --- a/src/backend/templates/letsencrypt.conf +++ /dev/null @@ -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; - } -} diff --git a/src/backend/templates/proxy_host.conf b/src/backend/templates/proxy_host.conf index 297ddc1..cb252fe 100644 --- a/src/backend/templates/proxy_host.conf +++ b/src/backend/templates/proxy_host.conf @@ -1,33 +1,15 @@ -# {{ domain_names | join: ", " }} -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; +{% include "_header_comment.conf" %} +server { set $server {{ forward_ip }}; set $port {{ forward_port }}; - {% if caching_enabled == 1 or caching_enabled == true -%} - # 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 -%} + {% include "_listen.conf" %} + {% include "_certificates.conf" %} + {% include "_assets.conf" %} + {% include "_exploits.conf" %} - {%- 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 %} + access_log /data/logs/proxy_host-{{ id }}.log proxy; # TODO: Advanced config options @@ -38,12 +20,7 @@ server { auth_basic_user_file /config/access/{{ access_list_id }}; {%- endif %} - {%- if ssl_enabled == 1 or ssl_enabled == true -%} - {%- if ssl_forced == 1 or ssl_forced == true -%} - # Force SSL - include conf.d/include/force-ssl.conf; - {%- endif -%} - {%- endif %} + {% include "_forced_ssl.conf" %} # Proxy! include conf.d/include/proxy.conf; diff --git a/src/backend/templates/redirection_host.conf b/src/backend/templates/redirection_host.conf index 440be91..100a886 100644 --- a/src/backend/templates/redirection_host.conf +++ b/src/backend/templates/redirection_host.conf @@ -1,34 +1,20 @@ -# {{ 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; + {% include "_listen.conf" %} + {% include "_certificates.conf" %} + {% include "_assets.conf" %} + {% include "_exploits.conf" %} - {%- if caching_enabled == 1 or caching_enabled == true %} - # 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 %} + access_log /data/logs/redirection_host-{{ id }}.log proxy; # TODO: Advanced config options # 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; + } } diff --git a/src/backend/templates/stream.conf b/src/backend/templates/stream.conf index 4a0c033..9bcd76d 100644 --- a/src/backend/templates/stream.conf +++ b/src/backend/templates/stream.conf @@ -1,14 +1,16 @@ +# ------------------------------------------------------------ # {{ incoming_port }} TCP: {{ tcp_forwarding }} UDP: {{ udp_forwarding }} +# ------------------------------------------------------------ {% if tcp_forwarding == 1 or tcp_forwarding == true -%} server { - listen {{ incoming_port }}; - proxy_pass {{ forward_ip }}:{{ forwarding_port }}; + listen {{ incoming_port }}; + proxy_pass {{ forward_ip }}:{{ forwarding_port }}; } {% endif %} {% if udp_forwarding == 1 or udp_forwarding == true %} server { - listen {{ incoming_port }} udp; - proxy_pass {{ forward_ip }}:{{ forwarding_port }}; + listen {{ incoming_port }} udp; + proxy_pass {{ forward_ip }}:{{ forwarding_port }}; } {% endif %}