apiVersion: apps/v1 kind: Deployment metadata: name: socktop-webterm labels: app: socktop-webterm spec: replicas: 3 selector: matchLabels: app: socktop-webterm template: metadata: labels: app: socktop-webterm spec: # Use standard pod networking hostNetwork: false dnsPolicy: ClusterFirst # Security context for the pod securityContext: runAsUser: 100 runAsGroup: 101 fsGroup: 101 # Init container to set up configuration initContainers: - name: init-config image: gt.wittyoneoff.com/jason/socktop-webterm:0.2.2 imagePullPolicy: Always command: ["/bin/bash", "-c"] args: - | set -e echo "Setting up configuration directories..." mkdir -p /var/lib/socktop/.config/socktop/certs mkdir -p /var/lib/socktop/.config/alacritty if [ -f "/home/socktop/.config/socktop/profiles.json" ]; then cp /home/socktop/.config/socktop/profiles.json /var/lib/socktop/.config/socktop/profiles.json echo "Copied profiles.json" fi if [ -f "/home/socktop/.config/alacritty/alacritty.toml" ]; then cp /home/socktop/.config/alacritty/alacritty.toml /var/lib/socktop/.config/alacritty/alacritty.toml echo "Copied alacritty.toml" fi if [ -f "/home/socktop/.config/alacritty/catppuccin-frappe.toml" ]; then cp /home/socktop/.config/alacritty/catppuccin-frappe.toml /var/lib/socktop/.config/alacritty/catppuccin-frappe.toml echo "Copied catppuccin-frappe.toml" fi if [ -d "/home/socktop/.config/socktop/certs" ]; then cp /home/socktop/.config/socktop/certs/*.pem /var/lib/socktop/.config/socktop/certs/ 2>/dev/null || true echo "Copied certificates" fi # Fix paths in profiles.json if [ -f "/var/lib/socktop/.config/socktop/profiles.json" ]; then sed -i 's|/home/socktop/.config/socktop/rpi-|/var/lib/socktop/.config/socktop/certs/rpi-|g' /var/lib/socktop/.config/socktop/profiles.json echo "Updated certificate paths" fi echo "Configuration setup complete" volumeMounts: - name: config mountPath: /home/socktop/.config/socktop/profiles.json subPath: profiles.json - name: config mountPath: /home/socktop/.config/alacritty/alacritty.toml subPath: alacritty.toml - name: config mountPath: /home/socktop/.config/alacritty/catppuccin-frappe.toml subPath: catppuccin-frappe.toml - name: certs mountPath: /home/socktop/.config/socktop/certs readOnly: true - name: socktop-home mountPath: /var/lib/socktop securityContext: runAsUser: 100 runAsGroup: 101 containers: - name: webterm image: gt.wittyoneoff.com/jason/socktop-webterm:0.3.12 imagePullPolicy: Always command: ["/docker-entrypoint.sh"] args: [ "webterm-server", "--host", "0.0.0.0", "--port", "8082", "--command", "/usr/local/bin/session-shell.sh", ] ports: - name: http containerPort: 8082 protocol: TCP - name: agent containerPort: 3001 protocol: TCP env: - name: TERM value: "xterm-256color" - name: TZ value: "America/New_York" - name: RUST_LOG value: "info" # Disable socktop's local process-kill feature for every socktop # launched anywhere under this pod, regardless of command line. # The restricted shell also passes --no-kill, but this is the layer # a visitor cannot route around (no way to unset env from the # restricted shell). - name: SOCKTOP_NO_KILL value: "1" resources: limits: cpu: "2000m" memory: "1Gi" requests: cpu: "500m" memory: "256Mi" livenessProbe: httpGet: path: / port: 8082 initialDelaySeconds: 10 periodSeconds: 30 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: / port: 8082 initialDelaySeconds: 5 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 3 volumeMounts: - name: config mountPath: /home/socktop/.config/socktop/profiles.json subPath: profiles.json - name: config mountPath: /home/socktop/.config/alacritty/alacritty.toml subPath: alacritty.toml - name: config mountPath: /home/socktop/.config/alacritty/catppuccin-frappe.toml subPath: catppuccin-frappe.toml - name: certs mountPath: /home/socktop/.config/socktop/certs readOnly: true - name: socktop-home mountPath: /var/lib/socktop # 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: drop: - ALL 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 # three file caps must come back or the entrypoint crashes on # mkdir/chown/chmod. Sessions still get zero caps — session- # shell.sh drops them all via setpriv --inh-caps -all. - CHOWN - DAC_OVERRIDE - FOWNER readOnlyRootFilesystem: false runAsUser: 0 runAsGroup: 0 volumes: - name: config configMap: name: socktop-webterm-config - name: certs secret: secretName: socktop-webterm-certs optional: true - name: socktop-home emptyDir: {} restartPolicy: Always