~/ ~/documents ~/software ~/pictures github (opens in new tab)

Hardening is Not an Accident

A secure system is not a happy coincidence. It is the result of deliberate, structured friction.

We like to think our systems are safe because we chose a good password or hid behind a firewall. In reality, security is about reducing the surface area of execution until only what is strictly necessary remains. Hardening isn’t a single switch; it is a series of constraints designed to make exploitation too expensive to pursue.

If you don’t explicitly configure your system to resist, it won’t.

The Core as a Fortress

The kernel is the ultimate prize for any attacker. If they compromise the kernel, they own the machine. That is why we don’t leave kernel defaults untouched:

Ephemeral State as a Shield

Most systems accumulate state like dust, leaving long-lived artifacts for attackers to discover. We reject filesystem longevity.

We use an ephemeral root filesystem. On every single boot, the root directory (/) is wiped clean (using automated Btrfs subvolume rollback), resetting the system back to its declared, pristine configuration. If malware finds a way to write to a system directory, it is obliterated on the next reboot.

Only explicitly declared, stateful paths are allowed to survive, bound securely to our /persist volume. If it isn’t defined in our impermanence configuration, it is temporary.

We complement this stateless root with strict operational boundaries:

Sandbox the Execution

Securing the system is pointless if we allow user-facing applications to run unconstrained.

We enforce AppArmor system-wide. Every application must operate within a strict jail. If a web browser or media player is compromised, AppArmor ensures it cannot wander outside its designated profile to read private keys or scan your local network. We load default system profiles and cache them at boot to keep boundaries rigid without sacrificing performance.

To defend against active file compromise, we supplement AppArmor with on-access antivirus scanning via ClamAV, continuously monitoring user ingestion points like /home/user/Downloads to neutralize threats the instant they hit the filesystem.

The Privilege Fallacy

Sudo is a relic of an era when we trusted too much. It grants blanket access and leaves a massive footprint. We can do better.

In a modern, secure setup, we disable sudo entirely.

Instead, we use systemd’s run0 and Polkit for fine-grained, policy-driven authorization. When you request elevated privileges, the system should ask who you are. We enforce password requirements for the wheel group on every elevation. If you want root access, you write your password. No exceptions, no shortcuts.

To cement the elimination of default administrative access, the root user’s password login is completely disabled (hashedPassword = null). Direct console access is blocked; administrative access is strictly a policy-driven, authenticated transition.

The Gates Are Locked

Brute-force is a basic threat, but it is highly effective against lazy configurations. We close that door by combining password quality with strict authentication policies:

Centralized Zero-Trust Identity

On an ephemeral root filesystem, local user directories and static credentials are an operational liability and a security vector for configuration drift. We reject localized user databases.

We enforce a centralized, cryptographically secured identity model:

No Whispers on the Wire

SSH is your front door. If you leave it weak, someone will walk in. Hardening SSH is about eliminating legacy choices and forcing modern cryptographic standards:

Hardware-Rooted Identity

Cryptographic keys stored in software are only as secure as the system memory they live in. If memory is compromised, your identity is stolen.

We ground our identity in hardware. By enabling TPM 2.0 and PKCS#11 support, we bind cryptographic operations directly to physical silicon. Private keys are generated or stored on hardware security tokens and the motherboard’s security module, ensuring they can never be exported or read in plain text from disk or RAM.

We extend this hardware root of trust to physical authentication using Yubikeys:

Modern Declarative Firewalls

Traditional firewalls are often complex, brittle legacy iptables scripts. We reject this.

We deploy a declarative firewall using nftables as the modern, high-performance packet-filtering backend. The default policy is simple: drop everything except what is explicitly authorized. To support mesh networking and internal services securely, we only dynamically trust inbound traffic arriving over secure, authenticated Tailscale VPN interfaces.

Physical Boundaries & USB Protection

No amount of software-level hardening matters if a rogue device can be plugged directly into your machine. Physical ports are a major vector for DMA (Direct Memory Access) and BadUSB attacks.

We run USBGuard to block unauthorized devices at the hardware bus-level. The policy defaults to blocking all incoming USB connections. Only explicitly authorized internal, hardwired interfaces (such as keyboards, mouse pads, and system-level Bluetooth modules) are whitelisted, alongside physical Yubikeys matching Vendor ID 1050:* to ensure authentication tokens function seamlessly while blocking all rogue peripherals instantly.

Declarative Secrets & Deployment Safety

Hardening configurations is pointless if your secrets are committed in plain text to a Git repository. More importantly, we must ensure they never end up in the globally readable Nix store.

We integrate sops-nix to encrypt all system secrets directly inside the source repository. Decryption happens dynamically at activation time on the target host using host-specific private SSH keys (e.g., ssh_host_ed25519_key) mapped natively via the modern age format. Because decryption occurs at runtime rather than during evaluation or build time, this completely prevents plaintext secrets from being baked into and exposed in the world-readable /nix/store.

