feat: SHARED_SECRET_LEGACY migration escape hatch for plugin secret rollout

Accepts one legacy secret alongside the real one so existing clients keep
registering while game machines migrate to websocket_secret.txt. Remove
SHARED_SECRET_LEGACY from .env after the rollout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-10 20:20:19 +02:00
parent 15ae870117
commit 52bf9342df
2 changed files with 21 additions and 3 deletions

21
main.py
View file

@ -1003,6 +1003,17 @@ if not _SHARED_SECRET_OK:
"SHARED_SECRET env var is unset or still the placeholder — "
"refusing ALL plugin WebSocket connections until it is set in .env"
)
# Migration escape hatch: while game machines are being migrated to
# websocket_secret.txt, a second (legacy) secret can be accepted alongside
# the real one. REMOVE SHARED_SECRET_LEGACY from .env once the plugin
# rollout is complete — with the old placeholder in it, this re-opens the
# known-secret hole it exists to close.
SHARED_SECRET_LEGACY = os.getenv("SHARED_SECRET_LEGACY", "")
if SHARED_SECRET_LEGACY:
logger.warning(
"SHARED_SECRET_LEGACY is set — legacy plugin secret accepted during "
"migration; remove it from .env after the plugin rollout"
)
# Secret key for signing session cookies. Fail closed: running with a
# publicly-known default would let anyone forge admin sessions.
SECRET_KEY = os.getenv("SECRET_KEY", "")
@ -2979,9 +2990,13 @@ async def ws_receive_snapshots(
# compare; refuse everything when the secret is not configured).
key = secret or x_plugin_secret or ""
# compare bytes: compare_digest(str, str) raises TypeError on non-ASCII
if not _SHARED_SECRET_OK or not hmac.compare_digest(
key.encode("utf-8", "replace"), SHARED_SECRET.encode("utf-8")
):
key_b = key.encode("utf-8", "replace")
auth_ok = _SHARED_SECRET_OK and hmac.compare_digest(
key_b, SHARED_SECRET.encode("utf-8")
)
if not auth_ok and SHARED_SECRET_LEGACY:
auth_ok = hmac.compare_digest(key_b, SHARED_SECRET_LEGACY.encode("utf-8"))
if not auth_ok:
# Reject without completing the WebSocket handshake
logger.warning(
f"Plugin WebSocket authentication failed from {websocket.client}"