diff --git a/.github/ISSUE_TEMPLATE/product_support.md b/.github/ISSUE_TEMPLATE/product_support.md new file mode 100644 index 00000000..449d4697 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/product_support.md @@ -0,0 +1,16 @@ +--- +name: Product Support +about: Need help configuring the software? +title: '' +labels: product-support +assignees: '' + +--- + +**Checklist** +- Please read the [setup instructions](https://nginxproxymanager.com/setup/) +- Please read the [FAQ](https://nginxproxymanager.com/faq/) + +**What is troubling you?** + +_Clear and concise description of what you're trying to do and what isn't working for you_ diff --git a/.version b/.version index 58594069..530cdd91 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.2.3 +2.2.4 diff --git a/README.md b/README.md index 6099b86c..396cbe57 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
-
+
diff --git a/backend/internal/access-list.js b/backend/internal/access-list.js
index 6de3310a..2f53ee1f 100644
--- a/backend/internal/access-list.js
+++ b/backend/internal/access-list.js
@@ -30,7 +30,7 @@ const internalAccessList = {
.omit(omissions())
.insertAndFetch({
name: data.name,
- satify_any: data.satify_any,
+ satisfy_any: data.satisfy_any,
owner_user_id: access.token.getUserId(1)
});
})
@@ -126,8 +126,8 @@ const internalAccessList = {
.query()
.where({id: data.id})
.patch({
- name: data.name,
- satify_any: data.satify_any,
+ name: data.name,
+ satisfy_any: data.satisfy_any,
});
}
})
diff --git a/backend/internal/nginx.js b/backend/internal/nginx.js
index 66fd2109..9972d417 100644
--- a/backend/internal/nginx.js
+++ b/backend/internal/nginx.js
@@ -273,6 +273,7 @@ const internalNginx = {
return new Promise((resolve, reject) => {
let template = null;
let filename = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf';
+
try {
template = fs.readFileSync(__dirname + '/../templates/letsencrypt-request.conf', {encoding: 'utf8'});
} catch (err) {
@@ -280,6 +281,8 @@ const internalNginx = {
return;
}
+ certificate.ipv6 = internalNginx.ipv6Enabled();
+
renderEngine
.parseAndRender(template, certificate)
.then((config_text) => {
diff --git a/backend/migrations/20200410143840_access_list_client_fix.js b/backend/migrations/20200410143840_access_list_client_fix.js
new file mode 100644
index 00000000..ee0f0906
--- /dev/null
+++ b/backend/migrations/20200410143840_access_list_client_fix.js
@@ -0,0 +1,34 @@
+const migrate_name = 'access_list_client_fix';
+const logger = require('../logger').migrate;
+
+/**
+ * Migrate
+ *
+ * @see http://knexjs.org/#Schema
+ *
+ * @param {Object} knex
+ * @param {Promise} Promise
+ * @returns {Promise}
+ */
+exports.up = function (knex/*, Promise*/) {
+ logger.info('[' + migrate_name + '] Migrating Up...');
+
+ return knex.schema.table('access_list', function (access_list) {
+ access_list.renameColumn('satify_any', 'satisfy_any');
+ })
+ .then(() => {
+ logger.info('[' + migrate_name + '] access_list Table altered');
+ });
+};
+
+/**
+ * Undo Migrate
+ *
+ * @param {Object} knex
+ * @param {Promise} Promise
+ * @returns {Promise}
+ */
+exports.down = function (knex, Promise) {
+ logger.warn('[' + migrate_name + '] You can\'t migrate down this one.');
+ return Promise.resolve(true);
+};
diff --git a/backend/models/access_list.js b/backend/models/access_list.js
index 482cfc47..1af9e832 100644
--- a/backend/models/access_list.js
+++ b/backend/models/access_list.js
@@ -90,7 +90,7 @@ class AccessList extends Model {
}
get satisfy() {
- return this.satify_any ? 'satisfy any' : 'satisfy all';
+ return this.satisfy_any ? 'satisfy any' : 'satisfy all';
}
}
diff --git a/backend/schema/endpoints/access-lists.json b/backend/schema/endpoints/access-lists.json
index 51a07ba6..646306b3 100644
--- a/backend/schema/endpoints/access-lists.json
+++ b/backend/schema/endpoints/access-lists.json
@@ -1,229 +1,227 @@
{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "endpoints/access-lists",
- "title": "Access Lists",
- "description": "Endpoints relating to Access Lists",
- "stability": "stable",
- "type": "object",
- "definitions": {
- "id": {
- "$ref": "../definitions.json#/definitions/id"
- },
- "created_on": {
- "$ref": "../definitions.json#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "../definitions.json#/definitions/modified_on"
- },
- "name": {
- "type": "string",
- "description": "Name of the Access List"
- },
- "directive": {
- "type": "string",
- "enum": ["allow", "deny"]
- },
- "address": {
- "oneOf": [
- {
- "type": "string",
- "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$"
- },
- {
- "type": "string",
- "pattern": "^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$"
- },
- {
- "type": "string",
- "pattern": "^any$"
- }
- ]
- },
- "satify_any": {
- "type": "boolean"
- },
- "meta": {
- "type": "object"
- }
- },
- "properties": {
- "id": {
- "$ref": "#/definitions/id"
- },
- "created_on": {
- "$ref": "#/definitions/created_on"
- },
- "modified_on": {
- "$ref": "#/definitions/modified_on"
- },
- "name": {
- "$ref": "#/definitions/name"
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- },
- "links": [
- {
- "title": "List",
- "description": "Returns a list of Access Lists",
- "href": "/nginx/access-lists",
- "access": "private",
- "method": "GET",
- "rel": "self",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "array",
- "items": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Create",
- "description": "Creates a new Access List",
- "href": "/nginx/access-list",
- "access": "private",
- "method": "POST",
- "rel": "create",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "required": [
- "name"
- ],
- "properties": {
- "name": {
- "$ref": "#/definitions/name"
- },
- "satify_any": {
- "$ref": "#/definitions/satify_any"
- },
- "items": {
- "type": "array",
- "minItems": 0,
- "items": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "username": {
- "type": "string",
- "minLength": 1
- },
- "password": {
- "type": "string",
- "minLength": 1
- }
- }
- }
- },
- "clients": {
- "type": "array",
- "minItems": 0,
- "items": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "address": {
- "$ref": "#/definitions/address"
- },
- "directive": {
- "$ref": "#/definitions/directive"
- }
- }
- }
- },
- "meta": {
- "$ref": "#/definitions/meta"
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Update",
- "description": "Updates a existing Access List",
- "href": "/nginx/access-list/{definitions.identity.example}",
- "access": "private",
- "method": "PUT",
- "rel": "update",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "name": {
- "$ref": "#/definitions/name"
- },
- "satify_any": {
- "$ref": "#/definitions/satify_any"
- },
- "items": {
- "type": "array",
- "minItems": 0,
- "items": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "username": {
- "type": "string",
- "minLength": 1
- },
- "password": {
- "type": "string",
- "minLength": 0
- }
- }
- }
- },
- "clients": {
- "type": "array",
- "minItems": 0,
- "items": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "address": {
- "$ref": "#/definitions/address"
- },
- "directive": {
- "$ref": "#/definitions/directive"
- }
- }
- }
- }
- }
- },
- "targetSchema": {
- "properties": {
- "$ref": "#/properties"
- }
- }
- },
- {
- "title": "Delete",
- "description": "Deletes a existing Access List",
- "href": "/nginx/access-list/{definitions.identity.example}",
- "access": "private",
- "method": "DELETE",
- "rel": "delete",
- "http_header": {
- "$ref": "../examples.json#/definitions/auth_header"
- },
- "targetSchema": {
- "type": "boolean"
- }
- }
- ]
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "endpoints/access-lists",
+ "title": "Access Lists",
+ "description": "Endpoints relating to Access Lists",
+ "stability": "stable",
+ "type": "object",
+ "definitions": {
+ "id": {
+ "$ref": "../definitions.json#/definitions/id"
+ },
+ "created_on": {
+ "$ref": "../definitions.json#/definitions/created_on"
+ },
+ "modified_on": {
+ "$ref": "../definitions.json#/definitions/modified_on"
+ },
+ "name": {
+ "type": "string",
+ "description": "Name of the Access List"
+ },
+ "directive": {
+ "type": "string",
+ "enum": ["allow", "deny"]
+ },
+ "address": {
+ "oneOf": [
+ {
+ "type": "string",
+ "pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$"
+ },
+ {
+ "type": "string",
+ "pattern": "^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$"
+ },
+ {
+ "type": "string",
+ "pattern": "^all$"
+ }
+ ]
+ },
+ "satisfy_any": {
+ "type": "boolean"
+ },
+ "meta": {
+ "type": "object"
+ }
+ },
+ "properties": {
+ "id": {
+ "$ref": "#/definitions/id"
+ },
+ "created_on": {
+ "$ref": "#/definitions/created_on"
+ },
+ "modified_on": {
+ "$ref": "#/definitions/modified_on"
+ },
+ "name": {
+ "$ref": "#/definitions/name"
+ },
+ "meta": {
+ "$ref": "#/definitions/meta"
+ }
+ },
+ "links": [
+ {
+ "title": "List",
+ "description": "Returns a list of Access Lists",
+ "href": "/nginx/access-lists",
+ "access": "private",
+ "method": "GET",
+ "rel": "self",
+ "http_header": {
+ "$ref": "../examples.json#/definitions/auth_header"
+ },
+ "targetSchema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/properties"
+ }
+ }
+ },
+ {
+ "title": "Create",
+ "description": "Creates a new Access List",
+ "href": "/nginx/access-list",
+ "access": "private",
+ "method": "POST",
+ "rel": "create",
+ "http_header": {
+ "$ref": "../examples.json#/definitions/auth_header"
+ },
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["name"],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/name"
+ },
+ "satisfy_any": {
+ "$ref": "#/definitions/satisfy_any"
+ },
+ "items": {
+ "type": "array",
+ "minItems": 0,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "username": {
+ "type": "string",
+ "minLength": 1
+ },
+ "password": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "clients": {
+ "type": "array",
+ "minItems": 0,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "address": {
+ "$ref": "#/definitions/address"
+ },
+ "directive": {
+ "$ref": "#/definitions/directive"
+ }
+ }
+ }
+ },
+ "meta": {
+ "$ref": "#/definitions/meta"
+ }
+ }
+ },
+ "targetSchema": {
+ "properties": {
+ "$ref": "#/properties"
+ }
+ }
+ },
+ {
+ "title": "Update",
+ "description": "Updates a existing Access List",
+ "href": "/nginx/access-list/{definitions.identity.example}",
+ "access": "private",
+ "method": "PUT",
+ "rel": "update",
+ "http_header": {
+ "$ref": "../examples.json#/definitions/auth_header"
+ },
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/name"
+ },
+ "satisfy_any": {
+ "$ref": "#/definitions/satisfy_any"
+ },
+ "items": {
+ "type": "array",
+ "minItems": 0,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "username": {
+ "type": "string",
+ "minLength": 1
+ },
+ "password": {
+ "type": "string",
+ "minLength": 0
+ }
+ }
+ }
+ },
+ "clients": {
+ "type": "array",
+ "minItems": 0,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "address": {
+ "$ref": "#/definitions/address"
+ },
+ "directive": {
+ "$ref": "#/definitions/directive"
+ }
+ }
+ }
+ }
+ }
+ },
+ "targetSchema": {
+ "properties": {
+ "$ref": "#/properties"
+ }
+ }
+ },
+ {
+ "title": "Delete",
+ "description": "Deletes a existing Access List",
+ "href": "/nginx/access-list/{definitions.identity.example}",
+ "access": "private",
+ "method": "DELETE",
+ "rel": "delete",
+ "http_header": {
+ "$ref": "../examples.json#/definitions/auth_header"
+ },
+ "targetSchema": {
+ "type": "boolean"
+ }
+ }
+ ]
}
diff --git a/backend/schema/endpoints/proxy-hosts.json b/backend/schema/endpoints/proxy-hosts.json
index af87c467..9a3fff2f 100644
--- a/backend/schema/endpoints/proxy-hosts.json
+++ b/backend/schema/endpoints/proxy-hosts.json
@@ -25,7 +25,7 @@
"forward_host": {
"type": "string",
"minLength": 1,
- "maxLength": 50
+ "maxLength": 255
},
"forward_port": {
"type": "integer",
diff --git a/backend/templates/letsencrypt-request.conf b/backend/templates/letsencrypt-request.conf
index 2adcdb32..cda2f892 100644
--- a/backend/templates/letsencrypt-request.conf
+++ b/backend/templates/letsencrypt-request.conf
@@ -2,6 +2,10 @@
server {
listen 80;
+{% if ipv6 -%}
+ listen [::]:80;
+{% endif %}
+
server_name {{ domain_names | join: " " }};
access_log /data/logs/letsencrypt-requests.log standard;
diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js
index 8ddfd43c..d98ccdbf 100644
--- a/docs/.vuepress/config.js
+++ b/docs/.vuepress/config.js
@@ -47,6 +47,7 @@ module.exports = {
["/screenshots/", "Screenshots"],
["/setup/", "Setup Instructions"],
["/advanced-config/", "Advanced Configuration"],
+ ["/faq/", "Frequently Asked Questions"],
["/third-party/", "Third Party"]
]
}
diff --git a/docs/advanced-config/README.md b/docs/advanced-config/README.md
index a53fc6d9..30daf259 100644
--- a/docs/advanced-config/README.md
+++ b/docs/advanced-config/README.md
@@ -1,6 +1,6 @@
# Advanced Configuration
-### Disabling IPv6
+## Disabling IPv6
On some docker hosts IPv6 may not be enabled. In these cases, the following message may be seen in the log:
@@ -14,7 +14,7 @@ The easy fix is to add a Docker environment variable to the Nginx Proxy Manager
```
-### Custom Nginx Configurations
+## Custom Nginx Configurations
If you are a more advanced user, you might be itching for extra Nginx customizability.
@@ -33,7 +33,7 @@ You can add your custom configuration snippet files at `/data/nginx/custom` as f
Every file is optional.
-### X-FRAME-OPTIONS Header
+## X-FRAME-OPTIONS Header
You can configure the [`X-FRAME-OPTIONS`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) header
value by specifying it as a Docker environment variable. The default if not specified is `deny`.
diff --git a/docs/faq/README.md b/docs/faq/README.md
new file mode 100644
index 00000000..7a2bae16
--- /dev/null
+++ b/docs/faq/README.md
@@ -0,0 +1,16 @@
+# FAQ
+
+## Do I have to use Docker?
+
+Yes, that's how this project is packaged.
+
+This makes it easier to support the project when I have control over the version of Nginx and NodeJS
+being used. In future this could change if the backend was no longer using NodeJS and it's long list
+of dependencies.
+
+
+## Can I run it on a Raspberry Pi?
+
+Yes! The docker image is multi-arch and is built for a variety of architectures. If yours is
+[not listed](https://hub.docker.com/r/jc21/nginx-proxy-manager/tags) please open a
+[GitHub issue](https://github.com/jc21/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=).
diff --git a/docs/setup/README.md b/docs/setup/README.md
index f6c8f946..3acdc7bf 100644
--- a/docs/setup/README.md
+++ b/docs/setup/README.md
@@ -103,7 +103,7 @@ The docker images support the following architectures:
The docker images are a manifest of all the architecture docker builds supported, so this means
you don't have to worry about doing anything special and you can follow the common instructions above.
-Check out the [dockerhub tags](https://cloud.docker.com/repository/registry-1.docker.io/jc21/nginx-proxy-manager/tags)
+Check out the [dockerhub tags](https://hub.docker.com/r/jc21/nginx-proxy-manager/tags)
for a list of supported architectures and if you want one that doesn't exist,
[create a feature request](https://github.com/jc21/nginx-proxy-manager/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=).
diff --git a/frontend/js/app/nginx/access/form.ejs b/frontend/js/app/nginx/access/form.ejs
index 3f127cc0..94423db9 100644
--- a/frontend/js/app/nginx/access/form.ejs
+++ b/frontend/js/app/nginx/access/form.ejs
@@ -25,7 +25,7 @@