To prevent misconfigurations, we embed custom assertions at compile time. The system will actively block deployment unless security preconditions are fully satisfied (for example, refusing build completion if a host secrets file is deployed without a master user password being securely declared).

Living Proof of Security

Security is a dynamic state that must be continuously verified. Auditing provides the “proof of life” for your policies, ensuring that the intended hardening measures are actually enforced and have not drifted over time. A secure system must be verifiable. We treat logs not just as debugging tools, but as an append-only, tamper-evident audit trail.

By centralizing logs via a secure transport (e.g., encrypted syslog or remote journald forwarding), we ensure that even if an attacker gains root access to a local machine, they cannot scrub their footprint. The audit trail lives off-host, creating a definitive record of system events that exists independently of the hardened target.

In a declarative system like NixOS, auditing is shifted “left.” We do not just audit the running system; we audit the specification of the system. Our entire audit logging configuration is defined in Nix and automatically deployed across all production hosts. Our build pipeline automatically validates that security-critical sysctl settings, AppArmor profiles, and file permissions are defined exactly as intended. If a change deviates from the hard-coded security baseline, the CI/CD pipeline fails, preventing the deployment of a weakened configuration. Simultaneously, automated agents periodically reconcile the running state against the declared state. Any unauthorized manual change to system binaries or critical configuration files triggers an immediate alert and a forced revert to the pristine, defined state.

We deploy 24 audit rules across 9 categories of critical system operations, each declaratively configured and automatically enabled on all non-ephemeral hosts. Every process invocation via execve is logged, capturing the complete command chain and execution context—the forensic foundation for detecting malicious code execution or unauthorized privilege escalation. All modifications to file permissions (chmod, fchmod), ownership (chown, fchown), and extended attributes (setxattr, lsetxattr, etc.) are logged. Critical system databases (/etc/passwd, /etc/group, /etc/shadow) are monitored for write access, and failed file access attempts (EACCES, EPERM) are explicitly captured to detect brute-force or permission-probing attacks. Kernel module load, finalize, and delete operations are logged, with the /etc/audit/ directory and /var/log/audit/ logs themselves under write-access monitoring to prevent tampering. All login and session lifecycle events are captured via /var/log/lastlog, /var/run/utmp, /var/log/wtmp, and /var/log/btmp. Hostname and domain name changes (sethostname, setdomainname) are logged, along with modifications to /etc/hosts and /etc/issue. System-level mount and unmount operations are logged to detect unauthorized filesystem operations or rootkit-level attacks attempting to bind malicious filesystems.

Real-time observability auditing must also be actionable. We implement an “audit-to-alert” loop using the Linux Audit framework (auditd), monitoring sensitive system calls—such as execve, ptrace, and module loading—specifically looking for patterns that deviate from normal system activity. The audit daemon is configured with strict capacity management: if disk usage crosses 10% of available space, a syslog alert is generated. If usage approaches the administrative limit (5%), critical alerts ensure the audit system never silently fails due to space exhaustion. Because our identity is hardware-rooted (e.g., via Yubikeys), the audit subsystem correlates physical token removals with session lifecycle events. A security-critical event, such as a user elevation attempt followed by the physical removal of an authentication token, is treated as a high-severity incident, triggering an automated session lock and a security notification.

Audit logs are automatically rotated to prevent disk space exhaustion while maintaining forensic history: the audit daemon rotates logs at 100MB per file, retaining 10 rotated archives. Audit logs persist across reboots via our impermanence configuration, stored in /var/log/. The systemd journal enforces strict retention limits (1 day retention, 100MB maximum system journal, 50MB runtime journal) to prevent log-based denial-of-service attacks while maintaining recent operational history. Auditing provides the definitive proof that our declared hardening policies are not merely theoretical—they are operationally enforced, continuously verified, and persistently recorded.

Continuous CVE & Supply Chain Auditing

Hardening a host at rest is only half the battle. If a system closure contains packages with known vulnerabilities (CVEs), runtime constraints can only mitigate, not prevent, exploitation. Security auditing must be automated, declarative, and executed continuously before system closures ever reach physical hardware.

In our CI/CD pipeline, every host system configuration is compiled to its complete top-level Nix store derivation (.config.system.build.toplevel) and rigorously audited prior to deployment:

By integrating continuous vulnerability scanning directly into the Flake deployment workflow, a system image with unresolved CVEs is rejected in CI before it can ever be booted on a machine.

Respecting Reality

Hardening is not a static checklist or a one-time configuration pass; it is an ongoing posture of deliberate friction.

A secure system demands defense in depth across every layer: from kernel sysctl constraints and ephemeral root filesystems, to hardware-backed identity, physical USB bus isolation, and automated supply chain auditing in CI. When every open service, unconstrained process, and legacy default is treated as an explicit policy decision, security stops being a matter of faith.

By enforcing these boundaries declaratively, we eliminate configuration drift and human error. Hardening is how we bring physical and operational reality into uncompromising alignment with our intentions.