Research and Development
The problem with installing Ubuntu with LVM using the installer will only give you 1 GB of swap space, which is insufficient for 16 GB of RAM. Also, it would install everything into a single root partition. Should more flexibility and control over the partitioning is required with encrypted LVM setup, read on.
Even before starting the installer it is critical to select the correct boot mode. Ubuntu (and flavours like Kubuntu, Lubuntu, Xubuntu, etc.) uses hybrid bootable images that have two alternate boot-loaders:
The ISO images can boot in several possible combinations of mode and partitioning:
More on boot modes here.
If the boot hasn't been interrupted to choose a language the Welcome dialog with start-up options will be displayed. Choose Try Ubuntu.
Once the Live Desktop environment has started use the terminal shell command-line to issue a series of commands to pre-prepare the target device before executing the Installer itself.
For all or most of the commands the elevated privileges will be needed:
$ sudo -i
Identify Installation Target Device:
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
loop0 7:0 0 2G 1 loop /rofs
loop1 7:1 0 217.9M 1 loop /snap/gnome-3-34-1804/60
loop2 7:2 0 55.3M 1 loop /snap/core18/1885
loop3 7:3 0 50.7M 1 loop /snap/snap-store/481
loop4 7:4 0 30.9M 1 loop /snap/snapd/9721
loop5 7:5 0 62.1M 1 loop /snap/gtk-common-themes/1506
sda 8:0 0 54G 0 disk
sr0 11:0 1 2.7G 0 rom /cdrom
Here the installation target device is sda
but it may vary so examine the SIZE
to ensure the correct target.
Set up an environment variable to re-use in all future commands. Doing this will allow to copy and paste the instructions directly into the terminal. In this example it will be /dev/sda
:
$ export DEV="/dev/sda"
On systems with NVME storage devices the naming scheme is /dev/nvme${CONTROLLER}n${NAMESPACE}p${PARTITION}
, so if there is only one device it is likely it would require:
$ export DEV="/dev/nvme0n1"
Finally, set an environment variable for the encrypted device-mapper naming that omits the leading path /dev/
part:
$ export DM="${DEV##*/}"
If NVME devices needing a 'p' for partition suffix are needed:
$ export DEVP="${DEV}$( if [[ "$DEV" =~ "nvme" ]]; then echo "p"; fi )"
$ export DM="${DM}$( if [[ "$DM" =~ "nvme" ]]; then echo "p"; fi )"
Create a disk label and add four partitions. We'll be creating a GPT (GUID Partition Table) so it is compatible with both UEFI and BIOS mode installations. We'll also create partitions for both modes in addition to the partitions for the encrypted /boot/
and /
file-systems.
The sgdisk
tool will be used in this tutorial. To understand its options, please read man 8 sgdisk
.
First check for any existing partitions on the device and if some are found consider to keep them or not. If they should be kept, DO NOT USE sgdisk --zap-all
command detailed next. Instead, consider freeing up disk space by shrinking or deleting individual existing partitions.
$ sgdisk --print $DEV
Creating new GPT entries in memory.
Disk /dev/sda: 113246208 sectors, 54.0 GiB
Model: VMware Virtual S
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 51FA5074-ACC1-4111-968E-1CDCC7DE5377
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 113246174
Partitions will be aligned on 2048-sector boundaries
Total free space is 113246141 sectors (50.0 GiB)
If it is safe to delete everything on this device, wipe out the existing partitioning metadata - DO NOT DO THIS if you are installing alongside existing partitions:
$ sgdisk --zap-all $DEV
Creating new GPT entries in memory.
GPT data structures destroyed! You may now partition the disk using fdisk or
other utilities.
Now create the partitions: A small bios_boot
(2MB) partition for BIOS-mode GRUB's core image, an 128MB EFI System Partition, a 768MB /boot/
and a final partition for the remaining space for the operating system.
Syntax: sgdisk --new=<partition_number>:<start>:<end>
where start
and end
can be relative values and when zero (0
) adopt the lowest or highest possible value respectively.
$ sgdisk --new=1:0:+768M $DEV
$ sgdisk --new=2:0:+2M $DEV
$ sgdisk --new=3:0:+128M $DEV
$ sgdisk --new=4:0:0 $DEV
$ sgdisk --typecode=1:8301 --typecode=2:ef02 --typecode=3:ef00 --typecode=4:8301 $DEV
$ sgdisk --change-name=1:/boot --change-name=2:GRUB --change-name=3:EFI-SP --change-name=4:rootfs $DEV
$ sgdisk --hybrid 1:2:3 $DEV
$ sgdisk --print $DEV
Disk /dev/sda: 113246208 sectors, 50.0 GiB
Model: VMware Virtual S
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 5B10576E-9A21-43EA-A0AD-58FDA9C337E3
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 113246174
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)
Number Start (sector) End (sector) Size Code Name
1 2048 1574911 768.0 MiB 8301 /boot
2 1574912 1579007 2.0 MiB EF02 GRUB
3 1579008 1841151 128.0 MiB EF00 EFI-SP
4 1841152 113246174 49.1 GiB 8301 rootfs
The default LUKS (Linux Unified Key Setup) format used by the cryptsetup
tool has changed since the release of 18.04 Bionic. 18.04 used version 1 (luks1
) but more recent Ubuntu releases default to version 2 (luks2
). GRUB only supports opening version 1 so we have to explicitly set luks1 in the commands we use or else GRUB will not be able to install to, or unlock, the encrypted device.
In summary, the LUKS container for /boot/
must currently use LUKS version 1 whereas the container for the operating system's root file-system can use the default LUKS version 2.
First the /boot/
partition:
$ cryptsetup luksFormat --type=luks1 ${DEVP}1
WARNING!
========
This will overwrite data on /dev/sda1 irrevocably.
Are you sure? (Type uppercase yes): YES
Enter passphrase for /dev/sda1:
Verify passphrase:
Now the operating system partition:
$ cryptsetup luksFormat ${DEVP}4
WARNING!
========
This will overwrite data on /dev/sda4 irrevocably.
Are you sure? (Type uppercase yes): YES
Enter passphrase for /dev/sda4:
Verify passphrase:
Now open the encrypted devices:
$ cryptsetup open ${DEVP}1 boot
Enter passphrase for /dev/sda1:
$ cryptsetup open ${DEVP}4 os
Enter passphrase for /dev/sda5:
$ ls /dev/mapper/
control boot os
After the Ubuntu installation is finished we will be adding key-files to both of these devices so that you'll only have to type the pass-phrase once for GRUB and thereafter the operating system will use embedded key-files to unlock without user intervention.
IMPORTANT: This step must be done otherwise the Installer's partitioner will disable the ability to write a file-system to this device without it having a partition table (Man-page for mkfs.ext4
):
$ mkfs.ext4 -L boot /dev/mapper/boot
mke2fs 1.44.6 (5-Mar-2019)
Creating filesystem with 196096 4k blocks and 49056 inodes
Filesystem UUID: 659410fb-29a7-40af-9a5f-4dc31b72ad0a
Superblock backups stored on blocks:
32768, 98304, 163840
Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done
Format the EFI-SP as FAT16 (Man-page for mkfs.vfat
):
$ mkfs.vfat -F 16 -n EFI-SP ${DEVP}3
mkfs.fat 4.1 (2017-01-24)
Now create the operating system LVM Volume Group (VG) and a Logical Volume (LV) for the root file-system.
Also create a 6GB LV device for swap
which, as well as being used to provide additional memory pages when free RAM space is low, is used to store a hibernation image of memory so the system can be completely powered off and can resume all applications where they left off. More on swap partitions here.
Man-pages for pvcreate
, vgcreate
and lvcreate
.
$ pvcreate /dev/mapper/os
Physical volume "/dev/mapper/os" successfully created.
$ vgcreate vg1 /dev/mapper/os
Volume group "vg1" successfully created
$ lvcreate -L 6G -n swap vg1
Logical volume "swap" created.
$ lvcreate -L 30G -n root vg1
Logical volume "root" created.
$ lvcreate -l 100%FREE -n home vg1
Logical volume "home" created.
Now minimise the Terminal window and start the Installer.
Choose the installation language and keyboard and then the software installation choices.
In the Installation Type options choose Something Else.
Select the boot
file-system device for formatting (/dev/mapper/boot
), press the Change button. Choose Use as Ext4... and Mount point /boot
.
Select the home
file-system device for formatting (/dev/mapper/vg1-home
), press the Change button, choose Use As Ext4... and Mount point /home
.
Select the root
file-system device for formatting (/dev/mapper/vg1-root
), press the Change button, choose Use As Ext4... and Mount point /
.
Select the swap
device (/dev/mapper/vg1-swap
), press the Change button, choose Use as swap area.
Select the boot-loader device (/dev/sda
as used here). Boot-loader device should always be a raw disk, not a partition or device-mapper node.
Press the Install Now button to write the changes to the disk and press the Continue button.
As soon as you have completed those forms switch to the Terminal to configure GRUB. These commands wait until the installer has created the GRUB directories and then adds a drop-in file telling GRUB to use an encrypted file-system. The command will not return to the shell prompt until the target directory has been created by the installer. In most cases that will have been done before this command is executed so it should instantly return:
$ while [ ! -d /target/etc/default/grub.d ]; do sleep 1; done; echo "GRUB_ENABLE_CRYPTODISK=y" > /target/etc/default/grub.d/local.cfg
This has to be done before the installer reaches the Install Bootloader stage at the end of the installation process.
If installation is successful choose the Continue Testing option.
Return to the Terminal and create a change-root environment to work in the newly installed OS (Man-pages for mount
, chroot
):
$ mount /dev/mapper/vg1-root /target
$ for n in proc sys dev etc/resolv.conf; do mount --rbind /$n /target/$n; done
$ chroot /target
$ mount -a
Note: The cryptsetup-initramfs
package is not available in 18.04 Bionic because the files are included in the main cryptsetup
package.
$ apt install -y cryptsetup-initramfs
This allows the encrypted volumes to be automatically unlocked at boot-time. The key-file and supporting scripts are added to the /boot/initrd.img-$VERSION
files.
This is safe because these files are themselves stored in the encrypted /boot/
which is unlocked by the GRUB boot-loader (which asks you to type the passphrase) which then loads the kernel and initrd.img
into RAM before handing execution over to the kernel. (Man-page for initramfs.conf
):
$ echo "KEYFILE_PATTERN=/etc/luks/*.keyfile" >> /etc/cryptsetup-initramfs/conf-hook
$ echo "UMASK=0077" >> /etc/initramfs-tools/initramfs.conf
Create a randomised key-file of 4096 bits (512 bytes), secure it, and add it to the LUKS volumes (Man-pages for dd
, chmod
):
$ mkdir /etc/luks
$ dd if=/dev/urandom of=/etc/luks/boot_os.keyfile bs=512 count=1
1+0 records in u=rx,go-rwx /etc/luks
1+0 records out
512 bytes copied, 0.00136846 s, 374 kB/s
$ chmod u=rx,go-rwx /etc/luks
$ chmod u=r,go-rwx /etc/luks/boot_os.keyfile
$ cryptsetup luksAddKey ${DEVP}1 /etc/luks/boot_os.keyfile
Enter any existing passphrase:
$ cryptsetup luksAddKey ${DEVP}4 /etc/luks/boot_os.keyfile
WARNING: Locking directory /run/cryptsetup is missing!
Enter any existing passphrase:
Add the keys to the crypttab
(Man-pages for crypttab
, blkid
):
$ echo "boot UUID=$(blkid -s UUID -o value ${DEVP}1) /etc/luks/boot_os.keyfile luks,discard" >> /etc/crypttab
$ echo "os UUID=$(blkid -s UUID -o value ${DEVP}4) /etc/luks/boot_os.keyfile luks,discard" >> /etc/crypttab
Finally update the initialramfs
files to add the cryptsetup
unlocking scripts and the key-file:
$ update-initramfs -u -k all
If everything has gone well the system is now ready to reboot.