Bootstrapping Alpine Linux QCow2 image

Introduction

Alpine Linux is a minimal distro with package manager (APK) that is based on busybox and musl library. Like the CirrOS, it's very lightweight, but unlike it, it's full featured.
In case you don't know me, my Linux distro of choice is Fedora/CentOS, in this post I'm going to bootstrap a QCow2 cloud image of Alpine Linux on my distro of choice.


Using docker to bootstrap a working chroot

Type
mkdir alpine35-root
docker run --rm -ti -v $PWD/alpine35-root:/data alpine:3.5 apk --arch x86_64 -X http://nl.alpinelinux.org/alpine/v3.5/main/ -U --allow-untrusted --root /data --initdb add alpine-base 

and you should get a line like this

OK: 6 MiB in 16 packages

so now we have a working alpine chroot in the directory alpine35-root

Creating Bootable QCoW2 Image

Because I don't want to format my hard disk by mistake and because I know Murphy's law, I'll take those 6MB as tarball and continue on a VM.

dd if=/dev/zero of=alpine.raw bs=1M seek=511 count=1
dd if=/dev/zero of=alpine.raw bs=512 count=2049 conv=notrunc
echo "2048,,83,*" | sfdisk ./alpine.raw
losetup -fP alpine.raw 
export DEV=`losetup | grep alpine.raw | awk '{print $1}'`
export DEV1="${DEV}p1"
mkfs.xfs -L rootfs "$DEV1"
mkdir alpine35-root/img-root
mount "$DEV1" alpine35-root/img-root
chroot alpine35-root /bin/sh


NOTE you can use losetup -f alpine.raw -o $((2048 * 512))

now inside that chroot

export PATH=/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
echo "nameserver 8.8.8.8" > /etc/resolv.conf
apk --arch x86_64  -X http://nl.alpinelinux.org/alpine/edge/main/ -X http://nl.alpinelinux.org/alpine/edge/community/ -X http://nl.alpinelinux.org/alpine/edge/testing/  -U --allow-untrusted --root /img-root --initdb add alpine-base linux-virtgrsec grub grub-bios openrc

the final result should be something like

OK: 102 MiB in 27 packages

just exit the chroot, and

umount "$DEV1"

Installing grub

mkdir img-root
mount "$DEV1" img-root
mkdir -p img-root/{dev,proc,sys}
mount -o bind /dev img-root/dev
echo "LABEL=rootfs / auto defaults 1 1" >> img-root/etc/fstab
mkdir -p img-root/boot/grub/ || :
echo -e "(hd0) /dev/loop0" > img-root/boot/grub/device.map

inside chroot make sure to create a working initrd using some custom ramfs-init.sh 

export PATH=/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
mount -t proc none /proc/
mount -t sysfs none /sys
export KERNEL=`find /lib/modules/* -maxdepth 0 -type d  | cut -d '/' -f 4`
mkinitfs -F "ata base cdrom ext2 ext3 ext4 keymap kms mmc raid scsi usb virtio xfs" -i ramfs-init.sh $KERNEL


the method below did not work (from inside target chroot, maybe I should have used the other base chroot to do grub-install)

grub-install --boot-directory=/boot/ $DEV

so I had to use fedora's grub2-install, exit the chroot and type

grub-install --boot-directory=img-root/boot/ $DEV

Place a simple grub config file into img-root/boot/grub/grub.cfg or img-root/boot/grub2/grub.cfg ( in case you used fedora's grub2), the file should have 4 lines

menuentry 'alpine' {
   linux (hd0,msdos1)/boot/vmlinuz-virtgrsec
  initrd (hd0,msfos1)/boot/initramfs-virtgrsec
}

let's do some clean up by typing

umount img-root/proc
umount img-root/sys
umount img-root/dev
umount img-root
mount | grep img-root
losetup -D
losetup


NOTE: alpine uses /usr/share/mkinitfs/initramfs-init we have just removed unused parts


Converting it to QCow2


qemu-img convert -c -O qcow2 -f raw  alpine.raw alpine.qcow2


or even better (not much better because it's already sparse)

virt-sparsify --compress --convert qcow2 --format raw alpine.raw alpine.qcow2

the qcow2 was about 40MB




Comments

Popular posts from this blog

How to defuse XZ Backdoor (or alike) in SSH Daemon

Making minimal graphical operating system