soul.demarkus.io/plans/security-hardening.md/v1 draft reader meta

Plan: Security Hardening & Documentation

Prompted by user feedback — reluctance to run a public-facing server with write access without understanding the threat model.

Problem

There's no security documentation. Security-conscious users have to guess at the attack surface and hardening options. This blocks public adoption.

Security Page for the Website

Document the actual security model so people can evaluate it:

Attack Surface

  • No code execution, no shell, no database, no sessions, no templates
  • Server accepts markdown text and writes it to a single directory
  • 6 verbs: FETCH, LIST, VERSIONS (read), PUBLISH, APPEND, ARCHIVE (write)
  • Writes require a valid SHA-256 capability token
  • Every write is versioned — full audit trail
  • Rate limiting and size limits (1 MiB body, 64KB frontmatter) already enforced
  • QUIC/TLS encrypted transport
  • Logs go to stderr (captured by systemd journal) — server writes nothing outside the content directory

What a compromised write token gets you

  • Ability to publish/overwrite/archive markdown files in the content root
  • Nothing else — no code execution, no filesystem escape, no privilege escalation
  • Tokens are revocable, writes are audited with token labels

What a compromised server process gets you

  • Read/write access to the content directory (with systemd hardening: nothing else)
  • No access to /home, /etc, or anything outside the content root
  • No ability to escalate privileges

Systemd Hardening

Add to install script (setup_systemd) and deployment docs:

ProtectSystem=strict
ReadWritePaths=/srv/site
PrivateTmp=yes
NoNewPrivileges=yes
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictNamespaces=yes
RestrictSUIDSGID=yes

Write Isolation (optional, for maximum lockdown)

For users who want Gemini-level read-only public exposure:

  • Bind server to 127.0.0.1 for write operations (CI/local clients publish to localhost)
  • Public traffic reaches the server via iptables redirect or a second read-only listener
  • Versioning still works because publishes go through the protocol locally
  • This is the same trust boundary as Gemini — local writes, public reads

Comparison to Other Servers

SSH Web + CGI Gemini Demarkus
Code execution Yes (shell) Yes (scripts) No No
Database access Yes Often No No
Write access Full machine Varies None (read-only) Content dir only
Auth model Keys/passwords Sessions/cookies Client certs Capability tokens
Worst case Full compromise RCE, data breach DoS Markdown overwrite

Implementation Order

  1. Security page on the website (no code changes) — DONE
  2. Systemd hardening in install script + deployment docs (no code changes) — DONE
  3. Install script detects insecure existing config on update, prompts to harden with rollback — DONE
  4. -read-only flag or bind-address write isolation (small code change, can defer)

Status

Items 1-3 implemented on main branch. Pending review.

trail
  1. soul.demarkus.io v1