ifcurl/forgejo/server-config/ifcurl-preview.service
Bruno Postle 275e74cce8 ifcurl-preview.service: fix cache path for non-root service user
Use CacheDirectory=ifcurl (systemd creates /var/cache/ifcurl owned by the
service user) and XDG_CACHE_HOME=/var/cache so platformdirs resolves to
that path.  Remove the /root/.cache/ifcurl ReadWritePaths entry which was
inaccessible to the unprivileged ifcurl user.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 21:48:58 +01:00

70 lines
2.5 KiB
Desktop File

[Unit]
Description=ifcurl preview service
Documentation=https://github.com/brunopostle/ifcurl
After=network.target
[Service]
Type=simple
User=ifcurl
Group=ifcurl
EnvironmentFile=-/etc/ifcurl/env
ExecStart=/usr/local/bin/ifcurl serve \
--host 127.0.0.1 \
--port 8000 \
--allowed-hosts git.example.com
Restart=on-failure
RestartSec=5
# Cache configuration — override in /etc/ifcurl/env:
# IFCURL_CACHE_MAX_GB=10 max bare-repo disk cache
# IFCURL_T2_MAX=8 max IFC blobs in memory
# IFCURL_T3_MAX=64 max selector results in memory
# IFCURL_SANDBOX_TIMEOUT=120 max seconds per render subprocess
# IFCURL_SANDBOX_MEMORY_MB=0 address-space cap per subprocess (0=unlimited)
# ---------------------------------------------------------------------------
# Sandbox hardening
#
# These directives apply to the service process and all child processes
# (including the render subprocesses spawned by sandbox.py). Together they
# limit the blast radius if a crafted IFC file achieves code execution inside
# a render subprocess.
#
# ProtectSystem=strict + ReadWritePaths: the service can only write to its own
# cache directory; the Forgejo data tree (/var/lib/forgejo, /etc/forgejo) is
# read-only from this unit's perspective.
#
# NoNewPrivileges + RestrictSUIDSGID: prevent privilege escalation via
# setuid binaries or capabilities.
#
# SystemCallFilter: allow the syscalls a Python/C-extension process needs
# (@system-service covers the common POSIX set; @process covers fork/clone
# which ifcopenshell's geometry iterator requires). execve/execveat are NOT
# in either set, so an attacker with code execution cannot exec a shell.
# The filter applies to child processes too (inherited across fork).
#
# RestrictAddressFamilies: the service only needs TCP/UDP to reach git hosts.
# AF_UNIX is excluded so a compromised subprocess cannot connect to the
# Forgejo Unix socket if one is present.
# ---------------------------------------------------------------------------
Environment=XDG_CACHE_HOME=/var/cache
CacheDirectory=ifcurl
NoNewPrivileges=yes
RestrictSUIDSGID=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/cache/ifcurl
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictNamespaces=yes
LockPersonality=yes
RestrictRealtime=yes
RestrictAddressFamilies=AF_INET AF_INET6
SystemCallArchitectures=native
SystemCallFilter=@system-service @process @files @io-event @network-io
SystemCallErrorNumber=EPERM
[Install]
WantedBy=multi-user.target