Skip to content

feat: Add SSH service for local VSCode Remote-SSH access#2792

Open
gpothier wants to merge 2 commits intoOpenHands:mainfrom
gpothier:feature/ssh-service-squashed
Open

feat: Add SSH service for local VSCode Remote-SSH access#2792
gpothier wants to merge 2 commits intoOpenHands:mainfrom
gpothier:feature/ssh-service-squashed

Conversation

@gpothier
Copy link
Copy Markdown

@gpothier gpothier commented Apr 10, 2026

  • A human has tested these changes.

Why

With the "Remote - SSH" extension, a local VSCode (or downstream fork) instance can connect to a remote machine and run everything except UI from there: git cloning, running the app, etc. Although the OpenHands UI embeds VSCode, that embedded version can be less useful than the local "desktop" version. For instance, for debugging:

imagen

This PR (along with the sister PR in the OpenHands repo) enables one-click opening of a local VSCode window connected to the sandbox:

imagen

Summary

Add SSH server support to agent-server containers, enabling users to connect via their local VSCode with the Remote-SSH extension.

There is a "sister" PR in the OpenHands repo that completes this feature with a settings page to add SSH keys and a button to directly open the local VSCode: OpenHands/OpenHands#13844

Changes

Dockerfile

  • Install openssh-server package
  • Generate SSH host keys (ed25519, rsa, ecdsa) at build time
  • Configure sshd for non-root operation (port 2222, key-based auth)
  • Set proper permissions on host keys for openhands group

SSH Service (ssh_service.py)

  • New service to manage SSH server lifecycle
  • Populates ~/.ssh/authorized_keys from OH_SSH_PUBLIC_KEYS env var
  • Starts sshd as non-root user on port 2222
  • Integrated with agent-server startup

Configuration

  • Add SSH_PORT (default: 2222) to config
  • Add OH_SSH_PUBLIC_KEYS environment variable support (see sister PR for how where this comes from)

Technical Details

  • SSH server runs on port 2222 (non-privileged)
  • Host keys readable by openhands group (mode 640)
  • PAM disabled (requires root)
  • Public key authentication preferred
  • Password authentication available as fallback

Issue Number

How to Test

When both this PR and the sister PR are in use, a new button appears in the Code button's popup (see screenshot above). This button opens a new VSCode window connected to the sandbox through SSH. When the window opens for the first time, VSCode asks to accept the remote SSH host ID, like when one connects to a new SSH host (just type yes).

Video/Screenshots

imagen

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

There seems to be a bug in VSCode itself with multiple VSCode windows connecting to the same SSH host at different ports, where it wrongly assumes this is the same host. So currently this feature only allows one local VSCode window at a time, at least until this is resolved on the VSCode side.

Add SSH server support to agent-server containers, enabling users to connect
via their local VSCode with the Remote-SSH extension.

## Changes

### Dockerfile
- Install openssh-server package
- Generate SSH host keys (ed25519, rsa, ecdsa) at build time
- Configure sshd for non-root operation (port 2222, key-based auth)
- Set proper permissions on host keys for openhands group

### SSH Service (ssh_service.py)
- New service to manage SSH server lifecycle
- Populates ~/.ssh/authorized_keys from OH_SSH_PUBLIC_KEYS env var
- Starts sshd as non-root user on port 2222
- Integrated with agent-server startup

### Configuration
- Add SSH_PORT (default: 2222) to config
- Add OH_SSH_PUBLIC_KEYS environment variable support

## Technical Details

- SSH server runs on port 2222 (non-privileged)
- Host keys readable by openhands group (mode 640)
- PAM disabled (requires root)
- Public key authentication preferred
- Password authentication available as fallback

Co-authored-by: openhands <openhands@all-hands.dev>
@gpothier gpothier marked this pull request as ready for review April 10, 2026 01:23
@all-hands-bot
Copy link
Copy Markdown
Collaborator

[Automatic Post]: I have assigned @simonrosenberg as a reviewer based on git blame information. Thanks in advance for the help!

@enyst
Copy link
Copy Markdown
Collaborator

enyst commented Apr 14, 2026

Thank you so much for this!

@OpenHands Do a /codereview on this PR. Make sure you read the sister PR too.

@openhands-ai
Copy link
Copy Markdown

openhands-ai bot commented Apr 14, 2026

I'm on it! enyst can track my progress at all-hands.dev

Copy link
Copy Markdown
Collaborator

@enyst enyst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read this together with the sister PR (OpenHands/OpenHands#13844). I like the overall direction, but I found a few important issues that should be fixed before this ships. The inline comments call out the details.

This review was created by an AI assistant (OpenHands) on behalf of the user.

mkdir -p /run/sshd; \
chmod 755 /run/sshd; \
# Generate host keys
ssh-keygen -A; \
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generating the SSH host keys at image-build time bakes the same private keys into every container built from this image. That defeats host identity verification: VS Code/SSH will see unrelated runtimes as the same host, and anyone with access to the image can impersonate another sandbox's host key. These keys need to be generated per container start (or injected/mounted per runtime), not once in the Dockerfile layer.

# Allow sshd to run on non-privileged port without root
sed -i 's/#UsePAM yes/UsePAM no/' /etc/ssh/sshd_config; \
# Set password for openhands user (password: "openhands")
echo "${USERNAME}:openhands" | chpasswd; \
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This enables password auth and sets a globally known credential (openhands / openhands). In the sister PR, the SSH endpoint is exposed directly to the user through sandbox.exposed_urls, so there is no app-level auth once someone can reach host:port. That means anyone who can hit the runtime can log in, even when the user configured SSH keys. I think we should disable PasswordAuthentication entirely here and require the per-user public keys instead.

start_vscode_service(),
start_desktop_service(),
start_tool_preload_service(),
start_ssh_service(),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We start the SSH service here, but the shutdown path below still only stops VSCode, desktop, and tool-preload. That leaves sshd unmanaged across FastAPI lifespan restarts/tests and can leak the child process or keep the port bound. Please add a matching stop_ssh_service() in the finally gather(...) block.

@openhands-ai

This comment was marked as duplicate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants