fedora-iot / greenboot

Generic Health Checking Framework for systemd
GNU Lesser General Public License v2.1
101 stars 29 forks source link

Support readonly `/boot` mount #136

Closed cgwalters closed 2 months ago

cgwalters commented 7 months ago

In some image based systems (e.g. ostree/bootc) like (e.g. Fedora CoreOS) we mount /boot as read-only by default. The rationale for this is few things should be touching /boot. Those that do (like ostree) do:

This is easy to do with systemd, just add PrivateMounts=true to the units for the first part, then in the code check if /boot is readonly, and if so mount -o remount,rw /boot.

cgwalters commented 7 months ago

https://github.com/coreos/fedora-coreos-config/blob/5ec7272869c3aed246c5280592420f0e93818cf2/overlay.d/05core/usr/lib/systemd/system/coreos-ignition-firstboot-complete.service#L11 for example

say-paul commented 7 months ago

With composefs enabled, the entire file-system will be readonly, so we need to ensure the transient rootfs in enabled too. thoughts? @cgwalters @jmarrero

cgwalters commented 7 months ago

/boot is not a composefs, so it is unrelated. (Also composefs is an overlay, there is still an underlying writable root)

On Wed, Mar 27, 2024, at 3:41 AM, Sayan Paul wrote:

With composefs enabled, the entire file-system will be readonly, so we need to ensure the transient rootfs in enabled too. thoughts? @cgwalters https://github.com/cgwalters @jmarrero https://github.com/jmarrero

— Reply to this email directly, view it on GitHub https://github.com/fedora-iot/greenboot/issues/136#issuecomment-2022133446, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAB3TACH2VR5IKLW3O4IAL3Y2JZ4HAVCNFSM6AAAAABE7KY3NKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMRSGEZTGNBUGY. You are receiving this because you were mentioned.Message ID: @.***>

say-paul commented 2 months ago

@cgwalters I tried the following approach as suggested but seems even if /boot is mounted as rw, grubedit-env fails to edit /boot/grub2/grubenv.

[core@localhost ~]$ sudo systemctl status greenboot-grub2-set-success.service
× greenboot-grub2-set-success.service - Mark boot as successful in grubenv
     Loaded: loaded (/usr/lib/systemd/system/greenboot-grub2-set-success.service; enabled; preset: enabled)
     Active: failed (Result: exit-code) since Wed 2024-08-07 13:38:59 UTC; 6min ago
    Process: 945 ExecStartPre=/bin/sh -c if grep -q " /boot .* ro," /proc/mounts; then mount -o remount,rw /boot || exit 1; fi (code=exited, status=0/SUCCESS)
    Process: 959 ExecStart=/usr/bin/grub2-editenv /boot/grub2/grubenv set boot_success=1 (code=exited, status=1/FAILURE)
    Process: 961 ExecStopPost=/bin/sh -c if grep -q " /boot .* rw," /proc/mounts; then mount -o remount,ro /boot || exit 1; fi (code=exited, status=0/SUCCESS)
   Main PID: 959 (code=exited, status=1/FAILURE)
        CPU: 2.010s

Aug 07 13:38:57 localhost.localdomain systemd[1]: Starting Mark boot as successful in grubenv...
Aug 07 13:38:59 localhost.localdomain grub2-editenv[959]: /usr/bin/grub2-editenv: error: cannot open `/boot/grub2/grubenv': Read-only file system.
Aug 07 13:38:59 localhost.localdomain systemd[1]: greenboot-grub2-set-success.service: Main process exited, code=exited, status=1/FAILURE
Aug 07 13:38:59 localhost.localdomain systemd[1]: greenboot-grub2-set-success.service: Failed with result 'exit-code'.
Aug 07 13:38:59 localhost.localdomain systemd[1]: Failed to start Mark boot as successful in grubenv.
Aug 07 13:38:59 localhost.localdomain systemd[1]: greenboot-grub2-set-success.service: Consumed 2.010s CPU time.
[core@localhost ~]$ sudo systemctl cat greenboot-grub2-set-success.service

[Unit]
Description=Mark boot as successful in grubenv
Requires=boot-complete.target
After=boot-complete.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/bin/sh -c 'if grep -q " /boot .* ro," /proc/mounts; then mount -o remount,rw /boot || exit 1; fi'
ExecStart=/usr/bin/grub2-editenv /boot/grub2/grubenv set boot_success=1
ExecStart=/usr/bin/grub2-editenv /boot/grub2/grubenv unset boot_counter
ExecStopPost=/bin/sh -c 'if grep -q " /boot .* rw," /proc/mounts; then mount -o remount,ro /boot || exit 1; fi'
PrivateMounts=true

[Install]
WantedBy=multi-user.target
[core@localhost ~]$
cgwalters commented 2 months ago

if grep -q " /boot .* ro," /proc/mounts;

It's simpler and less "ad hoc string parsing" to use test -w /boot

I tried the following approach as suggested but seems even if /boot is mounted as rw, grubedit-env fails to edit /boot/grub2/grubenv.

At a quick look at the docs for PrivateMounts= in man systemd.exec, it looks like a separate namespace is set up for each Exec* invocation, so you'll need to do it like this:

PrivateMounts=true
ExecStart=/bin/sh -c 'test -w /boot || mount -o remount,rw /boot && /usr/bin/grub2-editenv /boot/grub2/grubenv set boot_success=1 && /usr/bin/grub2-editenv /boot/grub2/grubenv unset boot_counter

or so

cgwalters commented 2 months ago

(Or probably cleaner, split that out to its own file instead of inline script, or even better make it not shell script)

say-paul commented 2 months ago

yes, figured that out later, applied the similar approach here PR: #146