mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-28 21:17:36 +00:00
Some checks failed
License Headers / check (push) Has been cancelled
The board ran a hand-created unhardened ROOT sbxwaf on :8085 while the package shipped only a worker@ fan-out that crash-looped (panic on the root-only cookie-audit log) and, as secubox-waf + RuntimeDirectory=secubox, re-chowned the shared /run/secubox on every crash-restart — breaking every secubox-user socket bind (profiles 502s). Ship ONE hardened unit (secubox-waf-ng.service, :8085, User=secubox-waf, full sandbox, NO RuntimeDirectory — sbxwaf only connects to waker.sock). postinst asserts the perms a non-root sbxwaf needs (haproxy-routes.json 0644, cookie-audit ledger writable by secubox-waf), disables leftover worker@ units, drops the /etc override. Validated live on gk2: 264 routes, real routing, admin/gitea/billets/yacy 200; a WAF restart no longer touches /run/secubox and profiles stays up. Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
93 lines
3.7 KiB
Desktop File
93 lines
3.7 KiB
Desktop File
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
|
# SecuBox-Deb :: secubox-waf-ng — Go WAF single reverse-proxy unit (#896 reconcile)
|
|
#
|
|
# Reconcile of the WAF-ng drift (2026-07-21): the board ran a HAND-CREATED,
|
|
# UNHARDENED root unit on :8085 (HAProxy srv0 -> 127.0.0.1:8085) while the
|
|
# package shipped only a worker@ fan-out template that crash-looped (it panicked
|
|
# on the cookie-audit log — writable only by root — and, running as secubox-waf
|
|
# with RuntimeDirectory=secubox, each crash-restart re-chowned the SHARED
|
|
# /run/secubox, breaking every secubox-user service's socket bind). This single
|
|
# unit REPLACES both: one hardened, non-root sbxwaf on :8085, reproducible from
|
|
# source. See docs / issue #896.
|
|
#
|
|
# KEY DIFFERENCES from the (retired) worker@ template:
|
|
# * single instance on :8085 (matches the live HAProxy backend), NOT an
|
|
# nft-DNAT fan-out to :8081/:8082 (that never actually ran);
|
|
# * single --vhost-signals file (secubox-sleeper's reader falls back to the
|
|
# legacy non-@ path);
|
|
# * NO RuntimeDirectory=secubox — sbxwaf only CONNECTS to the waker socket
|
|
# (/run/secubox/waker.sock) as a client; it creates nothing under
|
|
# /run/secubox, so it must not take ownership of the shared dir. /run/secubox
|
|
# stays in ReadWritePaths so the client connect works under ProtectSystem.
|
|
#
|
|
# PREREQUISITES the postinst asserts (so a non-root sbxwaf can read them):
|
|
# * /etc/secubox/waf/haproxy-routes.json readable (0644 — [host,port] pairs,
|
|
# not secret); root-only 0600 makes sbxwaf load 0 routes -> 421 everything;
|
|
# * /var/log/secubox/cookie-audit/server.jsonl writable by secubox-waf.
|
|
|
|
[Unit]
|
|
Description=SecuBox WAF-NG Go reverse-proxy (single instance, port 8085)
|
|
Documentation=https://github.com/CyberMind-FR/secubox-deb/issues/896
|
|
After=network.target
|
|
ConditionPathExists=/usr/sbin/sbxwaf
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=secubox-waf
|
|
Group=secubox-waf
|
|
|
|
ExecStart=/usr/sbin/sbxwaf \
|
|
--listen 127.0.0.1:8085 \
|
|
--routes /etc/secubox/waf/haproxy-routes.json \
|
|
--rules /etc/secubox/waf/waf-rules.json \
|
|
--on-demand-vhosts /etc/secubox/waf/on-demand-vhosts.json \
|
|
--vhost-signals /var/cache/secubox/waf/vhost-signals.json \
|
|
--threat-log /var/log/secubox/waf/waf-threats.log \
|
|
--cookie-audit-log /var/log/secubox/cookie-audit/server.jsonl \
|
|
--media-cache-dir /var/cache/secubox/waf/media \
|
|
--crowdsec-url http://10.100.0.1:8080
|
|
|
|
# --crowdsec-url is the real CrowdSec LAPI (LXC-bridge gateway 10.100.0.1:8080),
|
|
# NOT 127.0.0.1:8080 (the nft DNAT VIP that would self-loop into this proxy).
|
|
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
|
|
# ── Hardening ─────────────────────────────────────────────────────────────────
|
|
# Same posture as the worker@ template (secubox-mesh convention, §6 CSPN), minus
|
|
# RuntimeDirectory (see header). /run/secubox is READ-WRITE so the client connect
|
|
# to waker.sock works under ProtectSystem=strict.
|
|
NoNewPrivileges=yes
|
|
ProtectSystem=strict
|
|
ProtectHome=yes
|
|
PrivateTmp=yes
|
|
PrivateDevices=yes
|
|
ReadOnlyPaths=/etc/secubox
|
|
ReadWritePaths=/var/log/secubox/waf /var/log/secubox/cookie-audit /var/cache/secubox/waf /run/secubox
|
|
|
|
CapabilityBoundingSet=
|
|
AmbientCapabilities=
|
|
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
|
|
|
ProtectKernelTunables=yes
|
|
ProtectKernelModules=yes
|
|
ProtectKernelLogs=yes
|
|
ProtectControlGroups=yes
|
|
RestrictNamespaces=yes
|
|
LockPersonality=yes
|
|
RestrictSUIDSGID=yes
|
|
RestrictRealtime=yes
|
|
|
|
SystemCallArchitectures=native
|
|
SystemCallFilter=@system-service
|
|
SystemCallFilter=~@privileged @resources
|
|
MemoryDenyWriteExecute=yes
|
|
|
|
MemoryHigh=256M
|
|
MemoryMax=384M
|
|
TasksMax=512
|
|
RuntimeMaxSec=12h
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|