Skip to content

Mark ingress proxy as trusted in Z-Wave JS app#4597

Draft
AlCalzone wants to merge 1 commit into
home-assistant:masterfrom
AlCalzone:zwave_js-1.3.1
Draft

Mark ingress proxy as trusted in Z-Wave JS app#4597
AlCalzone wants to merge 1 commit into
home-assistant:masterfrom
AlCalzone:zwave_js-1.3.1

Conversation

@AlCalzone

@AlCalzone AlCalzone commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Fixes the issues reported in zwave-js/zwave-js-ui#4535

Summary by CodeRabbit

  • Bug Fixes
    • Fixed a validation error appearing in Z-Wave JS UI logs caused by improper proxy configuration handling.
    • Enhanced application compatibility with proxy layers and ingress endpoints, ensuring proper request processing and improved system stability.

@coderabbitai

coderabbitai Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 98ce42ca-c0db-4d14-897d-67283fbaefc9

📥 Commits

Reviewing files that changed from the base of the PR and between ad8a4b3 and 09f3b83.

📒 Files selected for processing (3)
  • zwave_js/CHANGELOG.md
  • zwave_js/config.yaml
  • zwave_js/rootfs/etc/services.d/zwave_js/run

📝 Walkthrough

Walkthrough

A patch release (1.3.1) that updates the addon manifest version and configures the Z-Wave JS UI service to trust proxy connections by setting the TRUST_PROXY environment variable, with corresponding changelog documentation.

Changes

Cohort / File(s) Summary
Patch Release 1.3.1 - Proxy Configuration
zwave_js/CHANGELOG.md, zwave_js/config.yaml, zwave_js/rootfs/etc/services.d/zwave_js/run
Version bump to 1.3.1, changelog entry documenting proxy validation error fix, and service script now exports TRUST_PROXY=1 before starting zwave-js-ui to enable proxy trust configuration.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: marking the ingress proxy as trusted in the Z-Wave JS app by setting TRUST_PROXY=1.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@agners
agners requested a review from Copilot April 29, 2026 08:09
robertsLando added a commit to zwave-js/zwave-js-ui that referenced this pull request Apr 29, 2026
Home Assistant addon defaults TRUST_PROXY=1 (home-assistant/addons#4597),
which covers the largest user group affected by #4535. A redundant UI
field would just create two ways to configure the same thing.

Reverts:
- trustProxy field on GatewayConfig
- gateway.trustProxy in managedExternally
- Trust Proxy text field in Settings.vue General panel
- Trust Proxy entry in docs/usage/setup.md
- Settings fallback in configureTrustProxy()

Kept (still needed):
- parseTrustProxy() coercion — required for TRUST_PROXY=1 to be
  applied as a hop count rather than the IP literal "1" (the prior
  env-var path forwarded the raw string to app.set, which Express
  treats as an IP, leaving trust proxy effectively off).
- Removal of the X-Forwarded-For auto-detect middleware (spoofable).
- Removal of the blanket xForwardedForHeader validator suppression.

Refs #4535

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Z-Wave JS add-on to trust the Home Assistant ingress proxy when running Z-Wave JS UI, addressing validation errors in logs related to proxy configuration.

Changes:

  • Export TRUST_PROXY=1 in the add-on service run script so Z-Wave JS UI treats the ingress proxy as trusted.
  • Bump add-on version from 1.3.0 to 1.3.1.
  • Add a 1.3.1 changelog entry describing the proxy-related fix.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
zwave_js/rootfs/etc/services.d/zwave_js/run Exports TRUST_PROXY=1 before launching zwave-js-ui.
zwave_js/config.yaml Version bump to 1.3.1.
zwave_js/CHANGELOG.md Adds release note for 1.3.1 describing the proxy validation log fix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

robertsLando added a commit to zwave-js/zwave-js-ui that referenced this pull request Apr 29, 2026
## Summary

Fixes two issues affecting users behind reverse proxies (notably the
Home Assistant addon).

### Socket reconnection loop (Fixes #4564)

When the server's auth middleware rejected a socket connection (e.g.
expired JWT), the client silently looped on reconnection forever.
Socket.IO v4 emits `connect_error` on rejected handshakes, but the
client only listened for `error`.

- `src/App.vue` — JWT expiry is checked client-side before attempting to
connect; an expired token triggers `logout()` instead of a connect
attempt.
- `src/App.vue` — added a `connect_error` handler. Three consecutive
auth failures trigger logout (single failures are tolerated to ride out
transient issues); other errors update status to *Reconnecting*.

