🔍 Context:
Ubuntu’s Autoinstall (Subiquity) typically bakes full disk encryption directly into autoinstall.yaml, making it an all-or-nothing setup—either every install uses encryption, or none do. This becomes limiting when you want a single ISO image to support both encrypted and unencrypted installs without user interaction. In this blog, we will show how to use an initrd hook and a simple trigger mechanism to dynamically choose the right config at install time—enabling flexible, environment-aware deployments from a unified base image. Refer the blog post to understand more about how FDE can be done with autoinstall process.
🛠️ How it works:
The idea is to leverage Ubuntu’s initrd hooks to inject a small script that decides which autoinstall.yaml config to use — with or without full disk encryption — at runtime.
Here’s the breakdown:
- Two Configs Inside the ISO
-
fde/user-data
→ contains full disk encryption. -
nofde/user-data
→ no encryption. Both set of configurations are included in the ISO in a known directory.
-
- A tiny empty image file (~350KB) will be created with a custom label "fde" will be used as the trigger. When installing, if this image is attached (eg: via USB, Floppy, cloud-init disk), it will appear in the system as:
/dev/disk/by-label/fde
- Initrd Hook Script
A custom initrd hook script run very early in the boot process, just before we move to the installer fs. It will-
- Check if the
/dev/disk/by-label/fde
exists. - If yes → copies
fde/user-data
to a target folder. - If no → copies
nofde/user-data
instead.
- Check if the
- Update the
/boot/grub/grub.cfg
to pick up the configuration for autoinstall from the target folder.
🧪 Modifications:
I used Ubuntu 24.04 live server image for this. Let's make a copy of the iso content for us to make our modifications
mkdir -p /mnt/iso ~/edit
sudo mount -o loop ubuntu-24.04.2-live-server-amd64.iso /mnt/iso
rsync -a /mnt/iso/ ~/edit
sudo umount /mnt/iso
- Let's first create a folder in the base iso which will hold both fde and no-fde autoinstall configurations.
cd ~/edit
mkdir -p cidata/{fde,nofde}
cat <<EOF >> cidata/fde/user-data
... Your autoinstall.yaml content with full drive encryption enabled...
EOF
touch cidata/fde/meta-data
cat <<EOF >> cidata/nofde/user-data
... Your autoinstall.yaml content without full drive encryption enabled...
EOF
touch cidata/nofde/meta-data
- We need to add a hook to the initrd. So let's first extract the existing initrd.
rm -rf ~/edit/initrd-unpacked/
mkdir -p ~/edit/initrd-unpacked
unmkinitramfs ~/edit/casper/initrd ~/edit/initrd-unpacked/
- Create a file
~/edit/initrd-unpacked/main/scripts/casper-bottom/98mount-cidata
with by below content
#!/bin/sh
PREREQ=""
prereqs() {
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
mkdir -p /root/setup
if [ -b /dev/disk/by-label/fde ]; then
cp -r /root/cdrom/cidata/fde/. /root/setup/
echo "FDE data copied to /root/setup/" > /dev/console
else
cp -r /root/cdrom/cidata/nofde/. /root/setup/
echo "No FDE data found, copied nofde data to /root/setup/" > /dev/console
fi
chown -R root:root /root/setup/
exit 0
- Provide executable permission to the file and Update the ORDER file located at
~/edit/initrd-unpacked/main/scripts/casper-bottom
# ORDER file before
...
/scripts/casper-bottom/61desktop_canary_tweaks "$@"
[ -e /conf/param.conf ] && . /conf/param.conf
/scripts/casper-bottom/99casperboot "$@"
[ -e /conf/param.conf ] && . /conf/param.conf
# ORDER file after
...
/scripts/casper-bottom/61desktop_canary_tweaks "$@"
[ -e /conf/param.conf ] && . /conf/param.conf
/scripts/casper-bottom/98mount-cidata "$@"
[ -e /conf/param.conf ] && . /conf/param.conf
/scripts/casper-bottom/99casperboot "$@"
[ -e /conf/param.conf ] && . /conf/param.conf
- Now that the changes are done, let's re-build the initrd
cd ~/edit/initrd-unpacked
cd early
find . -print0 | cpio --null --create --format=newc > /tmp/initrd
cd ../early2
find . -print0 | cpio --null --create --format=newc >> /tmp/initrd
cd ../early3
find . -print0 | cpio --null --create --format=newc >> /tmp/initrd
cd ../main
find . | cpio --create --format=newc | xz --format=lzma >> /tmp/initrd
rm -f ~/edit/casper/initrd
mv /tmp/initrd ~/edit/casper/initrd
- Update the
grub.cfg
for the autoinstall to pick up configuration from the/setup
path on the filesystem.\
menuentry "Install Ubuntu Server" {
set gfxpayload=keep
linux /casper/vmlinuz quiet autoinstall ds=nocloud\;s=/setup apparmor=0 --- ---
initrd /casper/initrd
}
- Generate the md5 sums.
cd ~/edit
sudo rm -f md5sum.txt
sudo bash -c "find . -type f ! -path './isolinux/*' ! -path './boot/*' -print0 | xargs -0 md5sum > md5sum.txt"
- Rebuild the iso.
sudo xorriso -as mkisofs -J -R -r \
-V Ubuntu-Server \
-o custom.iso \
--grub2-mbr <path to your custom xx-Boot-NoEmul.img> \
-partition_offset 16 \
--mbr-force-bootable \
-append_partition 2 <GUID> <path to your custom xx-Boot-NoEmul.img> \
-appended_part_as_gpt \
-iso_mbr_part_type <GUID>\
-c 'boot.catalog' \
-b 'boot/grub/i386-pc/eltorito.img' \
-no-emul-boot -boot-load-size 4 -boot-info-table --grub2-boot-info \
-eltorito-alt-boot \
-e '--interval:appended_partition_2:::' \
-no-emul-boot \
~/edit/
✅ Conclusion
With this setup, we have made Ubuntu's Autoinstall system a tad bit smarter and more flexible- capable of deciding at runtime whether to apply full disk encryption, all without any user interaction or multiple full ISO images; all we need is to mount an extra empty, labeled image. This approach is a bit better for reproducible, automated deployments.
Top comments (0)