Created
February 11, 2026 15:26
-
-
Save Juma7C9/49103015adcd90980d3b951e1c0ed362 to your computer and use it in GitHub Desktop.
cloud-config user configuration (btrfs+luks+ssh+unattended upgrades)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #cloud-config | |
| # Cloud config user configuration for Debian cloud images: | |
| # * Adds ssh keys and reenable root account; | |
| # * Enables unatted upgrades; | |
| # * Converts the disk image to btrfs and rollbacks if it fails; | |
| # * Encrypts the disk with an inital password, which can be changed after the first boot; | |
| # * Prepares some coffee (not yet). | |
| disable_root: false | |
| ssh_authorized_keys: | |
| - your-keys-here | |
| - your-keys-here | |
| apt: | |
| conf: | | |
| APT::Periodic { | |
| Update-Package-Lists "1"; | |
| Unattended-Upgrade "1"; | |
| }; | |
| Unattended-Upgrade { | |
| Origins-Pattern { | |
| "origin=Debian,codename=${distro_codename}-updates"; | |
| "origin=Debian,codename=${distro_codename},label=Debian"; | |
| "origin=Debian,codename=${distro_codename},label=Debian-Security"; | |
| "origin=Debian,codename=${distro_codename}-security,label=Debian-Security"; | |
| }; | |
| MinimalSteps "true"; | |
| Remove-Unused-Kernel-Packages "true"; | |
| Remove-New-Unused-Dependencies "true"; | |
| }; | |
| package_update: true | |
| package_upgrade: true | |
| package_reboot_if_required: true | |
| packages: | |
| - openssh-server | |
| - unattended-upgrades | |
| - dropbear-initramfs | |
| - cryptsetup-initramfs | |
| - btrfs-progs | |
| write_files: | |
| # Dropbear (initramfs ssh server) configuration | |
| - path: /etc/dropbear/initramfs/dropbear.conf | |
| content: | | |
| DROPBEAR_OPTIONS="-sjk -c /usr/bin/cryptroot-unlock" | |
| - path: /run/tmpfiles.d/dropbear-initramfs.conf | |
| content: | | |
| L /etc/dropbear/initramfs/authorized_keys - - - - /root/.ssh/authorized_keys | |
| # Initial luks passphrase: adjust as needed, and/or change afterwards using | |
| # `cryptsetup ...' | |
| - path: /run/initial-luks-pw | |
| permissions: '0600' | |
| content: 'changeme' | |
| # First phase, after system initialization copy a minimal rootfs to ram, | |
| # and use systemd's soft-reboot to switch and unload the real rootfs | |
| - path: /run/cloud-init-scripts/init.sh | |
| permissions: '0755' | |
| content: | | |
| #!/bin/bash | |
| findmnt -no PARTUUID / >/etc/root-partuuid | |
| echo "root PARTUUID=$(</etc/root-partuuid) none luks" >/etc/crypttab | |
| systemd-tmpfiles --create | |
| # If `/boot` resides under the same fs as `/`, | |
| # use EFIpart as /boot to avoid encrypting the boot partition | |
| if ! (mountpoint -q /boot) && (mountpoint -q /boot/efi); then | |
| EFI_PART=/dev/disk/by-uuid/$(findmnt -no UUID /boot/efi) | |
| umount /boot/efi | |
| mkdir /run/boot | |
| rmdir /boot/efi | |
| mount $EFI_PART /run/boot | |
| mv /boot/* /run/boot | |
| umount /run/boot | |
| sed 's#/boot/efi#/boot#' -i /etc/fstab | |
| mount /boot | |
| fi | |
| # If `/run/nextroot` contains a valid rootfs, systemd will automatically | |
| # switch to it on soft-reboot. | |
| # See also https://unix.stackexchange.com/questions/226872/ | |
| mkdir /run/nextroot | |
| mount -t tmpfs -o size=640M none /run/nextroot | |
| mkdir /run/nextroot/{proc,sys,dev,run,usr{,/share},var,tmp,root,oldroot} | |
| cp -ax /{bin,etc,sbin,lib,root} /run/nextroot/ | |
| cp -ax /usr/{bin,sbin,lib,lib64,libexec} /run/nextroot/usr/ | |
| cp -ax /usr/share/{dbus-1,polkit-1} /run/nextroot/usr/share | |
| cp -ax /var/{lib,lock,log,run,tmp} /run/nextroot/var | |
| cp -a /run/{cloud-init-scripts,nextroot/var/lib/cloud/scripts/per-boot}/repart.sh | |
| systemctl soft-reboot | |
| # Second phase, we are in the volatile (tmpfs) rootfs | |
| # Find the real rootfs, try to convert it to btrfs, then convert the partition to LUKS | |
| - path: /run/cloud-init-scripts/repart.sh | |
| permissions: '0755' | |
| content: | | |
| #!/bin/bash -x | |
| # Try to avoid OOMs | |
| modprobe zram | |
| zramctl --find --algorithm zstd \ | |
| --size "$(($(grep -Po 'MemTotal:\s*\K\d+' /proc/meminfo) * 2))KiB" | |
| mkswap -U clear /dev/zram0 | |
| swapon /dev/zram0 | |
| # Try to convert rootfs to btrfs, rollback if it fails | |
| # (see https://bugzilla.kernel.org/show_bug.cgi?id=206995) | |
| # Resize the part to make room for LUKS header, then encrypt it | |
| ROOT_PART=/dev/disk/by-partuuid/$(</etc/root-partuuid) | |
| e2fsck -fy $ROOT_PART | |
| btrfs-convert --no-progress --uuid copy --copy-label $ROOT_PART | |
| mkdir /run/btrfs | |
| if (mount $ROOT_PART /run/btrfs); then | |
| btrfs subvolume delete /run/btrfs/ext2_saved | |
| btrfs filesystem defrag -r -f /run/btrfs | |
| btrfs filesystem balance --full-balance /run/btrfs | |
| btrfs filesystem resize -32M /run/btrfs | |
| sed 's/ext4/btrfs/' -i /run/btrfs/etc/fstab | |
| umount /run/btrfs | |
| else | |
| btrfs-convert --no-progress --rollback $ROOT_PART | |
| NEW_SIZE=$(( $(lsblk -nbo SIZE $ROOT_PART) / 1024 - 32*1024 )) | |
| resize2fs $ROOT_PART ${NEW_SIZE}K | |
| fi | |
| # Default initial password is "debian" | |
| cryptsetup reencrypt --new --pbkdf-memory 262144 --reduce-device-size 32M \ | |
| --key-file - $ROOT_PART </run/initial-luks-pw | |
| # Soft-reboot a last time and reconfigure initramfs | |
| cryptsetup open --key-file - $ROOT_PART root </run/initial-luks-pw | |
| mount /dev/mapper/root /run/nextroot | |
| mount --bind /boot /run/nextroot/boot | |
| mount -t tmpfs none /run/nextroot/var/lib/cloud/scripts/per-boot | |
| cp -a /run/{cloud-init-scripts,nextroot/var/lib/cloud/scripts/per-boot}/update-initramfs.sh | |
| systemctl soft-reboot | |
| # Third (final) phase: we are back in the real, now luks-encrypted rootfs; | |
| # reconfigure the GRUB image and config, together with the initramfs image, | |
| # and finally reboot to the configured system. | |
| - path: /run/cloud-init-scripts/update-initramfs.sh | |
| permissions: '0755' | |
| content: | | |
| #!/bin/bash -x | |
| # Use a different dir to temporarely store the init images, | |
| # as the /boot (previously EFI) part may be too small. | |
| update-initramfs -u -b /run/boot | |
| rm /boot/initrd.img* | |
| cp -ax /run/boot/initrd.img* /boot | |
| # Suppose we have at most an `EFI` dir under the root of mounted FSs... | |
| efi_dir="$(find $(findmnt -lno TARGET) -maxdepth 1 -name EFI -type d -printf '%h')" | |
| if [ -n "$efi_dir" ]; then efi_opt="--efi-directory=$efi_dir"; fi | |
| grub-install "$efi_opt" | |
| update-grub | |
| export SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT=1 | |
| systemctl reboot | |
| runcmd: | |
| - bash -x /run/cloud-init-scripts/init.sh |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment