Compare commits

...

64 Commits

Author SHA1 Message Date
milad nazari
04636b71a9 add feature: set default server 2024-12-22 01:49:05 +03:30
milad nazari
1353937c36 fix copy address 2024-12-21 21:12:24 +03:30
milad nazari
f68c1b7c29 add Diffie-Hellman Parameters to cipher suites 2024-12-21 21:05:09 +03:30
milad nazari
32e0784865 support more cipher suites 2024-12-21 20:20:54 +03:30
milad nazari
f386f6b640 remove elliptic-curve 2024-12-14 01:40:01 +03:30
milad nazari
5ba7363c9e fix ssl cipher bug 2024-12-13 11:30:58 +03:30
milad nazari
2e45444328 change ssl_ciphers for more compatibility 2024-12-12 23:48:51 +03:30
milad nazari
eb5c51a657 add support more cipher suites
this cipher suites need for old iot devices
2024-12-12 20:42:22 +03:30
milad nazari
cb795565ea add ssl_key_type in swagger
fix ci test error
2024-12-12 12:08:03 +03:30
milad nazari
04b3608b4e remove elliptic-curve from certbot command options 2024-12-12 01:49:57 +03:30
milad nazari
111fc287eb Revert "add elliptic-curve"
This reverts commit 95a94a4f8c.
2024-12-12 01:49:19 +03:30
milad nazari
95a94a4f8c add elliptic-curve 2024-12-12 01:15:39 +03:30
milad nazari
5e7b69c396 add update cipher suites 2024-12-12 00:46:14 +03:30
milad nazari
2723de24fd add ssl_ecdh_curve for more compatibility 2024-12-11 23:31:39 +03:30
milad nazari
891877afb6 fix ssl key-type certificate 2024-12-11 11:51:58 +03:30
milad nazari
8e9e033a72 fix indent: tab to space 2024-12-09 11:30:10 +03:30
milad nazari
e6ec74c2f7 feat: add support for selecting SSL key type (ECDSA/RSA)
Added the ability to specify the SSL key type (ECDSA or RSA) for each site in the Nginx Proxy Manager. This enhancement is particularly useful for environments with IoT devices that have limitations with specific key types, such as RSA-only support. The implementation includes:

- Backend support for storing and validating the `ssl_key_type` field.
- Swagger schema updated to validate the new input.
- Frontend update to allow users to select the SSL key type via a dropdown menu.

This feature ensures greater flexibility and compatibility in managing SSL certificates for diverse setups.
2024-12-09 11:27:52 +03:30
jc21
b3de76c945 Merge pull request #4192 from badkeyy/bugfix/fix-user-edit-email-format-check
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
Enforce email format when editing user
2024-12-04 14:50:42 +10:00
jc21
fcf4117f8e Merge pull request #4206 from badkeyy/bugfix/update-dashboard-stats-on-change
Update the dashboard stats every time the dashboard is shown
2024-12-04 13:08:21 +10:00
Julian Gassner
d26e8c1d0c Change onRender function to always update the dashboard stats 2024-12-04 03:45:56 +01:00
Julian Gassner
19ed4c1212 Change click to submit 2024-12-04 03:08:49 +01:00
Julian Gassner
03018d252b Merge branch 'NginxProxyManager:develop' into bugfix/fix-user-edit-email-format-check 2024-12-04 01:58:08 +01:00
jc21
8351dd41f6 Merge pull request #4199 from NginxProxyManager/dependabot/npm_and_yarn/test/cross-spawn-7.0.6
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
Bump cross-spawn from 7.0.3 to 7.0.6 in /test
2024-12-02 10:45:00 +10:00
jc21
97212f2686 Merge pull request #4123 from NginxProxyManager/dependabot/npm_and_yarn/frontend/elliptic-6.6.0
Bump elliptic from 6.5.7 to 6.6.0 in /frontend
2024-12-02 10:44:20 +10:00
dependabot[bot]
fe068a8b51 Bump cross-spawn from 7.0.3 to 7.0.6 in /test
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-01 22:49:09 +00:00
jc21
61e2bde98f Merge pull request #4184 from NginxProxyManager/dependabot/npm_and_yarn/backend/cross-spawn-7.0.6
Bump cross-spawn from 7.0.3 to 7.0.6 in /backend
2024-12-02 08:48:08 +10:00
Julian Gassner
81c9038929 Refactor user form structure 2024-11-27 18:27:11 +01:00
jc21
4ea50ca40c Merge pull request #4126 from jonasrdl/remove-deprecated-version-line
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
docs(setup): Remove deprecated version from docker-compose.yml
2024-11-26 07:37:41 +10:00
jc21
53ed12bcf2 Merge pull request #4163 from Jasparigus/stream_error_correction
Fix Container Bootloop if Stream is used for http/https ports
2024-11-26 07:37:14 +10:00
jc21
cb3e4ed59c Merge pull request #4137 from irexyc/add-woff2-asset
Add woff2 format to assets.conf for Cache Assets
2024-11-26 07:35:57 +10:00
jc21
b20dc5eade Merge pull request #4167 from NginxProxyManager/dependabot/npm_and_yarn/test/eslint/plugin-kit-0.2.3
Bump @eslint/plugin-kit from 0.2.0 to 0.2.3 in /test
2024-11-26 07:35:10 +10:00
jc21
586afc0c91 Merge pull request #4187 from kerstenremco/avatar
Fix entries of a deleted user break the UI
2024-11-26 07:31:03 +10:00
Remco Kersten
93ea17a9bb Fix entries of a deleted user break the UI 2024-11-25 20:37:49 +01:00
dependabot[bot]
2075f98cad Bump cross-spawn from 7.0.3 to 7.0.6 in /backend
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-24 03:36:44 +00:00
jc21
07a4e5791f Merge pull request #4179 from tametsi/develop
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
Return generic auth error to prevent user enumeration attacks
2024-11-23 22:39:37 +10:00
tametsi
640a1eeb68 Return generic auth error to prevent user enumeration attacks
On invalid user/password error the error message "Invalid email or password" is returned.
Thereby, no information about the existence of the user is given.
2024-11-22 10:37:09 +01:00
dependabot[bot]
20646e7bb5 Bump @eslint/plugin-kit from 0.2.0 to 0.2.3 in /test
Bumps [@eslint/plugin-kit](https://github.com/eslint/rewrite) from 0.2.0 to 0.2.3.
- [Release notes](https://github.com/eslint/rewrite/releases)
- [Changelog](https://github.com/eslint/rewrite/blob/main/release-please-config.json)
- [Commits](https://github.com/eslint/rewrite/compare/core-v0.2.0...plugin-kit-v0.2.3)

---
updated-dependencies:
- dependency-name: "@eslint/plugin-kit"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-15 21:19:05 +00:00
Jasper Stubbe
87998a03ce Fix bootloop if stream is used for http/https port 2024-11-14 11:39:48 -08:00
irexyc
a0b26b9e98 Add woff2 format to assets.conf for Cache Assets 2024-11-04 20:01:39 +08:00
Jonas Riedel
d6791f4e38 docs(setup): Remove deprecated version from docker-compose.yml 2024-10-31 11:25:38 +01:00
dependabot[bot]
62c94f3099 Bump elliptic from 6.5.7 to 6.6.0 in /frontend
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.7 to 6.6.0.
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.7...v6.6.0)

---
updated-dependencies:
- dependency-name: elliptic
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-31 02:19:58 +00:00
jc21
25a26d6175 Merge pull request #4112 from prospo/develop
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
feat: Add leaseweb to certbot-dns-plugins
2024-10-30 14:40:20 +10:00
jc21
17246e418f Merge pull request #4118 from mitossoft-rd/patch-1
Remove variable usage from proxy_pass directive to fix resolution issues
2024-10-30 14:39:48 +10:00
mitossoft-rd
f7d3ca0b07 Cleaning unused variable. 2024-10-28 15:18:54 +03:00
mitossoft-rd
a55de386e7 Fix URL format 2024-10-28 15:15:08 +03:00
mitossoft-rd
e9d4f5b827 Remove variable usage from proxy_pass directive to fix resolution issues
By using a static URL, the backend server can be accessed reliably, avoiding the common 404 errors or "no resolver defined" issues seen when variables are used.
2024-10-28 02:59:23 +03:00
Emil
1c1cee3836 feat: Add leaseweb to certbot-dns-plugins 2024-10-25 13:25:09 +00:00
jc21
eaf6335694 Merge pull request #4106 from dreik/develop
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
http2 directive migration
2024-10-25 08:53:08 +10:00
jc21
ffe05ebd41 Merge pull request #4108 from chrismaffey/patch-2
Update put.json
2024-10-25 08:06:50 +10:00
Chris Maffey
2e9a4f1aed Update put.json
Password can be left blank for updates.  Otherwise you have to reenter the password every time you save the auth list
2024-10-24 17:29:16 +13:00
jc21
d17c85e4c8 Merge pull request #4107 from chrismaffey/patch-1
Update _access.conf
2024-10-24 11:31:12 +10:00
Chris Maffey
dad8d0ca00 Update _access.conf
the pass_auth and satisfy_any properties and now boolean true/false, they do not == 1 so the switching in this template breaks
2024-10-24 14:04:17 +13:00
Sergey 'dreik' Kolesnik
d7e0558a35 http2 directive
to reduce warns in logs
2024-10-24 01:30:14 +03:00
jc21
ee41bb5562 Merge pull request #4078 from Guiorgy/patch-1
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
normalize indentations in certbot-dns-plugins.json
2024-10-22 10:14:31 +10:00
jc21
0cf6b9caa4 Merge pull request #4084 from ttodua/patch-1
doc(site) - default credentials change
2024-10-22 10:14:11 +10:00
T. Todua
68a9baf206 minor 2024-10-18 15:35:15 +04:00
T. Todua
d92421d098 doc(site) - default credentials change 2024-10-18 15:33:32 +04:00
Guiorgy
96c58b203e normalize indentations in certbot-dns-plugins.json 2024-10-17 15:34:04 +04:00
Jamie Curnow
d499e2bfef Push PR and github branch builds to separate docker image
All checks were successful
Close stale issues and PRs / stale (push) Successful in 4s
2024-10-17 10:00:12 +10:00
Jamie Curnow
2f9e062718 bump version 2024-10-17 09:05:25 +10:00
Jamie Curnow
edbed1af90 Adds tests for settings endpoints
and reenables dns cert test
and fixes problems with schema
2024-10-17 08:48:47 +10:00
jc21
8497022e41 Merge pull request #4076 from Nephiel/4074-fix-1
All checks were successful
Close stale issues and PRs / stale (push) Successful in 5s
Fix schema validation errors
2024-10-17 07:07:05 +10:00
Nephiel
fa2c814fcb Fix schema validation in Default Site
Should solve error `data/value must match exactly one schema in oneOf` when setting the Default Site to 404 or 444. #4074
2024-10-16 19:09:14 +00:00
Nephiel
d96a3987c0 Fix forward_scheme validation in Redirection Host
Should solve error `data/forward_scheme must be equal to one of the allowed values` when configuring a Redirection Host with scheme set to `auto`. #4074
2024-10-16 19:04:50 +00:00
61 changed files with 817 additions and 421 deletions

View File

@@ -1 +1 @@
2.12.0 2.12.1

10
Jenkinsfile vendored
View File

@@ -43,7 +43,7 @@ pipeline {
steps { steps {
script { script {
// Defaults to the Branch name, which is applies to all branches AND pr's // Defaults to the Branch name, which is applies to all branches AND pr's
buildxPushTags = "-t docker.io/jc21/${IMAGE}:github-${BRANCH_LOWER}" buildxPushTags = "-t docker.io/nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}"
} }
} }
} }
@@ -203,7 +203,13 @@ pipeline {
} }
steps { steps {
script { script {
npmGithubPrComment("Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`\n\n**Note:** ensure you backup your NPM instance before testing this PR image! Especially if this PR contains database changes.", true) npmGithubPrComment("""Docker Image for build ${BUILD_NUMBER} is available on
[DockerHub](https://cloud.docker.com/repository/docker/nginxproxymanager/${IMAGE}-dev)
as `nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}`
**Note:** ensure you backup your NPM instance before testing this image! Especially if there are database changes
**Note:** this is a different docker image namespace than the official image
""", true)
} }
} }
} }

