mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-27 16:51:12 -07:00
Mount the rootfs read-only on both images
The rootfs mounted read-write, so a slot ran with its own files open to change, and the factory-slot mounts rode along on the release image. Both images now carry the read-only-rootfs feature: the ro root line and the rcS default, the volatile links made at rootfs time, a writable copy of /var/lib at boot, a build failure for a post-install that needs the machine, and the removal of shadow, base-passwd, update-rc.d and update-alternatives. What must last or change at run time is handled file by file: - forgefirm-users renders the four account files from the record into /run/forgefirm/accounts and bind-mounts each copy over its /etc file (useradd and the rest are gone with shadow); a render writes through the mount, and the image's own files apply until the first render. - forgefirm-banner bind-mounts a copy of /etc/issue and writes the address block through it. - sshd keeps its host keys under /data/forgefirm/ssh, so the fingerprint survives updates; both sshd configs carry the same HostKey lines. - forgefirm-logging passes logrotate a state file under /var/run (logrotate refuses to run without one). - forgefirm-persist points the boot timestamp and the random seed at /data/forgefirm. The dev image appends the /factory slot mounts, without nofail (busybox mount hands it to the kernel, which rejects it). The rootfs command entries lose their semicolons: on scarthgap the value is the task's vardeps, split on whitespace, so "name;" left the function body out of the signature and a changed body did not remake the rootfs; with the bodies tracked, the dev image's DATETIME string needs a vardepsexclude. release.sh gains the read-only gate (root ro, no /factory line, ROOTFS_READ_ONLY=yes, host keys on /data). image.health checks the mounts, the account binds, the banner bind, the host keys and the dev-only /factory mounts. Proven on the bench reference (dev image 20260909140901): / ro, /data rw, /var/lib a tmpfs copy, the four account files and /etc/issue bound from tmpfs, the host keys in /data/forgefirm/ssh, no "Read-only file system" line in any log; forgectrl.auth and commission.account-login (a temporary account rendered, logged in over HTTPS and removed again), kernel.latch-locked-idle and motion.liveness-probe PASS; logrotate runs with the volatile state. forgetest unit tests 335 OK; both images build clean, and debugfs on the built rootfs shows every setting above.
This commit is contained in:
@@ -43,6 +43,10 @@ IMAGE_ROOTFS_EXTRA_SPACE = "262144"
|
||||
# Dev builds identify by build timestamp (matches the artifact name),
|
||||
# tagged so a bench machine is never mistaken for a release.
|
||||
FORGEFIRM_VERSION_STRING = "${DATETIME} (dev)"
|
||||
# The rootfs functions that write the string are in the do_rootfs
|
||||
# signature; DATETIME must not be, or the basehash changes at every parse
|
||||
# and bitbake refuses the build as non-deterministic.
|
||||
FORGEFIRM_VERSION_STRING[vardepsexclude] += "DATETIME"
|
||||
|
||||
# /etc/forgefirm-dev marks a dev image on the rootfs; its content is the
|
||||
# version string. The sshd init script starts sshd on a dev image without
|
||||
@@ -52,4 +56,20 @@ write_forgefirm_dev_marker() {
|
||||
echo "${FORGEFIRM_VERSION_STRING}" > ${IMAGE_ROOTFS}${sysconfdir}/forgefirm-dev
|
||||
}
|
||||
write_forgefirm_dev_marker[vardepsexclude] += "DATETIME"
|
||||
ROOTFS_POSTPROCESS_COMMAND += "write_forgefirm_dev_marker;"
|
||||
ROOTFS_POSTPROCESS_COMMAND += "write_forgefirm_dev_marker "
|
||||
|
||||
# The two factory rootfs slots, read-only under /factory/img1 and
|
||||
# /factory/img2: a bench convenience for reading a factory image in place
|
||||
# (the ffboot inventory reuses the mounts). The release image mounts no
|
||||
# factory slot; it reads one through a temporary read-only mount when it
|
||||
# needs to (ffboot -l). No nofail: busybox mount hands it to the kernel
|
||||
# as a filesystem parameter, which the kernel rejects (the mount fails
|
||||
# with EINVAL); mount -a goes on past a slot that does not mount anyway.
|
||||
add_factory_slot_mounts() {
|
||||
install -d ${IMAGE_ROOTFS}/factory/img1 ${IMAGE_ROOTFS}/factory/img2
|
||||
printf '%s\n' \
|
||||
'/dev/mmcblk2p1 /factory/img1 auto ro,noatime 0 0' \
|
||||
'/dev/mmcblk2p2 /factory/img2 auto ro,noatime 0 0' \
|
||||
>> ${IMAGE_ROOTFS}${sysconfdir}/fstab
|
||||
}
|
||||
ROOTFS_POSTPROCESS_COMMAND += "add_factory_slot_mounts "
|
||||
|
||||
@@ -45,17 +45,35 @@ IMAGE_INSTALL:remove = "python3 ${FORGEFIRM_RELEASE_TRIM}"
|
||||
# VIRTUAL-RUNTIME_base-utils-syslog (conf/distro/forgefirm.conf).
|
||||
IMAGE_INSTALL:append = " grblhal-glowforge forgectrl gfhome gfcloud v4l-utils fwup ffboot slotmigrate forgefirm-logging"
|
||||
|
||||
# forgefirm-users: replays the operator account record
|
||||
# forgefirm-users: renders the operator account record
|
||||
# (/data/forgefirm/users, written by forgectrl) into the system account
|
||||
# files at boot, before sshd, and on reload; also installs the warning an
|
||||
# interactive root shell prints. forgefirm-banner: keeps the control
|
||||
# panel addresses in the serial-console banner (/etc/issue).
|
||||
# forgefirm-persist: the boot timestamp and the random seed on /data.
|
||||
# avahi-daemon: mDNS, so the panel answers at https://forgefirm.local/
|
||||
# and shows up in service browsers. The daemon is installed by name (the
|
||||
# zeroconf distro feature stays off: it would bring libnss-mdns); the
|
||||
# build options and the configuration are in conf/distro/forgefirm.conf
|
||||
# and recipes-connectivity/avahi.
|
||||
IMAGE_INSTALL:append = " forgefirm-users forgefirm-banner avahi-daemon"
|
||||
IMAGE_INSTALL:append = " forgefirm-users forgefirm-banner forgefirm-persist avahi-daemon"
|
||||
|
||||
# The rootfs mounts read-only on both images; /data (p3) is the writable
|
||||
# partition. read-only-rootfs is poky's feature for it: the root line of
|
||||
# /etc/fstab (the BSP's, already ro) and ROOTFS_READ_ONLY in
|
||||
# /etc/default/rcS, the volatile links made at rootfs time
|
||||
# (populate-volatile.sh: /etc/resolv.conf, /tmp), a writable copy of
|
||||
# /var/lib at boot (read-only-rootfs-hook.sh), a build failure for a
|
||||
# package whose post-install must run on the machine, and the removal of
|
||||
# the packages a read-only rootfs cannot use (shadow, base-passwd,
|
||||
# update-rc.d, update-alternatives; the account files stay). What must
|
||||
# last or change at run time is handled file by file: the account files
|
||||
# and /etc/issue (forgefirm-users, forgefirm-banner), the sshd host keys
|
||||
# (recipes-connectivity/openssh), the timestamp and the random seed
|
||||
# (forgefirm-persist). The facts are on the docs site,
|
||||
# technical/forgefirm/image-and-bsp; scripts/release.sh checks the built
|
||||
# rootfs for this state.
|
||||
IMAGE_FEATURES += "read-only-rootfs"
|
||||
|
||||
# Root policy. root has no password and logs in at the serial console
|
||||
# only: that is the recovery path when the network, the panel or an
|
||||
@@ -105,7 +123,9 @@ write_forgefirm_version() {
|
||||
echo "ForgeFIRM ${FORGEFIRM_VERSION_STRING}" > ${IMAGE_ROOTFS}${sysconfdir}/motd
|
||||
}
|
||||
write_forgefirm_version[vardepsexclude] += "DATETIME"
|
||||
ROOTFS_POSTPROCESS_COMMAND += "write_forgefirm_version;"
|
||||
# No semicolon after a function name here or below (the vardeps rule in
|
||||
# classes/forgefirm-image-manifest.bbclass).
|
||||
ROOTFS_POSTPROCESS_COMMAND += "write_forgefirm_version "
|
||||
|
||||
# The license texts ride with the software. The license class writes
|
||||
# the image's license manifest (every installed package with its
|
||||
@@ -132,4 +152,4 @@ pack_licenses() {
|
||||
> "${IMAGE_ROOTFS}${datadir}/forgefirm/licenses.tar.gz"
|
||||
rm -rf "$d"
|
||||
}
|
||||
ROOTFS_POSTPROCESS_COMMAND += "pack_licenses;"
|
||||
ROOTFS_POSTPROCESS_COMMAND += "pack_licenses "
|
||||
|
||||
Reference in New Issue
Block a user