diff --git a/.version b/.version index 93ea0c95..1acd4dac 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.9.4 +2.9.5 diff --git a/README.md b/README.md index d3d629c0..8916b99a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@



- + @@ -17,7 +17,7 @@ Reddit - +

This project comes as a pre-built docker image that enables you to easily forward to your websites @@ -414,6 +414,50 @@ Special thanks to the following contributors:
RBXII3 + + + +
demize +
+ + + + +
PUP-Loki +
+ + + + +
Daniel Sörlöv +
+ + + + + + +
Theyooo +
+ + + + +
Justin Peacock +
+ + + + +
Chris Tracy +
+ + + + +
Fuechslein +
+ diff --git a/backend/internal/certificate.js b/backend/internal/certificate.js index 17130c29..96972fe1 100644 --- a/backend/internal/certificate.js +++ b/backend/internal/certificate.js @@ -1,19 +1,18 @@ -const fs = require('fs'); -const _ = require('lodash'); -const logger = require('../logger').ssl; -const error = require('../lib/error'); -const certificateModel = require('../models/certificate'); -const internalAuditLog = require('./audit-log'); -const tempWrite = require('temp-write'); -const utils = require('../lib/utils'); -const moment = require('moment'); -const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG; -const le_staging = process.env.NODE_ENV !== 'production'; -const internalNginx = require('./nginx'); -const internalHost = require('./host'); -const certbot_command = 'certbot'; -const le_config = '/etc/letsencrypt.ini'; -const dns_plugins = require('../global/certbot-dns-plugins'); +const _ = require('lodash'); +const fs = require('fs'); +const tempWrite = require('temp-write'); +const moment = require('moment'); +const logger = require('../logger').ssl; +const error = require('../lib/error'); +const utils = require('../lib/utils'); +const certificateModel = require('../models/certificate'); +const dnsPlugins = require('../global/certbot-dns-plugins'); +const internalAuditLog = require('./audit-log'); +const internalNginx = require('./nginx'); +const internalHost = require('./host'); +const letsencryptStaging = process.env.NODE_ENV !== 'production'; +const letsencryptConfig = '/etc/letsencrypt.ini'; +const certbotCommand = 'certbot'; function omissions() { return ['is_deleted']; @@ -21,14 +20,14 @@ function omissions() { const internalCertificate = { - allowed_ssl_files: ['certificate', 'certificate_key', 'intermediate_certificate'], - interval_timeout: 1000 * 60 * 60, // 1 hour - interval: null, - interval_processing: false, + allowedSslFiles: ['certificate', 'certificate_key', 'intermediate_certificate'], + intervalTimeout: 1000 * 60 * 60, // 1 hour + interval: null, + intervalProcessing: false, initTimer: () => { logger.info('Let\'s Encrypt Renewal Timer initialized'); - internalCertificate.interval = setInterval(internalCertificate.processExpiringHosts, internalCertificate.interval_timeout); + internalCertificate.interval = setInterval(internalCertificate.processExpiringHosts, internalCertificate.intervalTimeout); // And do this now as well internalCertificate.processExpiringHosts(); }, @@ -37,15 +36,15 @@ const internalCertificate = { * Triggered by a timer, this will check for expiring hosts and renew their ssl certs if required */ processExpiringHosts: () => { - if (!internalCertificate.interval_processing) { - internalCertificate.interval_processing = true; + if (!internalCertificate.intervalProcessing) { + internalCertificate.intervalProcessing = true; logger.info('Renewing SSL certs close to expiry...'); - let cmd = certbot_command + ' renew --non-interactive --quiet ' + - '--config "' + le_config + '" ' + + const cmd = certbotCommand + ' renew --non-interactive --quiet ' + + '--config "' + letsencryptConfig + '" ' + '--preferred-challenges "dns,http" ' + '--disable-hook-validation ' + - (le_staging ? '--staging' : ''); + (letsencryptStaging ? '--staging' : ''); return utils.exec(cmd) .then((result) => { @@ -93,11 +92,11 @@ const internalCertificate = { }); }) .then(() => { - internalCertificate.interval_processing = false; + internalCertificate.intervalProcessing = false; }) .catch((err) => { logger.error(err); - internalCertificate.interval_processing = false; + internalCertificate.intervalProcessing = false; }); } }, @@ -221,7 +220,7 @@ const internalCertificate = { await certificateModel .query() .deleteById(certificate.id); - + throw error; }); } else { @@ -448,11 +447,9 @@ const internalCertificate = { * @returns {Promise} */ writeCustomCert: (certificate) => { - if (debug_mode) { - logger.info('Writing Custom Certificate:', certificate); - } + logger.info('Writing Custom Certificate:', certificate); - let dir = '/data/custom_ssl/npm-' + certificate.id; + const dir = '/data/custom_ssl/npm-' + certificate.id; return new Promise((resolve, reject) => { if (certificate.provider === 'letsencrypt') { @@ -460,9 +457,9 @@ const internalCertificate = { return; } - let cert_data = certificate.meta.certificate; + let certData = certificate.meta.certificate; if (typeof certificate.meta.intermediate_certificate !== 'undefined') { - cert_data = cert_data + '\n' + certificate.meta.intermediate_certificate; + certData = certData + '\n' + certificate.meta.intermediate_certificate; } try { @@ -474,7 +471,7 @@ const internalCertificate = { return; } - fs.writeFile(dir + '/fullchain.pem', cert_data, function (err) { + fs.writeFile(dir + '/fullchain.pem', certData, function (err) { if (err) { reject(err); } else { @@ -524,7 +521,7 @@ const internalCertificate = { // Put file contents into an object let files = {}; _.map(data.files, (file, name) => { - if (internalCertificate.allowed_ssl_files.indexOf(name) !== -1) { + if (internalCertificate.allowedSslFiles.indexOf(name) !== -1) { files[name] = file.data.toString(); } }); @@ -582,7 +579,7 @@ const internalCertificate = { } _.map(data.files, (file, name) => { - if (internalCertificate.allowed_ssl_files.indexOf(name) !== -1) { + if (internalCertificate.allowedSslFiles.indexOf(name) !== -1) { row.meta[name] = file.data.toString(); } }); @@ -601,7 +598,7 @@ const internalCertificate = { }); }) .then(() => { - return _.pick(row.meta, internalCertificate.allowed_ssl_files); + return _.pick(row.meta, internalCertificate.allowedSslFiles); }); }); }, @@ -649,9 +646,9 @@ const internalCertificate = { return tempWrite(certificate, '/tmp') .then((filepath) => { return internalCertificate.getCertificateInfoFromFile(filepath, throw_expired) - .then((cert_data) => { + .then((certData) => { fs.unlinkSync(filepath); - return cert_data; + return certData; }).catch((err) => { fs.unlinkSync(filepath); throw err; @@ -667,33 +664,33 @@ const internalCertificate = { * @param {Boolean} [throw_expired] Throw when the certificate is out of date */ getCertificateInfoFromFile: (certificate_file, throw_expired) => { - let cert_data = {}; + let certData = {}; 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); + const regex = /(?:subject=)?[^=]+=\s+(\S+)/gim; + const 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]; + certData['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); + const regex = /^(?:issuer=)?(.*)$/gim; + const 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]; + certData['issuer'] = match[1]; }) .then(() => { return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout'); @@ -701,39 +698,39 @@ const internalCertificate = { .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 validFrom = null; + let validTo = null; - let lines = result.split('\n'); + const lines = result.split('\n'); lines.map(function (str) { - let regex = /^(\S+)=(.*)$/gim; - let match = regex.exec(str.trim()); + const regex = /^(\S+)=(.*)$/gim; + const 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); + const date = parseInt(moment(match[2], 'MMM DD HH:mm:ss YYYY z').format('X'), 10); if (match[1].toLowerCase() === 'notbefore') { - valid_from = date; + validFrom = date; } else if (match[1].toLowerCase() === 'notafter') { - valid_to = date; + validTo = date; } } }); - if (!valid_from || !valid_to) { + if (!validFrom || !validTo) { throw new error.ValidationError('Could not determine dates from certificate: ' + result); } - if (throw_expired && valid_to < parseInt(moment().format('X'), 10)) { + if (throw_expired && validTo < parseInt(moment().format('X'), 10)) { throw new error.ValidationError('Certificate has expired'); } - cert_data['dates'] = { - from: valid_from, - to: valid_to + certData['dates'] = { + from: validFrom, + to: validTo }; - return cert_data; + return certData; }).catch((err) => { throw new error.ValidationError('Certificate is not valid (' + err.message + ')', err); }); @@ -747,7 +744,7 @@ const internalCertificate = { * @returns {Object} */ cleanMeta: function (meta, remove) { - internalCertificate.allowed_ssl_files.map((key) => { + internalCertificate.allowedSslFiles.map((key) => { if (typeof meta[key] !== 'undefined' && meta[key]) { if (remove) { delete meta[key]; @@ -767,18 +764,16 @@ const internalCertificate = { requestLetsEncryptSsl: (certificate) => { logger.info('Requesting Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); - let cmd = certbot_command + ' certonly --non-interactive ' + - '--config "' + le_config + '" ' + + const cmd = certbotCommand + ' certonly --non-interactive ' + + '--config "' + letsencryptConfig + '" ' + '--cert-name "npm-' + certificate.id + '" ' + '--agree-tos ' + '--email "' + certificate.meta.letsencrypt_email + '" ' + '--preferred-challenges "dns,http" ' + '--domains "' + certificate.domain_names.join(',') + '" ' + - (le_staging ? '--staging' : ''); + (letsencryptStaging ? '--staging' : ''); - if (debug_mode) { - logger.info('Command:', cmd); - } + logger.info('Command:', cmd); return utils.exec(cmd) .then((result) => { @@ -788,14 +783,14 @@ const internalCertificate = { }, /** - * @param {Object} certificate the certificate row - * @param {String} dns_provider the dns provider name (key used in `certbot-dns-plugins.js`) - * @param {String | null} credentials the content of this providers credentials file - * @param {String} propagation_seconds the cloudflare api token + * @param {Object} certificate the certificate row + * @param {String} dns_provider the dns provider name (key used in `certbot-dns-plugins.js`) + * @param {String | null} credentials the content of this providers credentials file + * @param {String} propagation_seconds the cloudflare api token * @returns {Promise} */ requestLetsEncryptSslWithDnsChallenge: (certificate) => { - const dns_plugin = dns_plugins[certificate.meta.dns_provider]; + const dns_plugin = dnsPlugins[certificate.meta.dns_provider]; if (!dns_plugin) { throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`); @@ -803,46 +798,43 @@ const internalCertificate = { logger.info(`Requesting Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`); - const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id; - const credentials_cmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\''; - const prepare_cmd = 'pip install ' + dns_plugin.package_name + '==' + dns_plugin.package_version + ' ' + dns_plugin.dependencies; + const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id; + const credentialsCmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\''; + const prepareCmd = 'pip install ' + dns_plugin.package_name + '==' + dns_plugin.package_version + ' ' + dns_plugin.dependencies; // Whether the plugin has a ---credentials argument - const has_config_arg = certificate.meta.dns_provider !== 'route53'; + const hasConfigArg = certificate.meta.dns_provider !== 'route53'; - let main_cmd = - certbot_command + ' certonly --non-interactive ' + + let mainCmd = certbotCommand + ' certonly --non-interactive ' + '--cert-name "npm-' + certificate.id + '" ' + '--agree-tos ' + - '--email "' + certificate.meta.letsencrypt_email + '" ' + + '--email "' + certificate.meta.letsencrypt_email + '" ' + '--domains "' + certificate.domain_names.join(',') + '" ' + '--authenticator ' + dns_plugin.full_plugin_name + ' ' + ( - has_config_arg - ? '--' + dns_plugin.full_plugin_name + '-credentials "' + credentials_loc + '"' + hasConfigArg + ? '--' + dns_plugin.full_plugin_name + '-credentials "' + credentialsLocation + '"' : '' ) + ( - certificate.meta.propagation_seconds !== undefined - ? ' --' + dns_plugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds + certificate.meta.propagation_seconds !== undefined + ? ' --' + dns_plugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds : '' ) + - (le_staging ? ' --staging' : ''); + (letsencryptStaging ? ' --staging' : ''); // Prepend the path to the credentials file as an environment variable if (certificate.meta.dns_provider === 'route53') { - main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd; + mainCmd = 'AWS_CONFIG_FILE=\'' + credentialsLocation + '\' ' + mainCmd; } - if (debug_mode) { - logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd}`); - } + logger.info('Command:', `${credentialsCmd} && ${prepareCmd} && ${mainCmd}`); - return utils.exec(credentials_cmd) + return utils.exec(credentialsCmd) .then(() => { - return utils.exec(prepare_cmd) + return utils.exec(prepareCmd) .then(() => { - return utils.exec(main_cmd) + return utils.exec(mainCmd) .then(async (result) => { logger.info(result); return result; @@ -850,8 +842,8 @@ const internalCertificate = { }); }).catch(async (err) => { // Don't fail if file does not exist - const delete_credentials_cmd = `rm -f '${credentials_loc}' || true`; - await utils.exec(delete_credentials_cmd); + const delete_credentialsCmd = `rm -f '${credentialsLocation}' || true`; + await utils.exec(delete_credentialsCmd); throw err; }); }, @@ -870,7 +862,7 @@ const internalCertificate = { }) .then((certificate) => { if (certificate.provider === 'letsencrypt') { - let renewMethod = certificate.meta.dns_challenge ? internalCertificate.renewLetsEncryptSslWithDnsChallenge : internalCertificate.renewLetsEncryptSsl; + const renewMethod = certificate.meta.dns_challenge ? internalCertificate.renewLetsEncryptSslWithDnsChallenge : internalCertificate.renewLetsEncryptSsl; return renewMethod(certificate) .then(() => { @@ -908,16 +900,14 @@ const internalCertificate = { renewLetsEncryptSsl: (certificate) => { logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); - let cmd = certbot_command + ' renew --non-interactive ' + - '--config "' + le_config + '" ' + + const cmd = certbotCommand + ' renew --force-renewal --non-interactive ' + + '--config "' + letsencryptConfig + '" ' + '--cert-name "npm-' + certificate.id + '" ' + '--preferred-challenges "dns,http" ' + '--disable-hook-validation ' + - (le_staging ? '--staging' : ''); + (letsencryptStaging ? '--staging' : ''); - if (debug_mode) { - logger.info('Command:', cmd); - } + logger.info('Command:', cmd); return utils.exec(cmd) .then((result) => { @@ -931,7 +921,7 @@ const internalCertificate = { * @returns {Promise} */ renewLetsEncryptSslWithDnsChallenge: (certificate) => { - const dns_plugin = dns_plugins[certificate.meta.dns_provider]; + const dns_plugin = dnsPlugins[certificate.meta.dns_provider]; if (!dns_plugin) { throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`); @@ -939,23 +929,20 @@ const internalCertificate = { logger.info(`Renewing Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`); - let main_cmd = - certbot_command + ' renew --non-interactive ' + + let mainCmd = certbotCommand + ' renew --non-interactive ' + '--cert-name "npm-' + certificate.id + '" ' + '--disable-hook-validation' + - (le_staging ? ' --staging' : ''); + (letsencryptStaging ? ' --staging' : ''); // Prepend the path to the credentials file as an environment variable if (certificate.meta.dns_provider === 'route53') { - const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id; - main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd; + const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id; + mainCmd = 'AWS_CONFIG_FILE=\'' + credentialsLocation + '\' ' + mainCmd; } - if (debug_mode) { - logger.info('Command:', main_cmd); - } + logger.info('Command:', mainCmd); - return utils.exec(main_cmd) + return utils.exec(mainCmd) .then(async (result) => { logger.info(result); return result; @@ -970,28 +957,24 @@ const internalCertificate = { revokeLetsEncryptSsl: (certificate, throw_errors) => { logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); - const main_cmd = certbot_command + ' revoke --non-interactive ' + + const mainCmd = certbotCommand + ' revoke --non-interactive ' + '--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' + '--delete-after-revoke ' + - (le_staging ? '--staging' : ''); + (letsencryptStaging ? '--staging' : ''); // Don't fail command if file does not exist - const delete_credentials_cmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`; + const delete_credentialsCmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`; - if (debug_mode) { - logger.info('Command:', main_cmd + '; ' + delete_credentials_cmd); - } + logger.info('Command:', mainCmd + '; ' + delete_credentialsCmd); - return utils.exec(main_cmd) + return utils.exec(mainCmd) .then(async (result) => { - await utils.exec(delete_credentials_cmd); + await utils.exec(delete_credentialsCmd); logger.info(result); return result; }) .catch((err) => { - if (debug_mode) { - logger.error(err.message); - } + logger.error(err.message); if (throw_errors) { throw err; @@ -1004,9 +987,9 @@ const internalCertificate = { * @returns {Boolean} */ hasLetsEncryptSslCerts: (certificate) => { - let le_path = '/etc/letsencrypt/live/npm-' + certificate.id; + const letsencryptPath = '/etc/letsencrypt/live/npm-' + certificate.id; - return fs.existsSync(le_path + '/fullchain.pem') && fs.existsSync(le_path + '/privkey.pem'); + return fs.existsSync(letsencryptPath + '/fullchain.pem') && fs.existsSync(letsencryptPath + '/privkey.pem'); }, /** diff --git a/backend/setup.js b/backend/setup.js index b25ffc00..44673b37 100644 --- a/backend/setup.js +++ b/backend/setup.js @@ -201,9 +201,29 @@ const setupCertbotPlugins = () => { }); }; + +/** + * Starts a timer to call run the logrotation binary every two days + * @returns {Promise} + */ +const setupLogrotation = () => { + const intervalTimeout = 1000 * 60 * 60 * 24 * 2; // 2 days + + const runLogrotate = async () => { + await utils.exec('logrotate /etc/logrotate.d/nginx-proxy-manager'); + logger.info('Logrotate completed.'); + }; + + logger.info('Logrotate Timer initialized'); + setInterval(runLogrotate, intervalTimeout); + // And do this now as well + return runLogrotate(); +}; + module.exports = function () { return setupJwt() .then(setupDefaultUser) .then(setupDefaultSettings) - .then(setupCertbotPlugins); + .then(setupCertbotPlugins) + .then(setupLogrotation); }; diff --git a/backend/templates/dead_host.conf b/backend/templates/dead_host.conf index be53f6df..d94dff57 100644 --- a/backend/templates/dead_host.conf +++ b/backend/templates/dead_host.conf @@ -7,7 +7,8 @@ server { {% include "_hsts.conf" %} {% include "_forced_ssl.conf" %} - access_log /data/logs/dead_host-{{ id }}.log standard; + access_log /data/logs/dead-host-{{ id }}_access.log standard; + error_log /data/logs/dead-host-{{ id }}_error.log warn; {{ advanced_config }} diff --git a/backend/templates/default.conf b/backend/templates/default.conf index 56b67090..7eef11f6 100644 --- a/backend/templates/default.conf +++ b/backend/templates/default.conf @@ -12,7 +12,8 @@ server { #listen [::]:80; {% endif %} server_name default-host.localhost; - access_log /data/logs/default_host.log combined; + access_log /data/logs/default-host_access.log combined; + error_log /data/logs/default-host_error.log warn; {% include "_exploits.conf" %} {%- if value == "404" %} diff --git a/backend/templates/letsencrypt-request.conf b/backend/templates/letsencrypt-request.conf index cda2f892..676c8a60 100644 --- a/backend/templates/letsencrypt-request.conf +++ b/backend/templates/letsencrypt-request.conf @@ -8,7 +8,8 @@ server { server_name {{ domain_names | join: " " }}; - access_log /data/logs/letsencrypt-requests.log standard; + access_log /data/logs/letsencrypt-requests_access.log standard; + error_log /data/logs/letsencrypt-requests_error.log warn; include conf.d/include/letsencrypt-acme-challenge.conf; diff --git a/backend/templates/proxy_host.conf b/backend/templates/proxy_host.conf index 538b85e5..ec30cca0 100644 --- a/backend/templates/proxy_host.conf +++ b/backend/templates/proxy_host.conf @@ -19,8 +19,8 @@ proxy_set_header Connection $http_connection; proxy_http_version 1.1; {% endif %} - - access_log /data/logs/proxy_host-{{ id }}.log proxy; + access_log /data/logs/proxy-host-{{ id }}_access.log proxy; + error_log /data/logs/proxy-host-{{ id }}_error.log warn; {{ advanced_config }} diff --git a/backend/templates/redirection_host.conf b/backend/templates/redirection_host.conf index f42e146b..339fe72e 100644 --- a/backend/templates/redirection_host.conf +++ b/backend/templates/redirection_host.conf @@ -9,7 +9,8 @@ server { {% include "_hsts.conf" %} {% include "_forced_ssl.conf" %} - access_log /data/logs/redirection_host-{{ id }}.log standard; + access_log /data/logs/redirection-host-{{ id }}_access.log standard; + error_log /data/logs/redirection-host-{{ id }}_error.log warn; {{ advanced_config }} diff --git a/docker/Dockerfile b/docker/Dockerfile index d85782b6..c978f517 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -20,7 +20,7 @@ ENV SUPPRESS_NO_CONFIG_WARNING=1 \ RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \ && apt-get update \ - && apt-get install -y --no-install-recommends jq \ + && apt-get install -y --no-install-recommends jq logrotate \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* @@ -43,6 +43,9 @@ COPY docker/rootfs / # Remove frontend service not required for prod, dev nginx config as well RUN rm -rf /etc/services.d/frontend /etc/nginx/conf.d/dev.conf +# Change permission of logrotate config file +RUN chmod 644 /etc/logrotate.d/nginx-proxy-manager + VOLUME [ "/data", "/etc/letsencrypt" ] ENTRYPOINT [ "/init" ] HEALTHCHECK --interval=5s --timeout=3s CMD /bin/check-health diff --git a/docker/dev/Dockerfile b/docker/dev/Dockerfile index ae17e861..e7a1c319 100644 --- a/docker/dev/Dockerfile +++ b/docker/dev/Dockerfile @@ -7,7 +7,7 @@ ENV S6_LOGGING=0 \ RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \ && apt-get update \ - && apt-get install -y certbot jq python3-pip \ + && apt-get install -y certbot jq python3-pip logrotate \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* @@ -18,6 +18,7 @@ RUN cd /usr \ COPY rootfs / RUN rm -f /etc/nginx/conf.d/production.conf +RUN chmod 644 /etc/logrotate.d/nginx-proxy-manager # s6 overlay RUN curl -L -o /tmp/s6-overlay-amd64.tar.gz "https://github.com/just-containers/s6-overlay/releases/download/v1.22.1.0/s6-overlay-amd64.tar.gz" \ diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index a0b4547b..99f262f1 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -1,9 +1,9 @@ # WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production. -version: "3" +version: "3.5" services: - npm: image: nginxproxymanager:dev + container_name: npm_core build: context: ./ dockerfile: ./dev/Dockerfile @@ -36,6 +36,7 @@ services: db: image: jc21/mariadb-aria + container_name: npm_db networks: - nginx_proxy_manager environment: @@ -47,21 +48,26 @@ services: - db_data:/var/lib/mysql swagger: - image: 'swaggerapi/swagger-ui:latest' + image: "swaggerapi/swagger-ui:latest" + container_name: npm_swagger ports: - 3001:80 networks: - nginx_proxy_manager environment: URL: "http://127.0.0.1:3081/api/schema" - PORT: '80' + PORT: "80" depends_on: - npm volumes: npm_data: + name: npm_core_data le_data: + name: npm_le_data db_data: + name: npm_db_data networks: nginx_proxy_manager: + name: npm_network diff --git a/docker/rootfs/etc/logrotate.d/nginx-proxy-manager b/docker/rootfs/etc/logrotate.d/nginx-proxy-manager new file mode 100644 index 00000000..20c23ac6 --- /dev/null +++ b/docker/rootfs/etc/logrotate.d/nginx-proxy-manager @@ -0,0 +1,25 @@ +/data/logs/*_access.log /data/logs/*/access.log { + create 0644 root root + weekly + rotate 4 + missingok + notifempty + compress + sharedscripts + postrotate + /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true + endscript +} + +/data/logs/*_error.log /data/logs/*/error.log { + create 0644 root root + weekly + rotate 10 + missingok + notifempty + compress + sharedscripts + postrotate + /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true + endscript +} \ No newline at end of file diff --git a/docker/rootfs/etc/nginx/conf.d/default.conf b/docker/rootfs/etc/nginx/conf.d/default.conf index d1684ea7..a7634985 100644 --- a/docker/rootfs/etc/nginx/conf.d/default.conf +++ b/docker/rootfs/etc/nginx/conf.d/default.conf @@ -8,7 +8,7 @@ server { set $port "80"; server_name localhost-nginx-proxy-manager; - access_log /data/logs/default.log standard; + access_log /data/logs/fallback_access.log standard; error_log /dev/null crit; include conf.d/include/assets.conf; include conf.d/include/block-exploits.conf; @@ -29,7 +29,7 @@ server { set $port "443"; server_name localhost; - access_log /data/logs/default.log standard; + access_log /data/logs/fallback-access.log standard; error_log /dev/null crit; ssl_certificate /data/nginx/dummycert.pem; ssl_certificate_key /data/nginx/dummykey.pem; diff --git a/docker/rootfs/etc/nginx/conf.d/include/letsencrypt-acme-challenge.conf b/docker/rootfs/etc/nginx/conf.d/include/letsencrypt-acme-challenge.conf index d04f7033..ff2a7827 100644 --- a/docker/rootfs/etc/nginx/conf.d/include/letsencrypt-acme-challenge.conf +++ b/docker/rootfs/etc/nginx/conf.d/include/letsencrypt-acme-challenge.conf @@ -5,6 +5,7 @@ location ^~ /.well-known/acme-challenge/ { # Since this is for letsencrypt authentication of a domain and they do not give IP ranges of their infrastructure # we need to open up access by turning off auth and IP ACL for this location. auth_basic off; + auth_request off; allow all; # Set correct content type. According to this: diff --git a/docker/rootfs/etc/nginx/nginx.conf b/docker/rootfs/etc/nginx/nginx.conf index 40432968..4d5ee901 100644 --- a/docker/rootfs/etc/nginx/nginx.conf +++ b/docker/rootfs/etc/nginx/nginx.conf @@ -9,7 +9,7 @@ worker_processes auto; # Enables the use of JIT for regular expressions to speed-up their processing. pcre_jit on; -error_log /data/logs/error.log warn; +error_log /data/logs/fallback_error.log warn; # Includes files with directives to load dynamic modules. include /etc/nginx/modules/*.conf; @@ -46,8 +46,7 @@ http { log_format proxy '[$time_local] $upstream_cache_status $upstream_status $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] [Sent-to $server] "$http_user_agent" "$http_referer"'; log_format standard '[$time_local] $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] "$http_user_agent" "$http_referer"'; - - access_log /data/logs/default.log proxy; + access_log /data/logs/fallback_access.log proxy; # Dynamically generated resolvers file include /etc/nginx/conf.d/include/resolvers.conf; diff --git a/docs/yarn.lock b/docs/yarn.lock index 539f8307..df7550e6 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -2650,9 +2650,9 @@ color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-string@^1.5.2, color-string@^1.5.3: - version "1.5.3" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" - integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== + version "1.5.5" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014" + integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" diff --git a/frontend/js/app/nginx/certificates/form.js b/frontend/js/app/nginx/certificates/form.js index d5d3acd3..65a54b69 100644 --- a/frontend/js/app/nginx/certificates/form.js +++ b/frontend/js/app/nginx/certificates/form.js @@ -251,7 +251,7 @@ module.exports = Mn.View.extend({ text: input }; }, - createFilter: /^(?:[^.]+\.?)+[^.]$/ + createFilter: /^(?:\*\.)?(?:[^.*]+\.?)+[^.]$/ }); this.ui.dns_challenge_content.hide(); this.ui.credentials_file_content.hide(); diff --git a/frontend/js/app/nginx/proxy/form.js b/frontend/js/app/nginx/proxy/form.js index 8802b958..1dfb5c18 100644 --- a/frontend/js/app/nginx/proxy/form.js +++ b/frontend/js/app/nginx/proxy/form.js @@ -278,7 +278,7 @@ module.exports = Mn.View.extend({ text: input }; }, - createFilter: /^(?:\.)?(?:[^.*]+\.?)+[^.]$/ + createFilter: /^(?:\*\.)?(?:[^.*]+\.?)+[^.]$/ }); // Access Lists diff --git a/frontend/js/i18n/messages.json b/frontend/js/i18n/messages.json index 2fb4a04f..5be803ce 100644 --- a/frontend/js/i18n/messages.json +++ b/frontend/js/i18n/messages.json @@ -60,7 +60,7 @@ }, "footer": { "fork-me": "Fork me on Github", - "copy": "© 2019 jc21.com.", + "copy": "© 2021 jc21.com.", "theme": "Theme by Tabler" }, "dashboard": { diff --git a/global/certbot-dns-plugins.js b/global/certbot-dns-plugins.js index 461bb223..70f45ab8 100644 --- a/global/certbot-dns-plugins.js +++ b/global/certbot-dns-plugins.js @@ -293,6 +293,16 @@ certbot_dns_ispconfig:dns_ispconfig_endpoint = https://localhost:8080`, certbot_dns_isset:dns_isset_token=""`, full_plugin_name: 'certbot-dns-isset:dns-isset', }, + joker: { + display_name: 'Joker', + package_name: 'certbot-dns-joker', + package_version: '1.1.0', + dependencies: '', + credentials: `certbot_dns_joker:dns_joker_username = +certbot_dns_joker:dns_joker_password = +certbot_dns_joker:dns_joker_domain = `, + full_plugin_name: 'certbot-dns-joker:dns-joker', + }, //####################################################// linode: { display_name: 'Linode',