Fix actix worker wedge on session teardown; grant CAP_KILL; bump to 0.3.12
Terminal::stopping did child.kill() (error ignored) and then a blocking child.wait() on the actix worker thread. Since the 0.3.11 privilege drop, sessions run as `demo` and the server had no CAP_KILL, so kill() failed with EPERM and wait() blocked forever. With two workers, exactly every other request to :8082 then timed out — 5 days of readiness/liveness flapping on socktop.io and orphaned restricted-shell/socktop processes piling up in the pods. - Signal the session's whole process group (portable-pty setsid()s the child), not just the shell: a non-interactive bash defers signals while a foreground command runs, so the old SIGHUP/SIGKILL to the shell alone orphaned socktop anyway. - Reap on a dedicated thread: SIGHUP, 2 s grace, SIGKILL, 10 s deadline, then a blocking wait on that thread only. Signal failures are logged. - Drop the pty handles before handing off the child. - kubernetes/03-deployment.yaml: add CAP_KILL (sessions still lose every cap via setpriv). CI never applies the manifest — it was patched live. - tests/reaper_tests.rs pins the non-blocking return, process-group kill, and SIGHUP→SIGKILL escalation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -85,7 +85,7 @@ spec:
|
||||
|
||||
containers:
|
||||
- name: webterm
|
||||
image: gt.wittyoneoff.com/jason/socktop-webterm:0.3.11
|
||||
image: gt.wittyoneoff.com/jason/socktop-webterm:0.3.12
|
||||
imagePullPolicy: Always
|
||||
|
||||
command: ["/docker-entrypoint.sh"]
|
||||
@@ -165,13 +165,13 @@ spec:
|
||||
- name: socktop-home
|
||||
mountPath: /var/lib/socktop
|
||||
|
||||
# webterm-server runs as in-container root holding ONLY
|
||||
# CAP_SETUID/CAP_SETGID (everything else dropped, no privilege
|
||||
# escalation), so it can drop each websocket session to the
|
||||
# unprivileged `demo` user via session-shell.sh. The kernel then
|
||||
# refuses any signal a session aims at the server, the agent
|
||||
# (running as `socktop`), or another session's UID — the kill
|
||||
# feature's UI gating stops being the only line of defense.
|
||||
# webterm-server runs as in-container root holding only the caps
|
||||
# listed below (everything else dropped, no privilege escalation),
|
||||
# so it can drop each websocket session to the unprivileged `demo`
|
||||
# user via session-shell.sh. The kernel then refuses any signal a
|
||||
# session aims at the server, the agent (running as `socktop`), or
|
||||
# another session's UID — the kill feature's UI gating stops being
|
||||
# the only line of defense.
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
@@ -180,6 +180,14 @@ spec:
|
||||
add:
|
||||
- SETUID
|
||||
- SETGID
|
||||
# The server must be able to signal the sessions it spawned,
|
||||
# which run as `demo` — a different uid — so root needs
|
||||
# CAP_KILL for that. Without it every idle-timeout teardown
|
||||
# got EPERM, the session lived on, and (0.3.11) the actix
|
||||
# worker blocked in wait() on it: half of all requests hung.
|
||||
# Sessions still cannot signal anything: setpriv drops every
|
||||
# cap (including this one) before the restricted shell runs.
|
||||
- KILL
|
||||
# prepare_demo_home (entrypoint.sh) writes into and re-owns
|
||||
# /home/demo, which the image ships as demo-owned 700. With
|
||||
# ALL dropped, uid 0 has no implicit file privilege, so the
|
||||
|
||||
Reference in New Issue
Block a user