View File

@@ -1,7 +1,7 @@
<p align="center"> <p align="center">
<img src="https://nginxproxymanager.com/github.png"> <img src="https://nginxproxymanager.com/github.png">
<br><br> <br><br>
<img src="https://img.shields.io/badge/version-2.12.0-green.svg?style=for-the-badge"> <img src="https://img.shields.io/badge/version-2.12.1-green.svg?style=for-the-badge">
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager"> <a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge"> <img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
</a> </a>

View File

@@ -570,6 +570,7 @@ const internalCertificate = {
return internalCertificate.create(access, { return internalCertificate.create(access, {
provider: 'letsencrypt', provider: 'letsencrypt',
domain_names: data.domain_names, domain_names: data.domain_names,
ssl_key_type: data.ssl_key_type,
meta: data.meta meta: data.meta
}); });
}, },
@@ -832,6 +833,7 @@ const internalCertificate = {
const cmd = `${certbotCommand} certonly ` + const cmd = `${certbotCommand} certonly ` +
`--config '${letsencryptConfig}' ` + `--config '${letsencryptConfig}' ` +
`--key-type '${certificate.ssl_key_type}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' + '--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' + '--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-name "npm-${certificate.id}" ` + `--cert-name "npm-${certificate.id}" ` +
@@ -873,6 +875,7 @@ const internalCertificate = {
let mainCmd = certbotCommand + ' certonly ' + let mainCmd = certbotCommand + ' certonly ' +
`--config '${letsencryptConfig}' ` + `--config '${letsencryptConfig}' ` +
`--key-type '${certificate.ssl_key_type}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' + '--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' + '--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-name 'npm-${certificate.id}' ` + `--cert-name 'npm-${certificate.id}' ` +
@@ -969,6 +972,7 @@ const internalCertificate = {
const cmd = certbotCommand + ' renew --force-renewal ' + const cmd = certbotCommand + ' renew --force-renewal ' +
`--config '${letsencryptConfig}' ` + `--config '${letsencryptConfig}' ` +
`--key-type '${certificate.ssl_key_type}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' + '--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' + '--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-name 'npm-${certificate.id}' ` + `--cert-name 'npm-${certificate.id}' ` +
@@ -1002,6 +1006,7 @@ const internalCertificate = {
let mainCmd = certbotCommand + ' renew --force-renewal ' + let mainCmd = certbotCommand + ' renew --force-renewal ' +
`--config "${letsencryptConfig}" ` + `--config "${letsencryptConfig}" ` +
`--key-type '${certificate.ssl_key_type}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' + '--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' + '--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-name 'npm-${certificate.id}' ` + `--cert-name 'npm-${certificate.id}' ` +
@@ -1032,9 +1037,10 @@ const internalCertificate = {
*/ */
revokeLetsEncryptSsl: (certificate, throw_errors) => { revokeLetsEncryptSsl: (certificate, throw_errors) => {
logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', ')); logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
const mainCmd = certbotCommand + ' revoke ' + const mainCmd = certbotCommand + ' revoke ' +
`--config '${letsencryptConfig}' ` + `--config '${letsencryptConfig}' ` +
`--key-type '${certificate.ssl_key_type}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' + '--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' + '--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-path '/etc/letsencrypt/live/npm-${certificate.id}/fullchain.pem' ` + `--cert-path '/etc/letsencrypt/live/npm-${certificate.id}/fullchain.pem' ` +

View File

@@ -228,8 +228,32 @@ const internalHost = {
} }
return response; return response;
} },
/**
* Internal use only, checks to see if the there is another default server record
*
* @param {String} hostname
* @param {String} [ignore_type] 'proxy', 'redirection', 'dead'
* @param {Integer} [ignore_id] Must be supplied if type was also supplied
* @returns {Promise}
*/
checkDefaultServerNotExist: function (hostname) {
let promises = proxyHostModel
.query()
.where('default_server', true)
.andWhere('domain_names', 'not like', '%' + hostname + '%');
return Promise.resolve(promises)
.then((promises_results) => {
if (promises_results.length > 0){
return false;
}
return true;
});
}
}; };
module.exports = internalHost; module.exports = internalHost;

View File