### `ERR_ERL_UNEXPECTED_X_FORWARDED_FOR` validation error (Fixes #4535)

`express-rate-limit` emitted a noisy `ValidationError` on every restart
for users running behind a reverse proxy. The pre-existing `TRUST_PROXY`
env-var path looked correct but was effectively broken: `TRUST_PROXY=1`
was forwarded to `app.set('trust proxy', '1')` as a string, which
Express treats as the IP literal `"1"` — leaving trust proxy effectively
off and the validation error firing.

- `api/app.ts` — `parseTrustProxy()` coerces the raw env value:
`"true"`/`"false"` → boolean, numeric strings → hop count integer,
anything else → passed through verbatim (IP/CIDR list or preset name).
- `api/app.ts` — moved `app.set('trust proxy', ...)` into
`startServer()` via `configureTrustProxy()`, mirroring how HTTPS is
handled. Logs the resolved value at startup.
- `docs/guide/env-vars.md` — expanded the `TRUST_PROXY` description with
accepted formats and a note about `ERR_ERL_PERMISSIVE_TRUST_PROXY`
(don't set it to `true`; prefer an explicit hop count or IP list).

This pairs with home-assistant/addons#4597, which sets `TRUST_PROXY=1`
by default in the HA addon — that PR only actually resolves #4535 once
this coercion is in place.

An earlier revision of this PR added an X-Forwarded-For auto-detect
middleware and a UI toggle for trust proxy; both have been dropped.
Auto-detect was spoofable (any client could trip it via a forged XFF
header and bypass the login rate-limiter), and the UI field became
redundant once the HA addon set the env var by default.

## Test plan

- [ ] Verify socket connects successfully when running behind HA ingress
proxy
- [ ] Verify no `ERR_ERL_UNEXPECTED_X_FORWARDED_FOR` errors in logs when
behind proxy with `TRUST_PROXY=1`
- [ ] Verify expired JWT token triggers logout instead of reconnect loop
- [ ] Verify `TRUST_PROXY=1` is applied as a hop count (integer), not as
the IP literal `"1"`
- [ ] Verify `TRUST_PROXY` accepting an IP/CIDR list still works
- [ ] Verify direct connections (no proxy) are unaffected
- [ ] `npm run lint` passes

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@AlCalzone

Copy link
Copy Markdown
Contributor Author

@agners I figure it would be slightly more correct to pass the actual ingress proxy IP - any clue how I can determine that programmatically?

truecharts-admin added a commit to trueforge-org/truecharts that referenced this pull request May 5, 2026
…2 → 11.17.0 (#47891)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[docker.io/zwavejs/zwave-js-ui](https://redirect.github.com/zwave-js/zwave-js-ui)
| minor | `c43f88e` → `7982788` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/18710) for more information.

Add the preset `:preserveSemverRanges` to your config if you don't want
to pin your dependencies.

---

### Release Notes

<details>
<summary>zwave-js/zwave-js-ui (docker.io/zwavejs/zwave-js-ui)</summary>

###
[`v11.17.0`](https://redirect.github.com/zwave-js/zwave-js-ui/blob/HEAD/CHANGELOG.md#11170-2026-05-05)

[Compare
Source](https://redirect.github.com/zwave-js/zwave-js-ui/compare/v11.16.2...v11.17.0)

##### ✨ Features

- **hass:** add suggested\_area from node location
([#&#8203;4627](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4627))
([b9544ec](https://redirect.github.com/zwave-js/zwave-js-ui/commit/b9544ecd21d005d5b35769cc245d17e8a01a2455))

##### 🐛 Bug Fixes

- socket reconnection loop and trust proxy validation error
([#&#8203;4604](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4604))
([aed2082](https://redirect.github.com/zwave-js/zwave-js-ui/commit/aed2082a6576576783d6264e755ce42d6580f218)),
closes
[#&#8203;4564](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4564)
[#&#8203;4535](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4535)
[home-assistant/addons#4597](https://redirect.github.com/home-assistant/addons/issues/4597)
[#&#8203;4535](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4535)

##### 🔧 Chores

- **deps:** move frontend-only deps out of runtime deps
([#&#8203;4630](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4630))
([0ca1850](https://redirect.github.com/zwave-js/zwave-js-ui/commit/0ca1850792345fd88c0e623c8e00940433e2e37a))

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbImFwcC96d2F2ZWpzMm1xdHQiLCJhdXRvbWVyZ2UiLCJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->
markis pushed a commit to markis/cluster-config that referenced this pull request May 20, 2026
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[zwavejs/zwave-js-ui](https://redirect.github.com/zwave-js/zwave-js-ui)
| minor | `11.15.1` → `11.18.0` |

---

### Release Notes

<details>
<summary>zwave-js/zwave-js-ui (zwavejs/zwave-js-ui)</summary>

###
[`v11.18.0`](https://redirect.github.com/zwave-js/zwave-js-ui/blob/HEAD/CHANGELOG.md#11180-2026-05-18)

[Compare
Source](https://redirect.github.com/zwave-js/zwave-js-ui/compare/v11.17.0...v11.18.0)

##### ✨ Features

- bump zwave-js\@&#8203;15.24.0
([#&#8203;4634](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4634))
([6daab13](https://redirect.github.com/zwave-js/zwave-js-ui/commit/6daab1340d91636ed2f676c77d7a093c87f30ab3))
- **hass:** add reactive\_power/reactive\_energy, expand HA sensor
device classes, and fix unit casing
([#&#8203;4629](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4629))
([7e61d4b](https://redirect.github.com/zwave-js/zwave-js-ui/commit/7e61d4b35435edf3c27e77a56f6bebec62aeccd7))

##### 🐛 Bug Fixes

- **zwave:** restore debug log level on driver restart during capture
session
([#&#8203;4628](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4628))
([f6dc9f2](https://redirect.github.com/zwave-js/zwave-js-ui/commit/f6dc9f2488f857ccf204e92f54e4d6d5a42a19a1))

###
[`v11.17.0`](https://redirect.github.com/zwave-js/zwave-js-ui/blob/HEAD/CHANGELOG.md#11170-2026-05-05)

[Compare
Source](https://redirect.github.com/zwave-js/zwave-js-ui/compare/v11.16.2...v11.17.0)

##### ✨ Features

- **hass:** add suggested\_area from node location
([#&#8203;4627](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4627))
([b9544ec](https://redirect.github.com/zwave-js/zwave-js-ui/commit/b9544ecd21d005d5b35769cc245d17e8a01a2455))

##### 🐛 Bug Fixes

- socket reconnection loop and trust proxy validation error
([#&#8203;4604](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4604))
([aed2082](https://redirect.github.com/zwave-js/zwave-js-ui/commit/aed2082a6576576783d6264e755ce42d6580f218)),
closes
[#&#8203;4564](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4564)
[#&#8203;4535](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4535)
[home-assistant/addons#4597](https://redirect.github.com/home-assistant/addons/issues/4597)
[#&#8203;4535](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4535)

##### 🔧 Chores

- **deps:** move frontend-only deps out of runtime deps
([#&#8203;4630](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4630))
([0ca1850](https://redirect.github.com/zwave-js/zwave-js-ui/commit/0ca1850792345fd88c0e623c8e00940433e2e37a))

###
[`v11.16.2`](https://redirect.github.com/zwave-js/zwave-js-ui/blob/HEAD/CHANGELOG.md#11162-2026-04-27)

[Compare
Source](https://redirect.github.com/zwave-js/zwave-js-ui/compare/v11.16.1...v11.16.2)

##### ✨ Features

- bump
[@&#8203;zwave-js/server](https://redirect.github.com/zwave-js/server)@&#8203;3.8.0
([#&#8203;4621](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4621))
([d6902dc](https://redirect.github.com/zwave-js/zwave-js-ui/commit/d6902dc946373e97f8444aa6bd234f4fa0ccbff1))
- bump zwave-js\@&#8203;15.23.5
([#&#8203;4620](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4620))
([dd4bf1e](https://redirect.github.com/zwave-js/zwave-js-ui/commit/dd4bf1e90dffde0f04a3830e2235c4d22ffba7df))

###
[`v11.16.1`](https://redirect.github.com/zwave-js/zwave-js-ui/blob/HEAD/CHANGELOG.md#11161-2026-04-22)

[Compare
Source](https://redirect.github.com/zwave-js/zwave-js-ui/compare/v11.16.0...v11.16.1)

##### ✨ Features

- bump zwave-js\@&#8203;15.23.2
([#&#8203;4615](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4615))
([a21b2d8](https://redirect.github.com/zwave-js/zwave-js-ui/commit/a21b2d8be31ec701dfcb2fe3d7bdaabac479ed21))
- bump zwave-js\@&#8203;15.23.4
([#&#8203;4618](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4618))
([d496275](https://redirect.github.com/zwave-js/zwave-js-ui/commit/d496275a2993457f772d4f93e3db6f6f7a561d00))

##### 🔧 Chores

- **deps:** pin `zwave-js` dependency
([#&#8203;4617](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4617))
([da840fe](https://redirect.github.com/zwave-js/zwave-js-ui/commit/da840feab7581409275389ec5bdcc579d33de7d1))

###
[`v11.16.0`](https://redirect.github.com/zwave-js/zwave-js-ui/blob/HEAD/CHANGELOG.md#11160-2026-04-13)

[Compare
Source](https://redirect.github.com/zwave-js/zwave-js-ui/compare/v11.15.1...v11.16.0)

##### ✨ Features

- bump zwave-js\@&#8203;15.22.2
([#&#8203;4600](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4600))
([1e66ea6](https://redirect.github.com/zwave-js/zwave-js-ui/commit/1e66ea693809a91090cf54b23b70ab89c94003e3))
- bump zwave-js\@&#8203;15.23.0
([#&#8203;4613](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4613))
([802f564](https://redirect.github.com/zwave-js/zwave-js-ui/commit/802f56424d6b418406ba440fd6d36eb4cc7f2062))
- **ui:** move pagination to top of nodes table
([#&#8203;4605](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4605))
([08b46dc](https://redirect.github.com/zwave-js/zwave-js-ui/commit/08b46dc62780369a21b725f93451c1098cf72008)),
closes
[#&#8203;3603](https://redirect.github.com/zwave-js/zwave-js-ui/issues/3603)
[#&#8203;4488](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4488)

##### 🐛 Bug Fixes

- **docs:** clarify default value of BACKUPS\_DIR
([#&#8203;4597](https://redirect.github.com/zwave-js/zwave-js-ui/issues/4597))
([0224aca](https://redirect.github.com/zwave-js/zwave-js-ui/commit/0224aca52e2f784e2dbc3ce71e3f2adf07d24471))
- specify exact Node.js version in release workflow
([c80566f](https://redirect.github.com/zwave-js/zwave-js-ui/commit/c80566f7d02136dbe65bc39ad57900a023b9e43a))

</details>

---

### Configuration

📅 **Schedule**: (in timezone America/Los_Angeles)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/markis/cluster-config).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xODUuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE4NS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
@agners

agners commented Jun 26, 2026

Copy link
Copy Markdown
Member

@agners I figure it would be slightly more correct to pass the actual ingress proxy IP - any clue how I can determine that programmatically?

Sorry for the late reply.

Best practice is to not use the same port to expose the web interface optionally outside (ports key) and ingress. Currently that is the case, the "Z-Wave JS UI web port" 8091 is also used for ingress. Detecting ingress based on source IP works in theory (source IP would be whatever supervisor resolves to), but it is not ideal as source IP could potentially be spoofed and makes your code messier than it needs to be.

Instead, set ingress_port to 0, which means Supervisor will assign a ingress port on startup. You can then read the dynamically assign port using $(bashio::addon.ingress_port). Then create a separate server. For this server you can assume any connection to be pre-authenticated by Supervisor/Home Assistant Core. You can also read the headers X-Remote-User-Id, X-Remote-User-Name, and X-Remote-User-Display-Name (see also Authenticating a user when using ingress) to learn what user is accessing Z-Wave JS via Ingress. Today only admin users have access to Ingress. I probably need to expand that section a bit.

Note that with ESPHome and Music Assistant thins are a bit more complicated since they operate with host_network: true. In that mode, no "truly" internal IP is available. The hassio gateway IP used to be open to the public and only recently got protected. But this did not apply for regular apps like Z-Wave JS.

@agners

agners commented Jun 26, 2026

Copy link
Copy Markdown
Member

Btw, there is also the idea to support ingress via Unix domain socket which is by nature only available local and would secure access quite nicely.

@AlCalzone

Copy link
Copy Markdown
Contributor Author

I've started implementing the 2nd server approach in Z-Wave JS UI. I'll go with that.
Will mark this as draft for now, so I don't lose track of the explanation above.

@AlCalzone
AlCalzone marked this pull request as draft July 3, 2026 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants