| 1 | [Unit] |
| 2 | Description=lchat router (TLS terminator + host router) |
| 3 | After=network-online.target |
| 4 | Wants=network-online.target |
| 5 | |
| 6 | [Service] |
| 7 | # Optional overrides, e.g. LCHAT_FALLBACK_URL (where unknown hosts redirect |
| 8 | # instead of 404 — pairs with a wildcard DNS record; see cloud-provisioner.md). |
| 9 | EnvironmentFile=-/data/router.env |
| 10 | ExecStart=/opt/lchat/lchat router --registry /data/registry.db --acme-cache /data/acme |
| 11 | # add-instance triggers `systemctl reload lchat-router` to re-read the route table. |
| 12 | ExecReload=/bin/kill -HUP $MAINPID |
| 13 | Restart=always |
| 14 | RestartSec=1 |
| 15 | |
| 16 | # --- hardening (see docs/instance-router.md § Hardening) --- |
| 17 | User=lchat |
| 18 | Group=lchat |
| 19 | # The router is the ONLY unit that binds privileged ports (:80 + :443); this cap |
| 20 | # lets it do so as a non-root user. Every other unit binds a unix socket only. |
| 21 | AmbientCapabilities=CAP_NET_BIND_SERVICE |
| 22 | CapabilityBoundingSet=CAP_NET_BIND_SERVICE |
| 23 | NoNewPrivileges=yes |
| 24 | ProtectSystem=strict |
| 25 | ProtectHome=yes |
| 26 | ReadWritePaths=/data /run/lchat |
| 27 | PrivateTmp=yes |
| 28 | PrivateDevices=yes |
| 29 | ProtectClock=yes |
| 30 | ProtectHostname=yes |
| 31 | ProtectKernelTunables=yes |
| 32 | ProtectKernelModules=yes |
| 33 | ProtectKernelLogs=yes |
| 34 | ProtectControlGroups=yes |
| 35 | ProtectProc=invisible |
| 36 | RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX |
| 37 | RestrictNamespaces=yes |
| 38 | RestrictRealtime=yes |
| 39 | RestrictSUIDSGID=yes |
| 40 | LockPersonality=yes |
| 41 | # The Go binary never maps writable+executable memory, so this is safe (unlike the |
| 42 | # Node sidecar, whose JIT needs it — see lchat-bot@.service). |
| 43 | MemoryDenyWriteExecute=yes |
| 44 | SystemCallFilter=@system-service |
| 45 | SystemCallErrorNumber=EPERM |
| 46 | SystemCallArchitectures=native |
| 47 | MemoryMax=256M |
| 48 | TasksMax=256 |
| 49 | |
| 50 | [Install] |
| 51 | WantedBy=multi-user.target |
| 52 | |