@@ -181,9 +181,11 @@ const internalNginx = {
* @param {Object} host * @param {Object} host
* @returns {Promise} * @returns {Promise}
*/ */
generateConfig: (host_type, host) => { generateConfig: (host_type, host_row) => {
// Prevent modifying the original object:
let host = JSON.parse(JSON.stringify(host_row));
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type); const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
if (config.debug()) { if (config.debug()) {
logger.info('Generating ' + nice_host_type + ' Config:', JSON.stringify(host, null, 2)); logger.info('Generating ' + nice_host_type + ' Config:', JSON.stringify(host, null, 2));
} }

View File

@@ -43,6 +43,22 @@ const internalProxyHost = {
}); });
}); });
}) })
.then(() => {
// Get a list of the domain names and check each of them against default records
if (data.default_server){
if (data.domain_names.length > 1) {
throw new error.ValidationError('Default server cant be set for multiple domain!');
}
return internalHost
.checkDefaultServerNotExist(data.domain_names[0])
.then((result) => {
if (!result){
throw new error.ValidationError('One default server already exists');
}
});
}
})
.then(() => { .then(() => {
// At this point the domains should have been checked // At this point the domains should have been checked
data.owner_user_id = access.token.getUserId(1); data.owner_user_id = access.token.getUserId(1);
@@ -140,6 +156,22 @@ const internalProxyHost = {
}); });
} }
}) })
.then(() => {
// Get a list of the domain names and check each of them against default records
if (data.default_server){
if (data.domain_names.length > 1) {
throw new error.ValidationError('Default server cant be set for multiple domain!');
}
return internalHost
.checkDefaultServerNotExist(data.domain_names[0])
.then((result) => {
if (!result){
throw new error.ValidationError('One default server already exists');
}
});
}
})
.then(() => { .then(() => {
return internalProxyHost.get(access, {id: data.id}); return internalProxyHost.get(access, {id: data.id});
}) })
@@ -152,6 +184,7 @@ const internalProxyHost = {
if (create_certificate) { if (create_certificate) {
return internalCertificate.createQuickCertificate(access, { return internalCertificate.createQuickCertificate(access, {
domain_names: data.domain_names || row.domain_names, domain_names: data.domain_names || row.domain_names,
ssl_key_type: data.ssl_key_type || row.ssl_key_type,
meta: _.assign({}, row.meta, data.meta) meta: _.assign({}, row.meta, data.meta)
}) })
.then((cert) => { .then((cert) => {

View File

@@ -5,6 +5,8 @@ const authModel = require('../models/auth');
const helpers = require('../lib/helpers'); const helpers = require('../lib/helpers');
const TokenModel = require('../models/token'); const TokenModel = require('../models/token');
const ERROR_MESSAGE_INVALID_AUTH = 'Invalid email or password';
module.exports = { module.exports = {
/** /**
@@ -69,15 +71,15 @@ module.exports = {
}; };
}); });
} else { } else {
throw new error.AuthError('Invalid password'); throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
} }
}); });
} else { } else {
throw new error.AuthError('No password auth for user'); throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
} }
}); });
} else { } else {
throw new error.AuthError('No relevant user found'); throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
} }
}); });
}, },

View File

@@ -0,0 +1,39 @@
const migrate_name = 'identifier_for_migrate';
const logger = require('../logger').migrate;
/**
* Migrate
*
* @see http://knexjs.org/#Schema
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.up = function (knex) {
logger.info(`[${migrate_name}] Migrating Up...`);
return knex.schema.alterTable('proxy_host', (table) => {
table.enum('ssl_key_type', ['ecdsa', 'rsa']).defaultTo('ecdsa').notNullable();
}).then(() => {
logger.info(`[${migrate_name}] Column 'ssl_key_type' added to table 'proxy_host'`);
});
};
/**
* Undo Migrate
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.down = function (knex) {
logger.info(`[${migrate_name}] Migrating Down...`);
return knex.schema.alterTable('proxy_host', (table) => {
table.dropColumn('ssl_key_type');
}).then(() => {
logger.info(`[${migrate_name}] Column 'ssl_key_type' removed from table 'proxy_host'`);
});
};

View File

@@ -0,0 +1,39 @@
const migrate_name = 'identifier_for_migrate';
const logger = require('../logger').migrate;
/**
* Migrate
*
* @see http://knexjs.org/#Schema
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.up = function (knex) {
logger.info(`[${migrate_name}] Migrating Up...`);
return knex.schema.alterTable('certificate', (table) => {
table.enum('ssl_key_type', ['ecdsa', 'rsa']).defaultTo('ecdsa').notNullable();
}).then(() => {
logger.info(`[${migrate_name}] Column 'ssl_key_type' added to table 'proxy_host'`);
});
};
/**
* Undo Migrate
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.down = function (knex) {
logger.info(`[${migrate_name}] Migrating Down...`);
return knex.schema.alterTable('certificate', (table) => {
table.dropColumn('ssl_key_type');
}).then(() => {
logger.info(`[${migrate_name}] Column 'ssl_key_type' removed from table 'proxy_host'`);
});
};

View File

@@ -0,0 +1,40 @@
const migrate_name = 'identifier_for_migrate';
const logger = require('../logger').migrate;
/**
* Migrate Up
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.up = function (knex) {
logger.info(`[${migrate_name}] Migrating Up...`);
// Add default_server column to proxy_host table
return knex.schema.table('proxy_host', (table) => {
table.boolean('default_server').notNullable().defaultTo(false);
})
.then(() => {
logger.info(`[${migrate_name}] Column 'default_server' added to 'proxy_host' table`);
});
};
/**
* Migrate Down
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.down = function (knex) {
logger.info(`[${migrate_name}] Migrating Down...`);
// Remove default_server column from proxy_host table
return knex.schema.table('proxy_host', (table) => {
table.dropColumn('default_server');
})
.then(() => {
logger.info(`[${migrate_name}] Column 'default_server' removed from 'proxy_host' table`);
});
};

View File

@@ -21,6 +21,7 @@ const boolFields = [
'enabled', 'enabled',
'hsts_enabled', 'hsts_enabled',
'hsts_subdomains', 'hsts_subdomains',
'default_server',
]; ];
class ProxyHost extends Model { class ProxyHost extends Model {

View File

@@ -41,6 +41,15 @@
"owner": { "owner": {
"$ref": "./user-object.json" "$ref": "./user-object.json"
}, },
"ssl_key_type": {
"type": "string",
"enum": ["ecdsa", "rsa"],
"description": "Type of SSL key (either ecdsa or rsa)"
},
"default_server": {
"type": "boolean",
"description": "Defines if the server is the default for unmatched requests"
},
"meta": { "meta": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,

View File

@@ -23,9 +23,9 @@
"locations", "locations",
"hsts_enabled", "hsts_enabled",
"hsts_subdomains", "hsts_subdomains",
"certificate", "ssl_key_type",
"use_default_location", "default_server",
"ipv6" "certificate"
], ],
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@@ -152,11 +152,14 @@
} }
] ]
}, },
"use_default_location": { "ssl_key_type": {
"type": "boolean" "type": "string",
"enum": ["ecdsa", "rsa"],
"description": "Type of SSL key (either ecdsa or rsa)"
}, },
"ipv6": { "default_server": {
"type": "boolean" "type": "boolean",
"description": "Defines if the server is the default for unmatched requests"
} }
} }
} }

View File

@@ -28,7 +28,7 @@
}, },
"forward_scheme": { "forward_scheme": {
"type": "string", "type": "string",
"enum": ["http", "https"] "enum": ["auto", "http", "https"]
}, },
"forward_domain_name": { "forward_domain_name": {
"description": "Domain Name", "description": "Domain Name",

View File

@@ -25,7 +25,7 @@
"value": { "value": {
"description": "Value in almost any form", "description": "Value in almost any form",
"example": "congratulations", "example": "congratulations",
"oneOf": [ "anyOf": [
{ {
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
@@ -46,7 +46,10 @@
}, },
"meta": { "meta": {
"description": "Extra metadata", "description": "Extra metadata",
"example": {}, "example": {
"redirect": "http://example.com",
"html": "<h1>404</h1>"
},
"type": "object" "type": "object"
} }
} }

View File

@@ -19,7 +19,9 @@
"incoming_port": { "incoming_port": {
"type": "integer", "type": "integer",
"minimum": 1, "minimum": 1,
"maximum": 65535 "maximum": 65535,
"if": {"properties": {"tcp_forwarding": {"const": true}}},
"then": {"not": {"oneOf": [{"const": 80}, {"const": 443}]}}
}, },
"forwarding_host": { "forwarding_host": {
"anyOf": [ "anyOf": [

View File

@@ -49,8 +49,7 @@
"minLength": 1 "minLength": 1
}, },
"password": { "password": {
"type": "string", "type": "string"
"minLength": 1
} }
} }
} }

View File

@@ -94,9 +94,7 @@
"avatar": "", "avatar": "",
"roles": ["admin"] "roles": ["admin"]
}, },
"certificate": null, "certificate": null
"use_default_location": true,
"ipv6": true
} }
} }
}, },

View File

@@ -79,9 +79,7 @@
"nickname": "Admin", "nickname": "Admin",
"avatar": "", "avatar": "",
"roles": ["admin"] "roles": ["admin"]
}, }
"use_default_location": true,
"ipv6": true
} }
} }
}, },

View File

@@ -79,6 +79,12 @@
}, },
"locations": { "locations": {
"$ref": "../../../../components/proxy-host-object.json#/properties/locations" "$ref": "../../../../components/proxy-host-object.json#/properties/locations"
},
"ssl_key_type": {
"$ref": "../../../../components/proxy-host-object.json#/properties/ssl_key_type"
},
"default_server": {
"$ref": "../../../../components/proxy-host-object.json#/properties/default_server"
} }
} }
} }
@@ -129,9 +135,7 @@
"roles": ["admin"] "roles": ["admin"]
}, },
"certificate": null, "certificate": null,
"access_list": null, "access_list": null
"use_default_location": true,
"ipv6": true
} }
} }
}, },

View File

@@ -67,6 +67,12 @@
}, },
"locations": { "locations": {
"$ref": "../../../components/proxy-host-object.json#/properties/locations" "$ref": "../../../components/proxy-host-object.json#/properties/locations"
},
"ssl_key_type": {
"$ref": "../../../components/proxy-host-object.json#/properties/ssl_key_type"
},
"default_server": {
"$ref": "../../../components/proxy-host-object.json#/properties/default_server"
} }
} }
} }
@@ -114,9 +120,7 @@
"avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm", "avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
"roles": ["admin"] "roles": ["admin"]
}, },
"access_list": null, "access_list": null
"use_default_location": true,
"ipv6": true
} }
} }
}, },

View File

@@ -114,9 +114,7 @@
"avatar": "", "avatar": "",
"roles": ["admin"] "roles": ["admin"]
}, },
"certificate": null, "certificate": null
"use_default_location": true,
"ipv6": true
} }
} }
}, },

View File

@@ -99,9 +99,7 @@
"nickname": "Admin", "nickname": "Admin",
"avatar": "", "avatar": "",
"roles": ["admin"] "roles": ["admin"]
}, }
"use_default_location": true,
"ipv6": true
} }
} }
}, },

View File

@@ -129,9 +129,7 @@
"roles": ["admin"] "roles": ["admin"]
}, },
"certificate": null, "certificate": null,
"access_list": null, "access_list": null
"use_default_location": true,
"ipv6": true
} }
} }
}, },

View File

@@ -13,7 +13,8 @@
"name": "settingID", "name": "settingID",
"schema": { "schema": {
"type": "string", "type": "string",
"minLength": 1 "minLength": 1,
"enum": ["default-site"]
}, },
"required": true, "required": true,
"description": "Setting ID", "description": "Setting ID",
@@ -31,10 +32,21 @@
"minProperties": 1, "minProperties": 1,
"properties": { "properties": {
"value": { "value": {
"$ref": "../../../components/setting-object.json#/properties/value" "type": "string",
"minLength": 1,
"enum": ["congratulations", "404", "444", "redirect", "html"]
}, },
"meta": { "meta": {
"$ref": "../../../components/setting-object.json#/properties/meta" "type": "object",
"additionalProperties": false,
"properties": {
"redirect": {
"type": "string"
},
"html": {
"type": "string"
}
}
} }
} }
} }

View File

@@ -4,7 +4,7 @@
auth_basic "Authorization required"; auth_basic "Authorization required";
auth_basic_user_file /data/access/{{ access_list_id }}; auth_basic_user_file /data/access/{{ access_list_id }};
{% if access_list.pass_auth == 0 %} {% if access_list.pass_auth == 0 or access_list.pass_auth == true %}
proxy_set_header Authorization ""; proxy_set_header Authorization "";
{% endif %} {% endif %}
@@ -17,7 +17,7 @@
deny all; deny all;
# Access checks must... # Access checks must...
{% if access_list.satisfy_any == 1 %} {% if access_list.satisfy_any == 1 or access_list.satisfy_any == true %}
satisfy any; satisfy any;
{% else %} {% else %}
satisfy all; satisfy all;

View File

@@ -1,15 +1,20 @@
listen 80; listen 80{% if default_server == true %} default_server{% endif %};
{% if ipv6 -%} {% if ipv6 -%}
listen [::]:80; listen [::]:80{% if default_server == true %} default_server{% endif %};
{% else -%} {% else -%}
#listen [::]:80; #listen [::]:80;
{% endif %} {% endif %}
{% if certificate -%} {% if certificate -%}
listen 443 ssl{% if http2_support == 1 or http2_support == true %} http2{% endif %}; listen 443 ssl{% if default_server == true %} default_server{% endif %};
{% if ipv6 -%} {% if ipv6 -%}
listen [::]:443 ssl{% if http2_support == 1 or http2_support == true %} http2{% endif %}; listen [::]:443 ssl{% if default_server == true %} default_server{% endif %};
{% else -%} {% else -%}
#listen [::]:443; #listen [::]:443;
{% endif %} {% endif %}
{% endif %} {% endif %}
server_name {{ domain_names | join: " " }}; server_name {{ domain_names | join: " " }};
{% if http2_support == 1 or http2_support == true %}
http2 on;
{% else -%}
http2 off;
{% endif %}

View File

@@ -7,11 +7,7 @@
proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
set $proxy_forward_scheme {{ forward_scheme }}; proxy_pass {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }};
set $proxy_server "{{ forward_host }}";
set $proxy_port {{ forward_port }};
proxy_pass $proxy_forward_scheme://$proxy_server:$proxy_port{{ forward_path }};
{% include "_access.conf" %} {% include "_access.conf" %}
{% include "_assets.conf" %} {% include "_assets.conf" %}

View File

@@ -830,9 +830,9 @@ crc32-stream@^4.0.2:
readable-stream "^3.4.0" readable-stream "^3.4.0"
cross-spawn@^7.0.2: cross-spawn@^7.0.2:
version "7.0.3" version "7.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
dependencies: dependencies:
path-key "^3.1.0" path-key "^3.1.0"
shebang-command "^2.0.0" shebang-command "^2.0.0"

View File

@@ -53,9 +53,11 @@ COPY --from=testca /home/step/certs/root_ca.crt /etc/ssl/certs/NginxProxyManager
# Remove frontend service not required for prod, dev nginx config as well # Remove frontend service not required for prod, dev nginx config as well
RUN rm -rf /etc/s6-overlay/s6-rc.d/user/contents.d/frontend /etc/nginx/conf.d/dev.conf \ RUN rm -rf /etc/s6-overlay/s6-rc.d/user/contents.d/frontend /etc/nginx/conf.d/dev.conf \
&& chmod 644 /etc/logrotate.d/nginx-proxy-manager && chmod 644 /etc/logrotate.d/nginx-proxy-manager
COPY docker/start-container /usr/local/bin/start-container
RUN chmod +x /usr/local/bin/start-container
VOLUME [ "/data" ] VOLUME [ "/data" ]
ENTRYPOINT [ "/init" ] ENTRYPOINT [ "start-container" ]
LABEL org.label-schema.schema-version="1.0" \ LABEL org.label-schema.schema-version="1.0" \
org.label-schema.license="MIT" \ org.label-schema.license="MIT" \

View File

@@ -35,5 +35,8 @@ RUN rm -f /etc/nginx/conf.d/production.conf \
COPY --from=pebbleca /test/certs/pebble.minica.pem /etc/ssl/certs/pebble.minica.pem COPY --from=pebbleca /test/certs/pebble.minica.pem /etc/ssl/certs/pebble.minica.pem
COPY --from=testca /home/step/certs/root_ca.crt /etc/ssl/certs/NginxProxyManager.crt COPY --from=testca /home/step/certs/root_ca.crt /etc/ssl/certs/NginxProxyManager.crt
COPY start-container /usr/local/bin/start-container
RUN chmod +x /usr/local/bin/start-container
EXPOSE 80 81 443 EXPOSE 80 81 443
ENTRYPOINT [ "/init" ] ENTRYPOINT [ "start-container" ]

View File

@@ -1,7 +1,5 @@
text = True text = True
non-interactive = True non-interactive = True
webroot-path = /data/letsencrypt-acme-challenge webroot-path = /data/letsencrypt-acme-challenge
key-type = ecdsa
elliptic-curve = secp384r1
preferred-chain = ISRG Root X1 preferred-chain = ISRG Root X1
server = server =

View File

@@ -1,6 +1,4 @@
text = True text = True
non-interactive = True non-interactive = True
webroot-path = /data/letsencrypt-acme-challenge webroot-path = /data/letsencrypt-acme-challenge
key-type = ecdsa
elliptic-curve = secp384r1
preferred-chain = ISRG Root X1 preferred-chain = ISRG Root X1

View File

@@ -1,4 +1,4 @@
location ~* ^.*\.(css|js|jpe?g|gif|png|webp|woff|eot|ttf|svg|ico|css\.map|js\.map)$ { location ~* ^.*\.(css|js|jpe?g|gif|png|webp|woff|woff2|eot|ttf|svg|ico|css\.map|js\.map)$ {
if_modified_since off; if_modified_since off;
# use the public cache # use the public cache

View File

@@ -3,5 +3,7 @@ ssl_session_cache shared:SSL:50m;
# intermediate configuration. tweak to your needs. # intermediate configuration. tweak to your needs.
ssl_protocols TLSv1.2 TLSv1.3; ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; ssl_ciphers "ALL:RC4-SHA:AES128-SHA:AES256-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384:AES128-GCM-SHA256:RSA-AES256-CBC-SHA:RC4-MD5:DES-CBC3-SHA:AES256-SHA:RC4-SHA:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
ssl_prefer_server_ciphers off; ssl_prefer_server_ciphers off;
ssl_ecdh_curve X25519:prime256v1:secp384r1;
ssl_dhparam /etc/ssl/certs/dhparam.pem;

13
docker/start-container Normal file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
FILE="/etc/ssl/certs/dhparam.pem"
if [ ! -f "$FILE" ]; then
echo "the $FILE does not exist, creating..."
openssl dhparam -out "$FILE" 2048
else
echo "the $FILE already exists, skipping..."
fi
echo "run default script"
exec /init

View File

@@ -50,7 +50,6 @@ networks:
Let's look at a Portainer example: Let's look at a Portainer example:
```yml ```yml
version: '3.8'
services: services:
portainer: portainer:
@@ -92,8 +91,6 @@ This image supports the use of Docker secrets to import from files and keep sens
You can set any environment variable from a file by appending `__FILE` (double-underscore FILE) to the environmental variable name. You can set any environment variable from a file by appending `__FILE` (double-underscore FILE) to the environmental variable name.
```yml ```yml
version: '3.8'
secrets: secrets:
# Secrets are single-line text files where the sole content is the secret # Secrets are single-line text files where the sole content is the secret
# Paths in this example assume that secrets are kept in local folder called ".secrets" # Paths in this example assume that secrets are kept in local folder called ".secrets"

View File

@@ -9,7 +9,6 @@ outline: deep
Create a `docker-compose.yml` file: Create a `docker-compose.yml` file:
```yml ```yml
version: '3.8'
services: services:
app: app:
image: 'jc21/nginx-proxy-manager:latest' image: 'jc21/nginx-proxy-manager:latest'
@@ -55,7 +54,6 @@ are going to use.
Here is an example of what your `docker-compose.yml` will look like when using a MariaDB container: Here is an example of what your `docker-compose.yml` will look like when using a MariaDB container:
```yml ```yml
version: '3.8'
services: services:
app: app:
image: 'jc21/nginx-proxy-manager:latest' image: 'jc21/nginx-proxy-manager:latest'
@@ -137,5 +135,13 @@ Email: admin@example.com
Password: changeme Password: changeme
``` ```
Immediately after logging in with this default user you will be asked to modify your details and change your password. Immediately after logging in with this default user you will be asked to modify your details and change your password. You can change defaults with:
```
environment:
INITIAL_ADMIN_EMAIL: my@example.com
INITIAL_ADMIN_PASSWORD: mypassword1
```

View File

@@ -50,8 +50,7 @@ module.exports = Mn.View.extend({
onRender: function () { onRender: function () {
let view = this; let view = this;
if (typeof view.stats.hosts === 'undefined') { Api.Reports.getHostStats()
Api.Reports.getHostStats()
.then(response => { .then(response => {
if (!view.isDestroyed()) { if (!view.isDestroyed()) {
view.stats.hosts = response; view.stats.hosts = response;
@@ -61,7 +60,6 @@ module.exports = Mn.View.extend({
.catch(err => { .catch(err => {
console.log(err); console.log(err);
}); });
}
}, },
/** /**

View File

@@ -1,6 +1,6 @@
<td class="text-center"> <td class="text-center">
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>"> <div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span> <span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
</div> </div>
</td> </td>
<td> <td>

View File

@@ -1,6 +1,6 @@
<td class="text-center"> <td class="text-center">
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>"> <div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span> <span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
</div> </div>
</td> </td>
<td> <td>

View File

@@ -1,6 +1,6 @@
<td class="text-center"> <td class="text-center">
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>"> <div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span> <span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
</div> </div>
</td> </td>
<td> <td>

View File

@@ -72,7 +72,7 @@
</label> </label>
</div> </div>
</div> </div>
<div class="col-sm-12 col-md-12"> <div class="col-sm-6 col-md-6">
<div class="form-group"> <div class="form-group">
<label class="custom-switch"> <label class="custom-switch">
<input type="checkbox" class="custom-switch-input" name="allow_websocket_upgrade" value="1"<%- allow_websocket_upgrade ? ' checked' : '' %>> <input type="checkbox" class="custom-switch-input" name="allow_websocket_upgrade" value="1"<%- allow_websocket_upgrade ? ' checked' : '' %>>
@@ -81,6 +81,15 @@
</label> </label>
</div> </div>
</div> </div>
<div class="col-sm-6 col-md-6">
<div class="form-group">
<label class="custom-switch">
<input type="checkbox" class="custom-switch-input" name="default_server" value="1"<%- default_server ? ' checked' : '' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('proxy-hosts', 'default-server') %></span>
</label>
</div>
</div>
<div class="col-sm-12 col-md-12"> <div class="col-sm-12 col-md-12">
<div class="form-group"> <div class="form-group">
@@ -105,6 +114,15 @@
</select> </select>
</div> </div>
</div> </div>
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label class="form-label"><%- i18n('all-hosts', 'ssl-key-type') %></label>
<select name="ssl_key_type" class="form-control custom-select">
<option value="ecdsa" data-data="{&quot;id&quot;:&quot;ecdsa&quot;}" <%- ssl_key_type == 'ecdsa' ? 'selected' : '' %>>ECDSA</option>
<option value="rsa" data-data="{&quot;id&quot;:&quot;rsa&quot;}" <%- ssl_key_type == 'rsa' ? 'selected' : '' %>>RSA</option>
</select>
</div>
</div>
<div class="col-sm-6 col-md-6"> <div class="col-sm-6 col-md-6">
<div class="form-group"> <div class="form-group">
<label class="custom-switch"> <label class="custom-switch">

View File

@@ -167,6 +167,7 @@ module.exports = Mn.View.extend({
data.hsts_enabled = !!data.hsts_enabled; data.hsts_enabled = !!data.hsts_enabled;
data.hsts_subdomains = !!data.hsts_subdomains; data.hsts_subdomains = !!data.hsts_subdomains;
data.ssl_forced = !!data.ssl_forced; data.ssl_forced = !!data.ssl_forced;
data.default_server = !!data.default_server;
if (typeof data.meta === 'undefined') data.meta = {}; if (typeof data.meta === 'undefined') data.meta = {};
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1; data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;

View File

@@ -1,6 +1,6 @@
<td class="text-center"> <td class="text-center">
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>"> <div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span> <span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
</div> </div>
</td> </td>
<td> <td>

View File

@@ -1,6 +1,6 @@
<td class="text-center"> <td class="text-center">
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>"> <div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span> <span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
</div> </div>
</td> </td>
<td> <td>

View File

@@ -1,6 +1,6 @@
<td class="text-center"> <td class="text-center">
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>"> <div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span> <span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
</div> </div>
</td> </td>
<td> <td>

View File

@@ -1,10 +1,10 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <form>
<h5 class="modal-title"><%- i18n('users', 'form-title', {id: id}) %></h5> <div class="modal-header">
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal">&nbsp;</button> <h5 class="modal-title"><%- i18n('users', 'form-title', {id: id}) %></h5>
</div> <button type="button" class="close cancel" aria-label="Close" data-dismiss="modal">&nbsp;</button>
<div class="modal-body"> </div>
<form> <div class="modal-body">
<div class="row"> <div class="row">
<div class="col-sm-6 col-md-6"> <div class="col-sm-6 col-md-6">
<div class="form-group"> <div class="form-group">
@@ -49,10 +49,10 @@
</div> </div>
<% } %> <% } %>
</div> </div>
</form> </div>
</div> <div class="modal-footer">
<div class="modal-footer"> <button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button> <button type="submit" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
<button type="button" class="btn btn-teal save"><%- i18n('str', 'save') %></button> </div>
</div> </form>
</div> </div>

View File

@@ -19,7 +19,7 @@ module.exports = Mn.View.extend({
events: { events: {
'click @ui.save': function (e) { 'submit @ui.form': function (e) {
e.preventDefault(); e.preventDefault();
this.ui.error.hide(); this.ui.error.hide();
let view = this; let view = this;

View File

@@ -1,296 +1,298 @@
{ {
"en": { "en": {
"str": { "str": {
"email-address": "Email address", "email-address": "Email address",
"username": "Username", "username": "Username",
"password": "Password", "password": "Password",
"sign-in": "Sign in", "sign-in": "Sign in",
"sign-out": "Sign out", "sign-out": "Sign out",
"try-again": "Try again", "try-again": "Try again",
"name": "Name", "name": "Name",
"email": "Email", "email": "Email",
"roles": "Roles", "roles": "Roles",
"created-on": "Created: {date}", "created-on": "Created: {date}",
"save": "Save", "save": "Save",
"cancel": "Cancel", "cancel": "Cancel",
"close": "Close", "close": "Close",
"enable": "Enable", "enable": "Enable",
"disable": "Disable", "disable": "Disable",
"sure": "Yes I'm Sure", "sure": "Yes I'm Sure",
"disabled": "Disabled", "disabled": "Disabled",
"choose-file": "Choose file", "choose-file": "Choose file",
"source": "Source", "source": "Source",
"destination": "Destination", "destination": "Destination",
"ssl": "SSL", "ssl": "SSL",
"access": "Access", "access": "Access",
"public": "Public", "public": "Public",
"edit": "Edit", "edit": "Edit",
"delete": "Delete", "delete": "Delete",
"logs": "Logs", "logs": "Logs",
"status": "Status", "status": "Status",
"online": "Online", "online": "Online",
"offline": "Offline", "offline": "Offline",
"unknown": "Unknown", "unknown": "Unknown",
"expires": "Expires", "expires": "Expires",
"value": "Value", "value": "Value",
"please-wait": "Please wait...", "please-wait": "Please wait...",
"all": "All", "all": "All",
"any": "Any" "any": "Any"
}, },
"login": { "login": {
"title": "Login to your account" "title": "Login to your account"
}, },
"main": { "main": {
"app": "Nginx Proxy Manager", "app": "Nginx Proxy Manager",
"version": "v{version}", "version": "v{version}",
"welcome": "Welcome to Nginx Proxy Manager", "welcome": "Welcome to Nginx Proxy Manager",
"logged-in": "You are logged in as {name}", "logged-in": "You are logged in as {name}",
"unknown-error": "Error loading stuff. Please reload the app.", "unknown-error": "Error loading stuff. Please reload the app.",
"unknown-user": "Unknown User", "unknown-user": "Unknown User",
"sign-in-as": "Sign back in as {name}" "sign-in-as": "Sign back in as {name}"
}, },
"roles": { "roles": {
"title": "Roles", "title": "Roles",
"admin": "Administrator", "admin": "Administrator",
"user": "Apache Helicopter" "user": "Apache Helicopter"
}, },
"menu": { "menu": {
"dashboard": "Dashboard", "dashboard": "Dashboard",
"hosts": "Hosts" "hosts": "Hosts"
}, },
"footer": { "footer": {
"fork-me": "Fork me on Github", "fork-me": "Fork me on Github",
"copy": "&copy; 2024 <a href=\"{url}\" target=\"_blank\">jc21.com</a>.", "copy": "&copy; 2024 <a href=\"{url}\" target=\"_blank\">jc21.com</a>.",
"theme": "Theme by <a href=\"{url}\" target=\"_blank\">Tabler</a>" "theme": "Theme by <a href=\"{url}\" target=\"_blank\">Tabler</a>"
}, },
"dashboard": { "dashboard": {
"title": "Hi {name}" "title": "Hi {name}"
}, },
"all-hosts": { "all-hosts": {
"empty-subtitle": "{manage, select, true{Why don't you create one?} other{And you don't have permission to create one.}}", "empty-subtitle": "{manage, select, true{Why don't you create one?} other{And you don't have permission to create one.}}",
"details": "Details", "details": "Details",
"enable-ssl": "Enable SSL", "enable-ssl": "Enable SSL",
"force-ssl": "Force SSL", "force-ssl": "Force SSL",
"http2-support": "HTTP/2 Support", "http2-support": "HTTP/2 Support",
"domain-names": "Domain Names", "domain-names": "Domain Names",
"cert-provider": "Certificate Provider", "cert-provider": "Certificate Provider",
"block-exploits": "Block Common Exploits", "block-exploits": "Block Common Exploits",
"caching-enabled": "Cache Assets", "caching-enabled": "Cache Assets",
"ssl-certificate": "SSL Certificate", "ssl-certificate": "SSL Certificate",
"none": "None", "ssl-key-type": "SSL Key Type",
"new-cert": "Request a new SSL Certificate", "none": "None",
"with-le": "with Let's Encrypt", "new-cert": "Request a new SSL Certificate",
"no-ssl": "This host will not use HTTPS", "with-le": "with Let's Encrypt",
"advanced": "Advanced", "no-ssl": "This host will not use HTTPS",
"advanced-warning": "Enter your custom Nginx configuration here at your own risk!", "advanced": "Advanced",
"advanced-config": "Custom Nginx Configuration", "advanced-warning": "Enter your custom Nginx configuration here at your own risk!",
"advanced-config-var-headline": "These proxy details are available as nginx variables:", "advanced-config": "Custom Nginx Configuration",
"advanced-config-header-info": "Please note, that any add_header or set_header directives added here will not be used by nginx. You will have to add a custom location '/' and add the header in the custom config there.", "advanced-config-var-headline": "These proxy details are available as nginx variables:",
"hsts-enabled": "HSTS Enabled", "advanced-config-header-info": "Please note, that any add_header or set_header directives added here will not be used by nginx. You will have to add a custom location '/' and add the header in the custom config there.",
"hsts-subdomains": "HSTS Subdomains", "hsts-enabled": "HSTS Enabled",
"locations": "Custom locations" "hsts-subdomains": "HSTS Subdomains",
}, "locations": "Custom locations"
"locations": { },
"new_location": "Add location", "locations": {
"path": "/path", "new_location": "Add location",
"location_label": "Define location", "path": "/path",
"delete": "Delete" "location_label": "Define location",
}, "delete": "Delete"
"ssl": { },
"letsencrypt": "Let's Encrypt", "ssl": {
"other": "Custom", "letsencrypt": "Let's Encrypt",
"none": "HTTP only", "other": "Custom",
"letsencrypt-email": "Email Address for Let's Encrypt", "none": "HTTP only",
"letsencrypt-agree": "I Agree to the <a href=\"{url}\" target=\"_blank\">Let's Encrypt Terms of Service</a>", "letsencrypt-email": "Email Address for Let's Encrypt",
"delete-ssl": "The SSL certificates attached will NOT be removed, they will need to be removed manually.", "letsencrypt-agree": "I Agree to the <a href=\"{url}\" target=\"_blank\">Let's Encrypt Terms of Service</a>",
"hosts-warning": "These domains must be already configured to point to this installation", "delete-ssl": "The SSL certificates attached will NOT be removed, they will need to be removed manually.",
"no-wildcard-without-dns": "Cannot request Let's Encrypt Certificate for wildcard domains when not using DNS challenge", "hosts-warning": "These domains must be already configured to point to this installation",
"dns-challenge": "Use a DNS Challenge", "no-wildcard-without-dns": "Cannot request Let's Encrypt Certificate for wildcard domains when not using DNS challenge",
"certbot-warning": "This section requires some knowledge about Certbot and its DNS plugins. Please consult the respective plugins documentation.", "dns-challenge": "Use a DNS Challenge",
"dns-provider": "DNS Provider", "certbot-warning": "This section requires some knowledge about Certbot and its DNS plugins. Please consult the respective plugins documentation.",
"please-choose": "Please Choose...", "dns-provider": "DNS Provider",
"credentials-file-content": "Credentials File Content", "please-choose": "Please Choose...",
"credentials-file-content-info": "This plugin requires a configuration file containing an API token or other credentials to your provider", "credentials-file-content": "Credentials File Content",
"stored-as-plaintext-info": "This data will be stored as plaintext in the database and in a file!", "credentials-file-content-info": "This plugin requires a configuration file containing an API token or other credentials to your provider",
"propagation-seconds": "Propagation Seconds", "stored-as-plaintext-info": "This data will be stored as plaintext in the database and in a file!",
"propagation-seconds-info": "Leave empty to use the plugins default value. Number of seconds to wait for DNS propagation.", "propagation-seconds": "Propagation Seconds",
"processing-info": "Processing... This might take a few minutes.", "propagation-seconds-info": "Leave empty to use the plugins default value. Number of seconds to wait for DNS propagation.",
"passphrase-protection-support-info": "Key files protected with a passphrase are not supported." "processing-info": "Processing... This might take a few minutes.",
}, "passphrase-protection-support-info": "Key files protected with a passphrase are not supported."
"proxy-hosts": { },
"title": "Proxy Hosts", "proxy-hosts": {
"empty": "There are no Proxy Hosts", "title": "Proxy Hosts",
"add": "Add Proxy Host", "empty": "There are no Proxy Hosts",
"form-title": "{id, select, undefined{New} other{Edit}} Proxy Host", "add": "Add Proxy Host",
"forward-scheme": "Scheme", "form-title": "{id, select, undefined{New} other{Edit}} Proxy Host",
"forward-host": "Forward Hostname / IP", "forward-scheme": "Scheme",
"forward-port": "Forward Port", "forward-host": "Forward Hostname / IP",
"delete": "Delete Proxy Host", "forward-port": "Forward Port",
"delete-confirm": "Are you sure you want to delete the Proxy host for: <strong>{domains}</strong>?", "delete": "Delete Proxy Host",
"help-title": "What is a Proxy Host?", "delete-confirm": "Are you sure you want to delete the Proxy host for: <strong>{domains}</strong>?",
"help-content": "A Proxy Host is the incoming endpoint for a web service that you want to forward.\nIt provides optional SSL termination for your service that might not have SSL support built in.\nProxy Hosts are the most common use for the Nginx Proxy Manager.", "help-title": "What is a Proxy Host?",
"access-list": "Access List", "help-content": "A Proxy Host is the incoming endpoint for a web service that you want to forward.\nIt provides optional SSL termination for your service that might not have SSL support built in.\nProxy Hosts are the most common use for the Nginx Proxy Manager.",
"allow-websocket-upgrade": "Websockets Support", "access-list": "Access List",
"ignore-invalid-upstream-ssl": "Ignore Invalid SSL", "allow-websocket-upgrade": "Websockets Support",
"custom-forward-host-help": "Add a path for sub-folder forwarding.\nExample: 203.0.113.25/path/", "default-server": "Default Server",
"search": "Search Host…" "ignore-invalid-upstream-ssl": "Ignore Invalid SSL",
}, "custom-forward-host-help": "Add a path for sub-folder forwarding.\nExample: 203.0.113.25/path/",
"redirection-hosts": { "search": "Search Host…"
"title": "Redirection Hosts", },
"empty": "There are no Redirection Hosts", "redirection-hosts": {
"add": "Add Redirection Host", "title": "Redirection Hosts",
"form-title": "{id, select, undefined{New} other{Edit}} Redirection Host", "empty": "There are no Redirection Hosts",
"forward-scheme": "Scheme", "add": "Add Redirection Host",
"forward-http-status-code": "HTTP Code", "form-title": "{id, select, undefined{New} other{Edit}} Redirection Host",
"forward-domain": "Forward Domain", "forward-scheme": "Scheme",
"preserve-path": "Preserve Path", "forward-http-status-code": "HTTP Code",
"delete": "Delete Redirection Host", "forward-domain": "Forward Domain",
"delete-confirm": "Are you sure you want to delete the Redirection host for: <strong>{domains}</strong>?", "preserve-path": "Preserve Path",
"help-title": "What is a Redirection Host?", "delete": "Delete Redirection Host",
"help-content": "A Redirection Host will redirect requests from the incoming domain and push the viewer to another domain.\nThe most common reason to use this type of host is when your website changes domains but you still have search engine or referrer links pointing to the old domain.", "delete-confirm": "Are you sure you want to delete the Redirection host for: <strong>{domains}</strong>?",
"search": "Search Host" "help-title": "What is a Redirection Host?",
}, "help-content": "A Redirection Host will redirect requests from the incoming domain and push the viewer to another domain.\nThe most common reason to use this type of host is when your website changes domains but you still have search engine or referrer links pointing to the old domain.",
"dead-hosts": { "search": "Search Host…"
"title": "404 Hosts", },
"empty": "There are no 404 Hosts", "dead-hosts": {
"add": "Add 404 Host", "title": "404 Hosts",
"form-title": "{id, select, undefined{New} other{Edit}} 404 Host", "empty": "There are no 404 Hosts",
"delete": "Delete 404 Host", "add": "Add 404 Host",
"delete-confirm": "Are you sure you want to delete this 404 Host?", "form-title": "{id, select, undefined{New} other{Edit}} 404 Host",
"help-title": "What is a 404 Host?", "delete": "Delete 404 Host",
"help-content": "A 404 Host is simply a host setup that shows a 404 page.\nThis can be useful when your domain is listed in search engines and you want to provide a nicer error page or specifically to tell the search indexers that the domain pages no longer exist.\nAnother benefit of having this host is to track the logs for hits to it and view the referrers.", "delete-confirm": "Are you sure you want to delete this 404 Host?",
"search": "Search Host" "help-title": "What is a 404 Host?",
}, "help-content": "A 404 Host is simply a host setup that shows a 404 page.\nThis can be useful when your domain is listed in search engines and you want to provide a nicer error page or specifically to tell the search indexers that the domain pages no longer exist.\nAnother benefit of having this host is to track the logs for hits to it and view the referrers.",
"streams": { "search": "Search Host…"
"title": "Streams", },
"empty": "There are no Streams", "streams": {
"add": "Add Stream", "title": "Streams",
"form-title": "{id, select, undefined{New} other{Edit}} Stream", "empty": "There are no Streams",
"incoming-port": "Incoming Port", "add": "Add Stream",
"forwarding-host": "Forward Host", "form-title": "{id, select, undefined{New} other{Edit}} Stream",
"forwarding-port": "Forward Port", "incoming-port": "Incoming Port",
"tcp-forwarding": "TCP Forwarding", "forwarding-host": "Forward Host",
"udp-forwarding": "UDP Forwarding", "forwarding-port": "Forward Port",
"forward-type-error": "At least one type of protocol must be enabled", "tcp-forwarding": "TCP Forwarding",
"protocol": "Protocol", "udp-forwarding": "UDP Forwarding",
"tcp": "TCP", "forward-type-error": "At least one type of protocol must be enabled",
"udp": "UDP", "protocol": "Protocol",
"delete": "Delete Stream", "tcp": "TCP",
"delete-confirm": "Are you sure you want to delete this Stream?", "udp": "UDP",
"help-title": "What is a Stream?", "delete": "Delete Stream",
"help-content": "A relatively new feature for Nginx, a Stream will serve to forward TCP/UDP traffic directly to another computer on the network.\nIf you're running game servers, FTP or SSH servers this can come in handy.", "delete-confirm": "Are you sure you want to delete this Stream?",
"search": "Search Incoming Port…" "help-title": "What is a Stream?",
}, "help-content": "A relatively new feature for Nginx, a Stream will serve to forward TCP/UDP traffic directly to another computer on the network.\nIf you're running game servers, FTP or SSH servers this can come in handy.",
"certificates": { "search": "Search Incoming Port…"
"title": "SSL Certificates", },
"empty": "There are no SSL Certificates", "certificates": {
"add": "Add SSL Certificate", "title": "SSL Certificates",
"form-title": "Add {provider, select, letsencrypt{Let's Encrypt} other{Custom}} Certificate", "empty": "There are no SSL Certificates",
"delete": "Delete SSL Certificate", "add": "Add SSL Certificate",
"delete-confirm": "Are you sure you want to delete this SSL Certificate? Any hosts using it will need to be updated later.", "form-title": "Add {provider, select, letsencrypt{Let's Encrypt} other{Custom}} Certificate",
"help-title": "SSL Certificates", "delete": "Delete SSL Certificate",
"help-content": "SSL certificates (correctly known as TLS Certificates) are a form of encryption key which allows your site to be encrypted for the end user.\nNPM uses a service called Let's Encrypt to issue SSL certificates for free.\nIf you have any sort of personal information, passwords, or sensitive data behind NPM, it's probably a good idea to use a certificate.\nNPM also supports DNS authentication for if you're not running your site facing the internet, or if you just want a wildcard certificate.", "delete-confirm": "Are you sure you want to delete this SSL Certificate? Any hosts using it will need to be updated later.",
"other-certificate": "Certificate", "help-title": "SSL Certificates",
"other-certificate-key": "Certificate Key", "help-content": "SSL certificates (correctly known as TLS Certificates) are a form of encryption key which allows your site to be encrypted for the end user.\nNPM uses a service called Let's Encrypt to issue SSL certificates for free.\nIf you have any sort of personal information, passwords, or sensitive data behind NPM, it's probably a good idea to use a certificate.\nNPM also supports DNS authentication for if you're not running your site facing the internet, or if you just want a wildcard certificate.",
"other-intermediate-certificate": "Intermediate Certificate", "other-certificate": "Certificate",
"force-renew": "Renew Now", "other-certificate-key": "Certificate Key",
"test-reachability": "Test Server Reachability", "other-intermediate-certificate": "Intermediate Certificate",
"reachability-title": "Test Server Reachability", "force-renew": "Renew Now",
"reachability-info": "Test whether the domains are reachable from the public internet using Site24x7. This is not necessary when using the DNS Challenge.", "test-reachability": "Test Server Reachability",
"reachability-failed-to-reach-api": "Communication with the API failed, is NPM running correctly?", "reachability-title": "Test Server Reachability",
"reachability-failed-to-check": "Failed to check the reachability due to a communication error with site24x7.com.", "reachability-info": "Test whether the domains are reachable from the public internet using Site24x7. This is not necessary when using the DNS Challenge.",
"reachability-ok": "Your server is reachable and creating certificates should be possible.", "reachability-failed-to-reach-api": "Communication with the API failed, is NPM running correctly?",
"reachability-404": "There is a server found at this domain but it does not seem to be Nginx Proxy Manager. Please make sure your domain points to the IP where your NPM instance is running.", "reachability-failed-to-check": "Failed to check the reachability due to a communication error with site24x7.com.",
"reachability-not-resolved": "There is no server available at this domain. Please make sure your domain exists and points to the IP where your NPM instance is running and if necessary port 80 is forwarded in your router.", "reachability-ok": "Your server is reachable and creating certificates should be possible.",
"reachability-wrong-data": "There is a server found at this domain but it returned an unexpected data. Is it the NPM server? Please make sure your domain points to the IP where your NPM instance is running.", "reachability-404": "There is a server found at this domain but it does not seem to be Nginx Proxy Manager. Please make sure your domain points to the IP where your NPM instance is running.",
"reachability-other": "There is a server found at this domain but it returned an unexpected status code {code}. Is it the NPM server? Please make sure your domain points to the IP where your NPM instance is running.", "reachability-not-resolved": "There is no server available at this domain. Please make sure your domain exists and points to the IP where your NPM instance is running and if necessary port 80 is forwarded in your router.",
"download": "Download", "reachability-wrong-data": "There is a server found at this domain but it returned an unexpected data. Is it the NPM server? Please make sure your domain points to the IP where your NPM instance is running.",
"renew-title": "Renew Let's Encrypt Certificate", "reachability-other": "There is a server found at this domain but it returned an unexpected status code {code}. Is it the NPM server? Please make sure your domain points to the IP where your NPM instance is running.",
"search": "Search Certificate…" "download": "Download",
}, "renew-title": "Renew Let's Encrypt Certificate",
"access-lists": { "search": "Search Certificate…"
"title": "Access Lists", },
"empty": "There are no Access Lists", "access-lists": {
"add": "Add Access List", "title": "Access Lists",
"form-title": "{id, select, undefined{New} other{Edit}} Access List", "empty": "There are no Access Lists",
"delete": "Delete Access List", "add": "Add Access List",
"delete-confirm": "Are you sure you want to delete this access list?", "form-title": "{id, select, undefined{New} other{Edit}} Access List",
"public": "Publicly Accessible", "delete": "Delete Access List",
"public-sub": "No Access Restrictions", "delete-confirm": "Are you sure you want to delete this access list?",
"help-title": "What is an Access List?", "public": "Publicly Accessible",
"help-content": "Access Lists provide a blacklist or whitelist of specific client IP addresses along with authentication for the Proxy Hosts via Basic HTTP Authentication.\nYou can configure multiple client rules, usernames and passwords for a single Access List and then apply that to a Proxy Host.\nThis is most useful for forwarded web services that do not have authentication mechanisms built in or that you want to protect from access by unknown clients.", "public-sub": "No Access Restrictions",
"item-count": "{count} {count, select, 1{User} other{Users}}", "help-title": "What is an Access List?",
"client-count": "{count} {count, select, 1{Rule} other{Rules}}", "help-content": "Access Lists provide a blacklist or whitelist of specific client IP addresses along with authentication for the Proxy Hosts via Basic HTTP Authentication.\nYou can configure multiple client rules, usernames and passwords for a single Access List and then apply that to a Proxy Host.\nThis is most useful for forwarded web services that do not have authentication mechanisms built in or that you want to protect from access by unknown clients.",
"proxy-host-count": "{count} {count, select, 1{Proxy Host} other{Proxy Hosts}}", "item-count": "{count} {count, select, 1{User} other{Users}}",
"delete-has-hosts": "This Access List is associated with {count} Proxy Hosts. They will become publicly available upon deletion.", "client-count": "{count} {count, select, 1{Rule} other{Rules}}",
"details": "Details", "proxy-host-count": "{count} {count, select, 1{Proxy Host} other{Proxy Hosts}}",
"authorization": "Authorization", "delete-has-hosts": "This Access List is associated with {count} Proxy Hosts. They will become publicly available upon deletion.",
"access": "Access", "details": "Details",
"satisfy": "Satisfy", "authorization": "Authorization",
"satisfy-any": "Satisfy Any", "access": "Access",
"pass-auth": "Pass Auth to Host", "satisfy": "Satisfy",
"access-add": "Add", "satisfy-any": "Satisfy Any",
"auth-add": "Add", "pass-auth": "Pass Auth to Host",
"search": "Search Access…" "access-add": "Add",
}, "auth-add": "Add",
"users": { "search": "Search Access…"
"title": "Users", },
"default_error": "Default email address must be changed", "users": {
"add": "Add User", "title": "Users",
"nickname": "Nickname", "default_error": "Default email address must be changed",
"full-name": "Full Name", "add": "Add User",
"edit-details": "Edit Details", "nickname": "Nickname",
"change-password": "Change Password", "full-name": "Full Name",
"edit-permissions": "Edit Permissions", "edit-details": "Edit Details",
"sign-in-as": "Sign in as User", "change-password": "Change Password",
"form-title": "{id, select, undefined{New} other{Edit}} User", "edit-permissions": "Edit Permissions",
"delete": "Delete {name, select, undefined{User} other{{name}}}", "sign-in-as": "Sign in as User",
"delete-confirm": "Are you sure you want to delete <strong>{name}</strong>?", "form-title": "{id, select, undefined{New} other{Edit}} User",
"password-title": "Change Password{self, select, false{ for {name}} other{}}", "delete": "Delete {name, select, undefined{User} other{{name}}}",
"current-password": "Current Password", "delete-confirm": "Are you sure you want to delete <strong>{name}</strong>?",
"new-password": "New Password", "password-title": "Change Password{self, select, false{ for {name}} other{}}",
"confirm-password": "Confirm Password", "current-password": "Current Password",
"permissions-title": "Permissions for {name}", "new-password": "New Password",
"admin-perms": "This user is an Administrator and some items cannot be altered", "confirm-password": "Confirm Password",
"perms-visibility": "Item Visibility", "permissions-title": "Permissions for {name}",
"perms-visibility-user": "Created Items Only", "admin-perms": "This user is an Administrator and some items cannot be altered",
"perms-visibility-all": "All Items", "perms-visibility": "Item Visibility",
"perm-manage": "Manage", "perms-visibility-user": "Created Items Only",
"perm-view": "View Only", "perms-visibility-all": "All Items",
"perm-hidden": "Hidden", "perm-manage": "Manage",
"search": "Search User…" "perm-view": "View Only",
}, "perm-hidden": "Hidden",
"audit-log": { "search": "Search User…"
"title": "Audit Log", },
"empty": "There are no logs.", "audit-log": {
"empty-subtitle": "As soon as you or another user changes something, history of those events will show up here.", "title": "Audit Log",
"proxy-host": "Proxy Host", "empty": "There are no logs.",
"redirection-host": "Redirection Host", "empty-subtitle": "As soon as you or another user changes something, history of those events will show up here.",
"dead-host": "404 Host", "proxy-host": "Proxy Host",
"stream": "Stream", "redirection-host": "Redirection Host",
"user": "User", "dead-host": "404 Host",
"certificate": "Certificate", "stream": "Stream",
"access-list": "Access List", "user": "User",
"created": "Created {name}", "certificate": "Certificate",
"updated": "Updated {name}", "access-list": "Access List",
"deleted": "Deleted {name}", "created": "Created {name}",
"enabled": "Enabled {name}", "updated": "Updated {name}",
"disabled": "Disabled {name}", "deleted": "Deleted {name}",
"renewed": "Renewed {name}", "enabled": "Enabled {name}",
"meta-title": "Details for Event", "disabled": "Disabled {name}",
"view-meta": "View Details", "renewed": "Renewed {name}",
"date": "Date", "meta-title": "Details for Event",
"search": "Search Log…" "view-meta": "View Details",
}, "date": "Date",
"settings": { "search": "Search Log…"
"title": "Settings", },
"default-site": "Default Site", "settings": {
"default-site-description": "What to show when Nginx is hit with an unknown Host", "title": "Settings",
"default-site-congratulations": "Congratulations Page", "default-site": "Default Site",
"default-site-404": "404 Page", "default-site-description": "What to show when Nginx is hit with an unknown Host",
"default-site-444": "No Response (444)", "default-site-congratulations": "Congratulations Page",
"default-site-html": "Custom Page", "default-site-404": "404 Page",
"default-site-redirect": "Redirect" "default-site-444": "No Response (444)",
} "default-site-html": "Custom Page",
} "default-site-redirect": "Redirect"
}
}
} }

View File

@@ -10,6 +10,8 @@ const model = Backbone.Model.extend({
modified_on: null, modified_on: null,
domain_names: [], domain_names: [],
certificate_id: 0, certificate_id: 0,
ssl_key_type: 'ecdsa',
default_server: false,
ssl_forced: false, ssl_forced: false,
http2_support: false, http2_support: false,
hsts_enabled: false, hsts_enabled: false,

View File

@@ -14,6 +14,8 @@ const model = Backbone.Model.extend({
forward_port: null, forward_port: null,
access_list_id: 0, access_list_id: 0,
certificate_id: 0, certificate_id: 0,
ssl_key_type: 'ecdsa',
default_server: false,
ssl_forced: false, ssl_forced: false,
hsts_enabled: false, hsts_enabled: false,
hsts_subdomains: false, hsts_subdomains: false,

View File

@@ -14,6 +14,8 @@ const model = Backbone.Model.extend({
forward_domain_name: '', forward_domain_name: '',
preserve_path: true, preserve_path: true,
certificate_id: 0, certificate_id: 0,
ssl_key_type: 'ecdsa',
default_server: false,
ssl_forced: false, ssl_forced: false,
hsts_enabled: false, hsts_enabled: false,
hsts_subdomains: false, hsts_subdomains: false,

View File

@@ -2648,9 +2648,9 @@ electron-to-chromium@^1.3.47:
integrity sha512-67V62Z4CFOiAtox+o+tosGfVk0QX4DJgH609tjT8QymbJZVAI/jWnAthnr8c5hnRNziIRwkc9EMQYejiVz3/9Q== integrity sha512-67V62Z4CFOiAtox+o+tosGfVk0QX4DJgH609tjT8QymbJZVAI/jWnAthnr8c5hnRNziIRwkc9EMQYejiVz3/9Q==
elliptic@^6.5.3, elliptic@^6.5.4: elliptic@^6.5.3, elliptic@^6.5.4:
version "6.5.7" version "6.6.0"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.0.tgz#5919ec723286c1edf28685aa89261d4761afa210"
integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== integrity sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==
dependencies: dependencies:
bn.js "^4.11.9" bn.js "^4.11.9"
brorand "^1.1.0" brorand "^1.1.0"

View File

@@ -7,7 +7,7 @@
"credentials": "dns_acmedns_api_url = http://acmedns-server/\ndns_acmedns_registration_file = /data/acme-registration.json", "credentials": "dns_acmedns_api_url = http://acmedns-server/\ndns_acmedns_registration_file = /data/acme-registration.json",
"full_plugin_name": "dns-acmedns" "full_plugin_name": "dns-acmedns"
}, },
"active24":{ "active24":{
"name": "Active24", "name": "Active24",
"package_name": "certbot-dns-active24", "package_name": "certbot-dns-active24",
"version": "~=1.5.1", "version": "~=1.5.1",
@@ -303,6 +303,14 @@
"credentials": "dns_joker_username = <Dynamic DNS Authentication Username>\ndns_joker_password = <Dynamic DNS Authentication Password>\ndns_joker_domain = <Dynamic DNS Domain>", "credentials": "dns_joker_username = <Dynamic DNS Authentication Username>\ndns_joker_password = <Dynamic DNS Authentication Password>\ndns_joker_domain = <Dynamic DNS Domain>",
"full_plugin_name": "dns-joker" "full_plugin_name": "dns-joker"
}, },
"leaseweb": {
"name": "LeaseWeb",
"package_name": "certbot-dns-leaseweb",
"version": "~=1.0.1",
"dependencies": "",
"credentials": "dns_leaseweb_api_token = 01234556789",
"full_plugin_name": "dns-leaseweb"
},
"linode": { "linode": {
"name": "Linode", "name": "Linode",
"package_name": "certbot-dns-linode", "package_name": "certbot-dns-linode",
@@ -424,13 +432,13 @@
"full_plugin_name": "dns-rfc2136" "full_plugin_name": "dns-rfc2136"
}, },
"rockenstein": { "rockenstein": {
"name": "rockenstein AG", "name": "rockenstein AG",
"package_name": "certbot-dns-rockenstein", "package_name": "certbot-dns-rockenstein",
"version": "~=1.0.0", "version": "~=1.0.0",
"dependencies": "", "dependencies": "",
"credentials": "dns_rockenstein_token=<token>", "credentials": "dns_rockenstein_token=<token>",
"full_plugin_name": "dns-rockenstein" "full_plugin_name": "dns-rockenstein"
}, },
"route53": { "route53": {
"name": "Route 53 (Amazon)", "name": "Route 53 (Amazon)",
"package_name": "certbot-dns-route53", "package_name": "certbot-dns-route53",

View File

@@ -9,7 +9,7 @@ describe('Full Certificate Provisions', () => {
}); });
}); });
it.only('Should be able to create new http certificate', function() { it('Should be able to create new http certificate', function() {
cy.task('backendApiPost', { cy.task('backendApiPost', {
token: token, token: token,
path: '/api/nginx/certificates', path: '/api/nginx/certificates',
@@ -35,7 +35,7 @@ describe('Full Certificate Provisions', () => {
it('Should be able to create new DNS certificate with Powerdns', function() { it('Should be able to create new DNS certificate with Powerdns', function() {
cy.task('backendApiPost', { cy.task('backendApiPost', {
token: token, token: token,
path: '/api/certificates', path: '/api/nginx/certificates',
data: { data: {
domain_names: [ domain_names: [
'website2.example.com' 'website2.example.com'
@@ -45,7 +45,8 @@ describe('Full Certificate Provisions', () => {
dns_challenge: true, dns_challenge: true,
dns_provider: 'powerdns', dns_provider: 'powerdns',
dns_provider_credentials: 'dns_powerdns_api_url = http://ns1.pdns:8081\r\ndns_powerdns_api_key = npm', dns_provider_credentials: 'dns_powerdns_api_url = http://ns1.pdns:8081\r\ndns_powerdns_api_key = npm',
letsencrypt_agree: true letsencrypt_agree: true,
propagation_seconds: 5,
}, },
provider: 'letsencrypt' provider: 'letsencrypt'
} }

View File

@@ -1,6 +1,6 @@
/// <reference types="cypress" /> /// <reference types="cypress" />
describe('Hosts endpoints', () => { describe('Proxy Hosts endpoints', () => {
let token; let token;
before(() => { before(() => {

View File

@@ -0,0 +1,124 @@
/// <reference types="cypress" />
describe('Settings endpoints', () => {
let token;
before(() => {
cy.getToken().then((tok) => {
token = tok;
});
});
it('Get all settings', function() {
cy.task('backendApiGet', {
token: token,
path: '/api/settings',
}).then((data) => {
cy.validateSwaggerSchema('get', 200, '/settings', data);
expect(data.length).to.be.greaterThan(0);
});
});
it('Get default-site setting', function() {
cy.task('backendApiGet', {
token: token,
path: '/api/settings/default-site',
}).then((data) => {
cy.validateSwaggerSchema('get', 200, '/settings/{settingID}', data);
expect(data).to.have.property('id');
expect(data.id).to.be.equal('default-site');
});
});
it('Default Site congratulations', function() {
cy.task('backendApiPut', {
token: token,
path: '/api/settings/default-site',
data: {
value: 'congratulations',
},
}).then((data) => {
cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data);
expect(data).to.have.property('id');
expect(data.id).to.be.equal('default-site');
expect(data).to.have.property('value');
expect(data.value).to.be.equal('congratulations');
});
});
it('Default Site 404', function() {
cy.task('backendApiPut', {
token: token,
path: '/api/settings/default-site',
data: {
value: '404',
},
}).then((data) => {
cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data);
expect(data).to.have.property('id');
expect(data.id).to.be.equal('default-site');
expect(data).to.have.property('value');
expect(data.value).to.be.equal('404');
});
});
it('Default Site 444', function() {
cy.task('backendApiPut', {
token: token,
path: '/api/settings/default-site',
data: {
value: '444',
},
}).then((data) => {
cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data);
expect(data).to.have.property('id');
expect(data.id).to.be.equal('default-site');
expect(data).to.have.property('value');
expect(data.value).to.be.equal('444');
});
});
it('Default Site redirect', function() {
cy.task('backendApiPut', {
token: token,
path: '/api/settings/default-site',
data: {
value: 'redirect',
meta: {
redirect: 'https://www.google.com',
},
},
}).then((data) => {
cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data);
expect(data).to.have.property('id');
expect(data.id).to.be.equal('default-site');
expect(data).to.have.property('value');
expect(data.value).to.be.equal('redirect');
expect(data).to.have.property('meta');
expect(data.meta).to.have.property('redirect');
expect(data.meta.redirect).to.be.equal('https://www.google.com');
});
});
it('Default Site html', function() {
cy.task('backendApiPut', {
token: token,
path: '/api/settings/default-site',
data: {
value: 'html',
meta: {
html: '<p>hello world</p>'
},
},
}).then((data) => {
cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data);
expect(data).to.have.property('id');
expect(data.id).to.be.equal('default-site');
expect(data).to.have.property('value');
expect(data.value).to.be.equal('html');
expect(data).to.have.property('meta');
expect(data.meta).to.have.property('html');
expect(data.meta.html).to.be.equal('<p>hello world</p>');
});
});
});

View File

@@ -7,7 +7,7 @@ const BackendApi = function(config, token) {
this.axios = axios.create({ this.axios = axios.create({
baseURL: config.baseUrl, baseURL: config.baseUrl,
timeout: 60000, timeout: 90000,
}); });
}; };

View File

@@ -132,9 +132,9 @@
integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==
"@eslint/plugin-kit@^0.2.0": "@eslint/plugin-kit@^0.2.0":
version "0.2.0" version "0.2.3"
resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz#8712dccae365d24e9eeecb7b346f85e750ba343d" resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz#812980a6a41ecf3a8341719f92a6d1e784a2e0e8"
integrity sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig== integrity sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==
dependencies: dependencies:
levn "^0.4.1" levn "^0.4.1"
@@ -628,9 +628,9 @@ core-util-is@1.0.2:
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
cross-spawn@^7.0.0, cross-spawn@^7.0.2: cross-spawn@^7.0.0, cross-spawn@^7.0.2:
version "7.0.3" version "7.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
dependencies: dependencies:
path-key "^3.1.0" path-key "^3.1.0"
shebang-command "^2.0.0" shebang-command "^2.0.0"