Manually Installing Ubuntu 22.04 on Surface Go (with Hibernation)

Just a few months ago I had a post about installing Ubuntu 22.04 on Surface Go. And that guide is still valid. However, while using my Surface Go during vacation, I noticed I miss hibernation. Yes, deep sleep is nice enough but hibernation is much sweeter deal when you expect your device to wake up after a longer time period.

Do note I am a huge fan of encryption and thus this guide will have both data and swap encrypted thus complicating setup a bit.

As always, all starts with a creation of Ubuntu installation media and booting into it. If done from Windows, you can use the original instructions for both. If you already have Linux installed, you can check how to do it from grub. Either way, I'll asume you have it all booted and that you selected "Try Ubuntu" when offered.

From there we need to get into Terminal and become a root user.

sudo -i

The very next step should be setting up a few variables - host, user name, and disk. This way we can use them going forward and avoid accidental mistakes.


Disk setup is a bit more wasteful than what you would get following the original guide. While EFI and boot partitions are the same size, the swap partition has been increased to match RAM size (4 GB in my case). While you don't necessarily need it that big, it will help with hibernation if you do. And yes, I'm cheating a bit since the final swap size will be a bit under as encryption headers will take a bit of space. If you really need all MMC space you can get, system will work (most of the time) fine with 2 GB too.

blkdiscard -f $DISK
sgdisk --zap-all                     $DISK
sgdisk -n1:1M:+63M -t1:EF00 -c1:EFI  $DISK
sgdisk -n2:0:+640M -t2:8300 -c2:Boot $DISK
sgdisk -n3:0:+4G   -t3:8200 -c3:Swap $DISK
sgdisk -n4:0:0     -t4:8309 -c4:Root $DISK
sgdisk --print                       $DISK

While one could encrypt boot partition too, I usually don't do it as it prevents double prompt (grub has to unlock boot partition separately of others) and that is too annoying for my taste. I do encrypt both data and swap of course. Make sure they use the same password if you don't want to always enter password twice.

cryptsetup luksFormat -q --type luks2 \
    --cipher aes-xts-plain64 --key-size 256 \
    --pbkdf argon2i ${DISK}p4

cryptsetup luksFormat -q --type luks2 \
    --cipher aes-xts-plain64 --key-size 256 \
    --pbkdf argon2i ${DISK}p3

Once encryption is done, we need to load the devices. For the main partition I like to use hostname as it's displayed in the prompt.

cryptsetup luksOpen ${DISK}p4 ${HOST}

cryptsetup luksOpen ${DISK}p3 swap

Now we can format (and mount) all partitions.

yes | mkfs.ext4 /dev/mapper/${HOST}
mkdir /mnt/install
mount /dev/mapper/${HOST} /mnt/install/

mkswap /dev/mapper/swap

yes | mkfs.ext4 ${DISK}p2
mkdir /mnt/install/boot
mount ${DISK}p2 /mnt/install/boot/

mkfs.msdos -F 32 -n EFI -i 4d65646f ${DISK}p1
mkdir /mnt/install/boot/efi
mount ${DISK}p1 /mnt/install/boot/efi

To start the fun we need the debootstrap package. Do make sure you have Wireless network connected at this time as otherwise operation will not succeed.

apt update ; apt install --yes debootstrap

And then we can get basic OS on the disk. This will take a while.

debootstrap $(basename `ls -d /cdrom/dists/*/ | grep -v stable | head -1`) /mnt/install/

Our newly copied system is lacking a few files and we should make sure they exist before proceeding.

