Encrypt VPS based on cloudinit debian image

Introduction and warning

This tutorial is a recap on an adminsys operation made in march 2026 with other yunohost.pro adminsys. I think it could be useful for some people so i have documented it here.

This tutorial is quite advanced, freshly written, you should understand what you do in order to be able to debug. Make backup AND snapshot before everything else… Shrinking filesystem and partition may result in data loss if proper precautions are not taken.

Feel free to contribute if you discover some mistakes or misunderstood.

If you have a physical server or other kind of vps, this tuto could work too, however you should be careful with partition number and partition order.

Why encrypt a VPS (or a physical server) ? Which additional actions should be done

For data security offcourse. Encrypt your disk make harder to explore the data inside your VPS.
Note that if it’s a VPS (and not a physical hardware under your control) it’s probably possible for your hoster to find LUKS key inside a dump of your RAM.

Note that, encrypt your disk is really not useful if you let some tools running inside your system like qemu-guest-agent or if you have hoster public key configured in a user… So you have to remove qemu-guest-agent

apt remove qemu-guest-agent

Note: some features like root password reinit or graph monitoring inside your hoster web console won’t be able to work anymore.

And check authorized_keys

cat /root/.ssh/authorized_keys
cat /home/debian/.ssh/authorized_keys
cat /home/USER/.ssh/authorized_keys

May be the cloudinit config should be checked to, to avoid the hoster to be able to trigger it again and replace a password for example…

Prerequisite

  • A VPS build with cloudinit image (for example with proxmox or openstack), your partition should be like
$ lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda       8:0    0    5G  0 disk 
├─sda1    8:1    0  4.9G  0 part /
├─sda14   8:14   0    3M  0 part 
└─sda15   8:15   0  124M  0 part /boot/efi
  • A rescue mode allowing you to mount vps disk on another system

IMPORTANT: allmost all actions should be run in rescue mode, it’s possible that your disk is called /dev/sdb instead of /dev/sda in that context, you should adapt things…

1. Create a /boot partition

First of all, you need to create a /boot partition, this operation is quite risky cause you probably should reduce the size of / partition in order to have space for creating /boot partition.

Here we suggest to allocate 1G to /boot but it can be smaller, you probably should clean /boot more regularly if you do.

Reduce the size of /dev/sda1 ext4 file system

First check you have more than 1GB available on /dev/sda1

$ df -h /dev/sda1

Get exact number of bytes for the partition

$ parted -s -a opt /dev/sda1 "unit b print"
Model: Unknown (unknown)
Disk /dev/sda1: 5234474496B
Sector size (logical/physical): 512B/512B
Partition Table: loop
Disk Flags: 

Number  Start  End          Size         File system  Flags
 1      0B     5234474495B  5234474496B  ext4

Important: as we mention /dev/sda1 instead of /dev/sda we have the relative end (from the start) and not the end of the partition in absolute position in the disk…
Note: here i prefer to work with bytes, cause it could be confuse between GiB and GB when you use resize2fs and parted…

Remove 1GiB from the End value (here 5234474495B)

5234474495 - 1024*1024*1024 = 4160732671

Resize the FS

resize2fs /dev/sda1 4160732671B

Reduce the partition size of /dev/sda1

Display your partition number and the start and end in the disk

$ parted -s -a opt /dev/sda "unit b print free"
Model: QEMU QEMU HARDDISK (scsi)
Disk /dev/sda: 5368709120B
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags: 

Number  Start       End          Size         File system  Name  Flags
        17408B      1048575B     1031168B     Free Space
14      1048576B    4194303B     3145728B                        bios_grub
15      4194304B    134217727B   130023424B   fat16              boot, esp
 1      134217728B  5368692223B  5234474496B  ext4

Remove 1GiB from the End value (here 5368692223B)

5368692223 - 1024*1024*1024 = 4294950399

Resize the / partition (here sda1)

parted -s -a opt /dev/sda "resizepart 1 4294950399b"

Create /boot partition

parted -s -a opt /dev/sda "mkpart boot ext4 4294950400b 5368692223b"

