diff --git a/docker-compose.yml b/docker-compose.yml index ac0f64f..2ff821b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,7 @@ services: - NODE_ENV=development - FORCE_COLOR=1 volumes: + - ./data:/data - ./data/letsencrypt:/etc/letsencrypt - .:/app - ./rootfs/etc/nginx:/etc/nginx diff --git a/rootfs/etc/nginx/conf.d/default.conf b/rootfs/etc/nginx/conf.d/default.conf index f24cbaf..490e286 100644 --- a/rootfs/etc/nginx/conf.d/default.conf +++ b/rootfs/etc/nginx/conf.d/default.conf @@ -8,8 +8,9 @@ server { include conf.d/include/block-exploits.conf; - set $server 127.0.0.1; - set $port 81; + set $forward_scheme http; + set $server 127.0.0.1; + set $port 81; location /health { access_log off; diff --git a/rootfs/etc/nginx/conf.d/include/proxy.conf b/rootfs/etc/nginx/conf.d/include/proxy.conf index d02653a..b84a451 100644 --- a/rootfs/etc/nginx/conf.d/include/proxy.conf +++ b/rootfs/etc/nginx/conf.d/include/proxy.conf @@ -3,4 +3,4 @@ proxy_set_header Host $host; proxy_set_header X-Forwarded-Scheme $scheme; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; -proxy_pass http://$server:$port; +proxy_pass $forward_scheme://$server:$port; diff --git a/rootfs/etc/nginx/nginx.conf b/rootfs/etc/nginx/nginx.conf index 1b6f4fd..14efa59 100644 --- a/rootfs/etc/nginx/nginx.conf +++ b/rootfs/etc/nginx/nginx.conf @@ -54,6 +54,11 @@ http { # Dynamically generated resolvers file include /etc/nginx/conf.d/include/resolvers.conf; + # Default upstream scheme + map $host $forward_scheme { + default http; + } + # Files generated by NPM include /etc/nginx/conf.d/*.conf; include /data/nginx/proxy_host/*.conf; diff --git a/src/backend/internal/proxy-host.js b/src/backend/internal/proxy-host.js index 7c627b7..407b01a 100644 --- a/src/backend/internal/proxy-host.js +++ b/src/backend/internal/proxy-host.js @@ -48,6 +48,11 @@ const internalProxyHost = { // At this point the domains should have been checked data.owner_user_id = access.token.getUserId(1); + // Ignoring upstream ssl errors only applies when upstream scheme is https + if (data.forward_scheme === 'http') { + data.ignore_invalid_upstream_ssl = false; + } + return proxyHostModel .query() .omit(omissions()) @@ -163,7 +168,12 @@ const internalProxyHost = { // Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here. data = _.assign({}, { domain_names: row.domain_names - },data); + }, data); + + // Ignoring upstream ssl errors only applies when upstream scheme is https + if (typeof data.forward_scheme !== 'undefined' && data.forward_scheme === 'http') { + data.ignore_invalid_upstream_ssl = false; + } return proxyHostModel .query() diff --git a/src/backend/migrations/20181213013211_forward_scheme.js b/src/backend/migrations/20181213013211_forward_scheme.js new file mode 100644 index 0000000..6b74a4d --- /dev/null +++ b/src/backend/migrations/20181213013211_forward_scheme.js @@ -0,0 +1,37 @@ +'use strict'; + +const migrate_name = 'forward_scheme'; +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('proxy_host', function (proxy_host) { + proxy_host.string('forward_scheme').notNull().defaultTo('http'); + proxy_host.integer('ignore_invalid_upstream_ssl').notNull().unsigned().defaultTo(0); + }) + .then(() => { + logger.info('[' + migrate_name + '] proxy_host 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/src/backend/schema/endpoints/proxy-hosts.json b/src/backend/schema/endpoints/proxy-hosts.json index 4cfb596..3f457ef 100644 --- a/src/backend/schema/endpoints/proxy-hosts.json +++ b/src/backend/schema/endpoints/proxy-hosts.json @@ -18,6 +18,10 @@ "domain_names": { "$ref": "../definitions.json#/definitions/domain_names" }, + "forward_scheme": { + "type": "string", + "enum": ["http", "https"] + }, "forward_host": { "type": "string", "minLength": 1, @@ -48,6 +52,11 @@ "example": true, "type": "boolean" }, + "ignore_invalid_upstream_ssl": { + "description": "Ignore invalid upstream SSL certificates", + "example": true, + "type": "boolean" + }, "access_list_id": { "$ref": "../definitions.json#/definitions/access_list_id" }, @@ -71,6 +80,9 @@ "domain_names": { "$ref": "#/definitions/domain_names" }, + "forward_scheme": { + "$ref": "#/definitions/forward_scheme" + }, "forward_host": { "$ref": "#/definitions/forward_host" }, @@ -95,6 +107,9 @@ "allow_websocket_upgrade": { "$ref": "#/definitions/allow_websocket_upgrade" }, + "ignore_invalid_upstream_ssl": { + "$ref": "#/definitions/ignore_invalid_upstream_ssl" + }, "access_list_id": { "$ref": "#/definitions/access_list_id" }, @@ -138,6 +153,7 @@ "additionalProperties": false, "required": [ "domain_names", + "forward_scheme", "forward_host", "forward_port" ], @@ -145,6 +161,9 @@ "domain_names": { "$ref": "#/definitions/domain_names" }, + "forward_scheme": { + "$ref": "#/definitions/forward_scheme" + }, "forward_host": { "$ref": "#/definitions/forward_host" }, @@ -169,6 +188,9 @@ "allow_websocket_upgrade": { "$ref": "#/definitions/allow_websocket_upgrade" }, + "ignore_invalid_upstream_ssl": { + "$ref": "#/definitions/ignore_invalid_upstream_ssl" + }, "access_list_id": { "$ref": "#/definitions/access_list_id" }, @@ -203,6 +225,9 @@ "domain_names": { "$ref": "#/definitions/domain_names" }, + "forward_scheme": { + "$ref": "#/definitions/forward_scheme" + }, "forward_host": { "$ref": "#/definitions/forward_host" }, @@ -227,6 +252,9 @@ "allow_websocket_upgrade": { "$ref": "#/definitions/allow_websocket_upgrade" }, + "ignore_invalid_upstream_ssl": { + "$ref": "#/definitions/ignore_invalid_upstream_ssl" + }, "access_list_id": { "$ref": "#/definitions/access_list_id" }, diff --git a/src/backend/templates/proxy_host.conf b/src/backend/templates/proxy_host.conf index b8a301d..911fb3c 100644 --- a/src/backend/templates/proxy_host.conf +++ b/src/backend/templates/proxy_host.conf @@ -1,8 +1,9 @@ {% include "_header_comment.conf" %} server { - set $server "{{ forward_host }}"; - set $port {{ forward_port }}; + set $forward_scheme {{ forward_scheme }}; + set $server "{{ forward_host }}"; + set $port {{ forward_port }}; {% include "_listen.conf" %} {% include "_certificates.conf" %} diff --git a/src/frontend/js/app/nginx/dead/form.js b/src/frontend/js/app/nginx/dead/form.js index 39d08e8..881778e 100644 --- a/src/frontend/js/app/nginx/dead/form.js +++ b/src/frontend/js/app/nginx/dead/form.js @@ -83,7 +83,7 @@ module.exports = Mn.View.extend({ data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1'; } else { - data.certificate_id = parseInt(data.certificate_id, 0); + data.certificate_id = parseInt(data.certificate_id, 10); } let method = App.Api.Nginx.DeadHosts.create; diff --git a/src/frontend/js/app/nginx/proxy/form.ejs b/src/frontend/js/app/nginx/proxy/form.ejs index eca0ca6..6cda34f 100644 --- a/src/frontend/js/app/nginx/proxy/form.ejs +++ b/src/frontend/js/app/nginx/proxy/form.ejs @@ -20,7 +20,16 @@ -
+
+
+ + +
+
+
@@ -50,7 +59,7 @@
-
+
+ +
+
+ +
+
+
diff --git a/src/frontend/js/app/nginx/proxy/form.js b/src/frontend/js/app/nginx/proxy/form.js index 378b9b6..89410e3 100644 --- a/src/frontend/js/app/nginx/proxy/form.js +++ b/src/frontend/js/app/nginx/proxy/form.js @@ -26,10 +26,20 @@ module.exports = Mn.View.extend({ access_list_select: 'select[name="access_list_id"]', ssl_forced: 'input[name="ssl_forced"]', http2_support: 'input[name="http2_support"]', + forward_scheme: 'select[name="forward_scheme"]', + ignore_ssl: 'input[name="ignore_invalid_upstream_ssl"]', letsencrypt: '.letsencrypt' }, events: { + 'change @ui.forward_scheme': function () { + let val = this.ui.forward_scheme.val(); + this.ui.ignore_ssl + .prop('disabled', val === 'http') + .parents('.form-group') + .css('opacity', val === 'https' ? 1 : 0.5); + }, + 'change @ui.certificate_select': function () { let id = this.ui.certificate_select.val(); if (id === 'new') { @@ -43,8 +53,6 @@ module.exports = Mn.View.extend({ .prop('disabled', !enabled) .parents('.form-group') .css('opacity', enabled ? 1 : 0.5); - - this.ui.http2_support.prop('disabled', !enabled); }, 'click @ui.save': function (e) { @@ -59,10 +67,11 @@ module.exports = Mn.View.extend({ let data = this.ui.form.serializeJSON(); // Manipulate - data.forward_port = parseInt(data.forward_port, 10); - data.block_exploits = !!data.block_exploits; - data.caching_enabled = !!data.caching_enabled; - data.allow_websocket_upgrade = !!data.allow_websocket_upgrade; + data.forward_port = parseInt(data.forward_port, 10); + data.block_exploits = !!data.block_exploits; + data.caching_enabled = !!data.caching_enabled; + data.allow_websocket_upgrade = !!data.allow_websocket_upgrade; + data.ignore_invalid_upstream_ssl = data.forward_scheme === 'https' ? !!data.ignore_invalid_upstream_ssl : false; if (typeof data.ssl_forced !== 'undefined' && data.ssl_forced === '1') { data.ssl_forced = true; @@ -92,7 +101,7 @@ module.exports = Mn.View.extend({ data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1'; } else { - data.certificate_id = parseInt(data.certificate_id, 0); + data.certificate_id = parseInt(data.certificate_id, 10); } let method = App.Api.Nginx.ProxyHosts.create; @@ -147,7 +156,6 @@ module.exports = Mn.View.extend({ }); // Access Lists - this.ui.letsencrypt.hide(); this.ui.access_list_select.selectize({ valueField: 'id', labelField: 'name', @@ -207,6 +215,8 @@ module.exports = Mn.View.extend({ view.ui.certificate_select[0].selectize.setValue(view.model.get('certificate_id')); } }); + + this.ui.forward_scheme.trigger('change'); }, initialize: function (options) { diff --git a/src/frontend/js/app/nginx/proxy/list/item.ejs b/src/frontend/js/app/nginx/proxy/list/item.ejs index d6252dd..d91a598 100644 --- a/src/frontend/js/app/nginx/proxy/list/item.ejs +++ b/src/frontend/js/app/nginx/proxy/list/item.ejs @@ -23,7 +23,7 @@
-
<%- forward_host %>:<%- forward_port %>
+
<%- forward_scheme %>://<%- forward_host %>:<%- forward_port %>
<%- certificate && certificate_id ? i18n('ssl', certificate.provider) : i18n('ssl', 'none') %>
diff --git a/src/frontend/js/app/nginx/redirection/form.js b/src/frontend/js/app/nginx/redirection/form.js index 971082e..182ebf6 100644 --- a/src/frontend/js/app/nginx/redirection/form.js +++ b/src/frontend/js/app/nginx/redirection/form.js @@ -86,7 +86,7 @@ module.exports = Mn.View.extend({ data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1'; } else { - data.certificate_id = parseInt(data.certificate_id, 0); + data.certificate_id = parseInt(data.certificate_id, 10); } let method = App.Api.Nginx.RedirectionHosts.create; diff --git a/src/frontend/js/i18n/messages.json b/src/frontend/js/i18n/messages.json index f96c300..c091439 100644 --- a/src/frontend/js/i18n/messages.json +++ b/src/frontend/js/i18n/messages.json @@ -93,6 +93,7 @@ "empty": "There are no Proxy Hosts", "add": "Add Proxy Host", "form-title": "{id, select, undefined{New} other{Edit}} Proxy Host", + "forward-scheme": "Scheme", "forward-host": "Forward Hostname / IP", "forward-port": "Forward Port", "delete": "Delete Proxy Host", @@ -100,7 +101,8 @@ "help-title": "What is a Proxy Host?", "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.", "access-list": "Access List", - "allow-websocket-upgrade": "Allow Websocket HTTP Upgrades" + "allow-websocket-upgrade": "Websockets Support", + "ignore-invalid-upstream-ssl": "Ignore Invalid SSL" }, "redirection-hosts": { "title": "Redirection Hosts", diff --git a/src/frontend/js/models/proxy-host.js b/src/frontend/js/models/proxy-host.js index 2fe7d00..41e45d0 100644 --- a/src/frontend/js/models/proxy-host.js +++ b/src/frontend/js/models/proxy-host.js @@ -7,25 +7,27 @@ const model = Backbone.Model.extend({ defaults: function () { return { - id: undefined, - created_on: null, - modified_on: null, - domain_names: [], - forward_host: '', - forward_port: null, - access_list_id: 0, - certificate_id: 0, - ssl_forced: false, - caching_enabled: false, - allow_websocket_upgrade: false, - block_exploits: false, - http2_support: false, - advanced_config: '', - meta: {}, + id: undefined, + created_on: null, + modified_on: null, + domain_names: [], + forward_scheme: 'http', + forward_host: '', + forward_port: null, + access_list_id: 0, + certificate_id: 0, + ssl_forced: false, + caching_enabled: false, + allow_websocket_upgrade: false, + block_exploits: false, + http2_support: false, + ignore_invalid_upstream_ssl: false, + advanced_config: '', + meta: {}, // The following are expansions: - owner: null, - access_list: null, - certificate: null + owner: null, + access_list: null, + certificate: null }; } });