Bootstrapping a Linux system with debootstrap
Hieronder volgen een aantal notieties die ik gemaakt toen ik probeerde zelf een minimalistisch Linux systeem te bootstrappen met behulp van debootstrap.
debootstrap is een tool in Debian om een Debian systeem te instaleren zonder de extra complexiteit van een complete installer. Het installeert een minimaal systeem waarop netwerk en de package manager werkend zijn, zodat je de rest later kunt installeren via de packet manager (i.e. met apt install)
Deze aantekeningen zijn helaas nog een rommeltje, ik heb nog geen tijd gehad er een coherent verhaal van te maken.
Algemene tips:
Hiermee kun je een back-up maken van het filesystem, waarmee je makkelijk een diff kunt doen, zodat je goed kunt zien wat elke stap doet:
sudo rsync -ricDogpl --delete mnt/ mnt_backup/ > rsync_output.txt
Met dit commando kun je zien welke functies er in een lib zitten:
objdump -T space/lib/x86_64-linux-gnu/libgcc_s.so.1
Zo kun je zien met welke parameters de kernel gestart is:
cat /proc/cmdline
Je kunt informatie vinden over het eerste proces dat door de kernel gestart is met:
cat /proc/1/status
Om te kijken of je netwerk het doet, kun je het IP-adres van Google gebruiken dat je kunt pingen: 172.217.168.196
Met lsblk kun je de bestaande block devices printen.
De versie van Debian staat in /etc/debian_version
Als je wilt weten wat de dynamic linker van je systeem is, kun je het volgende commando runnen op een willekeurige executable (bijvoorbeeld gawk):
readelf -l /usr/bin/gawk | grep interpreter
Dit geeft iets terug als: /lib64/ld-linux-x86-64.so.2
Het file systeem maken
Create a new virtual machine and a virtual disk image (.vdi) in VirtualBox.
Make sure the EFI option is enabled. You can find it in "System" --> "Motherboard" --> "Enable EFI (special OSes only)"
Vervolgens willen we een systeem op de lege disk zetten. Hiervoor moeten we de .vdi image mounten in het host systeem. Dat gaat via twee stappen. Eerst verbinden we de .vdi image met een virtueel block device, en daarna mounten we het virtuele block device in een directory in het host filesystem.
Eerst laden we de benodigde kernel driver met:
sudo modprobe nbd
Nadat je dit gedaan hebt, ontstaan er nieuwe device nodes met namen als nbd0, nbd1, etc. onder /dev
De volgende stap is dat we de .vdi image verbinden met een virtual block device, via dit qemu-commando:
sudo qemu-nbd -c /dev/nbd0 FromScratch02.vdi
Om de verbinding weer te verbreken gebruiken we het commando:
sudo qemu-nbd -d /dev/nbd0
In plaats van een VirtualBox virtual disk image, kun je ook een plain binary file gebruiken:
Maak een lege binary file van ongeveer 4 GB:
dd if=/dev/zero of=diskimage.bin bs=1M count=1000
Hiermee verbind je een binaire file met een loopback block device. De -f optie zorgt dat er automatisch een vrij block device gevonden wordt. De -P optie zorgt dat de kernel de partitietabel scant.
sudo losetup -fP diskimage.bin
Controleer eventueel met losetup -a of het device correct gekoppeld is.
Zo disconnect je weer:
sudo losetup -d /dev/loop16
Als je QEMU gebruikt als virtual machine in plaats van VirtualBox, heb je dit nodig:
Dit kopieert een BIOS flash image naar je huidige directory:
cp /usr/share/qemu/OVMF.fd .
De virtual machine starten doe je met:
sudo kvm -drive file=/dev/loop16,format=raw,media=disk -drive if=pflash,format=raw,readonly=off,file=OVMF.fd -m 4G
Als je het Tianocore-logo ziet, moet je een paar keer de ESC-toets indrukken of ingedrukt houden, zodat het bootconfiguratiemenu verschijnt.
Als het QEMU-window je muis heeft gecaptured, kun je die weer releasen met: Ctrl+Alt+G
Vervolgens maken we met fdisk de benodigde partities aan, te weten een EFI system-partitie, en een normale Linux filesystem-partitie.
sudo fdisk /dev/nbd0
Eerst moet je met het 'g'-commando een nieuwe GPT-partitietabel maken.
Daarna met 'n' een nieuwe partitie. De EFI-partitie kan 512 MB groot zijn. Daarna met 't' het type veranderen in EFI System.
Als je nu ls /dev/ doet, zie je dat er naast nbd0, nu ook nbd0p1 bestaat. Dit is de partitie die
we zojuist gemaakt hebben. De volgende stap is om er een filesystem op te zetten:
sudo mkfs.fat -F 32 /dev/nbd0p1
mkswap /dev/nbd0p2
sudo mkfs.ext4 /dev/nbd0p3
Daarna mounten we deze filesystems, zodat we er bestanden op kunnen zetten:
sudo mount /dev/nbd0p1 mnt_efi
swapon /dev/nbd0p2
sudo mount /dev/nbd0p3 mnt
sudo grub-install --target=x86_64-efi --efi-directory=mnt_efi --bootloader-id=mysystem
Dit genereert een EFI-folder, met daarin de folder mysystem, met daarin de bestanden voor
de grub bootloader, en een grub.cfg file.
Vervolgens moeten we het VirtualBox-systeem zo configureren dat de bootloader gevonden wordt. Start hiervoor de virtual machine op. Er volgt een foutmelding dat er niks geboot kan worden, en als je op een toets drukt, kom je in een soort BIOS settings menu.
Ga dan naar de menu-optie "Boot Maintenance Manager" --> "Boot Options" --> "Add Boot Option"
Als het goed is kom je dan in een soort file explorer, met als root een ingewikkelde naam die begint met [PciRoot (0x0)/Pci.... etc.]. Als je op enter drukt, zie je de folderstructuur van de EFI-partitie. Browse nu naar de folder 'mysystem' en selecteer het bestand grubx64.efi. Vervolgens moet je deze entry een naam geven. Daarna kun je deze bootoptie committen met "Commit Changes and Exit"
Als je nu teruggaat naar het main menu, en daarna naar de "Boot Manager", zie je als het goed is je nieuwe bootconfiguratie in de lijst staan.
Als je die selecteert en op enter drukt, start als het goed is de grub shell op.
Instaleren van een minimaal linux systeem
Als je debootstrap nog niet hebt, moet je het eerst installeren met
sudo apt install debootstrap
Vervolgens zetten we er een minimalistisch Linux-systeem op:
sudo debootstrap --arch=amd64 --variant=minbase stable mnt http://deb.debian.org/debian
Je kunt het downloaden van de packages en het installeren ook in twee stappen doen:
Hiermee download je alleen de .deb-packages zonder ze te installeren:
sudo debootstrap --arch=amd64 --download-only --variant=minbase stable ./test http://deb.debian.org/debian
(de .deb-packages komen te staan in .../test/var/cache/apt/archives/)
Hiermee installeer je de packages in de mnt-folder:
sudo debootstrap --arch=amd64 --variant=minbase stable ./mnt http://deb.debian.org/debian
Optioneel:
Change-root naar het OS-bestandssysteem, en maak daar de efi-directory aan. Die is nodig om te dienen als mountpoint voor de EFI-partitie.
sudo chroot ./mnt
mkdir /boot/efi
exit
sudo mount /dev/nbd0p2 mnt2
sudo mount --bind /dev mnt/dev
sudo mount -t devpts none mnt/dev/pts
sudo mount -t proc none mnt/proc
sudo mount -t sysfs none mnt/sys
sudo mount -t tmpfs none mnt/tmp
sudo mount /dev/nbd0p1 mnt/boot/efi
Of in één regel:
sudo mount /dev/nbd0p2 mnt; sudo mount --bind /dev mnt/dev; sudo mount -t devpts none mnt/dev/pts; sudo mount -t proc none mnt/proc; sudo mount -t sysfs none mnt/sys; sudo mount -t tmpfs none mnt/tmp;
Daarna:
sudo chroot ./mnt
Om af te sluiten:
sudo umount mnt/tmp
sudo umount mnt/sys
sudo umount mnt/proc
sudo umount mnt/dev/pts
sudo umount mnt/dev
sudo umount mnt
Change-root naar het nieuwe systeem, en installeer de kernel met:
apt install linux-image-6.1.0-15-amd64
(met apt search linux-image kun je zien welke kernels beschikbaar zijn)
Vervolgens passen we grub.cfg aan met de naam van de kernel die we zojuist geïnstalleerd hebben.
menuentry "My Linux System" {
set root=(hd0,gpt2)
linux /boot/vmlinuz-6.1.0-15-amd64 root=/dev/sda2 ro
initrd /boot/initrd.img-6.1.0-15-amd64
}
Als je de kernel messages wilt loggen, kun je deze kernelparameters toevoegen aan de linux-regel:
console=ttyS0 console=tty0 ignore_loglevel
Als het init-proces niet op de verwachte plek staat, moet je dat aangeven met: rdinit=/bin/sh
In je virtual machine-instellingen moet je dan een COM-poort aanzetten die naar een raw file redirect.
Als het goed is kun je nu je systeem booten in VirtualBox.
Hier volgen nog wat meer aantekeningen over de grub.cfg-file:
Dit zou je er bijvoorbeeld in kunnen zetten:
set part_uuid=382cd7d3-69a3-0f41-b651-f77341e81a59
menuentry "Linux1" {
set root=(hd0,gpt2)
linux /boot/vmlinuz-6.1.0-9-amd64 root=PARTUUID=$part_uuid rw
initrd /boot/initrd.img-6.1.0-9-amd64
}
Het initrd-gedeelte kun je ook weglaten, maar in de praktijk is het lastig een kernel
te bouwen waarin alles zit dat je nodig hebt.
Let op het verschil tussen UUID’s van een filesystem en van een partitie.
`sudo lsblk` geeft je de UUID van de partities, bijvoorbeeld:
dev/loop16p1: UUID="8AB1-B786" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="8b29baef-2bb9-0d4e-9653-58df1608e84f"
dev/loop16p2: UUID="0fce7761-ff51-409e-ace4-40f6e1378341" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="382cd7d3-69a3-0f41-b651-f77341e81a59"
In de grub.cfg van mijn host-systeem staat dit (ik weet niet wat het exact doet).
De eigenlijke grub-configuratie staat in: /boot/grub/grub.cfg
search.fs_uuid 893810a2-ad60-4b63-b88a-ab8e0539fd56 root
set prefix=($root)'/boot/grub'
configfile $prefix/grub.cfg
In een regulier werkend systeem wordt grub.cfg meestal aangepast door bepaalde tools:
grub-mkconfig maakt de configuratiefile voor GRUB aan. Deze moet terechtkomen
in /boot/grub/grub.cfg.
update-grub is een klein scriptje dat grub-mkconfig aanroept, en het resultaat naar /boot/grub/grub.cfg schrijft.
Het init proces dat als het goed is gaat draaien als we het systeem nu booten, is het init-proces van het initrd-systeem. Dit start daarna het init-proces dat op het reguliere filesystem staat, maar dat hebben we nog niet. We krijgen daar een warning over, maar komen wel in een eenvoudige shell terecht, die een beetje gebrekkig werkt.
Deze shell laat je nog inloggen zonder wachtwoord, maar als je straks inlogt via systemd heb
je een wachtwoord nodig. Stel dat daarom nu in met het passwd-commando.
Het filesystem is momenteel nog read-only gemount. Om het als read-write te mounten moet je de fstab editen:
/dev/sda1 /boot/efi vfat umask=0077 0 2
/dev/sda2 / ext4 defaults 0 2
In de chroot-omgeving kunnen we eenvoudig met apt install extra packages installeren. We
willen systemd als init-proces gebruiken, dus die installeren we, en ook de packages waarin
de commando’s ip en ping zitten:
sudo apt install systemd
sudo apt install iproute2 (voor het `ip`-commando)
sudo apt install iputils-ping
sudo apt install network-manager
Nog wat meer (optionele?) packages:
apt install systemd-resolved
apt install ifupdown
apt install procps (voor o.a. het `ps`-commando)
Zet de gewenste hostname in /etc/hostname
Ook moet je dit in je /etc/hosts-file zetten:
127.0.0.1 localhost
127.0.1.1 MyHostName
Op een gegeven moment raakte er iets corrupt in de dpkg-database, en moest ik deze twee (lege) files weggooien:
/var/lib/dpkg/info/xauth.list
/var/lib/dpkg/info/xclip.list
Op een ander moment raakte er iets corrupt in de apt-database en moest ik dit weggooien:
rm -r /var/lib/apt/lists/*
apt update
mount -t sysfs sysfs /sys
mount /proc/ /proc/ -t proc
Of zoals ik het in het officiële init-script van de initrd zie staan:
mount -t sysfs -o nodev,noexec,nosuid sysfs /sys
mount -t proc -o nodev,noexec,nosuid proc /proc
Netwerk
Dit zijn wat oude notities over de netwerkinterface:
systemctl start systemd-resolved.service
In /etc/resolv.conf moet je wat dingen neerzetten die je kunt kopiëren van je host-systeem.
Een van de .deb-packages maakt van resolv.conf een link naar iets met systemd, maar dat heb ik nog niet.
Zo kun je zien hoe je netwerkinterfaces heten:
ls /sys/class/net
Ook moet support voor de netwerkadapter aanwezig zijn in de kernel, dus die moet je speciaal bouwen, of een initramfs gebruiken.
Stel dat de netwerkadapter ens3 heet, dan moet je de volgende regel opnemen
in /etc/network/interfaces:
iface ens3 inet dhcp
Daarna zet je hem aan met het commando:
ifup ens3
See also:
https://www.debian.org/releases/stable/i386/apds03.en.html