fix: ACME ownership self-heal + daily timer, apply-all force, firewall baseline re-stamp
acme:
- acme.sh chmods its tree to owner-only (700/600) every run, which
broke the two-user model: a tree left owner-only by one user made
every acme.sh call of the other exit 2
- normalize_acme_home() reopens group access (sudo chmod g+rwX,
files only — setgid dirs trip RestrictSUIDSGID); _run_acme_preflight
is the choke point before every daemon acme.sh call + startup
- acme service now runs as the daemon user; --log persists the raw CA
transcript; SYS_LOG=6 journals manual issue/renew runs
- timer daily-only: two runs/day landed inside ZeroSSL's 24h
validation backoff (Retry-After: 86400) — a permanent renewal lockout
- _collect_acme no longer raises on cert-list failure; reports
status.error (AcmeState.status) so the certs page can surface it
firewall: re-stamp the applied baseline on live zone mutations
(interfaces/services/rich-rules/masquerade/forward-ports) so cancel-all
reverts to post-mutation state, not a stale install-era snapshot;
set_masquerade syncs the declarative config for existing zones;
add_forward_port records toaddr only with toport
status: apply-all accepts {"force": true} (forwarded to the firewall
apply only); ApplyConfirm force checkbox; applyResultToasts() — the
errors map wins over the 200; ActionButton checks errors before the
success toast; dashboard uses ApplyConfirm
system_import: drift re-imports carry the existing apply-meta; first
import stamps the adopted content as applied (it is the running state)
— no phantom pending changes
nginx: get_config only re-saves when migration actually changed the
config (no more owner/mtime churn on every read)
install: repair mis-owned top-level system dirs (tmpfiles
unsafe-path-transition), warn with a full-repair command for deeper
mis-ownership
daemon/server: loop.get_exception_handler() (aiohttp API fix)
tests: 888 pytest + 24 node passing; ruff clean
This commit is contained in:
@@ -3,8 +3,15 @@ Description=Vacuum Wall ACME Certificate Renewal
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User={{ USER_NAME }}
|
||||
# Run as the daemon user, not the WebUI user: it owns the project tree
|
||||
# (and the ACME home) in production, and acme.sh chmods its config home
|
||||
# to 700 and its keys/confs to 600 on every run. Running as the WebUI
|
||||
# user left the tree unreadable to the daemon (and vice versa) whenever
|
||||
# the two users' runs interleaved.
|
||||
User={{ USER_DAEMON_NAME }}
|
||||
WorkingDirectory={{ PROJECT_DIR }}
|
||||
Environment=ACME_HOME={{ PROJECT_DIR }}/data/acme
|
||||
Environment=HOME={{ PROJECT_DIR }}
|
||||
ExecStart={{ ACME_HOME }}/acme.sh --cron --home {{ ACME_HOME }} --config-home {{ ACME_HOME }}
|
||||
# --log: persistent on-disk transcript of the raw CA exchange (journald
|
||||
# captures stdout regardless; the file survives journal retention).
|
||||
ExecStart={{ ACME_HOME }}/acme.sh --cron --home {{ ACME_HOME }} --config-home {{ ACME_HOME }} --log
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
Description=Vacuum Wall ACME Certificate Renewal Timer
|
||||
|
||||
[Timer]
|
||||
# Daily only: ZeroSSL backs off a failed validation for 24h per domain
|
||||
# (Retry-After: 86400). With two runs a day every attempt landed inside
|
||||
# the previous attempt's backoff window, re-arming it — a permanent
|
||||
# renewal lockout. Attempts >24h apart are required for the backoff to
|
||||
# ever expire (acme.sh discussion #6419).
|
||||
OnCalendar=*-*-* 00:00:00
|
||||
OnCalendar=*-*-* 12:00:00
|
||||
Persistent=true
|
||||
RandomizedDelaySec=300
|
||||
|
||||
|
||||
@@ -16,6 +16,12 @@ TimeoutStopSec=15
|
||||
Environment=PATH=/usr/local/bin:/usr/bin
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
Environment=ACME_HOME={{ PROJECT_DIR }}/data/acme
|
||||
# acme.sh routes its _info/_err lines through logger(1) -> journald when
|
||||
# SYS_LOG is set (default: off). This journals manual issue/renew runs
|
||||
# in real time under this unit, whose subprocess stdout is otherwise
|
||||
# captured by the daemon and never seen by the journal.
|
||||
# Levels: 3=error, 6=info, 7=debug.
|
||||
Environment=SYS_LOG=6
|
||||
Environment=HOME={{ PROJECT_DIR }}
|
||||
|
||||
# Runtime directories created before namespace setup. ProtectSystem=strict
|
||||
|
||||
Reference in New Issue
Block a user