diff --git a/acl/docs/architecture.md b/acl/docs/architecture.md index dce81d48c89..083b09d5cb4 100644 --- a/acl/docs/architecture.md +++ b/acl/docs/architecture.md @@ -53,8 +53,9 @@ ACL's primary boot path uses **systemd-boot** with **Unified Kernel Images (UKI) The `/usr` partition (USR-A) is a read-only btrfs filesystem with zstd compression. **dm-verity** provides block-level integrity verification: -- The verity hash tree is appended to the USR partition data. -- At boot, `systemd-veritysetup` activates the verity device using kernel command-line parameters embedded in the UKI: `systemd.verity_usr_data`, `systemd.verity_usr_hash`, and `systemd.verity_usr_options=hash-offset=,panic-on-corruption`. +- The verity hash tree is stored in a dedicated hash partition (HASH-A, immediately following USR-A on disk). +- At boot, `systemd-veritysetup` activates the verity device using kernel command-line parameters embedded in the UKI: `systemd.verity_usr_data=UUID=`, `systemd.verity_usr_hash=UUID=`, and `systemd.verity_usr_options=panic-on-corruption`. +- The UKI uses filesystem/verity UUIDs (not partition UUIDs) so the same UKI works for both A and B slots. - Any corruption of `/usr` causes an immediate kernel panic, preventing the system from running a tampered image. The A/B partition scheme (USR-A / USR-B) enables safe updates: the inactive slot is written, verified, and then atomically switched on reboot. diff --git a/build_library/build_image_util.sh b/build_library/build_image_util.sh index bf7f4dbba1b..3625ab9386a 100755 --- a/build_library/build_image_util.sh +++ b/build_library/build_image_util.sh @@ -886,6 +886,8 @@ EOF "${BUILD_LIBRARY_DIR}/disk_util" --disk_layout="${disk_layout}" --arch="${BOARD}" verity \ --root_hash="${BUILD_DIR}/${image_name%.bin}_verity.txt" \ + --verity_uuid="${BUILD_DIR}/${image_name%.bin}_verity_uuid.txt" \ + --fs_uuid="${BUILD_DIR}/${image_name%.bin}_fs_uuid.txt" \ "${BUILD_DIR}/${image_name}" # Magic alert! Root hash injection works by writing the hash value to a @@ -953,6 +955,15 @@ EOF if [[ -f "${verity_hash_file}" ]]; then bootloader_args+=(--verity_hash="${verity_hash_file}") fi + # Pass UUID files for UUID-based cmdline construction + local verity_uuid_file="${BUILD_DIR}/${image_name%.bin}_verity_uuid.txt" + if [[ -f "${verity_uuid_file}" ]]; then + bootloader_args+=(--verity_uuid="${verity_uuid_file}") + fi + local fs_uuid_file="${BUILD_DIR}/${image_name%.bin}_fs_uuid.txt" + if [[ -f "${fs_uuid_file}" ]]; then + bootloader_args+=(--fs_uuid="${fs_uuid_file}") + fi fi else bootloader_args+=(--noverity) diff --git a/build_library/disk_layout_uki.json b/build_library/disk_layout_uki.json index 5d4a16e79f5..c964f7af8f6 100644 --- a/build_library/disk_layout_uki.json +++ b/build_library/disk_layout_uki.json @@ -23,23 +23,37 @@ "uuid": "7130c94a-213a-4e5a-8e26-6cce9662f132", "type": "flatcar-rootfs", "blocks": "2097152", - "fs_blocks": "260094", + "fs_blocks": "262144", "fs_type": "btrfs", "fs_compression": "zstd", "mount": "/usr", + "verity_hash": "3", "features": [ "prioritize", "verity" ] }, "3": { + "label": "HASH-A", + "uuid": "b736baf1-cdb4-4535-beba-ddaaa30ad7b7", + "type": "dps-usr-verity", + "blocks": "20480" + }, + "4": { "label": "USR-B", "uuid": "e03dd35c-7c2d-4a47-b3fe-27f15780a57c", "type": "flatcar-rootfs", "blocks": "2097152", - "fs_blocks": "262144" + "fs_blocks": "262144", + "verity_hash": "5" }, - "4": { + "5": { + "label": "HASH-B", + "uuid": "35bdf78b-c453-4661-98e6-f834f534ef5b", + "type": "dps-usr-verity", + "blocks": "20480" + }, + "6": { "label": "OEM", "fs_label": "OEM", "type": "data", @@ -48,7 +62,7 @@ "fs_compression": "zlib", "mount": "/oem" }, - "5": { + "7": { "label": "ROOT", "fs_label": "ROOT", "type": "dps-root", @@ -58,21 +72,21 @@ } }, "vm": { - "5": { + "7": { "label": "ROOT", "fs_label": "ROOT", "blocks": "12943360" } }, "azure": { - "5": { + "7": { "label": "ROOT", "fs_label": "ROOT", "blocks": "58875904" } }, "vagrant": { - "5": { + "7": { "label": "ROOT", "fs_label": "ROOT", "blocks": "33845248" @@ -92,6 +106,12 @@ "type": "blank" }, "5": { + "type": "blank" + }, + "6": { + "type": "blank" + }, + "7": { "label": "ROOT", "fs_label": "ROOT", "type": "0fc63daf-8483-4772-8e79-3d69d8477de4", diff --git a/build_library/disk_util b/build_library/disk_util index 9a37343cb10..2a0c3018ae0 100755 --- a/build_library/disk_util +++ b/build_library/disk_util @@ -34,6 +34,11 @@ DPS_ROOT_GUIDS = { 'aarch64': 'B921B045-1DF0-41C3-AF44-4C6F280D3FAE', } +DPS_USR_VERITY_GUIDS = { + 'x86_64': '77FF5F63-E7B6-4633-ACF4-1565B864C0E6', + 'aarch64': '6E11A4E7-FBCA-4DED-B9E9-E1A512BB664E', +} + # Map BOARD names used by the build system to machine architecture values. BOARD_TO_ARCH = { 'amd64-usr': 'x86_64', @@ -44,18 +49,24 @@ def _resolve_dps_types(config, arch): """Replace symbolic DPS partition type names with architecture-specific GUIDs. Currently supported placeholders: - dps-root → DPS root partition GUID for the target architecture + dps-root → DPS root partition GUID for the target architecture + dps-usr-verity → DPS /usr verity hash partition GUID """ + dps_map = { + 'dps-root': DPS_ROOT_GUIDS, + 'dps-usr-verity': DPS_USR_VERITY_GUIDS, + } for layout in config.get('layouts', {}).values(): for part in layout.values(): if not isinstance(part, dict): continue ptype = part.get('type', '') - if ptype == 'dps-root': - guid = DPS_ROOT_GUIDS.get(arch) + guids = dps_map.get(ptype) + if guids is not None: + guid = guids.get(arch) if guid is None: raise InvalidLayout( - 'No DPS root GUID defined for architecture %r' % arch) + 'No %s GUID defined for architecture %r' % (ptype, arch)) part['type'] = guid @@ -73,7 +84,7 @@ def LoadPartitionConfig(options): '_comment', 'type', 'num', 'label', 'blocks', 'block_size', 'fs_blocks', 'fs_block_size', 'fs_type', 'features', 'uuid', 'part_alignment', 'mount', 'binds', 'fs_subvolume', 'fs_bytes_per_inode', 'fs_inode_size', 'fs_label', - 'fs_compression')) + 'fs_compression', 'verity_hash')) integer_layout_keys = set(( 'blocks', 'block_size', 'fs_blocks', 'fs_block_size', 'part_alignment', 'fs_bytes_per_inode', 'fs_inode_size')) @@ -807,6 +818,12 @@ def Tune(options): def Verity(options): """Hash verity protected filesystems. + Only processes partitions that have a filesystem (fs_type) and either a + verity_hash cross-reference to a dedicated hash partition or the legacy + 'verity' feature flag for inline hash mode. At build time only the + active USR slot (the one with 'prioritize') is formatted, so the + inactive slot is skipped naturally. + Args: options: Flags passed to the script """ @@ -815,7 +832,15 @@ def Verity(options): GetPartitionTableFromImage(options, config, partitions) for part_num, part in partitions.items(): - if 'verity' not in part.get('features', []): + hash_part_num = part.get('verity_hash') + has_verity_feature = 'verity' in part.get('features', []) + + if not hash_part_num and not has_verity_feature: + continue + + # Skip partitions without a filesystem — at build time only the active + # slot (USR-A with 'prioritize') is formatted; USR-B is empty. + if not part.get('fs_type'): continue if not part['image_compat']: @@ -826,22 +851,56 @@ def Verity(options): elif part.get('fs_type', None) == 'btrfs': ReadWriteSubvol(options, part, disable_rw=True) - with PartitionLoop(options, part) as loop_dev: - verityout = SudoOutput(['veritysetup', 'format', '--hash=sha256', - '--data-block-size', part['fs_block_size'], - '--hash-block-size', part['fs_block_size'], - '--data-blocks', part['fs_blocks'], - '--hash-offset', part['fs_bytes'], - loop_dev, loop_dev]).decode('utf8') - print(verityout.strip()) - m = re.search(r'Root hash:\s+([a-f0-9]{64})$', verityout, re.IGNORECASE|re.MULTILINE) - if not m: - raise Exception("Failed to parse verity output!") - - if options.root_hash != None: - with open(options.root_hash, "w") as hash_file: - hash_file.write(m.group(1)) - hash_file.write("\n") + if hash_part_num: + # Separate hash partition mode: write hash tree to a dedicated partition. + hash_part = partitions[hash_part_num] + if not hash_part['image_compat']: + raise InvalidLayout("Hash partition is incompatible with existing image") + + with PartitionLoop(options, part) as data_loop, \ + PartitionLoop(options, hash_part) as hash_loop: + verityout = SudoOutput(['veritysetup', 'format', '--hash=sha256', + '--data-block-size', part['fs_block_size'], + '--hash-block-size', part['fs_block_size'], + '--data-blocks', part['fs_blocks'], + data_loop, hash_loop]).decode('utf8') + print(verityout.strip()) + + # Read the btrfs FS UUID from the data partition for cmdline use. + if options.fs_uuid: + fs_uuid_out = SudoOutput( + ['blkid', '-s', 'UUID', '-o', 'value', data_loop]).decode('utf8') + with open(options.fs_uuid, 'w') as f: + f.write(fs_uuid_out.strip() + '\n') + + else: + # Legacy inline mode: hash tree appended within the same partition. + with PartitionLoop(options, part) as loop_dev: + verityout = SudoOutput(['veritysetup', 'format', '--hash=sha256', + '--data-block-size', part['fs_block_size'], + '--hash-block-size', part['fs_block_size'], + '--data-blocks', part['fs_blocks'], + '--hash-offset', part['fs_bytes'], + loop_dev, loop_dev]).decode('utf8') + print(verityout.strip()) + + m = re.search(r'Root hash:\s+([a-f0-9]{64})$', verityout, re.IGNORECASE|re.MULTILINE) + if not m: + raise Exception("Failed to parse verity root hash!") + + if options.root_hash != None: + with open(options.root_hash, "w") as hash_file: + hash_file.write(m.group(1)) + hash_file.write("\n") + + # Extract the verity superblock UUID from veritysetup output. + if options.verity_uuid: + m_uuid = re.search(r'UUID:\s+([a-f0-9-]{36})$', verityout, re.IGNORECASE|re.MULTILINE) + if not m_uuid: + raise Exception("Failed to parse verity UUID!") + with open(options.verity_uuid, 'w') as f: + f.write(m_uuid.group(1)) + f.write("\n") def Extract(options): @@ -1118,6 +1177,8 @@ def main(argv): a = actions.add_parser('verity', help='compute verity hashes') a.add_argument('disk_image', help='path to disk image file') a.add_argument('--root_hash', help='name of file to contain root hash') + a.add_argument('--verity_uuid', help='name of file to contain verity superblock UUID') + a.add_argument('--fs_uuid', help='name of file to contain data partition filesystem UUID') a.set_defaults(func=Verity) a = actions.add_parser('extract', help='extract a single partition') diff --git a/build_library/rpm/build_image_util.sh b/build_library/rpm/build_image_util.sh index 4aadbcc7bbd..0b5646d58f1 100644 --- a/build_library/rpm/build_image_util.sh +++ b/build_library/rpm/build_image_util.sh @@ -592,6 +592,11 @@ EOF sudo sed -i 's|/var/run/|/run/|g' "${root_fs_dir}/usr/lib/systemd/system/rpcbind.socket" fi + # Enable tridentd.socket - listens for trident API requests + info "RPM mode: Enabling tridentd.socket" + sudo mkdir -p "${root_fs_dir}/usr/lib/systemd/system/sockets.target.wants" + sudo ln -sf ../tridentd.socket "${root_fs_dir}/usr/lib/systemd/system/sockets.target.wants/tridentd.socket" + # Create /var/lib/nfs directories needed by rpc-statd and NFS server via tmpfiles # The nfs-utils RPM only creates v4recovery; sm and sm.bak are missing from the package # /var is stateful so we use tmpfiles.d to create these at boot, not mkdir at build time diff --git a/build_library/rpm/grub.cfg b/build_library/rpm/grub.cfg index 377c3915d61..41e0f2ad38e 100644 --- a/build_library/rpm/grub.cfg +++ b/build_library/rpm/grub.cfg @@ -116,17 +116,17 @@ set azl_kernel="@@KERNEL@@" # systemd-veritysetup-generator reads usrhash= to create /dev/mapper/usr set verity_usrhash="@@USRHASH@@" -# Azure Linux GRUB doesn't have gptprio.next command -# Boot directly from USR-A partition. A/B updates would require custom handling. -# USR-A partition UUID from disk_layout.json +# Filesystem UUID (btrfs) and verity superblock UUID, injected at build time. +# These are A/B agnostic: the update agent swaps which partition holds the +# active UUID, and systemd resolves UUID= to the correct device. +set usr_fs_uuid="@@FSUUID@@" +set verity_uuid="@@VERITYUUID@@" + +# Partition UUIDs for non-verity recovery menu entries only. set usr_a_uuid="7130c94a-213a-4e5a-8e26-6cce9662f132" set usr_b_uuid="e03dd35c-7c2d-4a47-b3fe-27f15780a57c" -# Verity hash offset: fs_blocks * fs_block_size = 260094 * 4096 = 1065345024 -# The hash tree is stored at the end of the USR partition after the filesystem data -set verity_hash_offset="1065345024" - -set verity_cmdline="@@MOUNTUSR@@ usrhash=$verity_usrhash systemd.verity_usr_data=PARTUUID=$usr_a_uuid systemd.verity_usr_hash=PARTUUID=$usr_a_uuid systemd.verity_usr_options=hash-offset=$verity_hash_offset,panic-on-corruption" +set verity_cmdline="@@MOUNTUSR@@ usrhash=$verity_usrhash systemd.verity_usr_data=UUID=$usr_fs_uuid systemd.verity_usr_hash=UUID=$verity_uuid systemd.verity_usr_options=panic-on-corruption" menuentry "Azure Container Linux default" --id=flatcar --unrestricted { linux$suf $azl_kernel $verity_cmdline $linux_cmdline diff --git a/build_library/rpm/grub_install.sh b/build_library/rpm/grub_install.sh index 563ebe6a46f..234629517be 100644 --- a/build_library/rpm/grub_install.sh +++ b/build_library/rpm/grub_install.sh @@ -125,9 +125,31 @@ grub_provision_rpm() { warn "RPM mode: Verity enabled but no hash file provided" sed_cmds+=(-e 's/@@USRHASH@@//') fi + + # Inject filesystem and verity UUIDs for A/B agnostic boot + if [[ -n "${FLAGS_fs_uuid}" && -f "${FLAGS_fs_uuid}" ]]; then + local grub_fs_uuid + grub_fs_uuid=$(cat "${FLAGS_fs_uuid}") + info "RPM mode: Injecting FS UUID ${grub_fs_uuid}" + sed_cmds+=(-e "s/@@FSUUID@@/${grub_fs_uuid}/") + else + warn "RPM mode: No FS UUID file provided" + sed_cmds+=(-e 's/@@FSUUID@@//') + fi + if [[ -n "${FLAGS_verity_uuid}" && -f "${FLAGS_verity_uuid}" ]]; then + local grub_verity_uuid + grub_verity_uuid=$(cat "${FLAGS_verity_uuid}") + info "RPM mode: Injecting verity UUID ${grub_verity_uuid}" + sed_cmds+=(-e "s/@@VERITYUUID@@/${grub_verity_uuid}/") + else + warn "RPM mode: No verity UUID file provided" + sed_cmds+=(-e 's/@@VERITYUUID@@//') + fi else sed_cmds+=(-e 's/@@MOUNTUSR@@/mount.usr/') sed_cmds+=(-e 's/@@USRHASH@@//') + sed_cmds+=(-e 's/@@FSUUID@@//') + sed_cmds+=(-e 's/@@VERITYUUID@@//') fi if [[ -n "${kernel_name}" ]]; then sed_cmds+=(-e "s|@@KERNEL@@|/flatcar/${kernel_name}|") diff --git a/build_library/rpm/package_catalog.yaml b/build_library/rpm/package_catalog.yaml index 9bddf920d7e..dcc93e3ad99 100644 --- a/build_library/rpm/package_catalog.yaml +++ b/build_library/rpm/package_catalog.yaml @@ -31,6 +31,7 @@ packages: - policycoreutils - ca-certificates - irqbalance + - trident sys-libs/systemd-libs: systemd-libs sys-apps/systemd-networkd: systemd-networkd net-misc/systemd-networkd: systemd-networkd diff --git a/build_library/rpm/rpm_install.sh b/build_library/rpm/rpm_install.sh index 2805920bdb2..03c4103a415 100644 --- a/build_library/rpm/rpm_install.sh +++ b/build_library/rpm/rpm_install.sh @@ -947,48 +947,50 @@ EOF sudo chroot "${root_fs_dir}" semodule -X 200 -i "${policy_hotfix}" sudo rm -f "${root_fs_dir}/${policy_hotfix}" - # Remove unnecessary SELinux policy modules (minimize the policy) - info "RPM mode: Minimizing SELinux policy". - sudo chroot "${root_fs_dir}" semodule -X 100 -r \ - abrt accountsd acct acpi afs aide aisexec alsa amanda amavis amtu anaconda \ - apache apcupsd apt aptcacher arpwatch asterisk auditadm automount avahi \ - awstats backup bacula bind bird bitlbee blueman bluetooth boinc brctl \ - bugzilla cachefilesd calamaris canna cdrecord certbot certmaster certmonger \ - certwatch cfengine cgmanager cgroup chkrootkit chromium chronyd clamav \ - cloudinit cobbler cockpit collectd colord comsat condor consolesetup \ - container_compat corosync couchdb courier cpucontrol cpufreqselector crio \ - cryfs ctdb cups cvs cyphesis cyrus daemontools dante dbadm dbskk ddclient \ - devicekit dhcp dictd dirmngr distcc djbdns dkim dmidecode dnsmasq docker \ - dovecot dphysswapfile dpkg drbd eg25manager entropyd evolution exim fail2ban \ - fakehwclock fapolicyd fcoe fetchmail finger firewalld firstboot fprintd ftp \ - games gatekeeper gdomap geoclue git gitosis glance glusterfs gnome gnomeclock \ - gpg gpm gpsd gssproxy guest hadoop haproxy hddtemp hostapd hwloc hypervkvp \ - i18n_input icecast ifplugd iiosensorproxy inetd inn iodine ipsec irc ircd \ - irqbalance iscsi isns jabber java kdump kerberos kerneloops keystone kismet \ - knot ksmtuned kubernetes l2tp ldap libmtp lightsquid likewise lircd livecd \ - lldpad loadkeys logadm logrotate logwatch lowmemorymonitor lpd lsm mailman \ - man2html mandb matrixd mcelog mediawiki memcached memlockd milter minidlna \ - minissdpd modemmanager mojomojo mon mongodb monit mono monop mozilla mpd \ - mplayer mrtg munin mysql nagios ncftool nessus netlabel networkmanager nis \ - node_exporter nsd nslcd ntop ntp numad nut nx obex obfs4proxy oddjob oident \ - openarc openca openct openhpi openoffice opensm openvpn openvswitch pacemaker pads \ - passenger pcscd pegasus perdition pingd pkcs pki plymouthd podman portmap \ - portreserve portslave postfix postfixpolicyd postgresql postgrey \ - powerprofiles ppp prelink prelude privoxy procmail psad publicfile \ - pulseaudio puppet pwauth pxe pyzor qemu qmail qpid quantum quota rabbitmq \ - radius radvd rasdaemon razor rdisc realmd redis remotelogin resmgr rhsmcertd \ - rkhunter rlogin rngd rootlesskit rpc rpcbind rpm rshd rssh rtkit rwho samba \ - samhain sanlock sasl sblim screen secadm sendmail sensord setroubleshoot \ - seunshare shibboleth shorewall shutdown sigrok slocate slpd slrnpull smartmon \ - smokeping smstools snmp snort sosreport soundserver spamassassin squid stubby \ - stunnel sudo svnserve switcheroo sxid sympa syncthing sysstat systemtap \ - tboot tcpd tcsd telepathy telnet tftp tgtd thunderbird thunderbolt timidity \ - tmpreaper tomcat tor tpm2 transproxy tripwire tuned tvtime tzdata ucspitcp \ - ulogd uml updfstab uptime usbguard usbmodules usbmuxd userhelper usernetctl \ - uucp uuidd uwimap varnishd vbetool vdagent vhostmd virt vlock vmware vnstatd \ - vpn watchdog wdmd webadm webalizer wine wireguard wireshark wm xen xfs \ - xguest xscreensaver xserver zabbix zarafa zebra zfs zosremote \ - > /dev/null + # TODO: Re-enable + # TEMPORARILY DISABLED TO UNBLOCK TRIDENT INSTALL + # # Remove unnecessary SELinux policy modules (minimize the policy) + # info "RPM mode: Minimizing SELinux policy." + # sudo chroot "${root_fs_dir}" semodule -X 100 -r \ + # abrt accountsd acct acpi afs aide aisexec alsa amanda amavis amtu anaconda \ + # apache apcupsd apt aptcacher arpwatch asterisk auditadm automount avahi \ + # awstats backup bacula bind bird bitlbee blueman bluetooth boinc brctl \ + # bugzilla cachefilesd calamaris canna cdrecord certbot certmaster certmonger \ + # certwatch cfengine cgmanager cgroup chkrootkit chromium chronyd clamav \ + # cloudinit cobbler cockpit collectd colord comsat condor consolesetup \ + # container_compat corosync couchdb courier cpucontrol cpufreqselector crio \ + # cryfs ctdb cups cvs cyphesis cyrus daemontools dante dbadm dbskk ddclient \ + # devicekit dhcp dictd dirmngr distcc djbdns dkim dmidecode dnsmasq docker \ + # dovecot dphysswapfile dpkg drbd eg25manager entropyd evolution exim fail2ban \ + # fakehwclock fapolicyd fcoe fetchmail finger firewalld firstboot fprintd ftp \ + # games gatekeeper gdomap geoclue git gitosis glance glusterfs gnome gnomeclock \ + # gpg gpm gpsd gssproxy guest hadoop haproxy hddtemp hostapd hwloc hypervkvp \ + # i18n_input icecast ifplugd iiosensorproxy inetd inn iodine ipsec irc ircd \ + # irqbalance iscsi isns jabber java kdump kerberos kerneloops keystone kismet \ + # knot ksmtuned kubernetes l2tp ldap libmtp lightsquid likewise lircd livecd \ + # lldpad loadkeys logadm logrotate logwatch lowmemorymonitor lpd lsm mailman \ + # man2html mandb matrixd mcelog mediawiki memcached memlockd milter minidlna \ + # minissdpd modemmanager mojomojo mon mongodb monit mono monop mozilla mpd \ + # mplayer mrtg munin mysql nagios ncftool nessus netlabel networkmanager nis \ + # node_exporter nsd nslcd ntop ntp numad nut nx obex obfs4proxy oddjob oident \ + # openarc openca openct openhpi openoffice opensm openvpn openvswitch pacemaker pads \ + # passenger pcscd pegasus perdition pingd pkcs pki plymouthd podman portmap \ + # portreserve portslave postfix postfixpolicyd postgresql postgrey \ + # powerprofiles ppp prelink prelude privoxy procmail psad publicfile \ + # pulseaudio puppet pwauth pxe pyzor qemu qmail qpid quantum quota rabbitmq \ + # radius radvd rasdaemon razor rdisc realmd redis remotelogin resmgr rhsmcertd \ + # rkhunter rlogin rngd rootlesskit rpc rpcbind rpm rshd rssh rtkit rwho samba \ + # samhain sanlock sasl sblim screen secadm sendmail sensord setroubleshoot \ + # seunshare shibboleth shorewall shutdown sigrok slocate slpd slrnpull smartmon \ + # smokeping smstools snmp snort sosreport soundserver spamassassin squid stubby \ + # stunnel sudo svnserve switcheroo sxid sympa syncthing sysstat systemtap \ + # tboot tcpd tcsd telepathy telnet tftp tgtd thunderbird thunderbolt timidity \ + # tmpreaper tomcat tor tpm2 transproxy tripwire tuned tvtime tzdata ucspitcp \ + # ulogd uml updfstab uptime usbguard usbmodules usbmuxd userhelper usernetctl \ + # uucp uuidd uwimap varnishd vbetool vdagent vhostmd virt vlock vmware vnstatd \ + # vpn watchdog wdmd webadm webalizer wine wireguard wireshark wm xen xfs \ + # xguest xscreensaver xserver zabbix zarafa zebra zfs zosremote \ + # > /dev/null info "RPM mode: Adding SELinux policy compatibility fixes" # Add policy name compatibility symlink. The Gentoo MCS policy is diff --git a/build_library/rpm/uki_install.sh b/build_library/rpm/uki_install.sh index a4ea402fa86..2e9741c3af6 100755 --- a/build_library/rpm/uki_install.sh +++ b/build_library/rpm/uki_install.sh @@ -19,6 +19,10 @@ DEFINE_boolean verity ${FLAGS_FALSE} \ "Indicates that boot commands should enable dm-verity." DEFINE_string verity_hash "" \ "Path to the file containing the dm-verity root hash for /usr." +DEFINE_string verity_uuid "" \ + "Path to the file containing the dm-verity superblock UUID." +DEFINE_string fs_uuid "" \ + "Path to the file containing the btrfs filesystem UUID for /usr." # Parse flags FLAGS "$@" || exit 1 @@ -134,22 +138,20 @@ OSREL fi info "UKI/RPM: Using ukify from $(command -v ukify)" - # Read partition UUID and compute verity hash offset from the - # canonical UKI disk layout so the values never drift from the source. - local disk_layout_file="${BUILD_LIBRARY_DIR}/disk_layout_uki.json" - if [[ ! -f "${disk_layout_file}" ]]; then - die "UKI/RPM: disk_layout_uki.json not found at ${disk_layout_file}" + # Read filesystem and verity UUIDs from build artifact files. + # These are random per-image, generated by mkfs.btrfs and veritysetup + # format respectively, then extracted by disk_util verity. + local usr_fs_uuid="" + if [[ -n "${FLAGS_fs_uuid}" && -f "${FLAGS_fs_uuid}" ]]; then + usr_fs_uuid=$(cat "${FLAGS_fs_uuid}") + info "UKI/RPM: USR FS UUID = ${usr_fs_uuid}" fi - local usr_a_uuid - usr_a_uuid=$(jq -r '.layouts.base["2"].uuid' "${disk_layout_file}") - - local verity_hash_offset - verity_hash_offset=$(jq -r \ - '(.layouts.base["2"].fs_blocks | tonumber) as $b | (.metadata.fs_block_size | tonumber) as $s | ($b * $s)' \ - "${disk_layout_file}") - - info "UKI/RPM: USR-A uuid=${usr_a_uuid} verity hash-offset=${verity_hash_offset}" + local verity_superblock_uuid="" + if [[ -n "${FLAGS_verity_uuid}" && -f "${FLAGS_verity_uuid}" ]]; then + verity_superblock_uuid=$(cat "${FLAGS_verity_uuid}") + info "UKI/RPM: Verity superblock UUID = ${verity_superblock_uuid}" + fi local cmdline="" if [[ ${FLAGS_verity} -eq ${FLAGS_TRUE} ]]; then @@ -160,13 +162,27 @@ OSREL else die "UKI/RPM: Verity enabled but no hash file at ${FLAGS_verity_hash}" fi + if [[ -z "${usr_fs_uuid}" ]]; then + die "UKI/RPM: Verity enabled but no FS UUID available" + fi + if [[ -z "${verity_superblock_uuid}" ]]; then + die "UKI/RPM: Verity enabled but no verity UUID available" + fi cmdline="mount.usr=/dev/mapper/usr mount.usrflags=ro" - cmdline+=" systemd.verity_usr_data=PARTUUID=${usr_a_uuid}" - cmdline+=" systemd.verity_usr_hash=PARTUUID=${usr_a_uuid}" - cmdline+=" systemd.verity_usr_options=hash-offset=${verity_hash_offset},panic-on-corruption" + cmdline+=" systemd.verity_usr_data=UUID=${usr_fs_uuid}" + cmdline+=" systemd.verity_usr_hash=UUID=${verity_superblock_uuid}" + cmdline+=" systemd.verity_usr_options=panic-on-corruption" cmdline+=" usrhash=${usr_hash}" else - cmdline="mount.usr=PARTUUID=${usr_a_uuid} mount.usrflags=ro" + if [[ -n "${usr_fs_uuid}" ]]; then + cmdline="mount.usr=UUID=${usr_fs_uuid} mount.usrflags=ro" + else + # Fallback: read partition UUID from disk_layout.json + local disk_layout_file="${BUILD_LIBRARY_DIR}/disk_layout.json" + local usr_a_uuid + usr_a_uuid=$(jq -r '.layouts.base["3"].uuid' "${disk_layout_file}") + cmdline="mount.usr=PARTUUID=${usr_a_uuid} mount.usrflags=ro" + fi fi # Common base args — platform-agnostic, same for all image types. cmdline+=" root=LABEL=ROOT rootflags=rw"