mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 22:35:08 +00:00
The display process now serves a Unix socket, /run/ledmatrix/control.sock, carrying versioned newline-delimited JSON commands that are acknowledged. Stage 1 moves on-demand start/stop (plus status, hello and ping) onto it; the cache-file mailbox stays as the fallback for one release. - src/ipc/contract.py: typed request/response envelopes, command args, error codes, NDJSON framing with a 64 KiB limit, socket path rules. - src/ipc/server.py: threaded server owned by the display. Handlers only queue onto a bounded queue and ack with the request id; the render thread drains it where it reads the mailbox. Bounded clients, timeouts, garbage/oversize/disconnect handling; 0660 socket in the cache dir's group plus SO_PEERCRED checks; skips cleanly on Windows or when off. - src/ipc/client.py: one short-timeout request; any failure raises ControlError(reason). - api_v3/display.py: on-demand start/stop try the socket, fall back to the mailbox exactly as before, and report transport/socket_error. - display_controller.py: start/close the server; the mailbox handler body is extracted into _handle_on_demand_request and shared by both paths. - docs/IPC_CONTROL_SOCKET.md: protocol, security model, stage plan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
157 lines
8.4 KiB
Markdown
157 lines
8.4 KiB
Markdown
# Permissions
|
|
|
|
Who owns what on an installed system, which privileged commands the web
|
|
interface may run, and how to repair ownership when it goes wrong. The
|
|
installer, [`first_time_install.sh`](../first_time_install.sh), sets all of
|
|
this up; this page describes the result.
|
|
|
|
## Users and groups
|
|
|
|
| Account | Used by | Why |
|
|
|---|---|---|
|
|
| `root` | `ledmatrix.service` (the display) | The LED matrix library needs direct GPIO access |
|
|
| The installing user (e.g. `ledpi`) | `ledmatrix-web.service`, `ledmatrix-update-verify.service` | A web server should not run as root |
|
|
| `ledmatrix` group | shared files | Members: the installing user, `root`, and `daemon` if it exists. Created by [`setup_cache.sh`](../scripts/install/setup_cache.sh) and the installer |
|
|
|
|
The installer also adds the web user to `systemd-journal` and `adm` so the
|
|
**Logs** tab can read the journal. Group changes apply after the user logs
|
|
in again (services pick them up on restart).
|
|
|
|
## Files and directories
|
|
|
|
| Path | Owner | Mode | Notes |
|
|
|---|---|---|---|
|
|
| Project directory | web user | dirs `755`, files `644`, `*.sh` `755` | Set in the installer's "Normalize project file permissions" step |
|
|
| `config/` | web user | `2775` | |
|
|
| `config/config.json` | web user | `644` | Written by the web interface |
|
|
| `config/config_secrets.json` | web user : `ledmatrix` | `640` | Owned by the web user because the web interface writes it; root reads it regardless of mode |
|
|
| `plugin-repos/`, `plugins/` | web user | dirs `2775`, files `664` | The web interface installs and removes plugins |
|
|
| `assets/` | web user | dirs `755`, files `644` | Root writes downloaded logos regardless |
|
|
| `/var/cache/ledmatrix/` | `root:ledmatrix` | `2775` (setgid) | Shared cache: see below |
|
|
| Cache files | creator : `ledmatrix` | `660` | |
|
|
| `/run/ledmatrix/` | `root` | `755` | tmpfs; `RuntimeDirectory=` in `ledmatrix.service`, removed when the display stops |
|
|
| `/run/ledmatrix/control.sock` | `root` : cache directory's group (`ledmatrix`) | `660` | The display's control socket; only root and that group can connect. See [IPC_CONTROL_SOCKET.md](IPC_CONTROL_SOCKET.md#security-model) |
|
|
| `scripts/fix_perms/safe_plugin_rm.sh`, `safe_pip_install.sh` | `root:root` | `755` | Run as root through sudo, so the web user must not be able to edit them |
|
|
| `/etc/sudoers.d/ledmatrix_web`, `ledmatrix_wifi` | `root` | `440` | |
|
|
|
|
What keeps it that way at runtime:
|
|
|
|
- **Config files.** Saves go through
|
|
[`src/config_manager_atomic.py`](../src/config_manager_atomic.py), which
|
|
applies `get_config_file_mode()` (`640` for secrets, `644` otherwise) and,
|
|
when running as root, moves the file's group to the project directory's
|
|
group (`ensure_shared_group_ownership()` in
|
|
[`src/common/permission_utils.py`](../src/common/permission_utils.py)).
|
|
- **Cache files.** [`src/cache/disk_cache.py`](../src/cache/disk_cache.py)
|
|
sets every file it writes to `0660` and gives it the cache directory's
|
|
group, without relying on the setgid bit. So a file root writes stays
|
|
readable by the web user.
|
|
- **Plugin directories.** [`run.py`](../run.py) sets
|
|
`sys.dont_write_bytecode`, because root-owned `__pycache__` directories
|
|
inside a plugin stop the web user updating or removing it.
|
|
|
|
`ledmatrix-web.service` deliberately has no `CacheDirectory=`: systemd would
|
|
re-own `/var/cache/ledmatrix` to the web user and its primary group, and the
|
|
web interface could no longer read what the display writes (see the comment
|
|
in [`systemd/ledmatrix-web.service`](../systemd/ledmatrix-web.service)).
|
|
|
|
If `/var/cache/ledmatrix` is not usable, `CacheManager` falls back to
|
|
`~/.ledmatrix_cache`, `/opt/ledmatrix/cache` or a temp directory
|
|
([`src/cache_manager.py`](../src/cache_manager.py)). The two services then
|
|
may not share a cache, and the web UI shows stale or empty display status,
|
|
on-demand state and plugin health. Fix the directory rather than living
|
|
with the fallback.
|
|
|
|
## sudo rules
|
|
|
|
### `/etc/sudoers.d/ledmatrix_web`
|
|
|
|
Generated by `web_sudoers_rules()` in
|
|
[`scripts/install/lib_sudoers.sh`](../scripts/install/lib_sudoers.sh), the
|
|
only place these rules are defined. Installed by the installer and by
|
|
[`configure_web_sudo.sh`](../scripts/install/configure_web_sudo.sh), both of
|
|
which check them with `visudo -c` first. The web user may run, without a
|
|
password:
|
|
|
|
- `reboot`, `poweroff`
|
|
- `systemctl start|stop|restart|enable|disable|status ledmatrix.service`,
|
|
`systemctl is-active ledmatrix[.service]`
|
|
- `systemctl start|stop|restart ledmatrix-web.service`
|
|
- `bash <project>/scripts/fix_perms/safe_plugin_rm.sh *` — removes a
|
|
directory only if it resolves to a child of `plugin-repos/` or `plugins/`
|
|
- `bash <project>/scripts/fix_perms/safe_pip_install.sh *` — installs a
|
|
`requirements.txt` only if it is the project's own or one under
|
|
`plugin-repos/` or `plugins/`, so the root display service can import the
|
|
packages
|
|
- `journalctl -u ledmatrix.service *`, `-u ledmatrix *`, `-t ledmatrix *`,
|
|
tagged `NOEXEC`: journalctl opens a pager on a terminal, and a shell
|
|
escape from that pager would be a root shell
|
|
|
|
### `/etc/sudoers.d/ledmatrix_wifi`
|
|
|
|
Written by
|
|
[`scripts/install/configure_wifi_permissions.sh`](../scripts/install/configure_wifi_permissions.sh)
|
|
(run as the web user; the installer calls it). It refuses to grant a binary
|
|
that is not root-owned or is group/world-writable. The rules cover:
|
|
|
|
- `nmcli device wifi connect|disconnect *`, `nmcli device connect|disconnect *`,
|
|
`nmcli radio wifi on|off`
|
|
- `systemctl start|stop|restart hostapd`, `... dnsmasq`,
|
|
`systemctl restart NetworkManager`
|
|
- `sysctl -w net.ipv4.ip_forward=0|1`
|
|
- `nft add|delete table ip ledmatrix`
|
|
- `rfkill unblock wifi`
|
|
- `mkdir -p /etc/NetworkManager/dnsmasq-shared.d`
|
|
- `cp` of `/tmp/hostapd.conf` and `/tmp/dnsmasq.conf` to their fixed
|
|
destinations, and `rm -f /etc/dnsmasq.d/ledmatrix-captive.conf`
|
|
- `cp /tmp/ledmatrix-nm-dnsmasq.conf` to
|
|
`/etc/NetworkManager/dnsmasq-shared.d/ledmatrix-captive.conf`, and
|
|
`rm -f` of that file
|
|
|
|
**`iptables` is deliberately not granted.** The captive portal's rules are
|
|
built from the interface name and port, so a rule covering them would need a
|
|
trailing wildcard, and `iptables --modprobe=<path>` runs `<path>` as root: a
|
|
wildcard grant is a root shell for the web user. Doing it safely needs a
|
|
wrapper script that builds the rules itself, like `safe_plugin_rm.sh`. On a
|
|
stock Raspberry Pi OS image the default user's blanket `NOPASSWD` rule
|
|
(`/etc/sudoers.d/010_pi-nopasswd`) hides this gap.
|
|
|
|
### polkit
|
|
|
|
The same script installs `/etc/polkit-1/rules.d/10-ledmatrix-wifi.rules`,
|
|
which lets the web user perform any `org.freedesktop.NetworkManager.*`
|
|
action without authentication.
|
|
|
|
## Repair scripts
|
|
|
|
In [`scripts/fix_perms/`](../scripts/fix_perms/). Run them from the project
|
|
directory.
|
|
|
|
| Script | Run as | What it does | Notes |
|
|
|---|---|---|---|
|
|
| `fix_plugin_permissions.sh` | `sudo` | `plugins/` and `plugin-repos/` to `root:<user>`, dirs `2775`, files `664`; makes a `700` home directory `755` so root can traverse it | Safe. Group-writable, so the web user keeps write access |
|
|
| `fix_assets_permissions.sh` | `sudo` | `assets/` to `<user>:<group>`, mode `777` recursively | Works, but looser than the installer's `755`/`644` |
|
|
| `fix_cache_permissions.sh` | `sudo` | Runs [`setup_cache.sh`](../scripts/install/setup_cache.sh) for `/var/cache/ledmatrix` (`root:ledmatrix`, `2775`, files `660`), then makes `~/.ledmatrix_cache` (the fallback cache) `<user>:<group>` mode `777` | Safe. The `~/.ledmatrix_cache` mode is still `777` |
|
|
| `fix_web_permissions.sh` | the web user, **without** `sudo` | Resets project file ownership for the web user (it calls `sudo` itself), then makes `safe_plugin_rm.sh` and `safe_pip_install.sh` `root:root` `755` again and restores `config_secrets.json` to its owner, group `ledmatrix`, mode `640` | Refuses to run as root. It does not write sudoers rules |
|
|
| `safe_plugin_rm.sh`, `safe_pip_install.sh` | — | Called by the web interface through sudo | Not for manual use |
|
|
|
|
To reinstall the sudoers rules, run
|
|
`./scripts/install/configure_web_sudo.sh` (web rules) or
|
|
`./scripts/install/configure_wifi_permissions.sh` (WiFi rules and polkit) as
|
|
the web user, not with `sudo`.
|
|
|
|
After any of these, restart both services:
|
|
|
|
```bash
|
|
sudo systemctl restart ledmatrix.service ledmatrix-web.service
|
|
```
|
|
|
|
## Checking
|
|
|
|
```bash
|
|
ls -ld /var/cache/ledmatrix # drwxrwsr-x root ledmatrix
|
|
stat -c '%U:%G %a %n' config/config.json config/config_secrets.json
|
|
id # web user should list ledmatrix
|
|
sudo -l # lists the NOPASSWD rules
|
|
```
|