diff --git a/backend/migrations/20240310100432_proxy_protocol_streams.js b/backend/migrations/20240310100432_proxy_protocol_streams.js new file mode 100644 index 00000000..a2a9a51f --- /dev/null +++ b/backend/migrations/20240310100432_proxy_protocol_streams.js @@ -0,0 +1,41 @@ +const migrate_name = 'proxy_protocol_streams'; +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('stream', function (stream) { + stream.integer('enable_proxy_protocol').notNull().unsigned().defaultTo(0); + stream.string('load_balancer_ip').notNull().defaultTo(''); + }) + .then(() => { + logger.info('[' + migrate_name + '] stream Table altered'); + }); + +}; + +/** + * Undo Migrate + * + * @param {Object} knex + * @param {Promise} Promise + * @returns {Promise} + */ +exports.down = function (knex/*, Promise*/) { + return knex.schema.table('stream', function (stream) { + stream.dropColumn('enable_proxy_protocol'); + stream.dropColumn('load_balancer_ip'); + }) + .then(function () { + logger.info('[' + migrate_name + '] stream Table altered'); + }); +}; \ No newline at end of file diff --git a/backend/schema/endpoints/streams.json b/backend/schema/endpoints/streams.json index 159c8036..b1bcc37c 100644 --- a/backend/schema/endpoints/streams.json +++ b/backend/schema/endpoints/streams.json @@ -46,6 +46,16 @@ "udp_forwarding": { "type": "boolean" }, + "enable_proxy_protocol": { + "description": "Enable PROXY Protocol support", + "example": true, + "type": "boolean" + }, + "load_balancer_ip": { + "type": "string", + "minLength": 0, + "maxLength": 255 + }, "enabled": { "$ref": "../definitions.json#/definitions/enabled" }, @@ -78,6 +88,12 @@ "udp_forwarding": { "$ref": "#/definitions/udp_forwarding" }, + "enable_proxy_protocol": { + "$ref": "#/definitions/enable_proxy_protocol" + }, + "load_balancer_ip": { + "$ref": "#/definitions/load_balancer_ip" + }, "enabled": { "$ref": "#/definitions/enabled" }, @@ -137,6 +153,12 @@ "udp_forwarding": { "$ref": "#/definitions/udp_forwarding" }, + "enable_proxy_protocol": { + "$ref": "#/definitions/enable_proxy_protocol" + }, + "load_balancer_ip": { + "$ref": "#/definitions/load_balancer_ip" + }, "meta": { "$ref": "#/definitions/meta" } @@ -177,6 +199,12 @@ "udp_forwarding": { "$ref": "#/definitions/udp_forwarding" }, + "enable_proxy_protocol": { + "$ref": "#/definitions/enable_proxy_protocol" + }, + "load_balancer_ip": { + "$ref": "#/definitions/load_balancer_ip" + }, "meta": { "$ref": "#/definitions/meta" } diff --git a/backend/templates/stream.conf b/backend/templates/stream.conf index 76159a64..2694e6f5 100644 --- a/backend/templates/stream.conf +++ b/backend/templates/stream.conf @@ -1,31 +1,38 @@ # ------------------------------------------------------------ # {{ incoming_port }} TCP: {{ tcp_forwarding }} UDP: {{ udp_forwarding }} # ------------------------------------------------------------ +{% if enable_proxy_protocol == 1 or enable_proxy_protocol == true -%} +{% capture listen_extra_args %}proxy_protocol{% endcapture -%} +{% endif -%} {% if enabled %} {% if tcp_forwarding == 1 or tcp_forwarding == true -%} server { - listen {{ incoming_port }}; + listen {{ incoming_port }} {{ listen_extra_args }}; {% if ipv6 -%} - listen [::]:{{ incoming_port }}; + listen [::]:{{ incoming_port }} {{ listen_extra_args }}; {% else -%} - #listen [::]:{{ incoming_port }}; + #listen [::]:{{ incoming_port }} {{ listen_extra_args }}; {% endif %} proxy_pass {{ forwarding_host }}:{{ forwarding_port }}; +{% include '_proxy_protocol.conf' %} + # Custom include /data/nginx/custom/server_stream[.]conf; include /data/nginx/custom/server_stream_tcp[.]conf; } {% endif %} {% if udp_forwarding == 1 or udp_forwarding == true %} +{% # Proxy Protocol is not supported for UDP %} +{% assign listen_extra_args = "" %} server { - listen {{ incoming_port }} udp; + listen {{ incoming_port }} udp {{ listen_extra_args }}; {% if ipv6 -%} - listen [::]:{{ incoming_port }} udp; + listen [::]:{{ incoming_port }} udp {{ listen_extra_args }}; {% else -%} - #listen [::]:{{ incoming_port }} udp; + #listen [::]:{{ incoming_port }} udp {{ listen_extra_args }}; {% endif %} proxy_pass {{ forwarding_host }}:{{ forwarding_port }}; diff --git a/docs/advanced-config/README.md b/docs/advanced-config/README.md index 9d5dbe18..4ed0a536 100644 --- a/docs/advanced-config/README.md +++ b/docs/advanced-config/README.md @@ -209,14 +209,26 @@ You can customise the logrotate configuration through a mount (if your custom co For reference, the default configuration can be found [here](https://github.com/NginxProxyManager/nginx-proxy-manager/blob/develop/docker/rootfs/etc/logrotate.d/nginx-proxy-manager). -## Enabling Proxy Protocol for Proxy Hosts +## Enabling PROXY protocol for Proxy Hosts When running NPM behind a load balancer, you might want to use the [PROXY procotol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) to receive client information such as the source IP address (useful for banning IPs). When configuring the PROXY protocol for proxy hosts, NPM uses the ports 88 for http and 444 for https traffic to allow you to decide on a per host basis whether to use the PROSY protocol. -To enable the Proxy Protocol for your hosts you need to perform the following steps: +To enable the PROXY protocol for your hosts you need to perform the following steps: 1. Expose the ports `88` (and `444` is applicable) by adjusting your `docker-compose.yml` 2. Edit your proxy hosts to enable the PROXY protocol 3. Edit your upstream load balancer to redirect traffic to the port `88`/`444` and enable the PROXY protocol + +## Enabling PROXY protocol for Streams + +When running NPM behind a load balancer, you might want to use the [PROXY procotol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) to receive client information such as the source IP address (useful for banning IPs). + +Keep in mind that the PROXY procotol cannot be enabled for udp endpoints. + +To enable the PROXY protocol for streams: + +1. Expose the desired port by adjusting you `docker-compose.yml` +2. Edit the Stream to enable the PROXY protocol +3. Edit your upstream load balancer to enable the PROXY protocol diff --git a/frontend/js/app/nginx/stream/form.ejs b/frontend/js/app/nginx/stream/form.ejs index 1fc4f134..e4e5d0c3 100644 --- a/frontend/js/app/nginx/stream/form.ejs +++ b/frontend/js/app/nginx/stream/form.ejs @@ -42,6 +42,22 @@ +
+
+ +
+
+ +
+
+ + > +
+
<%- i18n('streams', 'forward-type-error') %>
diff --git a/frontend/js/app/nginx/stream/form.js b/frontend/js/app/nginx/stream/form.js index be8fc8bc..a645a1bf 100644 --- a/frontend/js/app/nginx/stream/form.js +++ b/frontend/js/app/nginx/stream/form.js @@ -14,6 +14,8 @@ module.exports = Mn.View.extend({ ui: { form: 'form', forwarding_host: 'input[name="forwarding_host"]', + enable_proxy_protocol: 'input[name="enable_proxy_protocol"]', + load_balancer_ip: 'input[name="load_balancer_ip"]', type_error: '.forward-type-error', buttons: '.modal-footer button', switches: '.custom-switch-input', @@ -22,6 +24,13 @@ module.exports = Mn.View.extend({ }, events: { + 'change @ui.enable_proxy_protocol': function () { + let checked = this.ui.enable_proxy_protocol.prop('checked'); + this.ui.load_balancer_ip + .prop('disabled', !checked) + .parents('.form-group') + .css('opacity', checked ? 1 : 0.5); + }, 'change @ui.switches': function () { this.ui.type_error.hide(); }, @@ -47,6 +56,7 @@ module.exports = Mn.View.extend({ data.forwarding_port = parseInt(data.forwarding_port, 10); data.tcp_forwarding = !!data.tcp_forwarding; data.udp_forwarding = !!data.udp_forwarding; + data.enable_proxy_protocol = !!data.enable_proxy_protocol; let method = App.Api.Nginx.Streams.create; let is_new = true; @@ -76,6 +86,10 @@ module.exports = Mn.View.extend({ } }, + onRender: function () { + this.ui.enable_proxy_protocol.trigger('change'); + }, + initialize: function (options) { if (typeof options.model === 'undefined' || !options.model) { this.model = new StreamModel.Model(); diff --git a/frontend/js/i18n/messages.json b/frontend/js/i18n/messages.json index 136e6ebd..05fe88a6 100644 --- a/frontend/js/i18n/messages.json +++ b/frontend/js/i18n/messages.json @@ -177,6 +177,8 @@ "protocol": "Protocol", "tcp": "TCP", "udp": "UDP", + "enable-proxy-protocol": "Enable Proxy Protocol", + "load-balancer-ip": "Load balancer or TCP proxy IP / CIDR range", "delete": "Delete Stream", "delete-confirm": "Are you sure you want to delete this Stream?", "help-title": "What is a Stream?", diff --git a/frontend/js/models/stream.js b/frontend/js/models/stream.js index ba035429..b4ee8f97 100644 --- a/frontend/js/models/stream.js +++ b/frontend/js/models/stream.js @@ -5,18 +5,20 @@ const model = Backbone.Model.extend({ defaults: function () { return { - id: undefined, - created_on: null, - modified_on: null, - incoming_port: null, - forwarding_host: null, - forwarding_port: null, - tcp_forwarding: true, - udp_forwarding: false, - enabled: true, - meta: {}, + id: undefined, + created_on: null, + modified_on: null, + incoming_port: null, + forwarding_host: null, + forwarding_port: null, + tcp_forwarding: true, + udp_forwarding: false, + enable_proxy_protocol: false, + load_balancer_ip: "", + enabled: true, + meta: {}, // The following are expansions: - owner: null + owner: null }; } });