Repository navigation
Fix gateways hanging instead of reconnecting when the signal server restarts - #54
Merged
Merged
Conversation
…tarts Devops-GW stopped serving today and needed a manual container restart. Its last log line was one second before the signal server restarted, and in the 40 minutes after it logged nothing at all — not even the "Reconnecting" line the retry loop prints on every attempt. It was stuck inside connect_and_run, not looping. The main loop awaited ws_stream.next() with no deadline. If the peer disappears without a clean close reaching us, that future never resolves, so the loop never returns Disconnected and the reconnect logic is never reached. The ping task notices, but breaking itself is all it can do — it has no way to tell the main loop. Adds a liveness deadline: any frame, pings and pongs included, marks the link alive, and 45s of total silence (three ping intervals) ends the connection so the existing backoff takes over. Two things that made this outage worse than it needed to be: - The signal server ran under nohup with nothing supervising it, so it dies on every reboot and stays dead. deploy.sh now installs a systemd unit with Restart=always, falling back to nohup where systemd is unavailable. - SSH tunnel URLs were logged verbatim, writing complete access tokens into /tmp/signal-server-rs.log — decodable, with the user's roles and email. Credential-bearing query parameters are now redacted on both sides.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Devops-GWstopped serving today and needed a manual container restart, which producedTCP connection timeoutin the browser.What happened
The container was
Runningthe whole time with 0 restarts. It simply stopped participating, and the signal server routed to its stale address.The retry loop logs
Connecting to …andReconnecting (delay: Nms)on every attempt. Forty minutes produced none of those lines, so the loop was never re-entered — it was stuck insideconnect_and_run.Cause
The main loop awaited
ws_stream.next()with no deadline. If the peer disappears without a clean close reaching us — a half-open socket — that future never resolves. The loop never returnsDisconnected, so the reconnect logic below it is unreachable.There is a ping task, but when its send fails it only
breaks itself. It has no channel back to the main loop, so its death is silent.The consequence is worse than one gateway: any signal server restart can leave gateways permanently hung, each needing a manual restart. Both of today's incidents fit that shape.
Fix
A liveness deadline in the select loop. Any frame — pings and pongs included — marks the link alive; 45s of total silence (three ping intervals, impossible on a healthy link since both ends ping every 15s) ends the connection so the existing exponential backoff takes over.
Two things that made it worse, also fixed
The signal server had no supervisor.
deploy.shlaunched it withnohup, so it dies on every reboot and stays dead — which is what took it down earlier today, and it surfaces in the console as "Contact your administrator to get server access" rather than as an outage. Now installs a systemd unit withRestart=alwaysandStartLimitIntervalSec=0, falling back tonohupwhere systemd isn't available. It also kills any leftover nohup instance before starting, so the port isn't already taken.Access tokens were being written to the logs. From the live log:
A complete, decodable access token in a log file on disk, carrying the user's roles, email and vuid — once per SSH session. DPoP-binding limits replay but this should not be there at all. Credential-bearing query parameters are now redacted on both the signal server and the gateway, keeping
host,portandserverIdso the line stays useful.Testing
55 gateway tests and 34 signal server tests pass, 9 new on redaction (token stripped, every secret parameter, case-insensitive, clean URLs untouched, malformed pairs).
deploy.shpassesbash -n.The hang fix itself is not covered by a test — reproducing it needs a half-open socket, which is awkward to fake in a unit test. The reasoning is solid and the change is small, but the real confirmation is a deliberate signal server restart with a gateway attached: it should log
no traffic for 45s, thenReconnecting, then re-register on its own. Worth doing on Staging-GW before trusting it everywhere.The systemd unit has not been run — it needs
systemctlon the actual host. Worth watching the firstdeploy.shclosely, particularly thatEnvironmentFileparses the existing.env(it isKEY=valuethroughout, which systemd accepts, but values containing spaces or quotes would need review).