Skip to content

fix: ship Colony Firewall Control 0.8.0 and Calamares 3.4.3, and install the firewall on offline installs - #6

Merged
MotherSphere merged 4 commits into
mainfrom
fix/firewall-0.8-offline-install
Oct 12, 2026
Merged

MotherSphere merged 4 commits into
mainfrom
fix/firewall-0.8-offline-install

Conversation

@MotherSphere

@MotherSphere MotherSphere commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Summary

Ships the security releases of the two programs [colony] builds from upstream, and makes every install, online or offline, end with the firewall installed and enabled.

Colony Firewall Control 0.2.2 to 0.8.0

0.5.0, 0.7.0 and 0.8.0 each carry security fixes.

  • PKGBUILD: 0.8.0, digest of the v0.8.0 tag tarball (f35da982..., the same one upstream's release PKGBUILD carries). The build itself did not change: same workspace layout, the eBPF crate still has its own lockfile and .cargo/config.toml, rust-toolchain.toml still pins a nightly that prepare() removes, protoc and !lto still needed. The one new file is the polkit action file /usr/share/polkit-1/actions/org.projectcolony.firewall.policy. New optional dependencies, as upstream has them: polkit, bubblewrap>=0.13.0, libbpf.
  • colony-firewall-control.install, rewritten for the 0.8.0 model:
    • post_install: start order is now table then daemon; sudo cfc for changes; group membership only lets the app and tray reach the daemon; turn off with disable --now, never a plain stop, because the network managers now require the table.
    • post_upgrade: re-enables an enabled colony-firewall-nft (and inbound) unit, so the RequiredBy=systemd-networkd NetworkManager links 0.7.0 added exist on machines enabled under 0.2.2. It leaves the running units alone and prints the order to apply the upgrade (restart colony-firewalld, then reload colony-firewall-nft). Upstream's scriptlet reloads the table here. Under a 0.2.2 daemon that would queue every new loopback flow, the resolved stub included, to a daemon without 0.8.0's loopback allowance.
  • colony-firewall-defaults (pkgrel 3):
    • Removes 10-wait-for-seed.conf. With CFC 0.7.0+ units it closes an ordering cycle (nft before daemon, daemon before seed, seed before nft). systemd cannot break that cycle, so none of the three units would start. systemd-analyze verify on the 0.8.0 units plus the old files reports Transaction order is cyclic. With the new files it is clean, also with systemd-networkd.service.requires/ linked.
    • The seed is now After=colony-firewalld, Before=network-pre.target, so the boot order is table, daemon, rules, then network. A failing seed leaves the table loaded (upstream's fail-closed contract) instead of keeping it off.
    • Depends on colony-firewall-control>=0.8.0. Its scriptlet text is updated.
  • The system bundle cfc rules bootstrap-defaults seeds is unchanged in 0.8.0. It is twelve program-scoped Allow rules and no Deny, so the new ranking (a program Deny beats a generic Allow) does not affect them. no_ui_action is Deny on every shipped profile, and the shipped daemon.toml does not override it.
  • colonyctl (pkgrel 5): cfc status is still the command the firewall section uses. status now reports the table separately: when it is loaded with no daemon, every new connection is refused without a prompt. The not-installed hint now points at colony-firewall-defaults, because -control alone enables nothing.

Calamares 3.4.2 to 3.4.3

  • New version and digest (144cbbf6..., which matches the one upstream publishes). Both downstream patches apply to 3.4.3 with no offset or fuzz, so no rebase was needed.
  • The users and bootloader module defaults the installer overrides are byte-identical between 3.4.2 and 3.4.3, and their version comments now say so.

The firewall on offline installs

Before this PR, the firewall reached the target only through a try_install from [colony] in packages.conf. Offline, that failed as a warning, and the machine rebooted with systemd-networkd and resolved enabled and no firewall.

  • The medium now carries /usr/share/colony/offline-repo/ with the three firewall packages, their .sig files and a signed colony-offline database. iso/build.sh assembles it through the new iso/offline-repo.sh. The live session never installs from it: a live medium with the firewall enabled would have a fail-closed table and no network to finish the install through.

  • shellprocess_cleanup.conf adds these steps after pacman-key --populate and before networking is enabled:

    • install into the target with pacman --config /usr/share/colony/offline-repo/pacman.conf. That configuration knows only this repository, with SigLevel = Required DatabaseRequired and LocalFileSigLevel = Required;
    • assert that the three units are enabled with systemctl is-enabled. The defaults scriptlet swallows a failed preset, so this check is what makes such a failure visible;
    • remove the repository and its sync database from the target.

    No step has a - prefix, so a failure stops the install before a bootloader exists. The try_install is gone, so online installs take the same path. The first colonyctl update moves the firewall to the newest build in [colony].

  • users.conf and gen-netinstall.py comments are updated to match. shellprocess_verify.conf checks only greeters, so it needs no change.

Signing, for G2's CI sign job

All three are signed today with the local COLONY_SIGNING_KEY:

File Signed by When
colony-firewall-control-0.8.0-1-x86_64.pkg.tar.zst.sig, colony-firewall-control-ebpf-0.8.0-1-x86_64.pkg.tar.zst.sig, colony-firewall-defaults-20260821-3-any.pkg.tar.zst.sig repo/build.sh (unchanged) package build
colony-offline.db.tar.zst.sig and colony-offline.files.tar.zst.sig (repo-add --sign; colony-offline.db.sig and colony-offline.files.sig are symlinks to them) iso/offline-repo.sh ISO build, before mkarchiso: the database is inside the squashfs
the image and its .sha256 (unchanged) iso/build.sh after mkarchiso

What G2 needs to know: the offline database signature is embedded in the image. The isolated sign job therefore has to produce colony-offline/ (packages, their .sig files, the signed database) and hand it to the keyless ISO job as an input, then sign the image after it.

Verification

  • Local:
    • .SRCINFO regenerated with makepkg --printsrcinfo for all four packages.
    • Both tarball digests checked against upstream's published ones.
    • The Calamares patches dry-run against 3.4.3.
    • systemd-analyze verify run on the old and new unit graphs, as described above.
    • tools/test-repo.sh passes in a user namespace with a throwaway key. Its new section assembles an offline repository with iso/offline-repo.sh, installs through the shipped pacman.conf, and is refused once the database signature is removed.
    • iso/offline-repo.sh refuses a missing package and a bad signature.
    • vm-headless.py check-firewall and exec exercised against a fake guest agent, in both the passing and the failing case.
  • CI (this PR), all green:
    • colony-firewall-control 0.8.0 builds in a clean archlinux:base-devel container: rustc and bpf-linker agree on LLVM 23, and the eBPF object carries .BTF (4 sections).
    • check() passes: every test suite is green, with one test skipped, live_query_finds_own_udp_socket, as before.
    • calamares 3.4.3 builds with both patches. colony-firewall-defaults and colonyctl build too.
    • namcap reports no errors and every .SRCINFO matches. namcap's split-PKGBUILD rule flagged the firewall's runtime depends missing from makedepends, an issue that predates this PR: the 0.2.2 recipe had never been built in CI. They are now listed there.
    • shellcheck, py_compile, and the extended test-repo.sh pass. The offline repository installs through its own pacman.conf, and is refused once unsigned.

Still needs a VM boot (G2 smoke test)

The ISO cannot be built in CI yet. Steps:

  1. Build the image.
  2. tools/vm-headless.py start install --offline, then install with the keyboard.
  3. tools/vm-headless.py installed --offline.
  4. tools/vm-headless.py check-firewall, which must pass: colony-firewall-nft, colony-firewalld and colony-firewall-seed active, the eBPF object present, and colonyctl status printed.
  5. Repeat with network, to check the online path and that DHCP and DNS work through the seeded rules at boot.
  6. Upgrade a 0.2.2 VM to this [colony] and check the scriptlet's re-enable and its message.

ISO size: expected about +8 MB. The published 0.2.2 firewall packages are 8.3 MB together, and an already compressed package gains nothing from xz. The last image is 1.68 GiB, against the 2 GiB per-asset limit. iso/build.sh now prints the repository's size on the medium, so the first build gives the exact delta.

Needs hardware (Lin)

  • A real install, online and offline, with Wi-Fi (iwd) and with NetworkManager (Plasma). With CFC 0.7.0+, the table's RequiredBy= holds both managers, so a table that fails to load keeps the network down.
  • The polkit prompt from the tray: pause needs a polkit agent in the session (on Hyprland, hyprpolkitagent).
  • Rebuild and publish [colony] and the ISO with the signing key.

CFC 0.5.0, 0.7.0 and 0.8.0 each carry security fixes; [colony] served 0.2.2.

- Install the polkit actions 0.8.0 asks about before the app or tray may
  pause, resume, import rules or store an Allow that names no program, and
  carry upstream's new optional dependencies (polkit, bubblewrap, libbpf).
- Rewrite the scriptlet for the 0.8.0 control model (sudo cfc for changes,
  restart the app and tray after an upgrade) and for the table that loads
  before the daemon and is required by the network managers. post_upgrade
  re-enables an enabled table so those links exist, and leaves the running
  units alone: a 0.8.0 table under an older daemon would queue every loopback
  flow, resolved's stub included, to a daemon that prompts for them.
- colony-firewall-defaults drops its nft drop-in. With the 0.7.0 units it
  closed an ordering cycle (table, daemon, seed, table) that systemd cannot
  break, so none of the three would start. The seed now runs before
  network-pre.target, after the daemon, and the package needs CFC >= 0.8.0.
- colonyctl status reports the table on its own: loaded with no daemon, it
  refuses every new connection without a prompt.
Partition fixes (/boot no longer marked as the EFI partition, which could
break systemd-gpt-auto-generator; current KPMcore) and Wayland keyboard input
in QML modules. Both downstream patches apply unchanged, and the users and
bootloader module defaults the installer overrides are identical to 3.4.2's.
The firewall reached the target only through a try_install from [colony]. An
offline install logged the failure as a warning and rebooted with networkd
and resolved enabled and no firewall.

The medium now carries colony-firewall-defaults, colony-firewall-control and
colony-firewall-control-ebpf as a small repository, with the signatures
repo/build.sh made and a colony-offline database that iso/offline-repo.sh
signs at build time. The cleanup step installs them into the target through
a pacman.conf that knows only that repository and requires every signature,
before it enables networking, checks that the three units are enabled, and
stops the install otherwise. Nothing installs it on the live session.

tools/test-repo.sh assembles that repository and installs through its
pacman.conf in CI, and refuses it unsigned. tools/vm-headless.py boots
without a network card (--offline) and checks the installed firewall through
the guest agent (check-firewall).
namcap's split PKGBUILD rule: makepkg -s installs only the global arrays, so
the depends of package_colony-firewall-control() belong in makedepends too.
@MotherSphere
MotherSphere merged commit dc53674 into main Oct 12, 2026
5 checks passed
@MotherSphere
MotherSphere deleted the fix/firewall-0.8-offline-install branch October 12, 2026 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant