Chapter 09 · Build Ubuntu

The Ubuntu laptop

A laptop inverts the VPS threat model. It has no public IP and nothing is scanning it, but it gets carried through airports, left in hotel rooms, and joins networks run by strangers. The controls that matter are physical and mobile, not perimeter.

This chapter is a delta

SSH hardening, UFW basics, unattended-upgrades and sysctl are covered in chapter 08 and apply identically here. What follows is only what differs because this machine has a keyboard, a lid, and legs.

The whole chapter is one question, asked from the other side: someone is holding your laptop, and you are not. What they get depends almost entirely on what state the machine was in when they picked it up.

How it was taken
WHAT THEY TRY, IN ORDER WHAT THEY HAVE AT THIS POINT 1 · Open the lid and look met by: the lock screen 2 · Reboot onto a USB stick met by: UEFI password, Secure Boot 3 · Pull the SSD and mount it met by: LUKS full-disk encryption 4 · Brute-force the passphrase met by: its length, and only that HOW MUCH OF YOUR DATA THEY GET WHAT IS ON THIS DISK ~/.ssh/id_ed25519 · the private key, and its passphrase browser profiles · live session cookies, saved logins ~/.bash_history · every command, every hostname ~/.aws ~/.kube ~/.config · long-lived cloud credentials every control below the lock screen assumes the machine is off — the lock screen and the idle timeout are what get it there a LUKS prompt boot order locked ciphertext only no rate limit locked, no grace reboot loses RAM ciphertext only no rate limit in · nothing asked not needed already mounted not needed nothing — a header, a salt, and 512 GB of noise nothing a thief gets · a lab is another matter everything this account can reach, immediately powered down · LUKS is armed the volume key exists nowhere in this machine it is derived from your passphrase at boot this is the only state where the disk is safe asleep · the volume key is live in RAM the disk was unlocked before the lid closed suspend does not undo that, it just hides it the lock screen is what stands in front of it open and unlocked · nothing is guarding it two minutes on a café table is long enough the disk is mounted, the agent may be loaded and this machine is a tailnet member a passphrase prompt, and nothing else no desktop, no filenames, no hostname to read the initramfs asks, and then it waits there is not even anything here to look at the lock screen, with no grace period lock-delay 0 locks the instant the screen blanks a grace period is a window someone else can use idle-delay 300 shuts it while you walk away your desktop, exactly as you left it mail, chat, and the terminal you were typing in not one control in this chapter has run yet this is the failure the idle timeout prevents the firmware will not change boot order a UEFI supervisor password blocks the menu USB and network boot are disabled outright Secure Boot rejects the unsigned kernel anyway a reboot throws away the key in RAM whatever was in memory goes with the power so this move drops them into the cold case which is exactly where you want them not needed — they are already logged in booting anything else would lose the session a running system is worth more than the disk this rung only matters for a machine that is off lsblk on their machine says crypto_LUKS a header, a salt, and an awful lot of noise no filenames, no sizes, no directory tree nothing to carve and nothing to grep the same ciphertext — RAM did not travel pulling the disk powers the machine down first the honest risk here is the memory, not the disk cold-boot and DMA attacks are a lab's job the filesystem is already mounted and open they do not have to remove anything at all ~/.ssh, the browser profile, the shell history all of it readable as you, right now an encrypted brick, and unlimited patience no hardware rate limiting here, unlike a phone no lockout, no attempt counter, no remote wipe passphrase length is the whole remaining control identical to the powered-off case now the machine is cold, whatever state it left in check crypttab if you ever hibernate plain swap would hold that key in the clear not needed — it was never locked to them they had it decrypted from the first second LUKS protects data at rest, and only at rest an unlocked laptop has no data at rest
  1. 0 · It is gone, and it was offThe volume key does not exist anywhere in the machine — it is derived from your passphrase during boot and lives only in RAM afterwards. A powered-down laptop is the one state where the disk is genuinely protected, and every control further down this ladder is really about getting the machine into it.
  2. 0 · It is gone, and it was asleepThis is the common case: lid closed, bag over the shoulder. The disk was unlocked before the lid came down, so the volume key is sitting in RAM right now. Suspend does not undo that — it hides it behind the lock screen. That is why lock-delay 0 matters more here than anywhere else.
  3. 0 · It is gone, and it was openTwo minutes at a café counter. The disk is mounted, your SSH agent may hold a loaded key, your browser is signed in to everything, and the machine is a member of your tailnet. Nothing in this chapter has any effect on this case except the idle timeout that should have fired.
  4. 1 · They open the lid — and get a promptA cold boot lands in the initramfs, which asks for the passphrase and then waits. No desktop, no hostname, no filenames, not even a username to try. There is nothing to look at, which is a surprisingly large part of the value.
  5. 1 · They open the lid — and get the lock screenlock-enabled true with lock-delay 0 means the screen locked the instant it blanked. A grace period exists so that you can wake the machine without typing; on a laptop that leaves the house it is a window someone else uses. Pair it with idle-delay 300 so the screen blanks while you are still walking away.
  6. 1 · They open the lid — and it is your desktopMail, chat, the terminal you were mid-command in. Disk encryption, Secure Boot and the UEFI password have all been bypassed without being attacked, because none of them apply to a running, unlocked session. This one rung is the difference between an expensive inconvenience and an incident.
  7. 2 · They try to boot their own OSThe classic evil-maid shortcut: boot a live USB and read the disk as an ordinary volume. A UEFI supervisor password stops the boot menu opening, disabling USB and network boot removes the target, and Secure Boot refuses the unsigned kernel even if they get that far. Check the last one with mokutil --sb-state.
  8. 2 · A reboot is the best thing they could do for youCutting power throws away everything in RAM, including the volume key. From here the sleeping case and the cold case are the same case — and the firmware controls above now apply. The thief has just moved themselves into the harder problem.
  9. 2 · There is no reason to rebootRebooting would cost them the unlocked session and gain them a LUKS prompt. A live, logged-in system is strictly more valuable than its disk. This rung only exists for a machine that was off — which is the argument for making sure yours is.
  10. 3 · They take the SSD outUnscrew it, put it in an enclosure, plug it into a machine they control — and lsblk says crypto_LUKS. A header, a salt, and several hundred gigabytes of noise. No filenames, no sizes, no directory structure to work from. This is the single control the whole chapter rests on.
  11. 3 · Pulling the disk means powering it offThe same ciphertext as the cold case — the RAM does not come with the drive. The honest risk in the sleeping state is not the disk at all, it is the memory: cold-boot and DMA attacks against a suspended machine are real, and they are a lab's job rather than an opportunist's. If you hibernate rather than suspend, that key gets written to swap, which is why /etc/crypttab matters.
  12. 3 · The filesystem is already open/dev/mapper/nvme0n1p3_crypt is mounted read-write and they are inside it. ~/.ssh, the browser profile, ~/.bash_history, every cloud credential in ~/.config — readable as you, immediately, with no attack of any kind involved.
  13. 4 · All that is left is the passphraseThey can copy the encrypted image and grind at it offline, on their own hardware, for as long as they care to. There is no hardware rate limiting the way a phone has, no lockout, no attempt counter, no remote wipe. Your passphrase length is the entire remaining control — pick it accordingly, and keep it in a password manager you can reach from another device.
  14. 4 · The same passphrase problem, one step laterWhatever state it left in, the machine is cold by now and the maths is identical. The extra thing to check for a laptop that sleeps is hibernation: hibernating writes RAM — including the decrypted volume key — to swap. lsblk -o NAME,FSTYPE | grep -i swap and cat /etc/crypttab tell you whether that landing spot is encrypted.
  15. 4 · There was never anything to brute-forceThe disk was decrypted from the moment they picked it up. LUKS protects data at rest, and an unlocked laptop has no data at rest. Every strong-passphrase argument on this rung applies to the other two scenarios and to none of this one.
