System Hardening

Overview

Hardening the OS itself: boot chain, kernel parameters, services, patches, and benchmarks.

A default Ubuntu install is built to boot and run, not to survive an attacker who already has a foothold. Hardening is the work of shrinking that gap: reducing what is exposed, restricting what each component can do, and keeping the system current so known bugs stop being free wins for anyone probing the box. This group treats the operating system itself as the target and walks through the controls that matter in the order you would apply them to a fresh host.

The mental model worth carrying is defense in depth. No single knob makes a machine secure. Instead you stack independent layers so that a bypass of one still leaves the attacker facing another. A patched kernel, a locked boot chain, sandboxed services, and a benchmark that tells you where the holes remain are four such layers, and this group covers each in turn.

The layers of OS hardening

Each layer defends a different stage of an attack. Reading them together shows why you want all four rather than picking a favorite.

LayerDefends againstCovered in
Patch managementExploitation of known CVEs in installed packages and the kernelPatch Management
Boot and kernel hardeningTampering with the boot chain and abuse of kernel interfacesBoot & Kernel Hardening
Systemd hardeningA compromised service escalating beyond its jobSystemd Hardening
BenchmarkingSilent configuration drift and unaudited gapsCIS Benchmark

The layers are ordered roughly by value for effort. Patching closes the largest number of real-world attack paths for the least work, so it comes first. Boot and kernel hardening raises the cost of privilege escalation. Service sandboxing contains the blast radius when something is compromised anyway. Benchmarking is the feedback loop that tells you the other three actually took effect.

Where the leverage is

Not every control earns its keep. A useful habit is to weigh each change against three questions: what does it stop, what does it cost to run, and how likely is it to break a workload. Automatic security patching scores well on all three and belongs on nearly every host. Kernel sysctl tweaks such as kernel.kptr_restrict are cheap and low risk. Disabling module autoloading is powerful but can break hardware, so it wants testing. This weighing is the difference between hardening and cargo-culting a checklist.

Trying it on the lab VM

Every page in this group is written against a disposable Ubuntu 24.04 LTS lab VM. The recommended setup is Multipass with a snapshot taken immediately after launch, so any change can be rolled back in seconds.

multipass launch 24.04 --name hardening-lab --memory 2G --disk 10G
multipass snapshot hardening-lab --name clean
multipass shell hardening-lab

Expected output:

Launched: hardening-lab
Snapshot taken: hardening-lab.clean
Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.8.0-generic x86_64)
ubuntu@hardening-lab:~$

A word on threat models

Hardening is only sensible relative to what you are defending against. A public-facing web server, an internal build host, and a laptop face different attackers with different goals, and applying every control everywhere wastes effort while breaking workloads. Before you start, name the threat: is it a remote attacker probing exposed services, a compromised dependency running as one of your users, or an insider with a shell trying to escalate. Most controls in this group defend the middle case (limiting what a foothold can become), which is why service sandboxing and kernel hardening get the most attention. Keep the threat model in mind as you read, and skip or adapt controls that do not fit yours.

If a change leaves the VM unbootable or unusable, which happens most often with boot and kernel edits, restore the snapshot and try again.

multipass restore hardening-lab.clean

Expected output:

This will delete the current instance state. Continue? (y/N) y
Snapshot restored: hardening-lab.clean

Practical Guidance

  1. Apply the layers in order of value for effort: patch first, then harden the boot chain and kernel, then sandbox services, and measure last.
  2. Snapshot the lab VM before every experiment so a broken change costs seconds to undo rather than a full rebuild.
  3. For every proposed control, ask what it stops, what it costs, and what it might break before applying it.
  4. Treat the benchmark as a feedback loop on the other layers, not a score to maximize for its own sake.
  5. Change one thing at a time and reboot where the change is boot-time, so you can attribute any breakage to a single edit.
  6. Document what you changed and why, so the next person (often future you) understands the reasoning behind a non-default setting.