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 @@
-
+
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',