echo $HOST > /mnt/install/etc/hostname
sed "s/ubuntu/$HOST/" /etc/hosts > /mnt/install/etc/hosts
sed '/cdrom/d' /etc/apt/sources.list > /mnt/install/etc/apt/sources.list
cp /etc/netplan/*.yaml /mnt/install/etc/netplan/

If you are installing via WiFi, you might as well copy your wireless credentials

mkdir -p /mnt/install/etc/NetworkManager/system-connections/
cp /etc/NetworkManager/system-connections/* /mnt/install/etc/NetworkManager/system-connections/

Finally we're ready to "chroot" into our new system.

mount --rbind /dev  /mnt/install/dev
mount --rbind /proc /mnt/install/proc
mount --rbind /sys  /mnt/install/sys
chroot /mnt/install \
    /usr/bin/env DISK=$DISK HOST=$HOST USER=$USER \
    bash --login

For the new system we need to setup the locale and the time zone.

locale-gen --purge "en_US.UTF-8"
update-locale LANG=en_US.UTF-8 LANGUAGE=en_US
dpkg-reconfigure --frontend noninteractive locales

dpkg-reconfigure tzdata

Now we're ready to onboard the latest Linux image. And yes, there is a Surface Go specific kernel, but it seems that 5.17 you get with OEM kernels is as good.

apt update
apt install --yes --no-install-recommends linux-oem-22.04

Then we install boot environment packages.

apt install --yes initramfs-tools cryptsetup keyutils grub-efi-amd64-signed shim-signed

Since we're dealing with encrypted data, we should auto mount it via crypttab. If there are multiple encrypted drives or partitions, keyscript really comes in handy to open them all with the same password..

echo "${HOST} UUID=$(blkid -s UUID -o value ${DISK}p4)  none \
    luks,discard,initramfs,keyscript=decrypt_keyctl" >> /etc/crypttab
echo "swap UUID=$(blkid -s UUID -o value ${DISK}p3) none \
    swap,luks,discard,initramfs,keyscript=decrypt_keyctl" >> /etc/crypttab
cat /etc/crypttab

To mount partitions, we need to do some fstab setup too:

echo "UUID=$(blkid -s UUID -o value /dev/mapper/${HOST}) \
    / ext4 noatime,nofail,x-systemd.device-timeout=5s 0 1" >> /etc/fstab
echo "PARTUUID=$(blkid -s PARTUUID -o value ${DISK}p2) \
    /boot ext4 noatime,nofail,x-systemd.device-timeout=5s 0 1" >> /etc/fstab
echo "PARTUUID=$(blkid -s PARTUUID -o value ${DISK}p1) \
    /boot/efi vfat noatime,nofail,x-systemd.device-timeout=5s 0 1" >> /etc/fstab
echo "UUID=$(blkid -s UUID -o value /dev/mapper/swap) \
    none swap defaults 0 0" >> /etc/fstab
cat /etc/fstab

While defaults are actually matching most of the needed values, I like to explicilty list them and manually configure the time after which sleep will be followed by automatic hybernation (10 minutes in my case).

sed -i 's/.*AllowSuspend=.*/AllowSuspend=yes/' \
sed -i 's/.*AllowHibernation=.*/AllowHibernation=yes/' \
sed -i 's/.*AllowSuspendThenHibernate=.*/AllowSuspendThenHibernate=yes/' \
sed -i 's/.*HibernateDelaySec=.*/HibernateDelaySec=10min/' \

Lid switch can be then easily setup to use suspend-then-hibernate.

sed -i 's/.*HandleLidSwitch=.*/HandleLidSwitch=suspend-then-hibernate/'
sed -i 's/.*HandleLidSwitchExternalPower=.*/HandleLidSwitchExternalPower=suspend-then-hibernate/'

I also like to adjust swappiness to make system a bit more lively.

echo "vm.swappiness=10" >> /etc/sysctl.conf

With all that out of way, we can finally update our boot environment.

KERNEL=`ls /usr/lib/modules/ | cut -d/ -f1 | sed 's/linux-image-//'`
update-initramfs -u -k $KERNEL

Grub update is the last part we need to make system bootable. And no, the second initramfs update is not optional as it needs to pickup RESUME variable.

    RESUME=UUID=$(blkid -s UUID -o value /dev/mapper/swap)\"/" \
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Ubuntu \
    --recheck --no-floppy
update-initramfs -u -k all

Finally we install out GUI environment. I personally like ubuntu-desktop-minimal but you can opt for ubuntu-desktop.

apt install --yes ubuntu-desktop-minimal

Since this will not install any browser, you can add Firefox package too (apt install firefox) but I like to download Chrome.

cd /tmp
wget --inet4-only
sudo apt install ./google-chrome-stable_current_amd64.deb

Having power button do a hibernate action requires a bit more effort. And while you can use suspend-then-hibernate here too, I personally prefer to have it linked to straight hibernate.

gsettings set org.gnome.settings-daemon.plugins.power power-button-action nothing
echo 'event=button/power' > /etc/acpi/events/power
echo 'action=sudo systemctl hibernate' >> /etc/acpi/events/power

Short package upgrade will not hurt.

add-apt-repository universe
apt update ; apt dist-upgrade --yes

The only remaining task before restart is to create the user, assign a few extra groups to it, and make sure its home has correct owner.

