Support for upstream ssl proxy hosts

This commit is contained in:
Jamie Curnow 2018-12-12 09:47:12 +10:00
parent cd40ca7f0a
commit c97e6ada5b
15 changed files with 155 additions and 38 deletions

View File

@ -11,6 +11,7 @@ services:
- NODE_ENV=development
- FORCE_COLOR=1
volumes:
- ./data:/data
- ./data/letsencrypt:/etc/letsencrypt
- .:/app
- ./rootfs/etc/nginx:/etc/nginx

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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()

View File

@ -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);
};

View File

@ -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"
},

View File

@ -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" %}

View File

@ -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;

View File

@ -20,7 +20,16 @@
<input type="text" name="domain_names" class="form-control" id="input-domains" value="<%- domain_names.join(',') %>" required>
</div>
</div>
<div class="col-sm-8 col-md-8">
<div class="col-sm-3 col-md-3">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'forward-scheme') %><span class="form-required">*</span></label>
<select name="forward_scheme" class="form-control custom-select" placeholder="http">
<option value="http" <%- forward_scheme === 'http' ? 'selected' : '' %>>http</option>
<option value="https" <%- forward_scheme === 'https' ? 'selected' : '' %>>https</option>
</select>
</div>
</div>
<div class="col-sm-5 col-md-5">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'forward-host') %><span class="form-required">*</span></label>
<input type="text" name="forward_host" class="form-control text-monospace" placeholder="" value="<%- forward_host %>" autocomplete="off" maxlength="50" required>
@ -50,7 +59,7 @@
</label>
</div>
</div>
<div class="col-sm-12 col-md-12">
<div class="col-sm-6 col-md-6">
<div class="form-group">
<label class="custom-switch">
<input type="checkbox" class="custom-switch-input" name="allow_websocket_upgrade" value="1"<%- allow_websocket_upgrade ? ' checked' : '' %>>
@ -59,6 +68,17 @@
</label>
</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="ignore_invalid_upstream_ssl" value="1"<%- ignore_invalid_upstream_ssl ? ' checked' : '' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('proxy-hosts', 'ignore-invalid-upstream-ssl') %></span>
</label>
</div>
</div>
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'access-list') %></label>

View File

@ -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) {

View File

@ -23,7 +23,7 @@
</div>
</td>
<td>
<div class="text-monospace"><%- forward_host %>:<%- forward_port %></div>
<div class="text-monospace"><%- forward_scheme %>://<%- forward_host %>:<%- forward_port %></div>
</td>
<td>
<div><%- certificate && certificate_id ? i18n('ssl', certificate.provider) : i18n('ssl', 'none') %></div>

View File

@ -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;

View File

@ -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",

View File

@ -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
};
}
});