Full-disk encryption only ever protects a machine that is off. Step through Open and unlocked and watch all four rungs collapse: LUKS, Secure Boot and the UEFI password are bypassed without being attacked, because none of them apply to a running session. The lock screen and the idle timeout are not minor settings next to disk encryption — they are what puts the disk into the state disk encryption defends.

Disk encryption is the whole game

Every other control on a laptop assumes the attacker does not have the disk. Without full-disk encryption, a stolen laptop hands over your SSH keys, your browser sessions, and your shell history in the time it takes to boot a USB stick.

# Are you actually encrypted? crypto_LUKS on the root device is the answer.
lsblk -o NAME,FSTYPE,MOUNTPOINT

# Detail on the LUKS header
sudo cryptsetup luksDump /dev/nvme0n1p3
LUKS is chosen at install time

Ubuntu's installer offers "Encrypt the new Ubuntu installation" only during a fresh install. Retrofitting encryption onto a running system means a backup, a reinstall, and a restore. If lsblk shows no crypto_LUKS, plan that reinstall — everything else in this guide is built on the assumption that the disk is safe at rest.

Two follow-ups people miss. Use a genuinely strong passphrase, since it is the only thing between a thief and the disk — unlike a phone, there is no hardware rate limiting on attempts. And if you hibernate, confirm swap is encrypted too, because hibernation writes RAM (including decrypted keys) to it:

lsblk -o NAME,FSTYPE | grep -i swap
cat /etc/crypttab

