pietinger Moderator
Joined: 17 Oct 2006 Posts: 5147 Location: Bavaria
|
Posted: Sat Nov 26, 2022 4:29 am Post subject: C6 Embedded Initramfs für signed IMA |
|
|
(Dieser Post ist Teil einer Installation-Anleitung. Falls nicht schon geschehen lies bitte: Installation Guide for Paranoid Dummies Post Nr. 2)
C.6 Embedded Initramfs für signed IMA
... auf vielfachen Wunsch eines Einzelnen durfte ich mich mit einem ungeliebten Thema auseinandersetzen ... Ja, ich mag eigentlich kein initramfs. Glücklicherweise ist vieles im Laufe der Zeit besser und einfacher geworden. Wenn Du die beiden Links ins Wiki aus dem vorherigen Post liest, vergiß einiges gleich wieder. Es ist nicht mehr notwendig ein Utility (gen_init_cpio) vorher manuell zu compilieren oder ausführbar zu machen - das macht der "make" automatisch selbst.
Im anderen Artikel steht, dass für ein embedded initramfs die Kernel Option "Initramfs source file(s)" auf ein Verzeichnis stehen sollte, in dem schon alle Dateien drin sind. Es geht aber noch einfacher: Wir zeigen in dieser Kernel Option nur auf eine Datei mit einer Liste aller nötigen Dateien und der "make" macht wieder alles selbst (Nein, der delegiert das auch nur an die Tools).
Falls Du mal die Bezeichnung "UKI" gehört haben solltest. Ja, wir haben damit ein "Unified Kernel Image" in dem alles eingepackt ist:
- Kernel selbst
- Kernel Command Line
- Microcode und Firmware (für z.B. Intel Prozessor und Intel GPU)
- Initramfs
Damit können wir dieses Image signieren und eine schönen SecureBoot machen. DAS ALLES MIT BORDMITTELN und nicht so idiotisch wie z.B. hier:
https://wiki.archlinux.org/title/Unified_kernel_image
(Sorry, aber da musste ich jetzt mal ein bischen lästern)
Falls Du mal selbst ein embedded initramfs bauen willst ... das wichtigste was Du wissen musst: Selbst wenn Du alle Devices mittels "mount -t devtmpfs none /dev " erstellen läßt, musst Du /dev/console zwingend vorher manuell in Deinem initramfs drin haben. Deshalb MUSS die Zeile "nod /dev/console 0600 0 0 c 5 1" zwingend in die initramfs_list. Gemeinerweise wäre das nicht nötig wenn Du Dein initramfs als EXTERNES CPIO-Archiv bauen würdest. (Erklärung auf Englisch findest Du hier: https://wiki.gentoo.org/wiki/User:Pietinger/Tutorials/Initramfs_Overview#Why_do_I_need_an_additional_line_when_using_an_embedded_initramfs_.3F )
Was aber wirklich gezickt hat, war nicht das Bauen des initramfs, sondern IMA ... Ja, sobald man IMA aktiviert, ist es auch aktiv ... und verhindert das Laden von ausführbaren Programmen die keine Signierung (früher: Hash) haben. Diese werden ja in den erweiterten Attributen des File-Systems gespeichert ... leider hat ein initramfs keine erweiterten Attribute. Hahaha
Wenn Du das INIT Skript betrachtest, wirst Du feststellen, dass nach der Aktivierung von IMA (durch das Laden der Policy) nicht mehr das im initramfs enthaltene busybox aufgerufen wird / werden kann, sondern nur noch das auf unserer "echten" Root Partition ... weil das eben signiert ist. Das ist auch der Grund warum busybox zwingend statisch gebaut werden muss. Denn der Aufruf von /mnt/root/bin/busybox ist nicht in der Lage Libraries nachzuladen. Ja, ich habe das hier gelesen und dachte es wäre eine gute Idee:
https://wiki.gentoo.org/wiki/Talk:Early_Userspace_Mounting#static_busybox_.3F
Ist aber leider nicht möglich. Das ist auch der Grund für den etwas merkwürdig aussehenden Aufruf von switch_root (am Ende von init). Glaub mir, jede andere Kombination führt zu einem Segmentation Fault und damit zu einer Kernel Panic ... rate mal wie ich zu dem 10. Post in A.2 gekommen bin ...
Falls Du mal dieses init File erweitern möchtest, solltest Du alles was Du einbauen willst VOR der Aktivierung von IMA machen.
Ich habe das mit meinem monolithischen Kernel Version 5.15.79 getestet, möchte das aber ausdrücklich noch als EXPERIMENTELL brandmarken. (weil das eben noch nicht lange genug getestet ist).
Voraussetzungen
Du hast einfach mal alles von C.2 bis C.5 gemacht (und verstanden), oder Du kannst alle Schritte gedanklich kombinieren
Vorbereitungen
0. Boote in den UNLOCKED Kernel und melde Dich dort als root an.
1. Das umkopieren des öffentlichen Schlüssels/Zertifikat nach /etc/ima ist zwar nicht zwingend nötig, aber damit ist es etwas aufgeräumter. Wir holen uns den Schlüssel nämlich später aus /etc/ima. Wenn Du einen anderen Namen verwendet hast musst Du das weiter unten anpassen oder Du renamest es gleich jetzt.
Code: | # cp /etc/MY/efikeys/DB.cer /etc/ima/. |
2. Wir benötigen busybox. Und zwar statisch. Stand heute benötigt das drei Use-Flags. Das kann sich aber wieder ändern. Probiere deshalb zuerst den "emerge -p" NUR mit Use-Flag "static", dann mit "-pam static". Und wenn sich nichts geändert haben sollte, benötigst Du:
Code: | USE="-pam static static-libs" emerge -pvD busybox |
Damit wird halt zwangsweise auch noch libxcrypt re-emerged. Natürlich musst Du auch das bei Gelegenheit in die /etc/portage/package.use reintun
Überprüfe sicherheitshalber ob busybox wirklich statisch gebaut wurde:
Code: | # ldd /bin/busybox
das Programm ist nicht dynamisch gelinkt |
3. Falls Du IMA bereits im Einsatz hast, vergiß nicht das alles zu signieren => C.4 und C.5.
4. Zuletzt entferne das bisherige Run-Skript aus dem Runlevel "boot" falls Du IMA bereits im Einsatz hattest:
Code: | # rc-update del loadimapolicy boot |
Erstellung initramfs
1. Diese Verzeichnisse und Dateien werden benötigt:
Code: | mkdir /usr/src/initramfs
# nano -w /usr/src/initramfs/initramfs_list
=>
dir /bin 755 0 0
dir /dev 755 0 0
dir /etc 755 0 0
dir /lib 755 0 0
dir /lib64 755 0 0
dir /mnt 755 0 0
dir /mnt/root 755 0 0
dir /proc 755 0 0
dir /root 700 0 0
dir /sbin 755 0 0
dir /sys 755 0 0
dir /usr 755 0 0
dir /usr/bin 755 0 0
dir /usr/lib64 755 0 0
dir /var 755 0 0
nod /dev/console 0600 0 0 c 5 1
file /init /usr/src/initramfs/init 755 0 0
file /DB.cer /etc/ima/DB.cer 755 0 0
file /policy.conf /etc/ima/policy.conf 755 0 0
file /bin/busybox /bin/busybox 755 0 0
file /bin/cat /bin/cat 755 0 0
file /bin/keyctl /bin/keyctl 755 0 0
file /usr/bin/evmctl /usr/bin/evmctl 755 0 0
file /lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2 755 0 0
file /lib64/libc.so.6 /lib64/libc.so.6 755 0 0
file /usr/lib64/libcrypto.so.1.1 /usr/lib64/libcrypto.so.1.1 755 0 0
file /lib64/libkeyutils.so.1.10 /lib64/libkeyutils.so.1.10 755 0 0
slink /lib64/libkeyutils.so.1 /lib64/libkeyutils.so.1.10 777 0 0
file /usr/lib64/libimaevm.so.3.0.0 /usr/lib64/libimaevm.so.3.0.0 755 0 0
slink /usr/lib64/libimaevm.so.3 /usr/lib64/libimaevm.so.3.0.0 777 0 0 |
Falls dieser Post schon etwas älter sein sollte, überprüfe mit "ldd" für die Kommandos "keyctl", "evmctl" und "cat", ob diese noch die obigen Libraries anziehen. Falls sich da was geändert haben sollte, musst Du natürlich die Liste anpassen. Überprüfe auch ob es nicht nur Softlinks auf die eigentliche Library sind (wie z.B. bei "libkeyutils" und "libimaevm"). Diese benötigen dann natürlich auch noch den entsprechenden Link-Eintrag.
2. In diesem init-File musst Du eine Zeile editieren. Ändere NUR die ID selbst ! Benutze NICHT die PARTUUID von Deiner Root Partition. Lasse das UUID großgeschrieben.
Code: | # nano -w /usr/src/initramfs/init
=>
#!/bin/busybox sh
### CHANGE THIS !
myrootpartition="UUID=c75f64b1-a1b1-4527-b996-4b4b9d24456c"
abend() {
echo "$@"
echo "You are now in a rescue shell."
busybox --install -s
exec /bin/sh
}
echo "Mounting proc, sys, devtmpfs and securityfs ..."
mount -t devtmpfs none /dev || abend "Error: mount /devtmpfs failed !"
mount -t proc none /proc || abend "Error: mount /proc failed !"
mount -t sysfs none /sys || abend "Error: mount /sysfs failed !"
mount -t securityfs securityfs /sys/kernel/security || abend "Error: mount /sys/kernel/securityfs failed !"
echo "Searching root partition device name of $myrootpartition ..."
rootdev=`findfs $myrootpartition` || abend "Error with findfs !"
echo "Found $rootdev as your root partition. Will mount it now ..."
mount -o ro $rootdev /mnt/root || abend "Error mounting root partition !"
### IMA Core routine begin
echo "Loading IMA certificate ..."
ima_id=$(/bin/keyctl newring _ima @u) >> /dev/null || abend "Error creating keyring !"
ima_key=`/usr/bin/evmctl import /DB.cer $ima_id` >> /dev/null || abend "Error importing certificate !"
/bin/keyctl setperm $ima_key 0x0b0b0000 >> /dev/null
/bin/keyctl setperm $ima_id 0x0b0b0000 >> /dev/null
echo "Loading custom IMA policy ..."
/bin/cat /policy.conf > /sys/kernel/security/integrity/ima/policy || abend "Error loading IMA policy !"
### IMA Core routine end
### Now IMA is activ and therefore a "busybox --install -s" would fail with "permission denied".
### This means, you cannot use abend() anymore here !
echo "Unmounting proc, sys, devtmpfs and securityfs ..."
/mnt/root/bin/busybox umount /proc /sys/kernel/security /sys /dev
echo "All done. Switching to real root."
exec /mnt/root/bin/busybox switch_root /mnt/root /sbin/init |
3. ERGÄNZE Deine Kernel Config mit:
Code: | General setup --->
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
(/usr/src/initramfs/initramfs_list) Initramfs source file(s)
[*] Support initial ramdisk/ramfs compressed using gzip
Built-in initramfs compression mode (Gzip) ---> |
Folgendes ...
Code: | Device Drivers --->
Generic Driver Options --->
-*- Maintain a devtmpfs filesystem to mount at /dev |
... müsste bei Verwendung unserer Gentoo Souces bereits automatisch enabled sein, weil:
Quote: | Selected by [y]:
- GENTOO_LINUX_UDEV [=y] && GENTOO_LINUX [=y] |
Eine Überprüfung schadet aber sicher nicht. Theoretisch kannst Du jetzt auch in Deiner Built-in Kernel Command Line den Paramter "root=... ro" raussschmeißen. Ist aber nicht nötig - der Kernel ignoriert das einfach.
4. Erstelle nun den neuen Kernel mit dem embedded intitramfs und installiere ihn als Deinen neuen EVERY-DAY-Kernel wie Du es sonst auch immer machst. Zum Beispiel:
Code: | # mount /boot
# cd /usr/src/linux
# make -j8
# sbsign --key ........ --cert /etc/MY/efikeys/DB.crt --output /boot/EFI/Boot/bzImage.efi arch/x86/boot/bzImage
# reboot |
Sonstiges
Falls Du mal dieses initramfs ändern möchtest (Dateien oder init) gibt es zwei Möglichkeiten bei einem "make" den Neubau zu erzwingen:
1. Du machst ein "make clean" vorher, ODER
2. Du löscht manuell all das:
Code: | # cd /usr/src/linux/usr
# rm initramfs_data.cpio
# rm initramfs_data.o
# rm initramfs_inc_data
# rm built-in.a
# rm .built-in.a.cmd
# rm .initramfs_data.cpio.*
# rm .initramfs_data.o.cmd
# rm .initramfs_inc_data.cmd |
. |
|