Originally created by: athanclark
All services share one Unix account, so a compromise of any of them reaches every secret and the journal group
- Severity: Low. This is defense in depth and needs code execution in one service first. It widens the reach of two network-facing parsers (the web server and the syslog receiver).
- Affected: Debian, Ubuntu, Fedora, Rocky and Arch (systemd units), FreeBSD (rc scripts), NixOS module.
- Location:
- deploy/debian-server/openchronology.service:10-13.
- openchronology-syslog.service:10-12, which runs as
User=openchronology with its own syslog.env.
- openchronology-journald.service:10-14, which adds
SupplementaryGroups=systemd-journal.
- deploy/debian-server/journald.env:20-21 (rendered in the env template at lines 211-214), where the SSH key is "readable by the openchronology user".
- postinst:24 and 35-40 (
/etc/openchronology 0750 root:openchronology, files 0640).
- nix/module.nix:60-64 and 306.
- Description: The web server, mail worker, syslog receiver (unauthenticated UDP/TCP parser) and journald follower all run as the same UID. Each env file is readable by that UID, so each process can read the others' secrets:
- openchronology.env: DATABASE_URL, AUTH_ENCRYPTION_KEY, RESEND_API_KEY, OAuth secrets, LDAP bind password, S3 keys, ADMIN_INITIAL_PASSWORD.
- syslog.env and journald.env: OCH_API_KEY.
- The journald SSH private key.
The journald follower also holds systemd-journal. With the same UID, and Yama ptrace_scope=0 (Debian's default), the web process can ptrace or write /proc/<pid>/mem of the follower and act with that group.
- Attack scenario:
- Precondition: code execution in the syslog receiver, for example through a parser bug reached over UDP from any host that can send to it.
- Steps: read /etc/openchronology/openchronology.env, then use DATABASE_URL (peer authentication as the same UID), AUTH_ENCRYPTION_KEY and the Resend key.
- Alternatively, from the web server: read journald_ssh_key and log into the remote log host, or inject into the journald follower and read the whole system journal.
- Impact: full platform compromise from the log receiver, and lateral movement to the remote log host.
- What defends against it today: the units use NoNewPrivileges, ProtectSystem=strict and ProtectHome. The receivers listen on loopback by default (syslog-receiver.mjs:137; syslog.env SYSLOG_HOST=127.0.0.1).
- Suggested fix: Run the syslog and journald units with
DynamicUser=yes, or as separate users (openchronology-syslog, openchronology-journald), each able to read only its own env file and key. Give each unit only the env file it needs (for example 0640 root:openchronology-syslog). Add ProtectProc=invisible, or at least PrivateUsers=, to the workers.
- Confidence: confirmed for file access. The ptrace path is likely and depends on the host's Yama setting.
From the security review of 2026-11-06 (commit 6776c44), finding L-14: hardening or information, filed publicly because it describes no way in. The exploitable findings are private security advisories.
🤖 Generated with Claude Code
https://claude.ai/code/session_011fFY8eruEi1xAkkMBni13B
Originally posted by: athanclark
Assessed while hardening deployment (#527). There's no small safe change here.
What it would take:
openchronology-logsuser in each package (Debianadduser, RPM%preuseradd, an Arch sysusers line);[#527] adds more systemd hardening to every unit in the meantime.
🤖 Generated with Claude Code
https://claude.ai/code/session_011fFY8eruEi1xAkkMBni13B
Related
Tickets:
#527Ticket changed by: athanclark