As others have said, you should tell Node-RED to trust the proxy, it should be setting the upstream address. If not, you will need to ask your IT team to update the headers for the proxied addresses. Here are the NGINX proxy settings I use:
# Common reverse proxy settings
# Don't forget to also add proxy_pass url;
proxy_set_header Forwarded "by=$host;for=$proxy_add_x_forwarded_for;host=$host;proto=$scheme";
proxy_set_header Via "$scheme/1.1 $host:$server_port";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Real-IP $remote_addr;
# Proxy timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# If proxied responses happening too slowly, try turning off the buffering
#proxy_buffering off;
And, for websockets:
# Common headers for proxy of websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "Upgrade";
proxy_set_header Connection $connection_upgrade_keepalive;
For that last setting, the NGINX main config should contain this:
# Upgrade WebSocket if requested, otherwise use keepalive
map $http_upgrade $connection_upgrade_keepalive {
default Upgrade;
'' '';
}
When using UIBUILDER, any reported client IP will already be using the best available remote address including from proxy headings. Control messages such as the client connect message also list all the headers.
In addition, if you choose to use a custom ExpressJS web server rather than the default Node-RED one, you can set the proxy trust in the uibuilder section of settings.js.
/** Custom settings for all uibuilder node instances */
uibuilder: {
/** Optional HTTP PORT.
* If set and different to Node-RED's uiPort, uibuilder will create
* a separate webserver for its own use.
*/
port: process.env.UIBPORT || 3001,
/** Optional: Change location of uibRoot
* If set, instead of something like `~/.node-red/uibuilder`, the uibRoot folder can be anywhere you like.
*/
uibRoot: process.env.UIBROOT || '/src/uibRoot', // path.join(os.homedir(), 'myuibroot'),
// For project-specific uibuilder folders:
// uibRoot: path.join(os.homedir(), '.node-red', 'projects', 'uibuilder')
/** Only used if a custom ExpressJS server in use (see port above)
* Optional: Default will be the same as Node-RED. @type {('http'|'https')}
*/
customType: 'http',
/** Only required if type is https, http2. Defines the cert & key. See Node-RED https settings for more details.
* If not defined, will use Node-RED's https properties.
* @type {Object<Buffer,Buffer>}
*/
// https: {
// key: 'keyname.key',
// cert: 'fullchain.cer'
// },
/** Optional: Custom ExpressJS server options
* Only required if using a custom webserver (see port setting above).
* For a full list of available options, refer to http://expressjs.com/en/api.html#app.settings.table
*/
serverOptions: {
// http://expressjs.com/en/api.html#trust.proxy.options.table
'trust proxy': true, // true/false; or subnet(s) to trust; or custom function returning true/false. default=false
/** Optional view engine - the engine must be installed into your userDir (e.g. where this file lives)
* If set as shown, ExpressJS will translate source files ending in .ejs to HTML.
* See https://expressjs.com/en/guide/using-template-engines.html for details.
*/
'view engine': 'ejs',
// Optional global settings for view engine
'view options': {},
// Custom properties: can be used as vars in view templates
'footon': 'bar stool',
},
/** Optional: Socket.IO Server options
* See https://socket.io/docs/v4/server-options/
* Note that the `path` property will be ignored, it is set by uibuilder itself.
* You can set anything else though you might break uibuilder unless you know what you are doing.
* @type {object}
*/
// socketOptions: {
// // Make the default buffer larger (default=1MB)
// maxHttpBufferSize: 1e8 // 100 MB
// },
/** Controls whether the uibuilder instance API feature is enabled
* Off by default since uncontrolled instance api's are a security and
* operational risk. Use with caution. See Docs for details.
*/
instanceApiAllowed: true,
hooks: {
/** Hook fn run every time any uibuilder node receives a message from the front-end client.
* You CAN:
* - Stop the inbound msg by returning false.
* - Change the msg that is received before it is output.
* You _could_ change the node's values BUT DO NOT! Bad things likely to happen if you do.
* @param {object} data
* @param {object} data.msg READ/WRITE. The msg that is being sent
* @param {object} data.node READ-ONLY! The settings of the node sending the message
* @returns {boolean} true = receive the msg. false = block the msg.
*/
msgReceived: (data) => {
const {msg, node} = data
// console.log('hooks:msgReceived - msg: ', msg)
// console.log('hooks:msgReceived - uibuilder url: ', node.url)
if (msg.blockme) return false // simplistic example
// Example of altering the msg
// msg._hook = 'I WAS HOOKED!'
// Block inputs except from logged in users
// if (!msg._client) return false
// Default, allows msgs to flow
return true
},
/** Hook fn run every time any uibuilder node is sending a msg to a front-end client
* This could therefore get run thousands of times! So best to filter up front as shown.
* You CAN:
* - stop the outbound msg by returning false.
* - Change the msg that is sent
* You _could_ change the node's values BUT DO NOT! Bad things likely to happen if you do.
* @param {object} data
* @param {object} data.msg READ/WRITE. The msg that is being sent
* @param {uibNode} data.node READ-ONLY! The settings of the node sending the message
* @returns {boolean} true = receive the msg. false = block the msg.
*/
msgSending: (data) => {
const {msg, node} = data
// Filter to restrict by topic
// if (msg.topic !== 'test') return false
// console.log('hooks:msgReceived - msg: ', msg)
// console.log('hooks:msgReceived - uibuilder url: ', node.url)
if (msg.blockme) return false // simplistic example
// Block outputs except to logged in users
// if (!msg._client) return false
// Default, allows msgs to flow
return true
},
/** Hook fn run whenever uibuilder looks up a connected client's details. NOTE:
* This will be run every time a client connects and every time it sends a msg to Node-RED.
* This lets you amend the details including out._client which can contain authenticated user details.
* @param {object} data
* @param {object} data.client Key client information - this can be amended in this fn
* @param {object} data.client._uib Standard uibuilder client meta info
* @param {object=} data.client._client Only if a recognised authenticated client
* @param {socketio.Socket} data.socket Reference to client socket connection
* @param {object} data.node READ-ONLY! The settings of the node sending the message
*/
clientDetails: (data) => {
const {out: client, socket, node} = data
// node.log(`[uibuilder:hooks:clientDetails] for ${node.url}`)
// console.log(socket.request.headers, socket.handshake.auth)
// Simplistic e.g. to block a specific user from being logged in
if (client?._client?.userId === 'horrible.user') delete client._client
},
/** Hook fn that allows overrides of Socket.IO connection headers. NOTE:
* Connection headers will only ever update when a client (re)connects or
* possibly also for long-polling requests.
* @see https://socket.io/docs/v4/server-api/#event-headers for details
* @param {object} data
* @param {object<string:string>} data.headers Response Headers
* @param {Express.Request} data.req ExpressJS Request object
*/
socketIoHeaders: (data) => {
const { headers, req } = data
// headers contains the response headers that go back to the client.
headers['x-wowser'] = 'The Client gets this'
// Simulate an authenticated user
// If these are set, a msg._client object is added to uibuilder output messages
// req.headers['remote-user'] = 'test-user'
// req.headers['remote-name'] = 'Test User'
// req.headers['remote-email'] = 'test.user@example.com'
// req.headers['x-user-role'] = 'test-role'
// req.headers['x-forwarded-groups'] = 'group1,group2'
// NB: This is NOT suitable for session controls since it will only update
// when a client (re)connects, not when otherwise sending/receiving msgs.
// Use the msgReceived amd msgSending hooks for session management.
},
},
},