Browse Source

zwave-js-ui: mount only the settings file, not the config directory

":Z" relabels what it mounts to container_file_t, recursively and on disk, and
init.sh lives in /etc/quadlets/zwave-js-ui. Mounting that directory into the
container therefore left a script systemd could no longer execute:
zwave-js-ui-init.service failed with 203/EXEC at every boot after the first
start of the container, and the container's Requires= on it kept the container
down too. A machine that survived its installation stopped surviving a reboot.

Found on the target machine, not in review: the first installation works,
because Ignition writes /etc with the right labels and the init service runs
before the container. The fault only appears at the second boot.

The README also gains a "Clearing the store" section. The seed is a first-boot
mechanism, so an operator who deletes settings.json -- after rotating the
security keys, for instance, since Z-Wave JS UI keeps its own copy of the old
ones -- has to restart the init service explicitly. "systemctl restart
zwave-js-ui.target" does not re-run it: it is RemainAfterExit=yes and already
active. Getting that wrong leaves a container unhealthy forever with a happy
driver, and authentication silently off.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
pull/11/head
Nicolas Massé 1 month ago
parent
commit
4ffd6d0f49
  1. 32
      cookbooks/zwave-js-ui/README.md
  2. 16
      cookbooks/zwave-js-ui/zwave-js-ui.container

32
cookbooks/zwave-js-ui/README.md

@ -198,8 +198,40 @@ a restart loop fixes.
artifact. What the external file buys is not secrecy: it is that the authoritative copy lives
outside the application, is restorable from a vault, and cannot be edited from the web
interface.
- **Only `external-settings.json` is mounted into the container, never the configuration
directory.** `:Z` relabels what it mounts to `container_file_t`, recursively and on disk, and
`init.sh` lives in that directory. Mounting the directory therefore leaves a script systemd
can no longer execute: `zwave-js-ui-init.service` fails with `203/EXEC` at every boot *after
the first start of the container*, and the container's `Requires=` on it keeps the container
down too. A machine that survived its installation stops surviving a reboot, which is the
worst shape a fault can take. The `mosquitto` cookbook mounts its whole directory and is fine
because it keeps no script there; this one does.
- **No Traefik and no TLS.** See "What this cookbook does NOT do".
## Clearing the store
The seed is a **first-boot** mechanism: `init.sh` writes `store/settings.json` only when it is
absent, because Z-Wave JS UI owns that file afterwards. Two consequences for an operator who
wipes the store, or who deletes `settings.json` to force the settings of
`external-settings.json` back in — after rotating the security keys, for instance, since
Z-Wave JS UI keeps its own copy of the old ones:
- **Delete the file with the container stopped, then restart the init service explicitly**, in
this order. `systemctl restart zwave-js-ui.target` is not enough: the init service is
`RemainAfterExit=yes` and already active, so it does not run again, and Z-Wave JS UI then
writes a `settings.json` that holds the external settings and **not** the seeded ones.
- The symptom of getting it wrong is a container that is `unhealthy` forever with a driver that
is perfectly happy: no `mqtt` section means no MQTT client, and `/health` answers 500. And,
more quietly, `gateway.authEnabled` is gone with it, so the web interface stops asking for a
password.
```sh
systemctl stop zwave-js-ui.service
rm /var/lib/virtiofs/data/zwave-js-ui/settings.json
systemctl restart zwave-js-ui-init.service # seeds it again
systemctl start zwave-js-ui.service
```
## Usage
In a separate terminal, follow the logs.

16
cookbooks/zwave-js-ui/zwave-js-ui.container

@ -59,9 +59,19 @@ Environment=STORE_DIR=/var/lib/virtiofs/data/zwave-js-ui
# SESSION_SECRET and the initial administrator credentials (site secrets).
EnvironmentFile=/etc/quadlets/zwave-js-ui/zwave-js-ui.env
# Configuration (read-only), at the same path inside and outside the container so
# that ZWAVE_EXTERNAL_SETTINGS names one path only.
Volume=/etc/quadlets/zwave-js-ui:/etc/quadlets/zwave-js-ui:ro,Z
# Configuration (read-only): the ONE file the container reads, at the same path
# inside and outside so that ZWAVE_EXTERNAL_SETTINGS names one path only.
#
# Never mount the whole /etc/quadlets/zwave-js-ui directory here. ":Z" relabels
# what it mounts to container_file_t, recursively and on disk, and init.sh lives
# in that directory: systemd can no longer execute it, so
# zwave-js-ui-init.service fails 203/EXEC at every boot after the first start of
# this container, and the Requires= above then keeps the container down with it.
# A machine that survived its install stops surviving a reboot.
#
# ConditionPathExists on this file above is also what keeps podman from creating
# a DIRECTORY at this path when the operator has not provided the file.
Volume=/etc/quadlets/zwave-js-ui/external-settings.json:/etc/quadlets/zwave-js-ui/external-settings.json:ro,Z
# Precious data: the store holds the node names, the associations, the NVM
# backups of the controller and the users of the web interface. It lives on the

Loading…
Cancel
Save