soul.demarkus.io:6309/plans/security-hardening.md/v4 complete reader meta

Plan: Security Hardening & Documentation — COMPLETE

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.

What Was Done

1. Security page on the website — DONE

  • docs/site/security/index.md + pages branch security.md
  • Attack surface, token compromise, process compromise analysis
  • Comparison table (SSH vs Web+CGI vs Gemini vs Demarkus)
  • Systemd hardening guide with verification command

2. Systemd hardening in install script + deployment docs — DONE

  • setup_systemd generates unit with ProtectSystem=strict, ReadWritePaths, NoNewPrivileges, etc.
  • Conditional ProtectHome (omitted when content root is under /home)
  • readlink -f to canonicalize content root before writing unit
  • Deployment docs updated with hardened systemd example

3. Install script detects insecure existing config on update — DONE

  • _do_update_inner checks for missing ProtectSystem in existing unit
  • Interactive prompt (defaults to yes, skips in non-interactive mode)
  • Backs up unit before modifying, rolls back if service fails to start
  • Points to public security docs URL

4. Read-only mode (-read-only flag) — DONE

  • DEMARKUS_READ_ONLY env var + -read-only flag on server
  • Handler rejects PUBLISH/APPEND/ARCHIVE with not-permitted status
  • Config accepts 1, true, yes as truthy values
  • Tests for handler rejection and config parsing

5. demarkus-publish CLI tool — DONE

  • server/cmd/demarkus-publish/main.go
  • Writes directly to versioned store on disk (same store.Write() as server)
  • Supports -body flag or stdin input
  • Detects duplicate content (no-op on unchanged)
  • Enables local publishing when server runs read-only

6. Read-only chroot install script — DONE

  • install-readonly.sh — separate script for maximum security deployments
  • Chroot structure: /srv/demarkus/{bin,content,tls}
  • Systemd unit with RootDirectory, ReadOnlyPaths=/, BindReadOnlyPaths=/dev/urandom
  • Installs demarkus-publish to /usr/local/bin for local publishing
  • SHA-256 checksum verification on downloads
  • Rejects dangerous root paths (/, /usr, /etc, etc.)
  • No auto firewall changes (user decides)

7. Cleanup

  • Removed redundant demarkus.service from repo root
  • Updated docs: reference (config table), server (read-only section), install (binary table, readonly option), deployment (security link)

Key Design Decisions

  • Shared store code: demarkus-publish calls store.Write() directly — same code path as the server. No duplication.
  • not-permitted not unauthorized: Read-only rejection is a server policy, not an auth failure.
  • Separate install script: Read-only chroot install is a different deployment model, kept separate to avoid complicating the main install script.
  • No iptables: Replaced fragile iptables write isolation with read-only mode + chroot, which is simpler and more secure.

Related documents

trail
  1. soul.demarkus.io:6309 v4