Problem
Coder's embedded SSH server (built on gliderlabs/ssh) unconditionally allows reverse port forwarding. In agent/agentssh/agentssh.go, the callback is hardcoded:
ReversePortForwardingCallback: func(ctx ssh.Context, bindHost string, bindPort uint32) bool {
// Allow reverse port forwarding all!
s.logger.Debug(ctx, "reverse port forward",
slog.F("bind_host", bindHost),
slog.F("bind_port", bindPort))
return true
},
This presents a sandbox escape vector when workspaces are used for AI agent containment:
- A user runs
ssh -R 2222:<local-machine>:22 <workspace> to open a reverse tunnel into the workspace
- Inside the workspace, anything (agent or human) can run
ssh -p 2222 localhost to reach the user's local machine
- This breaks out of the workspace sandbox, bypassing any network isolation applied within the workspace
Why existing mitigations don't cover this
- Agent Boundaries operate at the HTTP/HTTPS layer (domain-based allowlist via a proxy). They do not intercept raw TCP connections like SSH over a reverse-forwarded port.
CODER_BROWSER_ONLY=true blocks all SSH connections deployment-wide, which also prevents legitimate CLI/IDE access (Cursor, JetBrains, VS Code Remote, etc.).
sshd_config in the workspace image has no effect — Coder uses its own embedded SSH server, not OpenSSH.
- Kubernetes
NetworkPolicy can restrict egress but cannot control localhost traffic within the pod where the reverse-forwarded port is bound.
Proposed Solution
Add a configurable option to disable or restrict reverse port forwarding in the Coder agent SSH server. Ideally this would be controllable at:
- Template level — template admins can disable
ssh -R for specific workspace templates (e.g., AI agent workspaces) while leaving it enabled for human developer templates
- Deployment level — a deployment-wide flag as a fallback
The implementation would involve making the ReversePortForwardingCallback respect a configuration value instead of unconditionally returning true. A similar option could be offered for local port forwarding (ssh -L) via LocalPortForwardingCallback.
Use Case
Organizations running AI coding agents in Coder workspaces with process/network sandboxing (via Kubernetes, Agent Boundaries, nsjail, etc.) need to prevent agents from escaping the sandbox via SSH tunnels established by human users connecting to the workspace.
Problem
Coder's embedded SSH server (built on
gliderlabs/ssh) unconditionally allows reverse port forwarding. Inagent/agentssh/agentssh.go, the callback is hardcoded:This presents a sandbox escape vector when workspaces are used for AI agent containment:
ssh -R 2222:<local-machine>:22 <workspace>to open a reverse tunnel into the workspacessh -p 2222 localhostto reach the user's local machineWhy existing mitigations don't cover this
CODER_BROWSER_ONLY=trueblocks all SSH connections deployment-wide, which also prevents legitimate CLI/IDE access (Cursor, JetBrains, VS Code Remote, etc.).sshd_configin the workspace image has no effect — Coder uses its own embedded SSH server, not OpenSSH.NetworkPolicycan restrict egress but cannot controllocalhosttraffic within the pod where the reverse-forwarded port is bound.Proposed Solution
Add a configurable option to disable or restrict reverse port forwarding in the Coder agent SSH server. Ideally this would be controllable at:
ssh -Rfor specific workspace templates (e.g., AI agent workspaces) while leaving it enabled for human developer templatesThe implementation would involve making the
ReversePortForwardingCallbackrespect a configuration value instead of unconditionally returning true. A similar option could be offered for local port forwarding (ssh -L) viaLocalPortForwardingCallback.Use Case
Organizations running AI coding agents in Coder workspaces with process/network sandboxing (via Kubernetes, Agent Boundaries,
nsjail, etc.) need to prevent agents from escaping the sandbox via SSH tunnels established by human users connecting to the workspace.