deploy/systemd/gunnar.service
# gunnar on your own iron (roadmap phase 5).
#
# The same binary as the cluster form, under systemd instead of a kubelet. The
# hardening below is not decoration: it is the on-prem half of what the pod's
# `securityContext` says, written in the other syntax, so the two deployments
# are one product and not two.
#
# install -m 0644 deploy/systemd/gunnar.service /etc/systemd/system/
# install -m 0600 deploy/systemd/gunnar.env.example /etc/gunnar/gunnar.env
# $EDITOR /etc/gunnar/gunnar.env # the KEK goes here, and only here
# systemctl daemon-reload && systemctl enable --now gunnar
#
# `gunnar service install` writes this over D-Bus, in two files: the unit body,
# rendered from a Skidbladnir `ServiceSpec`, and the hardening block below,
# written as a `gunnar.service.d/10-hardening.conf` drop-in because Skidbladnir
# 0.1.12's spec models exactly ONE hardening directive and has no escape hatch
# for the other twenty-four. `gunnar service unit` prints both halves, and
# `service::tests::the_install_carries_every_hardening_line_the_repository_ships`
# compares them against THIS file, so the sentence above is checked rather than
# claimed. What the install does NOT take from here: the `--http-bind` and
# `--metrics-bind` arguments, and the `EnvironmentFile=` — an install has never
# written a KEK file, and giving it one is a different change from that one.
[Unit]
Description=gunnar — a git forge with an unbroken chain of custody
Documentation=https://github.com/vetra-se/gunnar
After=network-online.target
Wants=network-online.target
[Service]
Type=exec
ExecStart=/usr/local/bin/gunnar serve \
--ssh-bind=0.0.0.0:2222 \
--http-bind=0.0.0.0:8080 \
--grpc-bind=127.0.0.1:50051 \
--metrics-bind=127.0.0.1:9090
# **The KEK lives in this file and nowhere else.** 0600, root-owned, and NOT in
# an `Environment=` line — those are readable to every user on the box through
# `systemctl show`.
EnvironmentFile=/etc/gunnar/gunnar.env
User=gunnar
Group=gunnar
DynamicUser=no
StateDirectory=gunnar
StateDirectoryMode=0700
WorkingDirectory=/var/lib/gunnar
Restart=on-failure
RestartSec=2s
# ── the hardening, matching the pod's securityContext ────────────────────────
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
ProtectClock=yes
ProtectHostname=yes
ProtectProc=invisible
RestrictSUIDSGID=yes
RestrictRealtime=yes
RestrictNamespaces=yes
LockPersonality=yes
MemoryDenyWriteExecute=yes
RemoveIPC=yes
UMask=0077
CapabilityBoundingSet=
AmbientCapabilities=
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources @obsolete
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
[Install]
WantedBy=multi-user.target