no-mistakes(review): harden dsh token reload retry, legacy bypass, cookie verification
This commit is contained in:
@@ -19,8 +19,9 @@
|
|||||||
# 2. records it in /etc/dsh-web/launch-token,
|
# 2. records it in /etc/dsh-web/launch-token,
|
||||||
# 3. regenerates the nginx include /etc/dsh-web/nginx-login.conf (the
|
# 3. regenerates the nginx include /etc/dsh-web/nginx-login.conf (the
|
||||||
# `proxy_pass ...?token=` line consumed by /dsh-web-login),
|
# `proxy_pass ...?token=` line consumed by /dsh-web-login),
|
||||||
# 4. validates with `nginx -t` and reloads ONLY when the token changed,
|
# 4. reloads nginx ONLY when the token differs from the token nginx actually
|
||||||
# rolling the include back if validation fails,
|
# loaded (tracked in an applied-state stamp written only after a successful
|
||||||
|
# reload), rolling the include back on failure so the next run retries,
|
||||||
# 5. removes the legacy unauthenticated :8081 endpoint if it ever reappears.
|
# 5. removes the legacy unauthenticated :8081 endpoint if it ever reappears.
|
||||||
#
|
#
|
||||||
# Idempotent and safe to run at any time (systemd ExecStartPost or timer).
|
# Idempotent and safe to run at any time (systemd ExecStartPost or timer).
|
||||||
@@ -31,6 +32,7 @@ PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|||||||
JOURNAL_UNIT="dsh-web.service"
|
JOURNAL_UNIT="dsh-web.service"
|
||||||
TOKEN_FILE="/etc/dsh-web/launch-token"
|
TOKEN_FILE="/etc/dsh-web/launch-token"
|
||||||
INCLUDE_FILE="/etc/dsh-web/nginx-login.conf"
|
INCLUDE_FILE="/etc/dsh-web/nginx-login.conf"
|
||||||
|
STAMP_FILE="/etc/dsh-web/nginx-login.conf.applied"
|
||||||
SITE_ENABLED="/etc/nginx/sites-enabled/dsh"
|
SITE_ENABLED="/etc/nginx/sites-enabled/dsh"
|
||||||
LEGACY_8081="/etc/nginx/sites-enabled/dsh.token"
|
LEGACY_8081="/etc/nginx/sites-enabled/dsh.token"
|
||||||
STASH_DIR="/etc/nginx/sites-available"
|
STASH_DIR="/etc/nginx/sites-available"
|
||||||
@@ -44,16 +46,16 @@ die() { printf 'capture-dsh-token: ERROR: %s\n' "$*" >&2; exit 1; }
|
|||||||
# It bypassed Authentik entirely (listened on 0.0.0.0:8081 with no auth_request)
|
# It bypassed Authentik entirely (listened on 0.0.0.0:8081 with no auth_request)
|
||||||
# and must never come back. Stash it rather than delete so it is auditable.
|
# and must never come back. Stash it rather than delete so it is auditable.
|
||||||
if [ -e "$LEGACY_8081" ] || [ -L "$LEGACY_8081" ]; then
|
if [ -e "$LEGACY_8081" ] || [ -L "$LEGACY_8081" ]; then
|
||||||
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
|
TS="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||||
STASHED="$STASH_DIR/dsh.token.disabled-$STAMP"
|
STASHED="$STASH_DIR/dsh.token.disabled-$TS"
|
||||||
mv "$LEGACY_8081" "$STASHED"
|
mv "$LEGACY_8081" "$STASHED"
|
||||||
if nginx -t >/dev/null 2>&1; then
|
if ! nginx -t >/dev/null 2>&1; then
|
||||||
nginx -s reload
|
die "nginx config test failed after disabling $LEGACY_8081 (kept disabled at $STASHED). Fix the nginx config and rerun; the legacy :8081 endpoint will NOT be restored."
|
||||||
log "removed legacy :8081 endpoint -> $STASHED"
|
|
||||||
else
|
|
||||||
mv "$STASHED" "$LEGACY_8081"
|
|
||||||
die "nginx config test failed after removing $LEGACY_8081; restored it"
|
|
||||||
fi
|
fi
|
||||||
|
if ! nginx -s reload; then
|
||||||
|
die "nginx reload failed after disabling $LEGACY_8081 (kept disabled at $STASHED). Fix nginx and rerun; the legacy :8081 endpoint will NOT be restored."
|
||||||
|
fi
|
||||||
|
log "removed legacy :8081 endpoint -> $STASHED"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── 1. Read the latest launch token from the RUNNING service ────────────────
|
# ── 1. Read the latest launch token from the RUNNING service ────────────────
|
||||||
@@ -107,9 +109,21 @@ NEW_INCLUDE="$(mktemp "$INCLUDE_FILE.XXXXXX")"
|
|||||||
printf 'proxy_pass http://127.0.0.1:3080/?token=%s;\n' "$TOKEN" > "$NEW_INCLUDE"
|
printf 'proxy_pass http://127.0.0.1:3080/?token=%s;\n' "$TOKEN" > "$NEW_INCLUDE"
|
||||||
chmod 600 "$NEW_INCLUDE"
|
chmod 600 "$NEW_INCLUDE"
|
||||||
|
|
||||||
if [ -f "$INCLUDE_FILE" ] && cmp -s "$NEW_INCLUDE" "$INCLUDE_FILE"; then
|
# The stamp records the token nginx actually loaded. Comparing against it (not
|
||||||
rm -f "$NEW_INCLUDE"
|
# the on-disk include) means a failed or interrupted reload is retried on the
|
||||||
log "token unchanged; nginx not reloaded"
|
# next run instead of being mistaken for success.
|
||||||
|
APPLIED=""
|
||||||
|
[ -f "$STAMP_FILE" ] && APPLIED="$(cat "$STAMP_FILE" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [ "$APPLIED" = "$TOKEN" ]; then
|
||||||
|
if [ -f "$INCLUDE_FILE" ] && cmp -s "$NEW_INCLUDE" "$INCLUDE_FILE"; then
|
||||||
|
rm -f "$NEW_INCLUDE"
|
||||||
|
log "token unchanged; nginx not reloaded"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
mv "$NEW_INCLUDE" "$INCLUDE_FILE"
|
||||||
|
chmod 600 "$INCLUDE_FILE"
|
||||||
|
log "include file repaired to match the token nginx already serves"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -132,10 +146,23 @@ if ! nginx -t >/dev/null 2>&1; then
|
|||||||
fi
|
fi
|
||||||
die "nginx config test failed; previous include restored"
|
die "nginx config test failed; previous include restored"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! nginx -s reload; then
|
||||||
|
if [ -n "$RESTORE" ]; then
|
||||||
|
mv "$RESTORE" "$INCLUDE_FILE"
|
||||||
|
else
|
||||||
|
rm -f "$INCLUDE_FILE"
|
||||||
|
fi
|
||||||
|
die "nginx reload failed; previous include restored; will retry next run"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -n "$RESTORE" ]; then
|
if [ -n "$RESTORE" ]; then
|
||||||
rm -f "$RESTORE"
|
rm -f "$RESTORE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
nginx -s reload
|
printf '%s\n' "$TOKEN" > "$STAMP_FILE.tmp"
|
||||||
|
chmod 600 "$STAMP_FILE.tmp"
|
||||||
|
mv "$STAMP_FILE.tmp" "$STAMP_FILE"
|
||||||
|
|
||||||
log "token changed; nginx reloaded"
|
log "token changed; nginx reloaded"
|
||||||
log "login endpoint: https://tankodhs.sysloggh.net/dsh-web-login (Authentik-gated)"
|
log "login endpoint: https://tankodhs.sysloggh.net/dsh-web-login (Authentik-gated)"
|
||||||
|
|||||||
+24
-4
@@ -295,7 +295,9 @@ proxy_pass http://127.0.0.1:3080/?token=<TOKEN>;
|
|||||||
`scripts/capture-dsh-token.sh`) reads the latest launch token from the journal
|
`scripts/capture-dsh-token.sh`) reads the latest launch token from the journal
|
||||||
**of the service's current invocation**, writes `/etc/dsh-web/launch-token` and
|
**of the service's current invocation**, writes `/etc/dsh-web/launch-token` and
|
||||||
regenerates `/etc/dsh-web/nginx-login.conf`, reloading nginx only when the token
|
regenerates `/etc/dsh-web/nginx-login.conf`, reloading nginx only when the token
|
||||||
changed (`nginx -t` guards the reload, with rollback). It **never stops or
|
differs from the token nginx actually loaded (`nginx -t` guards the reload, and
|
||||||
|
the applied-state stamp is written only after a successful `nginx -s reload`, so
|
||||||
|
a failed or interrupted reload is retried on the next run). It **never stops or
|
||||||
starts `dsh-web`**. It is triggered by the `dsh-web.service` drop-in
|
starts `dsh-web`**. It is triggered by the `dsh-web.service` drop-in
|
||||||
`/etc/systemd/system/dsh-web.service.d/20-token-refresh.conf`
|
`/etc/systemd/system/dsh-web.service.d/20-token-refresh.conf`
|
||||||
(`ExecStartPost=/bin/systemctl --no-block start dsh-web-token.service`) and by
|
(`ExecStartPost=/bin/systemctl --no-block start dsh-web-token.service`) and by
|
||||||
@@ -359,20 +361,38 @@ ssh root@192.168.68.15 "pct exec 112 -- curl -s --max-time 3 -o /dev/null \
|
|||||||
|
|
||||||
# 3. Backend cookie mint + reuse (exactly what /dsh-web-login proxies to).
|
# 3. Backend cookie mint + reuse (exactly what /dsh-web-login proxies to).
|
||||||
TOKEN=$(ssh root@192.168.68.15 "pct exec 112 -- cat /etc/dsh-web/launch-token")
|
TOKEN=$(ssh root@192.168.68.15 "pct exec 112 -- cat /etc/dsh-web/launch-token")
|
||||||
ssh root@192.168.68.15 "pct exec 112 -- curl -s -D - -o /dev/null \
|
ssh root@192.168.68.15 "pct exec 112 -- curl -s -c /tmp/dsh.jar -o /dev/null \
|
||||||
-H 'Host: tankodhs.sysloggh.net' 'http://127.0.0.1:3080/?token=$TOKEN'"
|
-H 'Host: tankodhs.sysloggh.net' 'http://127.0.0.1:3080/?token=$TOKEN'"
|
||||||
# Expected: HTTP/1.1 303 + set-cookie: dsh-auth-... (authority tankodhs.sysloggh.net)
|
ssh root@192.168.68.15 "pct exec 112 -- curl -s -b /tmp/dsh.jar -o /dev/null \
|
||||||
|
-w '%{http_code}\n' -H 'Host: tankodhs.sysloggh.net' http://127.0.0.1:3080/"
|
||||||
|
# Expected: 200 — the minted dsh-auth-... cookie (authority
|
||||||
|
# tankodhs.sysloggh.net) is replayed on the next request and accepted.
|
||||||
|
|
||||||
# 4. Token refresh is non-disruptive and idempotent.
|
# 4. Token refresh is non-disruptive and idempotent.
|
||||||
ssh root@192.168.68.15 "pct exec 112 -- /opt/deepseek-harness/capture-dsh-token.sh"
|
ssh root@192.168.68.15 "pct exec 112 -- /opt/deepseek-harness/capture-dsh-token.sh"
|
||||||
# Expected: "token unchanged; nginx not reloaded" when nothing changed
|
# Expected: "token unchanged; nginx not reloaded" when nothing changed
|
||||||
```
|
```
|
||||||
|
|
||||||
**Restart durability (acceptance):** after `systemctl restart dsh-web`, (a) a
|
**Restart durability (acceptance):** after `systemctl restart dsh-web`, (a) the
|
||||||
cookie minted before the restart still returns `200` on `/`, and (b) the
|
cookie minted before the restart still returns `200` on `/`, and (b) the
|
||||||
refreshed `/etc/dsh-web/nginx-login.conf` carries the new token and mints a
|
refreshed `/etc/dsh-web/nginx-login.conf` carries the new token and mints a
|
||||||
fresh cookie. Both verified live 2026-09-11.
|
fresh cookie. Both verified live 2026-09-11.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 5. Cookie survives a dsh-web restart, and the new token mints a new cookie.
|
||||||
|
ssh root@192.168.68.15 "pct exec 112 -- systemctl restart dsh-web"
|
||||||
|
ssh root@192.168.68.15 "pct exec 112 -- /opt/deepseek-harness/capture-dsh-token.sh"
|
||||||
|
ssh root@192.168.68.15 "pct exec 112 -- curl -s -b /tmp/dsh.jar -o /dev/null \
|
||||||
|
-w '%{http_code}\n' -H 'Host: tankodhs.sysloggh.net' http://127.0.0.1:3080/"
|
||||||
|
# Expected: 200 — the pre-restart cookie is still accepted.
|
||||||
|
TOKEN=$(ssh root@192.168.68.15 "pct exec 112 -- cat /etc/dsh-web/launch-token")
|
||||||
|
ssh root@192.168.68.15 "pct exec 112 -- curl -s -c /tmp/dsh-new.jar -o /dev/null \
|
||||||
|
-H 'Host: tankodhs.sysloggh.net' 'http://127.0.0.1:3080/?token=$TOKEN'"
|
||||||
|
ssh root@192.168.68.15 "pct exec 112 -- curl -s -b /tmp/dsh-new.jar -o /dev/null \
|
||||||
|
-w '%{http_code}\n' -H 'Host: tankodhs.sysloggh.net' http://127.0.0.1:3080/"
|
||||||
|
# Expected: 200 — the refreshed token minted a fresh cookie.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Step 4: Platform C — Agent Zero (kagentz, CT 105 via Docker host .14)
|
### Step 4: Platform C — Agent Zero (kagentz, CT 105 via Docker host .14)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user