Skip to content

Hardened Device Images

LineInterpreter provides hardened Ubuntu Core 24 images that you deploy to your own hardware. The security controls are applied automatically at first boot via cloud-init. This page documents what those controls provide and why each one exists.


The image includes a single account — li-bootstrap — that exists only to run the provisioning script. The account is designed so that whoever is standing at the machine can complete provisioning without any involvement from LineInterpreter, while preventing any other use.

ControlDetail
No credentials in the imageThe account ships locked. A unique random password is generated at first boot and displayed above the login prompt.
Single allowed commandli-bootstrap has no general sudo rights. Its entire sudo permission is one path: sudo provision-once. Any other command is denied.
Root-owned wrapperprovision-once is owned by root and not writable by li-bootstrap, so the script cannot be replaced to escalate permissions.
Run-once enforcementA sentinel file (/var/lib/lineinterpreter/provisioned) prevents provision-once from running twice. A re-provision requires an admin to explicitly remove it.
Self-destructing accountOn successful provisioning, provision-once deletes the li-bootstrap account with userdel -r. The account ceases to exist.
No pre-provisioning SSHopenssh-server is not installed in the base image. SSH access is only configured by provision.sh during provisioning.

Ubuntu Core 24 uses a read-only squashfs root filesystem. This means:

  • Core OS files cannot be modified at runtime — even by root.
  • Only cryptographically signed snaps can be installed, as defined by the model assertion. An attacker with full shell access cannot install arbitrary software.
  • Secure Boot is enabled — only the trusted bootchain can start the system.
  • AppArmor confines each snap application, restricting its access to the host system.

The following sysctl parameters are applied at boot and persist across reboots:

IP Forwarding & Redirects

  • IP forwarding disabled (IPv4 and IPv6) — the device cannot be used as a router
  • Packet redirect sending disabled
  • Source routing, ICMP redirects, and secure ICMP redirects rejected

Suspicious Traffic

  • Martian (impossible source) packets are logged
  • Broadcast ICMP requests ignored
  • Bogus ICMP error responses ignored
  • TCP SYN cookies enabled — mitigates SYN flood DoS attacks
  • Reverse Path Filtering enabled — rejects packets that don’t have a valid return route

IPv6

  • IPv6 is completely disabled. Edge devices have no IPv6 requirement, so eliminating it removes the entire IPv6 attack surface.

Memory Safety

  • ASLR (randomize_va_space = 2) — full address space layout randomisation, making memory-based exploits significantly harder.

Kernel modules that are unused on a kiosk device are blacklisted so they cannot be loaded:

CategoryModules
Unused filesystemscramfs, freevxfs, jffs2, hfs, hfsplus, squashfs, udf
Physical data exfiltrationusb-storage — disabled by provision.sh at the end of provisioning. Left enabled in the image so the dashboard snap bundle can be transferred via USB stick before SSH exists. Pass --keep-usb to skip disabling it.
Uncommon network protocolsdccp, sctp, rds, tipc

USB storage is left enabled in the base image because it is needed to transfer the provisioning bundle before SSH exists. provision.sh blocks usb access the end of provisioning (unless —keep-usb passed), so it takes effect on the next boot.


Core dumps can expose sensitive data (memory contents, credentials, keys) if an attacker triggers a crash and retrieves the resulting file.

  • Hard limit of 0 for all users — core dumps are forbidden
  • fs.suid_dumpable = 0 — SUID processes cannot produce core dumps
  • kernel.core_pattern = |/bin/false — any dump attempt is silently discarded

  • /root is restricted to 700 — no world or group access
  • /tmp has the sticky bit set (1777) — users cannot delete each other’s temporary files

If an attacker were to compromise li-bootstrap credentials before provisioning:

  • They can run exactly one command as root: provision-once
  • That command only executes a specific script at a specific path — it does not provide a shell
  • The immutable OS means they cannot install software, modify system files, or persist a backdoor
  • The model assertion means no unauthorised snaps can be installed
  • Post-provisioning, the account no longer exists

After provisioning, li-bootstrap is gone. The only remote access path is the li-updater account, used to deliver snap updates over SSH. If those credentials were compromised:

  • No interactive shell — every SSH connection is routed through a restricted ForceCommand wrapper (li-updater-shell). The user cannot run arbitrary commands.
  • Allow-listed commands only — the wrapper permits a small, explicit set: upload files to ~/, install a signed snap update, verify checksums, check snap version/logs, and basic diagnostics (top, df, ip addr, etc.). All other commands are denied.
  • Single sudo commandli-updater can only run sudo update-dashboard, which itself only accepts a path-validated .snap file from the home directory, requires a matching Snap Store–signed assertion file, and calls snap install. There is no path to a root shell.
  • AppArmor enforcement — the update-dashboard wrapper runs under an AppArmor enforce profile that grants read-only access to ~/li-updater/*.snap, and hard-denies access to /etc/shadow, sudoers files, SSH keys, and /root.
  • Snap Store signaturessnap ack validates the cryptographic assertion before installation. An attacker cannot install an arbitrary or modified snap package even with full li-updater access.
  • Password authentication disabledli-updater ships with a locked password; SSH access requires the authorised key configured during provisioning.