Firmware and boot

  • Set a BIOS/UEFI supervisor password so the boot order cannot be changed.
  • Disable booting from USB and network once you no longer need it.
  • Leave Secure Boot enabled — Ubuntu supports it out of the box. Check with mokutil --sb-state.

These stop the evil-maid shortcut of booting another OS to read or tamper with your disk. They do not stop a determined attacker with the machine for a week, but they raise the cost substantially for the realistic case.

Lock screen and idle

gsettings set org.gnome.desktop.screensaver lock-enabled true
gsettings set org.gnome.desktop.screensaver lock-delay 0
gsettings set org.gnome.desktop.session idle-delay 300

lock-delay 0 means the lock takes effect the instant the screen blanks, with no grace period. On a machine that leaves the house, the grace period is a liability.

Hostile networks

A laptop joins conference Wi-Fi and hotel networks where other guests are on the same segment. Default-deny inbound is not paranoia here, it is the baseline:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable
sudo ufw status verbose

Note that you do not add an SSH allow rule. Tailscale traffic arrives over the tailscale0 interface, and binding sshd to the tailnet address (below) means the hotel network never sees the port at all.

Randomise your MAC address

By default your network card broadcasts a stable hardware address on every network you join, which is a durable tracking identifier across venues:

/etc/NetworkManager/conf.d/00-macrandomize.conf
[device]
wifi.scan-rand-mac-address=yes

[connection]
wifi.cloned-mac-address=stable
ethernet.cloned-mac-address=stable

stable generates a consistent address per network — so captive portals and MAC allow-lists keep working — while presenting a different one to each. Use random for a fresh address every connection, at the cost of re-authenticating to portals.

Joining the tailnet

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --ssh
tailscale ip -4

Tailscale SSH works as a server on Linux, so this is the simplest path: identity-based auth, no authorized_keys to maintain, and the ability to force periodic re-authentication through the check action in your ACL policy.

If you prefer OpenSSH, bind it to the tailnet address as on the Mac:

/etc/ssh/sshd_config.d/99-tailnet.conf
# Both families — `tailscale ip -4` and `tailscale ip -6`.
# One ListenAddress line stops sshd listening on everything else,
# and MagicDNS hands clients the IPv6 record too. See chapter 05.
ListenAddress 100.x.y.z
ListenAddress fd7a:115c:a1e0:ab12:4843:cd96:6475:ca41
PasswordAuthentication no
PermitRootLogin no
AllowUsers yourname
ClientAliveInterval 30
ClientAliveCountMax 6
LogLevel VERBOSE
A laptop is not always awake

Unlike the VPS, this machine sleeps when you close the lid, and a sleeping machine drops off the tailnet. If you want to reach it from your phone reliably, either disable suspend on lid close while on AC power, or accept that it is reachable only when open. tmux means an interrupted session survives the sleep and is waiting when it wakes.

AppArmor

Ubuntu ships AppArmor enabled, confining many services to a declared set of files and capabilities. It is easy to leave alone and worth confirming rather than assuming:

sudo aa-status

If a profile is in complain mode it logs violations without blocking. That is useful while developing a profile and pointless as a permanent state — move profiles you rely on to enforce.

Automatic updates

Same package as the VPS, different reboot policy. A laptop should never reboot itself while you are mid-sentence:

sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
/etc/apt/apt.conf.d/50unattended-upgrades
Unattended-Upgrade::Automatic-Reboot "false";

Then check /var/run/reboot-required yourself, or let the desktop notify you. Kernel and libssl updates do not take effect until a restart, so "patched" and "rebooted since patching" are different states.

Dev environment

sudo apt install -y tmux mosh git build-essential
curl -fsSL https://herdr.dev/install.sh | sh

# Same tmux config as the Mac — keep them identical across machines
# so your muscle memory works everywhere.

Keeping ~/.tmux.conf and ~/.config/herdr/config.toml byte-identical on every machine is worth the small effort of a dotfiles repo. Prefix keys that differ between hosts are a constant, low-grade tax.

Checklist

  • LUKS full-disk encryption confirmed with lsblk.
  • Strong disk passphrase, stored in a password manager you can reach from another device.
  • Swap encrypted if you hibernate.
  • UEFI supervisor password set; USB/network boot disabled; Secure Boot on.
  • Screen locks immediately on blank, idle timeout at five minutes or less.
  • UFW default-deny inbound, with no public SSH rule.
  • MAC address randomisation configured.
  • Tailscale joined; tailscale up --ssh or sshd bound to the tailnet address in both families.
  • AppArmor enforcing.
  • unattended-upgrades on, automatic reboot off.
  • tmux, mosh and Herdr installed with configs matching your other machines.

The commands in this guide change firewall and login settings, and can lock you out of a machine. Practise on something disposable first. Everything here is provided as is, with no warranty — you accept the risk of running it. Read the disclaimer.