Note: in my memories, it could be more complex if you have to align partition on sector/cylinder.
TODO: @lab.8916100448256 @Thatoo feel free to change this part of the tutorial if you remember it

Create /boot ext4 filesystem

mkfs.ext4 -L boot /dev/sda2

2. Move old /boot into the new one

Mount filesystems:

mkdir /mnt/system
mkdir /mnt/boot
mount /dev/sda1 /mnt/system
mount /dev/sda2 /mnt/boot

And move the files to the new /boot

mv /mnt/system/boot/* /mnt/boot/

3. Encrypt /

Make the filesystem slightly smaller to make space for the LUKS header

Run a filesystem check

e2fsck -f "/dev/sda1"

Compute new filesystem size. Be sure dumpe2fs is installed

BLOCK_SIZE=`dumpe2fs -h /dev/sda1 | grep "Block size" | cut -d ':' -f 2 | tr -d ' '`
BLOCK_COUNT=`dumpe2fs -h /dev/sda1 | grep "Block count" | cut -d ':' -f 2 | tr -d ' '`
SPACE_TO_FREE=$((1024 * 1024 * 32)) # 16MB should be enough, but add a safety margin
NEW_BLOCK_COUNT=$(($BLOCK_COUNT - $SPACE_TO_FREE / $BLOCK_SIZE))

IMPORTANT: before resizing, check that $BLOCK_SIZE $BLOCK_COUNT $SPACE_TO_FREE and $NEW_BLOCK_COUNT are not empty. If it’s empty, you check dumpe2fs is installed.

echo "$BLOCK_SIZE , $BLOCK_COUNT , $SPACE_TO_FREE , $NEW_BLOCK_COUNT"

Resize the filesystem

resize2fs -p "/dev/sda1" "$NEW_BLOCK_COUNT"

Run the encryption process

apt install cryptsetup
cryptsetup reencrypt --encrypt --reduce-device-size 16M "/dev/sda1"

Resize the filesystem to fill up the remaining space

cryptsetup open "/dev/sda1" recrypt
resize2fs /dev/mapper/recrypt
cryptsetup close recrypt

4. Chroot the VPS system

apt install cryptsetup -y
cryptsetup open /dev/sda1 crypt-root
mkdir /mnt/system
mount /dev/mapper/crypt-root /mnt/system 
mount /dev/sda2 /mnt/system/boot
mount /dev/sda15 /mnt/system/boot/efi
mount --bind /dev /mnt/system/dev 
mount --bind /dev/pts /mnt/system/dev/pts 
mount --bind /proc /mnt/system/proc 
mount --bind /sys /mnt/system/sys
chroot /mnt/system

5. Update crypttab, fstab and update grub

Disable linux UUID and PARTUUID in grub (you want /dev/mapper/crypt-root to be used)

Add a file to be sure linux UUID and PARTUUID are disabled in grub
/etc/default/grub.d/99_custom.cfg

# Use PARTUUID instead of UUID for root=
GRUB_DEVICE=/dev/mapper/crypt-root
GRUB_DISABLE_LINUX_UUID=true
GRUB_DISABLE_LINUX_PARTUUID=true

Update /etc/crypttab

Crypttab lists which disks should be decrypt at boot.

# <target name> <source device>         <key file>      <options>
crypt-root UUID=aaaaaaaaaa-bbbb-cccc-dddd-eeeeeeeeee none luks

With aaaaaaaaaa-bbbb-cccc-dddd-eeeeeeeeee replaced by the result of blkid -s UUID -o value /dev/sda1

Note: if you want to decrypt 2 disks instead of 1, you should install keyutils with apt install keyutils and add ,keyscript=decrypt_keyctl at the end of each lines in /etc/crypttab

Update /etc/fstab

/dev/mapper/crypt-root  / ext4 rw,discard,errors=remount-ro,x-systemd.growfs 0 1
PARTUUID=aaaaaaaaaaaaaa /boot ext4 rw,discard,errors=remount-ro,x-systemd.growfs 0 1
PARTUUID=bbbbbbbbbbbbb /boot/efi vfat defaults 0 0

Replace aaaaaaaaaaaaaa by the result of blkid -s PARTUUID -o value /dev/sda2

Replace bbbbbbbbbbbbb by the result of blkid -s PARTUUID -o value /dev/sda15

Update grub

update-grub2

Be sure it worked by running this command, it should return some lines

grep "root=/dev/mapper/crypt-root" /boot/grub/grub.cfg

6. Configure Dropbear, an ssh service to decrypt your disk

At this step, if you restart your VPS, you should be able to decrypt it with a VNC console, however VNC could be unencrypted / unauthenticated and is not really safe to decrypt your disk through it.

That’s why , we want a more secure way to do it, like an authenticated ssh service. However you can’t do it with openssh cause the service is not running cause your disk in not decrypted, you have to setup this service into the initramfs run earlier in the boot process… Dropbear is an ssh service dedicated to do this operation.

Note that a cryptroot-unlock package exists to do those operation however network config could be not totaly ok. Bellow are manual operations:

apt install busybox dropbear-initramfs initramfs-tools

Write your public ssh key into /etc/dropbear/initramfs/authorized_keys

Configure a dedicated SSH port for dropbear (distinct of openssh port) and run cryptroot-unlock automatically.

/etc/dropbear/initramfs/dropbear.conf

echo 'DROPBEAR_OPTIONS="-I 180 -j -k -p 2222 -s -c cryptroot-unlock"' >> /etc/dropbear/initramfs/dropbear.conf

Edit your /etc/initramfs-tools/initramfs.conf with good network config, think to replace <VARIABLE> by good values.

echo "ip=<PUBLIC_IP>::<PUBLIC_GATEWAY>:<MASK>:<FULL_HOSTNAME>:off:<IP_DNS_RESOLVER1>:<IP_DNS_RESOLVER2>" >> /etc/initramfs-tools/initramfs.conf

Ensure the keyboard layout is usable with your keyboard in /etc/default/keyboard (example for french keyboard)

XKBMODEL="pc105"
XKBLAYOUT="fr"
XKBVARIANT="latin9"

Update initramfs

update-initramfs -k all -u

:tada: Enjoy (or not)

Now, you can try to restart out of rescue mode and connect with SSH through the port 2222, you should be able to uncrypt your disk by providing passphrase and next to ssh with your standard port.

If yes you succeed to encrypt your / partition congratulations :tada:.

External ressources

Williamdes's blog - Encrypt an existing Debian 12 system with LUKS

6 Likes

Applied this this morning on my server.

It’s a local server so it was already encrypted but needed me to be here to restart it, now I can do it from anywhere :heart:

Here are the few differences I have on my server :

Details here

My /etc/crypttab files contains 2 lines :

sda5_crypt UUID=aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa none luks
disquerouge UUID=bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb /mnt/phrasedisquerouge luks

I do not remember exactly the cryptographic process for both disk, but they were different, and I stored a passphrase for the second disk on the first one, as it is crypted.
I hopped that your tutorial would also ask for the passphrase for the second disk but it did not work. Maybe crypting it by following your explanations would work, I do not know. (my server is running on YunoHost for something like 10 years now, I do not want to reset it now)

To add the passphrase in a file :
sudo cryptsetup luksAddKey /dev/sdb1 /mnt/phrasedisquerouge

With all this, I just installed dropbear as you said :

apt install busybox dropbear-initramfs initramfs-tools
echo 'DROPBEAR_OPTIONS="-I 180 -j -k -p 2222 -s -c cryptroot-unlock"' >> /etc/dropbear/initramfs/dropbear.conf
echo "ip=192.168.1.1::192.168.1.255:24:yunohost:off:176.9.93.198:176.9.1.117" >> /etc/initramfs-tools/initramfs.conf
# put the private ssh key in `/etc/dropbear/initramfs`
update-initramfs -k all -u
reboot

And enjoy :heart:

I just released a new version of the package with the fixed suggested by @Thatoo :

2 Likes