h0ffmann/skills
Star
Cite

APA

h0ffmann. (2026). skills [Computer software]. https://github.com/h0ffmann/skills

BibTeX

@software{h0ffmann_skills_2026,
  author = {h0ffmann},
  title = {skills},
  year = {2026},
  url = {https://github.com/h0ffmann/skills},
  license = {MPL-2.0},
  note = {ORCID 0009-0009-1056-7661}
}

Source: CITATION.cff

systemd-hardening

Harden a systemd service written in Nix (systemd.services.<name>) with sandboxing, capability limits and syscall filters, then prove it still works. Use when you write or review a NixOS service module.

nixsystemdsecurity

← Back to all skills · SKILL.md on GitHub

# systemd-hardening

A service gets only what it needs. Start from a tight base. Open each item that the service
really uses, and say why in a comment.

## Never harden these

`sshd`, `qemu-guest-agent` and `cloud-init` keep the machine reachable. Do not tighten them.

## The base

```nix
serviceConfig = {
  DynamicUser = true;                 # or a fixed User/Group when state must keep its owner
  StateDirectory = "<name>";          # /var/lib/<name>, owned by the service
  NoNewPrivileges = true;
  ProtectSystem = "strict";
  ProtectHome = true;
  PrivateTmp = true;
  PrivateDevices = true;
  ProtectKernelTunables = true;
  ProtectKernelModules = true;
  ProtectKernelLogs = true;
  ProtectControlGroups = true;
  ProtectClock = true;
  ProtectHostname = true;
  ProtectProc = "invisible";
  ProcSubset = "pid";
  RestrictNamespaces = true;
  RestrictRealtime = true;
  RestrictSUIDSGID = true;
  LockPersonality = true;
  MemoryDenyWriteExecute = true;      # remove for a JIT (node, JVM)
  RemoveIPC = true;
  CapabilityBoundingSet = "";
  AmbientCapabilities = "";
  RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
  SystemCallArchitectures = "native";
  SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
  UMask = "0077";
  MemoryMax = "256M";                 # a value that fits the host budget
  CPUQuota = "50%";
};
```

## Open only what is necessary

| The service needs | Add |
|---|---|
| a port below 1024 | `CapabilityBoundingSet` and `AmbientCapabilities` = `CAP_NET_BIND_SERVICE` |
| no network | `PrivateNetwork = true`, and `RestrictAddressFamilies = [ "AF_UNIX" ]` |
| a secret file | `LoadCredential = "env:/var/lib/<name>-secrets/<name>.env"` |
| a path to write | `ReadWritePaths`, the smallest path |
| a JIT | remove `MemoryDenyWriteExecute`, and say why |

## Prove it

1. Build the system.
2. Score the unit on a test machine or in a VM check: `systemd-analyze security <name>`. Put
   the score before and after in the PR.
3. Run the service and its VM test. Read the journal for `EPERM`, `EACCES` and `SIGSYS`. Each
   one names an item that you must open.
4. Never remove a hardening item only because a test fails. Find the one permission that the
   service needs and open only that.