| 1 | [Unit] |
| 2 | Description=shithub web server |
| 3 | After=network-online.target postgresql.service |
| 4 | Wants=network-online.target |
| 5 | Requires=postgresql.service |
| 6 | |
| 7 | [Service] |
| 8 | Type=simple |
| 9 | User=shithub |
| 10 | Group=shithub |
| 11 | EnvironmentFile=/etc/shithub/web.env |
| 12 | # Run pending migrations BEFORE starting. ExecStartPre failure |
| 13 | # prevents the unit from starting (correct behavior for a bad |
| 14 | # migration). Recovery is documented in runbooks/upgrade.md. |
| 15 | ExecStartPre=/usr/local/bin/shithubd migrate up |
| 16 | ExecStart=/usr/local/bin/shithubd web |
| 17 | Restart=on-failure |
| 18 | RestartSec=2 |
| 19 | LimitNOFILE=65535 |
| 20 | |
| 21 | # Hardening — match systemd-analyze security defaults where they |
| 22 | # don't conflict with the app's needs (writes to data root, opens |
| 23 | # port 8080). |
| 24 | NoNewPrivileges=yes |
| 25 | ProtectSystem=strict |
| 26 | ProtectHome=yes |
| 27 | PrivateTmp=yes |
| 28 | ReadWritePaths=/data /var/lib/shithub |
| 29 | ProtectKernelTunables=yes |
| 30 | ProtectKernelModules=yes |
| 31 | ProtectKernelLogs=yes |
| 32 | ProtectControlGroups=yes |
| 33 | RestrictNamespaces=yes |
| 34 | RestrictRealtime=yes |
| 35 | # RestrictSUIDSGID intentionally OFF: `git init --bare --shared=group` |
| 36 | # (used by InitBare for every new bare repo) calls chmod g+s on the |
| 37 | # directories it creates so cross-user writes (shithub user via the |
| 38 | # HTTPS path, git user via SSH-git) inherit the shared group on new |
| 39 | # pack files. With RestrictSUIDSGID=yes the kernel returned EPERM and |
| 40 | # git emitted "Could not make .../branches/ writable by group", so |
| 41 | # repo creation broke for any new owner namespace whose parent dir |
| 42 | # wasn't pre-staged. shithubd doesn't write executable files to disk, |
| 43 | # so the security loss is small; the alternative (stripping |
| 44 | # --shared=group and chmod'ing perms by hand) re-introduces the |
| 45 | # original SR2 #287 bug pattern. |
| 46 | LockPersonality=yes |
| 47 | MemoryDenyWriteExecute=yes |
| 48 | SystemCallArchitectures=native |
| 49 | |
| 50 | [Install] |
| 51 | WantedBy=multi-user.target |