diff --git a/.github/delete-merged-branch-config.yml b/.github/delete-merged-branch-config.yml index ece354f0..3b15c997 100644 --- a/.github/delete-merged-branch-config.yml +++ b/.github/delete-merged-branch-config.yml @@ -1,4 +1,4 @@ -exclude: +exclude: - main - stable - develop diff --git a/.github/workflows/caddy-latest.yml b/.github/workflows/caddy-latest.yml index 319c8284..3e7c6c38 100644 --- a/.github/workflows/caddy-latest.yml +++ b/.github/workflows/caddy-latest.yml @@ -27,7 +27,7 @@ jobs: run: | docker buildx imagetools create --tag ${{ steps.un.outputs.un }}/nginx-proxy-manager:caddy ${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:caddy-${{ github.ref_name }} docker buildx imagetools create --tag ghcr.io/${{ steps.un.outputs.un }}/nginx-proxy-manager:caddy ghcr.io/${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:caddy-${{ github.ref_name }} - + docker buildx imagetools create --tag ${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:caddy ${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:caddy-${{ github.ref_name }} docker buildx imagetools create --tag ${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:caddy-${{ github.run_number }} ${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:caddy-${{ github.ref_name }} docker buildx imagetools create --tag ghcr.io/${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:caddy ghcr.io/${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:caddy-${{ github.ref_name }} diff --git a/.github/workflows/docker-latest.yml b/.github/workflows/docker-latest.yml index d149e40b..5bfc595a 100644 --- a/.github/workflows/docker-latest.yml +++ b/.github/workflows/docker-latest.yml @@ -27,7 +27,7 @@ jobs: run: | docker buildx imagetools create --tag ${{ steps.un.outputs.un }}/nginx-proxy-manager:latest ${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:${{ github.ref_name }} docker buildx imagetools create --tag ghcr.io/${{ steps.un.outputs.un }}/nginx-proxy-manager:latest ghcr.io/${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:${{ github.ref_name }} - + docker buildx imagetools create --tag ${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:latest ${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:${{ github.ref_name }} docker buildx imagetools create --tag ${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:${{ github.run_number }} ${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:${{ github.ref_name }} docker buildx imagetools create --tag ghcr.io/${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:latest ghcr.io/${{ steps.un.outputs.un }}/${{ steps.rn.outputs.rn }}:${{ github.ref_name }} diff --git a/.gitignore b/.gitignore index 5fa0e3bf..089a3113 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ frontend/certbot-dns-plugins.js .idea desktop.files.json package-lock.json -yarn.lock +yarn.lock desktop.ini ### macOS ### diff --git a/LICENSE b/LICENSE index 8864d4a3..5285f420 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 +Copyright (c) 2017 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index e9295588..ea955308 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ so that the barrier for entry here is low. - Faster creation of TLS certificates can be achieved by eliminating unnecessary Nginx reloads and configuration creations. - Uses OCSP Stapling for enhanced security - If using custom certificates, upload the CA/Intermediate Certificate (file name: `chain.pem`) in the `/opt/npm/tls/custom/npm-[certificate-id]` folder (manual migration may be needed) -- Resolved dnspod plugin issue +- Resolved dnspod plugin issue - To migrate manually, delete all dnspod certs and recreate them OR change the credentials file as per the template given [here](https://github.com/ZoeyVid/NPMplus/blob/develop/global/certbot-dns-plugins.js) - Smaller docker image with alpine-based distribution - Admin backend interface runs with https @@ -76,9 +76,9 @@ so that the barrier for entry here is low. - `Server` response header hidden - PHP optional, with option to add extensions; available packages can be found [here](https://pkgs.alpinelinux.org/packages?branch=v3.18&repo=community&arch=x86_64&name=php81-*) and [here](https://pkgs.alpinelinux.org/packages?branch=v3.18&repo=community&arch=x86_64&name=php82-*) - Allows different acme servers/certbot config file (/opt/npm/tls/certbot/config.ini) -- Supports up to 99 domains per cert +- Supports up to 99 domains per cert - Brotli compression can be enabled -- HTTP/2 always enabled with fixed upload +- HTTP/2 always enabled with fixed upload - Allows infinite upload size - Automatic database vacuum (only sqlite) - Automatic cleaning of old certbot certs (set FULLCLEAN to true) @@ -118,7 +118,7 @@ so that the barrier for entry here is low. 2. Set `Scheme` to `https`, `Forward Hostname / IP` to `0.0.0.0`, `Forward Port` to `1` and enable `Websockets Support` (you can also use other values, since these get fully ignored) 3. Maybe set an Access List 4. Make your TLS Settings -5. +5. a) Custom Nginx Configuration (advanced tab), which looks the following for file server: - Note: the slash at the end of the file path is important ``` diff --git a/backend/internal/certificate.js b/backend/internal/certificate.js index 58a03512..f2dbe41c 100644 --- a/backend/internal/certificate.js +++ b/backend/internal/certificate.js @@ -800,7 +800,7 @@ const internalCertificate = { } else { cmd = cmd + ' --email "' + certificate.meta.letsencrypt_email + '" '; } - + logger.info('Command:', cmd); return utils.exec(cmd) diff --git a/backend/models/certificate.js b/backend/models/certificate.js index 4f0f2ef6..4cc9496f 100644 --- a/backend/models/certificate.js +++ b/backend/models/certificate.js @@ -27,17 +27,10 @@ class Certificate extends Model { if (typeof this.meta === 'undefined') { this.meta = {}; } - - this.domain_names.sort(); } $beforeUpdate () { this.modified_on = now(); - - // Sort domain_names - if (typeof this.domain_names !== 'undefined') { - this.domain_names.sort(); - } } static get name () { diff --git a/backend/models/dead_host.js b/backend/models/dead_host.js index 2e31043a..c68edac4 100644 --- a/backend/models/dead_host.js +++ b/backend/models/dead_host.js @@ -23,17 +23,10 @@ class DeadHost extends Model { if (typeof this.meta === 'undefined') { this.meta = {}; } - - this.domain_names.sort(); } $beforeUpdate () { this.modified_on = now(); - - // Sort domain_names - if (typeof this.domain_names !== 'undefined') { - this.domain_names.sort(); - } } static get name () { diff --git a/backend/models/proxy_host.js b/backend/models/proxy_host.js index d84181cf..8a405e41 100644 --- a/backend/models/proxy_host.js +++ b/backend/models/proxy_host.js @@ -24,17 +24,10 @@ class ProxyHost extends Model { if (typeof this.meta === 'undefined') { this.meta = {}; } - - this.domain_names.sort(); } $beforeUpdate () { this.modified_on = now(); - - // Sort domain_names - if (typeof this.domain_names !== 'undefined') { - this.domain_names.sort(); - } } static get name () { diff --git a/backend/models/redirection_host.js b/backend/models/redirection_host.js index c90a6de6..3d75e1fd 100644 --- a/backend/models/redirection_host.js +++ b/backend/models/redirection_host.js @@ -30,11 +30,6 @@ class RedirectionHost extends Model { $beforeUpdate () { this.modified_on = now(); - - // Sort domain_names - if (typeof this.domain_names !== 'undefined') { - this.domain_names.sort(); - } } static get name () { diff --git a/backend/schema/endpoints/certificates.json b/backend/schema/endpoints/certificates.json index 955ca75c..aea3e43c 100644 --- a/backend/schema/endpoints/certificates.json +++ b/backend/schema/endpoints/certificates.json @@ -53,12 +53,12 @@ }, "propagation_seconds": { "anyOf": [ - { + { "type": "integer", - "minimum": 0 + "minimum": 0 } ] - + } } } diff --git a/backend/templates/default.conf b/backend/templates/default.conf index 10b3941c..87970bad 100644 --- a/backend/templates/default.conf +++ b/backend/templates/default.conf @@ -4,22 +4,22 @@ server { listen 80 default_server; listen [::]:80 default_server; - + listen 443 ssl default_server; listen [::]:443 ssl default_server; - + listen 443 quic reuseport default_server; listen [::]:443 quic reuseport default_server; more_set_headers 'Alt-Svc: h3=":443"; ma=86400'; - + server_name _; - + include conf.d/include/brotli.conf; include conf.d/include/force-tls.conf; include conf.d/include/tls-ciphers.conf; include conf.d/include/acme-challenge.conf; include conf.d/include/block-exploits.conf; - + #ssl_certificate ; #ssl_certificate_key ; #ssl_trusted_certificate ; diff --git a/backend/templates/proxy_host.conf b/backend/templates/proxy_host.conf index f205d541..ddb35630 100644 --- a/backend/templates/proxy_host.conf +++ b/backend/templates/proxy_host.conf @@ -26,7 +26,7 @@ server { include conf.d/include/block-exploits.conf; {{ advanced_config }} - + {% if use_default_location %} location / { include conf.d/include/acme-challenge.conf; diff --git a/frontend/js/app/api.js b/frontend/js/app/api.js index 6e33a6dc..c9d554f1 100644 --- a/frontend/js/app/api.js +++ b/frontend/js/app/api.js @@ -143,7 +143,7 @@ function FileUpload(path, fd) { reject(new Error('Upload failed: ' + JSON.parse(xhr.responseText).error.message)); } catch (err) { reject(new Error('Upload failed: ' + xhr.status)); - } + } } else { resolve(xhr.responseText); } diff --git a/frontend/js/app/nginx/access/form.js b/frontend/js/app/nginx/access/form.js index b9eba8b3..4c2a0c44 100644 --- a/frontend/js/app/nginx/access/form.js +++ b/frontend/js/app/nginx/access/form.js @@ -108,7 +108,7 @@ module.exports = Mn.View.extend({ this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); }); }, - 'click @ui.access_add': function (e) { + 'click @ui.access_add': function (e) { e.preventDefault(); let clients = this.model.get('clients'); @@ -117,7 +117,7 @@ module.exports = Mn.View.extend({ collection: new Backbone.Collection(clients) })); }, - 'click @ui.auth_add': function (e) { + 'click @ui.auth_add': function (e) { e.preventDefault(); let items = this.model.get('items'); diff --git a/frontend/js/app/nginx/certificates/form.js b/frontend/js/app/nginx/certificates/form.js index 4ab537c4..f545230b 100644 --- a/frontend/js/app/nginx/certificates/form.js +++ b/frontend/js/app/nginx/certificates/form.js @@ -47,7 +47,7 @@ module.exports = Mn.View.extend({ other_intermediate_certificate: '#other_intermediate_certificate', other_intermediate_certificate_label: '#other_intermediate_certificate_label' }, - + events: { 'change @ui.dns_challenge_switch': function () { const checked = this.ui.dns_challenge_switch.prop('checked'); @@ -63,7 +63,7 @@ module.exports = Mn.View.extend({ this.ui.dns_provider.prop('required', false); this.ui.dns_provider_credentials.prop('required', false); this.ui.dns_challenge_content.hide(); - this.ui.test_domains_container.show(); + this.ui.test_domains_container.show(); } }, @@ -75,10 +75,10 @@ module.exports = Mn.View.extend({ this.ui.credentials_file_content.show(); } else { this.ui.dns_provider_credentials.prop('required', false); - this.ui.credentials_file_content.hide(); + this.ui.credentials_file_content.hide(); } }, - + 'click @ui.save': function (e) { e.preventDefault(); this.ui.le_error_info.hide(); @@ -97,7 +97,7 @@ module.exports = Mn.View.extend({ if (typeof data.meta === 'undefined') data.meta = {}; let domain_err = false; - if (!data.meta.dns_challenge) { + if (!data.meta.dns_challenge) { data.domain_names.split(',').map(function (name) { if (name.match(/\*/im)) { domain_err = true; @@ -119,7 +119,7 @@ module.exports = Mn.View.extend({ data.meta.dns_provider_credentials = undefined; data.meta.propagation_seconds = undefined; } else { - if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined; + if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined; } if (typeof data.domain_names === 'string' && data.domain_names) { @@ -278,7 +278,7 @@ module.exports = Mn.View.extend({ createFilter: /^(?:\*\.)?(?:[^.*]+\.?)+[^.]$/ }); this.ui.dns_challenge_content.hide(); - this.ui.credentials_file_content.hide(); + this.ui.credentials_file_content.hide(); this.ui.loader_content.hide(); this.ui.le_error_info.hide(); if (this.ui.domain_names[0]) { diff --git a/frontend/js/app/nginx/certificates/list/item.js b/frontend/js/app/nginx/certificates/list/item.js index 6e6cef40..db273e90 100644 --- a/frontend/js/app/nginx/certificates/list/item.js +++ b/frontend/js/app/nginx/certificates/list/item.js @@ -32,7 +32,7 @@ module.exports = Mn.View.extend({ let win = window.open($(e.currentTarget).attr('rel'), '_blank'); win.focus(); }, - + 'click @ui.download': function (e) { e.preventDefault(); App.Api.Nginx.Certificates.download(this.model.get('id')); diff --git a/frontend/js/app/nginx/dead/form.js b/frontend/js/app/nginx/dead/form.js index c8899398..e0c4bb76 100644 --- a/frontend/js/app/nginx/dead/form.js +++ b/frontend/js/app/nginx/dead/form.js @@ -87,7 +87,7 @@ module.exports = Mn.View.extend({ } else { this.ui.dns_provider.prop('required', false); this.ui.dns_provider_credentials.prop('required', false); - this.ui.dns_challenge_content.hide(); + this.ui.dns_challenge_content.hide(); } }, @@ -99,7 +99,7 @@ module.exports = Mn.View.extend({ this.ui.credentials_file_content.show(); } else { this.ui.dns_provider_credentials.prop('required', false); - this.ui.credentials_file_content.hide(); + this.ui.credentials_file_content.hide(); } }, @@ -124,13 +124,13 @@ module.exports = Mn.View.extend({ if (typeof data.meta === 'undefined') data.meta = {}; data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1; data.meta.dns_challenge = data.meta.dns_challenge == 1; - + if(!data.meta.dns_challenge){ data.meta.dns_provider = undefined; data.meta.dns_provider_credentials = undefined; data.meta.propagation_seconds = undefined; } else { - if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined; + if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined; } if (typeof data.domain_names === 'string' && data.domain_names) { @@ -138,7 +138,7 @@ module.exports = Mn.View.extend({ } // Check for any domain names containing wildcards, which are not allowed with letsencrypt - if (data.certificate_id === 'new') { + if (data.certificate_id === 'new') { let domain_err = false; if (!data.meta.dns_challenge) { data.domain_names.map(function (name) { diff --git a/frontend/js/app/nginx/proxy/form.js b/frontend/js/app/nginx/proxy/form.js index a6910153..45d78583 100644 --- a/frontend/js/app/nginx/proxy/form.js +++ b/frontend/js/app/nginx/proxy/form.js @@ -101,7 +101,7 @@ module.exports = Mn.View.extend({ } else { this.ui.dns_provider.prop('required', false); this.ui.dns_provider_credentials.prop('required', false); - this.ui.dns_challenge_content.hide(); + this.ui.dns_challenge_content.hide(); } }, @@ -113,13 +113,13 @@ module.exports = Mn.View.extend({ this.ui.credentials_file_content.show(); } else { this.ui.dns_provider_credentials.prop('required', false); - this.ui.credentials_file_content.hide(); + this.ui.credentials_file_content.hide(); } }, 'click @ui.add_location_btn': function (e) { e.preventDefault(); - + const model = new ProxyLocationModel.Model(); this.locationsCollection.add(model); }, @@ -155,17 +155,17 @@ module.exports = Mn.View.extend({ data.hsts_enabled = !!data.hsts_enabled; data.hsts_subdomains = !!data.hsts_subdomains; data.ssl_forced = !!data.ssl_forced; - + if (typeof data.meta === 'undefined') data.meta = {}; data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1; data.meta.dns_challenge = data.meta.dns_challenge == 1; - + if(!data.meta.dns_challenge){ data.meta.dns_provider = undefined; data.meta.dns_provider_credentials = undefined; data.meta.propagation_seconds = undefined; } else { - if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined; + if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined; } if (typeof data.domain_names === 'string' && data.domain_names) { @@ -173,7 +173,7 @@ module.exports = Mn.View.extend({ } // Check for any domain names containing wildcards, which are not allowed with letsencrypt - if (data.certificate_id === 'new') { + if (data.certificate_id === 'new') { let domain_err = false; if (!data.meta.dns_challenge) { data.domain_names.map(function (name) { diff --git a/frontend/js/app/nginx/proxy/location-item.ejs b/frontend/js/app/nginx/proxy/location-item.ejs index 466cb9ba..6124c8a1 100644 --- a/frontend/js/app/nginx/proxy/location-item.ejs +++ b/frontend/js/app/nginx/proxy/location-item.ejs @@ -61,4 +61,4 @@ <%- i18n('locations', 'delete') %> - + diff --git a/frontend/js/app/nginx/redirection/form.js b/frontend/js/app/nginx/redirection/form.js index 29e90f49..ad677919 100644 --- a/frontend/js/app/nginx/redirection/form.js +++ b/frontend/js/app/nginx/redirection/form.js @@ -87,7 +87,7 @@ module.exports = Mn.View.extend({ } else { this.ui.dns_provider.prop('required', false); this.ui.dns_provider_credentials.prop('required', false); - this.ui.dns_challenge_content.hide(); + this.ui.dns_challenge_content.hide(); } }, @@ -99,7 +99,7 @@ module.exports = Mn.View.extend({ this.ui.credentials_file_content.show(); } else { this.ui.dns_provider_credentials.prop('required', false); - this.ui.credentials_file_content.hide(); + this.ui.credentials_file_content.hide(); } }, @@ -122,17 +122,17 @@ module.exports = Mn.View.extend({ data.hsts_enabled = !!data.hsts_enabled; data.hsts_subdomains = !!data.hsts_subdomains; data.ssl_forced = !!data.ssl_forced; - + if (typeof data.meta === 'undefined') data.meta = {}; data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1; data.meta.dns_challenge = data.meta.dns_challenge == 1; - + if(!data.meta.dns_challenge){ data.meta.dns_provider = undefined; data.meta.dns_provider_credentials = undefined; data.meta.propagation_seconds = undefined; } else { - if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined; + if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined; } if (typeof data.domain_names === 'string' && data.domain_names) { @@ -140,7 +140,7 @@ module.exports = Mn.View.extend({ } // Check for any domain names containing wildcards, which are not allowed with letsencrypt - if (data.certificate_id === 'new') { + if (data.certificate_id === 'new') { let domain_err = false; if (!data.meta.dns_challenge) { data.domain_names.map(function (name) { @@ -153,7 +153,7 @@ module.exports = Mn.View.extend({ if (domain_err) { alert(i18n('ssl', 'no-wildcard-without-dns')); return; - } + } } else { data.certificate_id = parseInt(data.certificate_id, 10); } diff --git a/frontend/scss/tabler-extra.scss b/frontend/scss/tabler-extra.scss index 3ddd0ed4..fe757ba3 100644 --- a/frontend/scss/tabler-extra.scss +++ b/frontend/scss/tabler-extra.scss @@ -3,7 +3,7 @@ $yellow: #f1c40f; $blue: #467fcf; $pink: #f66d9b; -.tag { +.tag { margin-bottom: .5em; margin-right: .5em; } diff --git a/global/certbot-dns-plugins.js b/global/certbot-dns-plugins.js index 37c8f88d..693b21b8 100644 --- a/global/certbot-dns-plugins.js +++ b/global/certbot-dns-plugins.js @@ -240,7 +240,7 @@ dns_godaddy_key = abcdef0123456789abcdef01234567abcdef0123`, }`, full_plugin_name: 'dns-google', }, - //####################################################// + //####################################################// googledomains: { display_name: 'GoogleDomainsDNS', package_name: 'certbot-dns-google-domains', diff --git a/rootfs/bin/aio.sh b/rootfs/bin/aio.sh index d6452d9b..a4e2554a 100755 --- a/rootfs/bin/aio.sh +++ b/rootfs/bin/aio.sh @@ -1,6 +1,6 @@ #!/bin/sh -if [ "$NC_AIO" = "true" ] && [ ! -f /data/etc/aio.lock ]; then +if [ "$NC_AIO" = "true" ] && [ ! -f /data/etc/aio.lock ]; then while [ "$(healthcheck.sh)" != "OK" ]; do sleep 10s; done curl -POST http://127.0.0.1:48693/nginx/proxy-hosts -sH 'Content-Type: application/json' -d '{"domain_names":["'"$NC_DOMAIN"'"],"forward_scheme":"http","forward_host":"127.0.0.1","forward_port":11000,"allow_websocket_upgrade":true,"access_list_id":"0","certificate_id":"new","ssl_forced":true,"http2_support":true,"hsts_enabled":true,"hsts_subdomains":true,"meta":{"letsencrypt_email":"","letsencrypt_agree":true,"dns_challenge":false},"advanced_config":"","locations":[],"block_exploits":false,"caching_enabled":false}' -H "Authorization: Bearer $(curl -POST http://127.0.0.1:48693/tokens -sH 'Content-Type: application/json' -d '{"identity":"admin@example.com","secret":"iArhP1j7p1P6TA92FA2FMbbUGYqwcYzxC4AVEe12Wbi94FY9gNN62aKyF1shrvG4NycjjX9KfmDQiwkLZH1ZDR9xMjiG2QmoHXi"}' | jq -r .token)" touch /data/etc/aio.lock diff --git a/rootfs/bin/launch.sh b/rootfs/bin/launch.sh index 0be3e4bb..6b78759e 100755 --- a/rootfs/bin/launch.sh +++ b/rootfs/bin/launch.sh @@ -2,11 +2,11 @@ echo " ------------------------------------- - _ _ ___ __ __ _ + _ _ ___ __ __ _ | \ || . \| \ \ ___ | | _ _ ___ | || _/| || . \| || | |[_-[ |_\_||_| |_|_|_|| _/|_| \__|/__/ - |_| + |_| ------------------------------------- Version: $(jq -r .version /app/package.json) Date: $(date) diff --git a/rootfs/nftd/styles.css b/rootfs/nftd/styles.css index cfcd970b..100a7042 100644 --- a/rootfs/nftd/styles.css +++ b/rootfs/nftd/styles.css @@ -140,7 +140,7 @@ tr { border: 0; } tr:hover td { - color: #FFFFFF; + color: #FFFFFF; background: #3D4351; } tr td:first-of-type {