Problem
Follow-up to #22275 / #24026, which added --block-reverse-port-forwarding and --block-local-port-forwarding to the Coder agent SSH server.
X11 forwarding remains unconditionally enabled. In agent/agentssh/x11.go, the callback is hardcoded:
// x11Callback is called when the client requests X11 forwarding.
func (*Server) x11Callback(_ ssh.Context, _ ssh.X11) bool {
// Always allow.
return true
}
Note the value receiver on *Server — the callback has no access to agentssh.Config at all, so there is currently no way to gate it.
This leaves open the same class of sandbox escape that #22275 addressed, and arguably a more direct one. With ssh -X / ssh -Y, the agent opens x11 channels back to the client's X server. Anything running inside the workspace — an AI agent or any other process that can read the session's Xauthority file and connect to DISPLAY — can then:
- inject synthetic keystrokes and mouse events into the user's local desktop
- capture the contents of any window on the user's local machine
- read the local clipboard
Unlike ssh -R, this does not require the user to have deliberately set up a tunnel; it only requires X11 forwarding to be requested on the connection.
Why existing mitigations don't cover this
- Agent Boundaries operate at the HTTP/HTTPS layer and do not intercept the X11 protocol.
CODER_BROWSER_ONLY=true blocks all SSH connections deployment-wide, which also prevents legitimate CLI/IDE access.
sshd_config in the workspace image has no effect — Coder uses its own embedded SSH server, not OpenSSH.
- The new
--block-reverse-port-forwarding / --block-local-port-forwarding flags gate tcpip-forward, direct-tcpip, and the streamlocal variants. They do not gate the x11-req request or the x11 channel type.
Proposed Solution
Mirror the pattern established in #24026:
- Add
BlockX11Forwarding bool to agentssh.Config (agent/agentssh/agentssh.go).
- Add
BlockX11Forwarding bool to agent.Options and thread it through in agent/agent.go.
- Add a CLI flag in
cli/agent.go:
- Flag:
block-x11-forwarding
- Env:
CODER_AGENT_BLOCK_X11_FORWARDING
- Default:
false
- Description:
Block X11 forwarding through the SSH server (ssh -X, ssh -Y).
- Change
x11Callback to a pointer receiver and return false with a logger.Warn when the option is set, so the x11-req is rejected at request time.
- Defensively gate the
hasX11 branch in sessionHandler (agent/agentssh/agentssh.go ~L524) so DISPLAY is never injected into the session environment even if a request slips through.
As with #22275, ideally this is controllable at the template level (template admins disable X11 for AI agent templates while leaving it enabled for human developer templates), with a deployment-level flag as a fallback.
Use Case
Organizations running AI coding agents in Coder workspaces with process/network sandboxing need to prevent an agent inside the workspace from reaching back onto the connecting user's local desktop. Blocking ssh -R and ssh -L without also blocking X11 forwarding leaves the isolation incomplete.
Created on behalf of @ericpaulsen
Problem
Follow-up to #22275 / #24026, which added
--block-reverse-port-forwardingand--block-local-port-forwardingto the Coder agent SSH server.X11 forwarding remains unconditionally enabled. In
agent/agentssh/x11.go, the callback is hardcoded:Note the value receiver on
*Server— the callback has no access toagentssh.Configat all, so there is currently no way to gate it.This leaves open the same class of sandbox escape that #22275 addressed, and arguably a more direct one. With
ssh -X/ssh -Y, the agent opensx11channels back to the client's X server. Anything running inside the workspace — an AI agent or any other process that can read the session'sXauthorityfile and connect toDISPLAY— can then:Unlike
ssh -R, this does not require the user to have deliberately set up a tunnel; it only requires X11 forwarding to be requested on the connection.Why existing mitigations don't cover this
CODER_BROWSER_ONLY=trueblocks all SSH connections deployment-wide, which also prevents legitimate CLI/IDE access.sshd_configin the workspace image has no effect — Coder uses its own embedded SSH server, not OpenSSH.--block-reverse-port-forwarding/--block-local-port-forwardingflags gatetcpip-forward,direct-tcpip, and thestreamlocalvariants. They do not gate thex11-reqrequest or thex11channel type.Proposed Solution
Mirror the pattern established in #24026:
BlockX11Forwarding booltoagentssh.Config(agent/agentssh/agentssh.go).BlockX11Forwarding booltoagent.Optionsand thread it through inagent/agent.go.cli/agent.go:block-x11-forwardingCODER_AGENT_BLOCK_X11_FORWARDINGfalseBlock X11 forwarding through the SSH server (ssh -X, ssh -Y).x11Callbackto a pointer receiver and returnfalsewith alogger.Warnwhen the option is set, so thex11-reqis rejected at request time.hasX11branch insessionHandler(agent/agentssh/agentssh.go~L524) soDISPLAYis never injected into the session environment even if a request slips through.As with #22275, ideally this is controllable at the template level (template admins disable X11 for AI agent templates while leaving it enabled for human developer templates), with a deployment-level flag as a fallback.
Use Case
Organizations running AI coding agents in Coder workspaces with process/network sandboxing need to prevent an agent inside the workspace from reaching back onto the connecting user's local desktop. Blocking
ssh -Randssh -Lwithout also blocking X11 forwarding leaves the isolation incomplete.Created on behalf of @ericpaulsen