diff --git a/.version b/.version index b7b616779..2e12e4762 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.9.13 +2.9.14 diff --git a/Jenkinsfile b/Jenkinsfile index a0f21e109..860c88d0e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -62,6 +62,7 @@ pipeline { stage('Backend') { steps { echo 'Checking Syntax ...' + sh 'docker pull node:latest' // See: https://github.com/yarnpkg/yarn/issues/3254 sh '''docker run --rm \\ -v "$(pwd)/backend:/app" \\ diff --git a/README.md b/README.md index 7d03016a4..64c7c7ab6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@



- + @@ -110,9 +110,9 @@ Special thanks to the following contributors: + + + + +
- - -
Sebastian Valle +
+ +
chaptergy
@@ -242,9 +242,9 @@ Special thanks to the following contributors:
- - -
chaptergy +
+ +
Sebastian Valle
@@ -491,6 +491,26 @@ Special thanks to the following contributors:
bergi9
+ + +
luoweihua7 +
+
+ + +
Tobias Kneidl +
+
+ + +
Pius Walter +
+
diff --git a/backend/internal/certificate.js b/backend/internal/certificate.js index 55f55d080..351b9b3de 100644 --- a/backend/internal/certificate.js +++ b/backend/internal/certificate.js @@ -171,6 +171,7 @@ const internalCertificate = { // 3. Generate the LE config return internalNginx.generateLetsEncryptRequestConfig(certificate) .then(internalNginx.reload) + .then(async() => await new Promise((r) => setTimeout(r, 5000))) .then(() => { // 4. Request cert return internalCertificate.requestLetsEncryptSsl(certificate); @@ -870,8 +871,10 @@ const internalCertificate = { logger.info(`Requesting Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`); 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.version_requirement || '') + ' ' + dns_plugin.dependencies; + // Escape single quotes and backslashes + const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\'); + const credentialsCmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\''; + const prepareCmd = 'pip install ' + dns_plugin.package_name + (dns_plugin.version_requirement || '') + ' ' + dns_plugin.dependencies; // Whether the plugin has a ---credentials argument const hasConfigArg = certificate.meta.dns_provider !== 'route53'; diff --git a/backend/internal/ip_ranges.js b/backend/internal/ip_ranges.js index edf5c3a01..40e63ea40 100644 --- a/backend/internal/ip_ranges.js +++ b/backend/internal/ip_ranges.js @@ -9,6 +9,9 @@ const CLOUDFRONT_URL = 'https://ip-ranges.amazonaws.com/ip-ranges.json'; const CLOUDFARE_V4_URL = 'https://www.cloudflare.com/ips-v4'; const CLOUDFARE_V6_URL = 'https://www.cloudflare.com/ips-v6'; +const regIpV4 = /^(\d+\.?){4}\/\d+/; +const regIpV6 = /^(([\da-fA-F]+)?:)+\/\d+/; + const internalIpRanges = { interval_timeout: 1000 * 60 * 60 * 6, // 6 hours @@ -74,14 +77,14 @@ const internalIpRanges = { return internalIpRanges.fetchUrl(CLOUDFARE_V4_URL); }) .then((cloudfare_data) => { - let items = cloudfare_data.split('\n'); + let items = cloudfare_data.split('\n').filter((line) => regIpV4.test(line)); ip_ranges = [... ip_ranges, ... items]; }) .then(() => { return internalIpRanges.fetchUrl(CLOUDFARE_V6_URL); }) .then((cloudfare_data) => { - let items = cloudfare_data.split('\n'); + let items = cloudfare_data.split('\n').filter((line) => regIpV6.test(line)); ip_ranges = [... ip_ranges, ... items]; }) .then(() => { diff --git a/backend/setup.js b/backend/setup.js index 41436c8f4..47fd1e7b0 100644 --- a/backend/setup.js +++ b/backend/setup.js @@ -181,7 +181,9 @@ const setupCertbotPlugins = () => { // Make sure credentials file exists const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id; - const credentials_cmd = '[ -f \'' + credentials_loc + '\' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'; }'; + // Escape single quotes and backslashes + const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\'); + const credentials_cmd = '[ -f \'' + credentials_loc + '\' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'; }'; promises.push(utils.exec(credentials_cmd)); } }); diff --git a/backend/templates/default.conf b/backend/templates/default.conf index 5196f2854..ec68530ca 100644 --- a/backend/templates/default.conf +++ b/backend/templates/default.conf @@ -7,9 +7,9 @@ server { listen 80 default; {% if ipv6 -%} - listen [::]:80; + listen [::]:80 default; {% else -%} - #listen [::]:80; + #listen [::]:80 default; {% endif %} server_name default-host.localhost; access_log /data/logs/default-host_access.log combined; diff --git a/docker/rootfs/etc/nginx/conf.d/default.conf b/docker/rootfs/etc/nginx/conf.d/default.conf index 81d6ae488..37d316db5 100644 --- a/docker/rootfs/etc/nginx/conf.d/default.conf +++ b/docker/rootfs/etc/nginx/conf.d/default.conf @@ -30,7 +30,7 @@ server { set $port "443"; server_name localhost; - access_log /data/logs/fallback-access.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/proxy.conf b/docker/rootfs/etc/nginx/conf.d/include/proxy.conf index fcaaf0038..e2cb451ef 100644 --- a/docker/rootfs/etc/nginx/conf.d/include/proxy.conf +++ b/docker/rootfs/etc/nginx/conf.d/include/proxy.conf @@ -2,6 +2,8 @@ add_header X-Served-By $host; proxy_set_header Host $host; proxy_set_header X-Forwarded-Scheme $scheme; proxy_set_header X-Forwarded-Proto $scheme; +proxy_set_header X-Forwarded-Host $host; +proxy_set_header X-Forwarded-Port $port; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Real-IP $remote_addr; proxy_pass $forward_scheme://$server:$port$request_uri; diff --git a/docker/rootfs/etc/services.d/nginx/run b/docker/rootfs/etc/services.d/nginx/run index fe6ea44b3..508b7d733 100755 --- a/docker/rootfs/etc/services.d/nginx/run +++ b/docker/rootfs/etc/services.d/nginx/run @@ -24,7 +24,7 @@ chown root /tmp/nginx # Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]` # thanks @tfmm -echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" > /etc/nginx/conf.d/include/resolvers.conf +echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" > /etc/nginx/conf.d/include/resolvers.conf # Generate dummy self-signed certificate. if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ] diff --git a/docs/faq/README.md b/docs/faq/README.md index 1703e7055..cf739ead1 100644 --- a/docs/faq/README.md +++ b/docs/faq/README.md @@ -21,3 +21,6 @@ Your best bet is to ask the [Reddit community for support](https://www.reddit.co Gitter is best left for anyone contributing to the project to ask for help about internals, code reviews etc. +## When adding username and password access control to a proxy host, I can no longer login into the app. + +Having an Access Control List (ACL) with username and password requires the browser to always send this username and password in the `Authorization` header on each request. If your proxied app also requires authentication (like Nginx Proxy Manager itself), most likely the app will also use the `Authorization` header to transmit this information, as this is the standardized header meant for this kind of information. However having multiples of the same headers is not allowed in the [internet standard](https://www.rfc-editor.org/rfc/rfc7230#section-3.2.2) and almost all apps do not support multiple values in the `Authorization` header. Hence one of the two logins will be broken. This can only be fixed by either removing one of the logins or by changing the app to use other non-standard headers for authorization. \ No newline at end of file diff --git a/frontend/js/app/nginx/certificates/form.js b/frontend/js/app/nginx/certificates/form.js index 56b31cf2a..a56c3f8e0 100644 --- a/frontend/js/app/nginx/certificates/form.js +++ b/frontend/js/app/nginx/certificates/form.js @@ -278,9 +278,11 @@ module.exports = Mn.View.extend({ this.ui.credentials_file_content.hide(); this.ui.loader_content.hide(); this.ui.le_error_info.hide(); - const domainNames = this.ui.domain_names[0].value.split(','); - if (!domainNames || domainNames.length === 0 || (domainNames.length === 1 && domainNames[0] === "")) { - this.ui.test_domains_button.prop('disabled', true); + if (this.ui.domain_names[0]) { + const domainNames = this.ui.domain_names[0].value.split(','); + if (!domainNames || domainNames.length === 0 || (domainNames.length === 1 && domainNames[0] === "")) { + this.ui.test_domains_button.prop('disabled', true); + } } }, diff --git a/global/certbot-dns-plugins.js b/global/certbot-dns-plugins.js index 398291352..7f7d8c327 100644 --- a/global/certbot-dns-plugins.js +++ b/global/certbot-dns-plugins.js @@ -67,11 +67,11 @@ dns_azure_zone2 = example.org:/subscriptions/99800903-fb14-4992-9aff-12eaf274462 }, //####################################################// cloudflare: { - display_name: 'Cloudflare', - package_name: 'certbot-dns-cloudflare', - // version_requirement: '', // Official plugin, no version requirement - dependencies: 'cloudflare', - credentials: `# Cloudflare API token + display_name: 'Cloudflare', + package_name: 'certbot-dns-cloudflare', + version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version + dependencies: 'cloudflare', + credentials: `# Cloudflare API token dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567`, full_plugin_name: 'dns-cloudflare', }, @@ -93,11 +93,11 @@ dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567`, }, //####################################################// cloudxns: { - display_name: 'CloudXNS', - package_name: 'certbot-dns-cloudxns', - // version_requirement: '', // Official plugin, no version requirement - dependencies: '', - credentials: `dns_cloudxns_api_key = 1234567890abcdef1234567890abcdef + display_name: 'CloudXNS', + package_name: 'certbot-dns-cloudxns', + version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version + dependencies: '', + credentials: `dns_cloudxns_api_key = 1234567890abcdef1234567890abcdef dns_cloudxns_secret_key = 1122334455667788`, full_plugin_name: 'dns-cloudxns', }, @@ -143,12 +143,12 @@ dns_desec_endpoint = https://desec.io/api/v1/`, }, //####################################################// digitalocean: { - display_name: 'DigitalOcean', - package_name: 'certbot-dns-digitalocean', - // version_requirement: '', // Official plugin, no version requirement - dependencies: '', - credentials: 'dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff', - full_plugin_name: 'dns-digitalocean', + display_name: 'DigitalOcean', + package_name: 'certbot-dns-digitalocean', + version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version + dependencies: '', + credentials: 'dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff', + full_plugin_name: 'dns-digitalocean', }, //####################################################// directadmin: { @@ -163,20 +163,20 @@ directadmin_password = aSuperStrongPassword`, }, //####################################################// dnsimple: { - display_name: 'DNSimple', - package_name: 'certbot-dns-dnsimple', - // version_requirement: '', // Official plugin, no version requirement - dependencies: '', - credentials: 'dns_dnsimple_token = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw', - full_plugin_name: 'dns-dnsimple', + display_name: 'DNSimple', + package_name: 'certbot-dns-dnsimple', + version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version + dependencies: '', + credentials: 'dns_dnsimple_token = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw', + full_plugin_name: 'dns-dnsimple', }, //####################################################// dnsmadeeasy: { - display_name: 'DNS Made Easy', - package_name: 'certbot-dns-dnsmadeeasy', - // version_requirement: '', // Official plugin, no version requirement - dependencies: '', - credentials: `dns_dnsmadeeasy_api_key = 1c1a3c91-4770-4ce7-96f4-54c0eb0e457a + display_name: 'DNS Made Easy', + package_name: 'certbot-dns-dnsmadeeasy', + version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version + dependencies: '', + credentials: `dns_dnsmadeeasy_api_key = 1c1a3c91-4770-4ce7-96f4-54c0eb0e457a dns_dnsmadeeasy_secret_key = c9b5625f-9834-4ff8-baba-4ed5f32cae55`, full_plugin_name: 'dns-dnsmadeeasy', }, @@ -186,8 +186,8 @@ dns_dnsmadeeasy_secret_key = c9b5625f-9834-4ff8-baba-4ed5f32cae55`, package_name: 'certbot-dns-dnspod', version_requirement: '~=0.1.0', dependencies: '', - credentials: `dns_dnspod_email = "DNSPOD-API-REQUIRES-A-VALID-EMAIL" -dns_dnspod_api_token = "DNSPOD-API-TOKEN"`, + credentials: `dns_dnspod_email = "email@example.com" +dns_dnspod_api_token = "id,key"`, full_plugin_name: 'dns-dnspod', }, //####################################################// @@ -235,11 +235,11 @@ dns_godaddy_key = abcdef0123456789abcdef01234567abcdef0123`, }, //####################################################// google: { - display_name: 'Google', - package_name: 'certbot-dns-google', - // version_requirement: '', // Official plugin, no version requirement - dependencies: '', - credentials: `{ + display_name: 'Google', + package_name: 'certbot-dns-google', + version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version + dependencies: '', + credentials: `{ "type": "service_account", ... }`, @@ -319,11 +319,11 @@ dns_joker_domain = `, }, //####################################################// linode: { - display_name: 'Linode', - package_name: 'certbot-dns-linode', - // version_requirement: '', // Official plugin, no version requirement - dependencies: '', - credentials: `dns_linode_key = 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ64 + display_name: 'Linode', + package_name: 'certbot-dns-linode', + version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version + dependencies: '', + credentials: `dns_linode_key = 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ64 dns_linode_version = [|3|4]`, full_plugin_name: 'dns-linode', }, @@ -339,11 +339,11 @@ dns_loopia_password = abcdef0123456789abcdef01234567abcdef0123`, }, //####################################################// luadns: { - display_name: 'LuaDNS', - package_name: 'certbot-dns-luadns', - // version_requirement: '', // Official plugin, no version requirement - dependencies: '', - credentials: `dns_luadns_email = user@example.com + display_name: 'LuaDNS', + package_name: 'certbot-dns-luadns', + version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version + dependencies: '', + credentials: `dns_luadns_email = user@example.com dns_luadns_token = 0123456789abcdef0123456789abcdef`, full_plugin_name: 'dns-luadns', }, @@ -369,12 +369,12 @@ dns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123`, }, //####################################################// nsone: { - display_name: 'NS1', - package_name: 'certbot-dns-nsone', - // version_requirement: '', // Official plugin, no version requirement - dependencies: '', - credentials: 'dns_nsone_api_key = MDAwMDAwMDAwMDAwMDAw', - full_plugin_name: 'dns-nsone', + display_name: 'NS1', + package_name: 'certbot-dns-nsone', + version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version + dependencies: '', + credentials: 'dns_nsone_api_key = MDAwMDAwMDAwMDAwMDAw', + full_plugin_name: 'dns-nsone', }, //####################################################// oci: { @@ -392,11 +392,11 @@ key_file = ~/.oci/oci_api_key.pem`, }, //####################################################// ovh: { - display_name: 'OVH', - package_name: 'certbot-dns-ovh', - // version_requirement: '', // Official plugin, no version requirement - dependencies: '', - credentials: `dns_ovh_endpoint = ovh-eu + display_name: 'OVH', + package_name: 'certbot-dns-ovh', + version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version + dependencies: '', + credentials: `dns_ovh_endpoint = ovh-eu dns_ovh_application_key = MDAwMDAwMDAwMDAw dns_ovh_application_secret = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw dns_ovh_consumer_key = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw`, @@ -434,11 +434,11 @@ certbot_regru:dns_password=password`, }, //####################################################// rfc2136: { - display_name: 'RFC 2136', - package_name: 'certbot-dns-rfc2136', - // version_requirement: '', // Official plugin, no version requirement - dependencies: '', - credentials: `# Target DNS server + display_name: 'RFC 2136', + package_name: 'certbot-dns-rfc2136', + version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version + dependencies: '', + credentials: `# Target DNS server dns_rfc2136_server = 192.0.2.1 # Target DNS port dns_rfc2136_port = 53 @@ -452,11 +452,11 @@ dns_rfc2136_algorithm = HMAC-SHA512`, }, //####################################################// route53: { - display_name: 'Route 53 (Amazon)', - package_name: 'certbot-dns-route53', - // version_requirement: '', // Official plugin, no version requirement - dependencies: '', - credentials: `[default] + display_name: 'Route 53 (Amazon)', + package_name: 'certbot-dns-route53', + version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version + dependencies: '', + credentials: `[default] aws_access_key_id=AKIAIOSFODNN7EXAMPLE aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`, full_plugin_name: 'dns-route53', @@ -472,6 +472,16 @@ dns_transip_key_file = /etc/letsencrypt/transip-rsa.key`, full_plugin_name: 'dns-transip', }, //####################################################// + tencentcloud: { + display_name: 'Tencent Cloud', + package_name: 'certbot-dns-tencentcloud', + version_requirement: '~=2.0.0', + dependencies: '', + credentials: `dns_tencentcloud_secret_id = TENCENT_CLOUD_SECRET_ID +dns_tencentcloud_secret_key = TENCENT_CLOUD_SECRET_KEY`, + full_plugin_name: 'dns-tencentcloud', + }, + //####################################################// vultr: { display_name: 'Vultr', package_name: 'certbot-dns-vultr',