lacs-project / lacs-project/sysknife

Fedora Atomic: make install has never completed, and it fails halfway

Open
#301 4 comments 0 reactions 0 assignees View on GitHub
bug hard help wanted
Dominant language
Rust
Stars
12
Forks
19
Avg merge
18h 57m
Merged PRs (30d)
116

Description

Ran the Fedora Atomic VM for #217. It does not get as far as a story. `make install` aborts, and the run before it aborts is worse than the abort.

### What happened

Fedora 43 Silverblue, `tests/e2e/provision.sh`, the rpm-ostree branch:

```
install -Dm 755 packaging/sysknife-apt-pin-edit /usr/lib/sysknife/apt-pin-edit
install: cannot create directory '/usr/lib/sysknife': Read-only file system
make: *** [Makefile:74: daemon-install] Error 1

!!! PROVISIONING FAILED at step: make install (rpm-ostree paths)
```

On the guest:

```
/usr/local -> ../var/usrlocal (writable)
/usr/lib (Read-only file system)
```

### The override list was written before the helpers existed

`provision.sh` redirects four path variables on an ostree host:

```
SYSUSERS=/etc/sysusers.d TMPFILES=/etc/tmpfiles.d
SYSTEMD=/etc/systemd/system POLKIT=/etc/polkit-1/rules.d
```

`HELPERS ?= /usr/lib/sysknife` is not among them, and `/usr/lib` is exactly what rpm-ostree makes immutable.

Both the variable and that four-entry list shipped in the initial public release, `f1d9806`, 2026-06-15. The Makefile installed two helpers then. It installs twelve now. The override list has not changed once. So this has failed on every Fedora Atomic host since the repo went public, and `docs/distro-support.md`'s "eligible, awaiting a fresh VM run" was accurate that nobody had run it.

### Why adding `HELPERS=` to that list is the wrong fix

The path is a trust boundary, not a location. It is written down in three places that all have to agree:

| holder | count |
| --- | --- |
| `const` strings in the daemon (`actions/*.rs`) | 12 |
| `NOPASSWD` grants in `packaging/sysknife-sudoers` | 12 |
| the Makefile's `$(HELPERS)` install lines | 12 |

The sudoers grants are the reason the helpers exist. `grub-kargs-edit` replaced unconstrained `cp`, `python3` and `update-grub` grants (red-team findings HI2 and HI3); `sysctl-edit` exists so no bare `sysctl` grant is needed, because `sysctl -p ` would load an attacker's file. Each grant names one absolute path with a trailing `*`. Point the install somewhere else and every grant misses, so every helper-backed action fails with a sudo denial rather than a missing file.

And the obvious destination is worse than a miss. `/usr/local` on ostree is a symlink to `/var/usrlocal`, which is mutable state. Twelve scripts that root executes through a `NOPASSWD` wildcard grant do not belong in mutable state on a host whose selling point is that the system directories are not.

### `make install` is not atomic, which is the second defect

The run did not fail cleanly. Before it hit the helpers it had already written, on a host that now has no working install:

```
/usr/local/bin/sysknife-daemon installed
/etc/systemd/system/sysknife-daemon.service installed, daemon-reload done
/etc/polkit-1/rules.d/50-sysknife.rules installed
/etc/sudoers.d/sysknife installed (all 12 helper grants)
/usr/lib/sysknife/ absent
```

A systemd unit and twelve sudo grants for twelve scripts that are not there. Whatever is decided about the path, `daemon-install` failing partway should not leave that behind.

### Options, none of them one-liners

**Ship an RPM and layer it.** `rpm-ostree install ./sysknife.rpm` puts the helpers in `/usr/lib/sysknife` inside the ostree commit, immutable, exactly where the grants and the consts already point. Nothing else changes. It is the answer that matches how the platform works, and it needs a spec file and a build job.

**Move the helpers to `/etc/sysknife/helpers` for every distro.** `/etc` is writable on ostree and is config, not mutable state. It costs a coordinated change across 12 consts, 12 grants and the Makefile, and it weakens the boundary on non-ostree hosts, where `/usr/lib` is currently root-only by virtue of being `/usr`.

**Say Fedora Atomic is not installable and stop listing it as eligible.** `docs/distro-support.md:52`, `README.md:204` and `docs/release-readiness.md:29` name Silverblue 44. Cheapest, and it retires a platform the action set already covers.

I lean towards the RPM. Recording the reasoning rather than the conclusion, because the choice belongs to whoever owns the trust boundary.

### Not contributor work yet

Labelling this `maintainer-only` until the path question is settled. A PR that picks one of the three without that decision would be rework whichever way it went.

Two things a contributor could take today, independent of the outcome:

- `daemon-install` leaving a half-installed system. That is wrong under every option above.
- A check that the rpm-ostree branch overrides every Makefile path variable whose default sits under a read-only prefix. Derive the variables from the Makefile rather than listing them, the way `tests/e2e/provider-parity.test.sh` derives its provider list, so the next variable added joins the rule on its own. That test would have caught this in June.

Contributor guide

Open the contributing guide

Research direction

Start with Makefile:74, tests/e2e/provision.sh, and tests/e2e/provider-parity.test.sh. First resolve the helper-path decision with maintainers; independent work could then test that rpm-ostree path overrides cover Makefile variables and that daemon-install does not leave partial files. Done means the chosen install behavior is agreed and the relevant provisioning or failure-path tests pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
linux, rust
Domain
build-system, devops, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.