adduser --disabled-password --gecos '' -u 1002 $USER
usermod -a -G adm,cdrom,dialout,dip,lpadmin,plugdev,sudo,tty $USER
echo "$USER ALL=NOPASSWD:ALL" > /etc/sudoers.d/$USER
passwd $USER

As install is ready, we can exit our chroot environment.


And now we can unmount our disk, followed by a reboot.

umount /mnt/install/boot/efi
umount /mnt/install/boot
mount | tac | awk '/\/mnt/ {print $3}' | xargs -i{} umount -lf {}


If everything went fine, we can now hibernate.

systemctl hibernate

If you see hibernate not supported message, turn off the secure boot. At this time hibernation is not supported when secure boot is turned on.

Desire for a Story Arc

Being on vacation in not always healthy. Take me for example, I decided to binge watch both seasons of Star Trek Picard.

I didn't come into this from nothing. I did watch a few episodes from the first season before giving up. I was not really sure why but I simply stopped at one point. But idle hands and all that made me finally watch it all. And yes, I still find that Star Trek Picard is a bad series and a sad chapter for Jean Luc Picard.

There are many things wrong with it. And I don't think a desire to get as many old familiar faces into the show, whether it fits a previously established narative or not (e.g. Guinan and Dr. Soong being the leading example) is the main fault. It definitely doesn't help but I think I could live with it.

I belive the main driver for my dislake of the show is mostly connected to show's desire to have a story arc spanning a season. This makes writers focus on highly unrealistic scenarios just to keep excitement up. And yes, I do understand irony that complaining about unrealistic things in SciFi show might raise. God knows my favorite TNG had many those moments. But they were easier to swallow as they were fleeting and not pushed into viewer's face.

Here things went from bad to worse in the season two. While one could half-heartedly claim that future is just how it is, decision to have a second season in the year 2024 only served to expose problem more. It's just freaking two years into the future and you cannot just introduce "database of their entire life history" as a convinient plot point or have borg queen eat lead-acid battery to get Lithum (to name just two of many nonsenses).

I know, it might just be my age catching up to me and I simply don't understand why Picard is much better than anything else in the Star Trek universe. Be it as it may, I will continue my vacation with TNG decontamination. Good knows I need my Captain.

Encrypting SD Card Using BitLocker

As I wanted to use SD card to move files from my laptop to Surface Go, it made sense to encryt the data. Easy, just right-click on the drive and turn on BitLocker, I thought. However, my SD card didn't give me such option. For some reason Microsoft decided to not show this option in context menu.

However, one can still encrypt it using command line. Syntax it a bit annyoying but workable. In my example, I had my SD card mounted as drive S:.

manage-bde.exe -on S: -password -s

To check how the process goes, you can use -status parameter.

manage-bde.exe -status

To make things even better, once encrypted, these disks are usable on Ubuntu 22.04 too. You might just want to install exFAT support.

sudo add-apt-repository universe
sudo apt update
sudo apt install exfat-fuse exfatprogs

Post-Quantum Cryptography – The Last Round

Well, after a long time, NIST announced the first four quantum-resistant cryptographic algorithms.

Both of my StarTrek inspired favorites are actually in. CRYSTALS-Kyber is the one selected for a key exchange purposes and I fully expect to see it in some future OpenSSH version. Since dealing with ED25519 would require a quantum computer much bigger than currently available, eliptic curves are still probably the best default choice. However, you don't want to wait the last moment to switch. Considering there are still some system that only support RSA (yes Mikrotik, I'm talking about you), switch will take a while.

CRYSTALS-Dilithium, a part of the same family, got selected as one of three suggested digital signature algorithms. From practicality side, it will rarely, if ever, be used alone as its signature output is literaly larger than a KB. That said, there are a few suggested modes (e.g. Dilithium3-AES) keeping the reasonable key size AES provides while retaining quantum assurances of a lattice algorthm.

FALCON was also selected despite difficulty of a correct implementation and a huge potential for side-channel attacks. I guess a small memory footprint and impressive performance in embedded applications were enough to ensure its place among finalists.

Lastly, a SPHINCS+ came out of blue (at least for me) to take its place as the last of the finalists. Since it is slower and larger than either of the other two finalists, it's hardly a first choice. Regardless, using a different math approach compared to other two finalists was valuable enough to get it in.

NewHope, one of the round two finalists and already used by Chrome ended up like the recent Star Wars sequels. An early succes but ultimately not good enough to pursue.