diff --git a/rootfs/etc/nginx/nginx.conf b/rootfs/etc/nginx/nginx.conf index 8dddd30..101172b 100644 --- a/rootfs/etc/nginx/nginx.conf +++ b/rootfs/etc/nginx/nginx.conf @@ -54,6 +54,7 @@ http { include /data/nginx/proxy_host/*.conf; include /data/nginx/redirection_host/*.conf; include /data/nginx/dead_host/*.conf; + include /data/nginx/temp/*.conf; } stream { diff --git a/rootfs/etc/services.d/nginx/run b/rootfs/etc/services.d/nginx/run index 20e756f..c85e1a2 100755 --- a/rootfs/etc/services.d/nginx/run +++ b/rootfs/etc/services.d/nginx/run @@ -3,7 +3,7 @@ mkdir -p /tmp/nginx/body \ /var/log/nginx \ /data/{nginx,logs,access} \ - /data/nginx/{proxy_host,redirection_host,stream,dead_host} \ + /data/nginx/{proxy_host,redirection_host,stream,dead_host,temp} \ /var/lib/nginx/cache/{public,private} touch /var/log/nginx/error.log && chmod 777 /var/log/nginx/error.log diff --git a/src/backend/internal/certificate.js b/src/backend/internal/certificate.js index 034a6c4..051f559 100644 --- a/src/backend/internal/certificate.js +++ b/src/backend/internal/certificate.js @@ -10,6 +10,8 @@ const tempWrite = require('temp-write'); const utils = require('../lib/utils'); const moment = require('moment'); const debug_mode = process.env.NODE_ENV !== 'production'; +const internalNginx = require('./nginx'); +const internalHost = require('./host'); const certbot_command = '/usr/bin/certbot'; function omissions () { @@ -32,8 +34,6 @@ const internalCertificate = { * 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...'); @@ -75,18 +75,94 @@ const internalCertificate = { .omit(omissions()) .insertAndFetch(data); }) - .then(row => { - data.meta = _.assign({}, data.meta || {}, row.meta); + .then(certificate => { + if (certificate.provider === 'letsencrypt') { + // Request a new Cert from LE. Let the fun begin. + + // 1. Find out any hosts that are using any of the hostnames in this cert + // 2. Disable them in nginx temporarily + // 3. Generate the LE config + // 4. Request cert + // 5. Remove LE config + // 6. Re-instate previously disabled hosts + + // 1. Find out any hosts that are using any of the hostnames in this cert + return internalHost.getHostsWithDomains(certificate.domain_names) + .then(in_use_result => { + // 2. Disable them in nginx temporarily + return internalCertificate.disableInUseHosts(in_use_result) + .then(() => { + return in_use_result; + }); + }) + .then(in_use_result => { + // 3. Generate the LE config + return internalNginx.generateLetsEncryptRequestConfig(certificate) + .then(internalNginx.reload) + .then(() => { + // 4. Request cert + return internalCertificate.requestLetsEncryptSsl(certificate); + }) + .then(() => { + // 5. Remove LE config + return internalNginx.deleteLetsEncryptRequestConfig(certificate); + }) + .then(internalNginx.reload) + .then(() => { + // 6. Re-instate previously disabled hosts + return internalCertificate.enableInUseHosts(in_use_result); + }) + .then(() => { + return certificate; + }) + .catch(err => { + // In the event of failure, revert things and throw err back + return internalNginx.deleteLetsEncryptRequestConfig(certificate) + .then(() => { + return internalCertificate.enableInUseHosts(in_use_result); + }) + .then(internalNginx.reload) + .then(() => { + throw err; + }); + }); + }) + .then(() => { + // At this point, the letsencrypt cert should exist on disk. + // Lets get the expiry date from the file and update the row silently + return internalCertificate.getCertificateInfoFromFile('/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem') + .then(cert_info => { + return certificateModel + .query() + .patchAndFetchById(certificate.id, { + expires_on: certificateModel.raw('FROM_UNIXTIME(' + cert_info.dates.to + ')') + }) + .then(saved_row => { + // Add cert data for audit log + saved_row.meta = _.assign({}, saved_row.meta, { + letsencrypt_certificate: cert_info + }); + + return saved_row; + }); + }); + }); + } else { + return certificate; + } + }).then(certificate => { + + data.meta = _.assign({}, data.meta || {}, certificate.meta); // Add to audit log return internalAuditLog.add(access, { action: 'created', object_type: 'certificate', - object_id: row.id, + object_id: certificate.id, meta: data }) .then(() => { - return row; + return certificate; }); }); }, @@ -114,7 +190,6 @@ const internalCertificate = { .query() .omit(omissions()) .patchAndFetchById(row.id, data) - .debug() .then(saved_row => { saved_row.meta = internalCertificate.cleanMeta(saved_row.meta); data.meta = internalCertificate.cleanMeta(data.meta); @@ -221,6 +296,12 @@ const internalCertificate = { object_id: row.id, meta: _.omit(row, omissions()) }); + }) + .then(() => { + if (row.provider === 'letsencrypt') { + // Revoke the cert + return internalCertificate.revokeLetsEncryptSsl(row); + } }); }) .then(() => { @@ -229,7 +310,7 @@ const internalCertificate = { }, /** - * All Lists + * All Certs * * @param {Access} access * @param {Array} [expand] @@ -381,6 +462,7 @@ const internalCertificate = { } }); + // TODO: This uses a mysql only raw function that won't translate to postgres return internalCertificate.update(access, { id: data.id, expires_on: certificateModel.raw('FROM_UNIXTIME(' + validations.certificate.dates.to + ')'), @@ -428,82 +510,97 @@ const internalCertificate = { getCertificateInfo: (certificate, throw_expired) => { return tempWrite(certificate, '/tmp') .then(filepath => { - let cert_data = {}; - - return utils.exec('openssl x509 -in ' + filepath + ' -subject -noout') - .then(result => { - // subject=CN = something.example.com - let regex = /(?:subject=)?[^=]+=\s+(\S+)/gim; - let match = regex.exec(result); - - if (typeof match[1] === 'undefined') { - throw new error.ValidationError('Could not determine subject from certificate: ' + result); - } - - cert_data['cn'] = match[1]; - }) - .then(() => { - return utils.exec('openssl x509 -in ' + filepath + ' -issuer -noout'); - }) - .then(result => { - // issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 - let regex = /^(?:issuer=)?(.*)$/gim; - let match = regex.exec(result); - - if (typeof match[1] === 'undefined') { - throw new error.ValidationError('Could not determine issuer from certificate: ' + result); - } - - cert_data['issuer'] = match[1]; - }) - .then(() => { - return utils.exec('openssl x509 -in ' + filepath + ' -dates -noout'); - }) - .then(result => { - // notBefore=Jul 14 04:04:29 2018 GMT - // notAfter=Oct 12 04:04:29 2018 GMT - let valid_from = null; - let valid_to = null; - - let lines = result.split('\n'); - lines.map(function (str) { - let regex = /^(\S+)=(.*)$/gim; - let match = regex.exec(str.trim()); - - if (match && typeof match[2] !== 'undefined') { - let date = parseInt(moment(match[2], 'MMM DD HH:mm:ss YYYY z').format('X'), 10); - - if (match[1].toLowerCase() === 'notbefore') { - valid_from = date; - } else if (match[1].toLowerCase() === 'notafter') { - valid_to = date; - } - } - }); - - if (!valid_from || !valid_to) { - throw new error.ValidationError('Could not determine dates from certificate: ' + result); - } - - if (throw_expired && valid_to < parseInt(moment().format('X'), 10)) { - throw new error.ValidationError('Certificate has expired'); - } - - cert_data['dates'] = { - from: valid_from, - to: valid_to - }; - }) - .then(() => { + return internalCertificate.getCertificateInfoFromFile(filepath, throw_expired) + .then(cert_data => { fs.unlinkSync(filepath); return cert_data; }).catch(err => { fs.unlinkSync(filepath); - throw new error.ValidationError('Certificate is not valid (' + err.message + ')', err); + throw err; }); }); }, + /** + * Uses the openssl command to both validate and get info out of the certificate. + * It will save the file to disk first, then run commands on it, then delete the file. + * + * @param {String} certificate_file The file location on disk + * @param {Boolean} [throw_expired] Throw when the certificate is out of date + */ + getCertificateInfoFromFile: (certificate_file, throw_expired) => { + let cert_data = {}; + + return utils.exec('openssl x509 -in ' + certificate_file + ' -subject -noout') + .then(result => { + // subject=CN = something.example.com + let regex = /(?:subject=)?[^=]+=\s+(\S+)/gim; + let match = regex.exec(result); + + if (typeof match[1] === 'undefined') { + throw new error.ValidationError('Could not determine subject from certificate: ' + result); + } + + cert_data['cn'] = match[1]; + }) + .then(() => { + return utils.exec('openssl x509 -in ' + certificate_file + ' -issuer -noout'); + }) + .then(result => { + // issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 + let regex = /^(?:issuer=)?(.*)$/gim; + let match = regex.exec(result); + + if (typeof match[1] === 'undefined') { + throw new error.ValidationError('Could not determine issuer from certificate: ' + result); + } + + cert_data['issuer'] = match[1]; + }) + .then(() => { + return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout'); + }) + .then(result => { + // notBefore=Jul 14 04:04:29 2018 GMT + // notAfter=Oct 12 04:04:29 2018 GMT + let valid_from = null; + let valid_to = null; + + let lines = result.split('\n'); + lines.map(function (str) { + let regex = /^(\S+)=(.*)$/gim; + let match = regex.exec(str.trim()); + + if (match && typeof match[2] !== 'undefined') { + let date = parseInt(moment(match[2], 'MMM DD HH:mm:ss YYYY z').format('X'), 10); + + if (match[1].toLowerCase() === 'notbefore') { + valid_from = date; + } else if (match[1].toLowerCase() === 'notafter') { + valid_to = date; + } + } + }); + + if (!valid_from || !valid_to) { + throw new error.ValidationError('Could not determine dates from certificate: ' + result); + } + + if (throw_expired && valid_to < parseInt(moment().format('X'), 10)) { + throw new error.ValidationError('Certificate has expired'); + } + + cert_data['dates'] = { + from: valid_from, + to: valid_to + }; + + return cert_data; + }).catch(err => { + throw new error.ValidationError('Certificate is not valid (' + err.message + ')', err); + }); + }, + /** * Cleans the ssl keys from the meta object and sets them to "true" * @@ -521,6 +618,7 @@ const internalCertificate = { } } }); + return meta; }, @@ -531,13 +629,19 @@ const internalCertificate = { requestLetsEncryptSsl: certificate => { 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 ' + + let cmd = certbot_command + ' certonly --cert-name "npm-' + certificate.id + '" --agree-tos ' + '--email "' + certificate.meta.letsencrypt_email + '" ' + '--preferred-challenges "http" ' + '-n -a webroot -d "' + certificate.domain_names.join(',') + '" ' + - (debug_mode ? '--staging' : '')) + (debug_mode ? '--staging' : ''); + + if (debug_mode) { + logger.info('Command:', cmd); + } + + return utils.exec(cmd) .then(result => { - logger.info(result); + logger.success(result); return result; }); }, @@ -549,13 +653,49 @@ const internalCertificate = { renewLetsEncryptSsl: certificate => { 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' : '')) + let cmd = certbot_command + ' renew -n --force-renewal --disable-hook-validation --cert-name "npm-' + certificate.id + '" ' + (debug_mode ? '--staging' : ''); + + if (debug_mode) { + logger.info('Command:', cmd); + } + + return utils.exec(cmd) .then(result => { logger.info(result); return result; }); }, + /** + * @param {Object} certificate the certificate row + * @param {Boolean} [throw_errors] + * @returns {Promise} + */ + revokeLetsEncryptSsl: (certificate, throw_errors) => { + logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); + + let cmd = certbot_command + ' revoke --cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' + (debug_mode ? '--staging' : ''); + + if (debug_mode) { + logger.info('Command:', cmd); + } + + return utils.exec(cmd) + .then(result => { + logger.info(result); + return result; + }) + .catch(err => { + if (debug_mode) { + logger.error(err.message); + } + + if (throw_errors) { + throw err; + } + }); + }, + /** * @param {Object} certificate * @returns {Boolean} @@ -564,6 +704,66 @@ const internalCertificate = { let le_path = '/etc/letsencrypt/live/npm-' + certificate.id; return fs.existsSync(le_path + '/fullchain.pem') && fs.existsSync(le_path + '/privkey.pem'); + }, + + /** + * @param {Object} in_use_result + * @param {Integer} in_use_result.total_count + * @param {Array} in_use_result.proxy_hosts + * @param {Array} in_use_result.redirection_hosts + * @param {Array} in_use_result.dead_hosts + */ + disableInUseHosts: in_use_result => { + if (in_use_result.total_count) { + let promises = []; + + if (in_use_result.proxy_hosts.length) { + promises.push(internalNginx.bulkDeleteConfigs('proxy_host', in_use_result.proxy_hosts)); + } + + if (in_use_result.redirection_hosts.length) { + promises.push(internalNginx.bulkDeleteConfigs('redirection_host', in_use_result.redirection_hosts)); + } + + if (in_use_result.dead_hosts.length) { + promises.push(internalNginx.bulkDeleteConfigs('dead_host', in_use_result.dead_hosts)); + } + + return Promise.all(promises); + + } else { + return Promise.resolve(); + } + }, + + /** + * @param {Object} in_use_result + * @param {Integer} in_use_result.total_count + * @param {Array} in_use_result.proxy_hosts + * @param {Array} in_use_result.redirection_hosts + * @param {Array} in_use_result.dead_hosts + */ + enableInUseHosts: in_use_result => { + if (in_use_result.total_count) { + let promises = []; + + if (in_use_result.proxy_hosts.length) { + promises.push(internalNginx.bulkGenerateConfigs('proxy_host', in_use_result.proxy_hosts)); + } + + if (in_use_result.redirection_hosts.length) { + promises.push(internalNginx.bulkGenerateConfigs('redirection_host', in_use_result.redirection_hosts)); + } + + if (in_use_result.dead_hosts.length) { + promises.push(internalNginx.bulkGenerateConfigs('dead_host', in_use_result.dead_hosts)); + } + + return Promise.all(promises); + + } else { + return Promise.resolve(); + } } }; diff --git a/src/backend/internal/host.js b/src/backend/internal/host.js index 7d6c157..4b56003 100644 --- a/src/backend/internal/host.js +++ b/src/backend/internal/host.js @@ -6,6 +6,57 @@ const deadHostModel = require('../models/dead_host'); const internalHost = { + /** + * This returns all the host types with any domain listed in the provided domain_names array. + * This is used by the certificates to temporarily disable any host that is using the domain + * + * @param {Array} domain_names + * @returns {Promise} + */ + getHostsWithDomains: function (domain_names) { + let promises = [ + proxyHostModel + .query() + .where('is_deleted', 0), + redirectionHostModel + .query() + .where('is_deleted', 0), + deadHostModel + .query() + .where('is_deleted', 0) + ]; + + return Promise.all(promises) + .then(promises_results => { + let response_object = { + total_count: 0, + dead_hosts: [], + proxy_hosts: [], + redirection_hosts: [] + }; + + if (promises_results[0]) { + // Proxy Hosts + response_object.proxy_hosts = internalHost._getHostsWithDomains(promises_results[0], domain_names); + response_object.total_count += response_object.proxy_hosts.length; + } + + if (promises_results[1]) { + // Redirection Hosts + response_object.redirection_hosts = internalHost._getHostsWithDomains(promises_results[1], domain_names); + response_object.total_count += response_object.redirection_hosts.length; + } + + if (promises_results[1]) { + // Dead Hosts + response_object.dead_hosts = internalHost._getHostsWithDomains(promises_results[2], domain_names); + response_object.total_count += response_object.dead_hosts.length; + } + + return response_object; + }); + }, + /** * Internal use only, checks to see if the domain is already taken by any other record * @@ -87,6 +138,37 @@ const internalHost = { } return is_taken; + }, + + /** + * Private call only + * + * @param {Array} hosts + * @param {Array} domain_names + * @returns {Array} + */ + _getHostsWithDomains: function (hosts, domain_names) { + let response = []; + + if (hosts && hosts.length) { + hosts.map(function (host) { + let host_matches = false; + + domain_names.map(function (domain_name) { + host.domain_names.map(function (host_domain_name) { + if (domain_name.toLowerCase() === host_domain_name.toLowerCase()) { + host_matches = true; + } + }); + }); + + if (host_matches) { + response.push(host); + } + }); + } + + return response; } }; diff --git a/src/backend/internal/nginx.js b/src/backend/internal/nginx.js index 325ec3e..a3c3f68 100644 --- a/src/backend/internal/nginx.js +++ b/src/backend/internal/nginx.js @@ -1,13 +1,12 @@ '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 internalCertificate = require('./certificate'); -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 debug_mode = process.env.NODE_ENV !== 'production'; const internalNginx = { @@ -120,7 +119,7 @@ const internalNginx = { } let renderEngine = Liquid({ - root: __dirname + '/../templates/', + root: __dirname + '/../templates/' }); return new Promise((resolve, reject) => { @@ -154,6 +153,85 @@ const internalNginx = { }); }, + /** + * This generates a temporary nginx config listening on port 80 for the domain names listed + * in the certificate setup. It allows the letsencrypt acme challenge to be requested by letsencrypt + * when requesting a certificate without having a hostname set up already. + * + * @param {Object} certificate + * @returns {Promise} + */ + generateLetsEncryptRequestConfig: certificate => { + if (debug_mode) { + logger.info('Generating LetsEncrypt Request Config:', certificate); + } + + let renderEngine = Liquid({ + root: __dirname + '/../templates/' + }); + + return new Promise((resolve, reject) => { + let template = null; + let filename = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf'; + try { + template = fs.readFileSync(__dirname + '/../templates/letsencrypt-request.conf', {encoding: 'utf8'}); + } catch (err) { + reject(new error.ConfigurationError(err.message)); + return; + } + + renderEngine + .parseAndRender(template, certificate) + .then(config_text => { + fs.writeFileSync(filename, config_text, {encoding: 'utf8'}); + + if (debug_mode) { + logger.success('Wrote config:', filename, config_text); + } + + resolve(true); + }) + .catch(err => { + if (debug_mode) { + logger.warn('Could not write ' + filename + ':', err.message); + } + + reject(new error.ConfigurationError(err.message)); + }); + }); + }, + + /** + * This removes the temporary nginx config file generated by `generateLetsEncryptRequestConfig` + * + * @param {Object} certificate + * @param {Boolean} [throw_errors] + * @returns {Promise} + */ + deleteLetsEncryptRequestConfig: (certificate, throw_errors) => { + return new Promise((resolve, reject) => { + try { + let config_file = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf'; + + if (debug_mode) { + logger.warn('Deleting nginx config: ' + config_file); + } + + fs.unlinkSync(config_file); + } catch (err) { + if (debug_mode) { + logger.warn('Could not delete config:', err.message); + } + + if (throw_errors) { + reject(err); + } + } + + resolve(); + }); + }, + /** * @param {String} host_type * @param {Object} host @@ -184,6 +262,35 @@ const internalNginx = { resolve(); }); + }, + + /** + * @param {String} host_type + * @param {Array} hosts + * @returns {Promise} + */ + bulkGenerateConfigs: (host_type, hosts) => { + let promises = []; + hosts.map(function (host) { + promises.push(internalNginx.generateConfig(host_type, host)); + }); + + return Promise.all(promises); + }, + + /** + * @param {String} host_type + * @param {Array} hosts + * @param {Boolean} [throw_errors] + * @returns {Promise} + */ + bulkDeleteConfigs: (host_type, hosts, throw_errors) => { + let promises = []; + hosts.map(function (host) { + promises.push(internalNginx.deleteConfig(host_type, host, throw_errors)); + }); + + return Promise.all(promises); } }; diff --git a/src/backend/templates/_assets.conf b/src/backend/templates/_assets.conf index fb86fc2..dcb183c 100644 --- a/src/backend/templates/_assets.conf +++ b/src/backend/templates/_assets.conf @@ -1,4 +1,4 @@ {% if caching_enabled == 1 or caching_enabled == true -%} # Asset Caching include conf.d/include/assets.conf; -{%- endif %} +{% endif %} \ No newline at end of file diff --git a/src/backend/templates/_certificates.conf b/src/backend/templates/_certificates.conf index 832ac4a..7e1f275 100644 --- a/src/backend/templates/_certificates.conf +++ b/src/backend/templates/_certificates.conf @@ -1,12 +1,10 @@ -{%- if certificate and certificate_id > 0 -%} -{%- if certificate.provider == "letsencrypt" %} +{% 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 -%} - + 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 %} +{% endif %} \ No newline at end of file diff --git a/src/backend/templates/_exploits.conf b/src/backend/templates/_exploits.conf index c46f391..002970d 100644 --- a/src/backend/templates/_exploits.conf +++ b/src/backend/templates/_exploits.conf @@ -1,4 +1,4 @@ -{% if block_exploits == 1 or block_exploits == true -%} +{% if block_exploits == 1 or block_exploits == true %} # Block Exploits include conf.d/include/block-exploits.conf; -{%- endif -%} +{% endif %} \ No newline at end of file diff --git a/src/backend/templates/_forced_ssl.conf b/src/backend/templates/_forced_ssl.conf index 467faf3..7fade20 100644 --- a/src/backend/templates/_forced_ssl.conf +++ b/src/backend/templates/_forced_ssl.conf @@ -1,6 +1,6 @@ -{%- if certificate and certificate_id > 0 -%} -{%- if ssl_forced == 1 or ssl_forced == true -%} +{% 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 %} +{% endif %} +{% endif %} \ No newline at end of file diff --git a/src/backend/templates/_header_comment.conf b/src/backend/templates/_header_comment.conf index 481d0b7..8f996d3 100644 --- a/src/backend/templates/_header_comment.conf +++ b/src/backend/templates/_header_comment.conf @@ -1,3 +1,3 @@ # ------------------------------------------------------------ # {{ domain_names | join: ", " }} -# ------------------------------------------------------------ +# ------------------------------------------------------------ \ No newline at end of file diff --git a/src/backend/templates/_listen.conf b/src/backend/templates/_listen.conf index d6861ed..067b2dd 100644 --- a/src/backend/templates/_listen.conf +++ b/src/backend/templates/_listen.conf @@ -1,5 +1,5 @@ listen 80; -{%- if certificate -%} +{% if certificate -%} listen 443 ssl; -{%- endif %} - server_name {{ domain_names | join: " " }}; +{% endif %} + server_name {{ domain_names | join: " " }}; \ No newline at end of file diff --git a/src/backend/templates/letsencrypt-request.conf b/src/backend/templates/letsencrypt-request.conf new file mode 100644 index 0000000..4a5ed74 --- /dev/null +++ b/src/backend/templates/letsencrypt-request.conf @@ -0,0 +1,14 @@ +{% include "_header_comment.conf" %} + +server { + listen 80; + server_name {{ domain_names | join: " " }}; + + access_log /data/logs/letsencrypt-requests.log proxy; + + include conf.d/include/letsencrypt-acme-challenge.conf; + + location / { + return 404; + } +}