Skip to content

tehrhart/sshamrock

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SSHamrock

Browser-based SSH and SFTP — no extensions, no plugins, no VPN.

SSHamrock repackages Google's Secure Shell Chrome extension as a standalone web page. It ships as a single server that serves the web client and proxies the SSH connections on the same port.

SSHamrock connection dialog

Quick start

Interactive (on an existing server)

git clone https://github.com/tehrhart/sshamrock.git
cd sshamrock
sudo bash quickstart.sh

The installer prompts for your public hostname and identity provider, then handles everything: Python venv, relay server, static files, systemd service. Point any reverse proxy at port 8080 and you're live.

Automated (cloud VM bootstrap)

For DigitalOcean, AWS, GCP, or any cloud provider that supports startup scripts:

  1. Open sshamrock-bootstrap.sh
  2. Edit the settings at the top (hostname, Cloudflare team/audience, tunnel token)
  3. Paste the entire script as your VM's User Data / startup script

The VM boots, installs all dependencies (including cloudflared), configures the relay, and comes up ready. Logs at /var/log/sshamrock-init.log.

This works with any cloud VM — DigitalOcean Droplets, AWS EC2, GCP Compute Engine, Azure VMs, etc.

How it works

Browser (any Chromium)           SSHamrock Server              SSH Target
┌─────────────────────┐    ┌──────────────────────┐    ┌──────────────┐
│ hterm terminal       │    │ FastAPI (Python)      │    │              │
│ OpenSSH (WebAssembly)│─wss│  static web client    │    │   sshd       │
│ Relay client JS      │────│  /v4/connect → TCP    │────│              │
└─────────────────────┘    └──────────────────────┘    └──────────────┘
         ▲                          ▲
         └──── Authenticating reverse proxy ────┘
  1. User visits the page and authenticates via your identity provider
  2. The browser loads a full SSH client — hterm + OpenSSH compiled to WebAssembly
  3. User picks a host and the browser opens a WebSocket to the same server
  4. The relay bridges the WebSocket to a TCP connection to the target
  5. All SSH encryption happens inside the browser — the relay sees only opaque ciphertext

Authentication

SSHamrock itself doesn't authenticate users — it relies on a reverse proxy in front of it that handles SSO and passes identity headers. Any proxy that terminates auth and forwards to port 8080 will work:

Proxy How it works
Cloudflare Access Cloudflare Tunnel → port 8080. JWT in Cf-Access-Jwt-Assertion header. First-class support via RELAY_IDENTITY_PROVIDER=cloudflare-access.
Google IAP GCE/GKE backend → port 8080. JWT in x-goog-iap-jwt-assertion header. First-class support via RELAY_IDENTITY_PROVIDER=gcp-iap.
nginx + oauth2-proxy oauth2-proxy handles SSO, nginx forwards to port 8080. Set RELAY_IDENTITY_PROVIDER=none and RELAY_AUTH_REQUIRED=false (proxy handles auth).
Tailscale / ZeroTier Mesh VPN limits who can reach the server. Same config as above.
Any other Anything that authenticates the user and proxies to port 8080.

The quickstart installer prompts for Cloudflare or GCP IAP details. For other proxies, choose "none" and let your proxy handle authentication.

Features

  • SSH and SFTP in the browser — interactive terminal or command-line SFTP
  • SSH key management — import private keys, encrypted at rest with a passphrase (PBKDF2 + AES-256-GCM via Web Crypto API). Browser password manager can save the passphrase. First available key is auto-selected.
  • Saved connections — profiles persist in localStorage
  • URL-driven connections — pre-populate, autoconnect, and integrate with other tools (see below)
  • Subdomain routinghost123.ssh.example.com auto-connects to host123
  • Session resumption — relay buffers data during brief disconnects (v4 protocol)
  • Single binary deployment — one server, one port, one container

URL parameters

SSHamrock connections can be fully controlled via URL parameters, making it easy to integrate with wikis, ticketing systems, CMDBs, or any tool that can produce a link.

Parameter Example Description
host ?host=db.internal Target hostname
user ?user=root Username (SSH prompts if omitted)
port ?port=2222 Port (default: 22)
mode ?mode=sftp ssh or sftp
key ?key=id_ed25519 Select a specific imported key
autoconnect ?autoconnect=1 Skip the form, connect immediately

Examples:

# Pre-populate form
https://ssh.example.com/?user=root&host=db.internal

# One-click connect (prompts for key passphrase, then connects)
https://ssh.example.com/?user=deploy&host=web01.prod&autoconnect=1

# SFTP to a file server
https://ssh.example.com/?host=files.internal&mode=sftp&autoconnect=1

# No username — SSH will prompt
https://ssh.example.com/?host=bastion.internal&autoconnect=1

Subdomain routing

When BASE_DOMAIN is configured in config.js, SSHamrock extracts the target hostname from the subdomain:

https://db-server.ssh.example.com  →  connects to "db-server"
https://web01.ssh.example.com      →  connects to "web01"

With a Cloudflare wildcard DNS record (*.ssh.example.com) and a wildcard Access policy, users can reach any internal host by typing its name as a subdomain. Combine with ?autoconnect=1 for a zero-click experience after SSO.

To enable, edit src/js/config.js:

export const BASE_DOMAIN = 'ssh.example.com';

Configuration

Config lives at /etc/ssh-relay/env (written by the installer). Key settings:

RELAY_PUBLIC_HOST=ssh.example.com   # Your public hostname
RELAY_PUBLIC_PORT=443               # Public-facing port
RELAY_IDENTITY_PROVIDER=cloudflare-access  # or gcp-iap, or none
RELAY_AUTH_REQUIRED=true
RELAY_STATIC_DIR=/opt/ssh-relay/static

# Target policy (optional) — restrict which hosts the relay can reach
RELAY_TARGET_ALLOWLIST=10.0.0.0/8   # Comma-separated CIDRs

Full configuration reference: nassh-proxy docs.

Logging

SSHamrock writes two log streams by default:

Log Location Format Contents
Audit log /var/log/sshamrock/audit.jsonl JSON, one event per line Handshakes, session start/close, identity, target host/port, duration, bytes transferred
Access log journalctl -u ssh-relay Plain text (uvicorn) HTTP requests, WebSocket connections, Python errors, startup messages

The audit log rotates automatically (100 MB per file, 10 backups = 1 GB total).

Example audit events:

{"event": "handshake", "ts": "2026-04-22T...", "identity": {"email": "alice@example.com"}, "source_ip": "72.34.128.248"}
{"event": "session.start", "ts": "2026-04-22T...", "identity": {"email": "alice@example.com"}, "target_host": "db.internal", "target_port": 22}
{"event": "session.close", "ts": "2026-04-22T...", "duration_seconds": 847.3, "bytes_to_target": 15230, "bytes_from_target": 98412}

Querying logs with jq:

# Who connected today?
jq -r 'select(.event=="session.start") | "\(.ts) \(.identity.email) → \(.target_host)"' \
    /var/log/sshamrock/audit.jsonl

# Sessions longer than 1 hour
jq 'select(.event=="session.close" and .duration_seconds > 3600)' \
    /var/log/sshamrock/audit.jsonl

# Live tail
tail -f /var/log/sshamrock/audit.jsonl | jq .

Additional log sinks (configure in /etc/ssh-relay/env):

Sink Setting Use case
Splunk HEC RELAY_LOG_SINKS=stderr,file,splunk Central SIEM
Palo Alto User-ID RELAY_LOG_SINKS=stderr,file,pan Firewall user mapping

Security model

Network: The relay transports opaque SSH ciphertext — it cannot read passwords, keys, or session content. Loopback, link-local, and cloud metadata IPs are blocked by default.

Browser: A strict Content Security Policy (script-src 'self') prevents XSS. SSH private keys are encrypted at rest with PBKDF2 + AES-256-GCM and only decrypted into the WASM filesystem during an active connection. Stale keys are wiped on startup (crash recovery). URL parameters are stripped from browser history.

Headers: COOP/COEP enable SharedArrayBuffer for the WASM worker. Referrer-Policy: no-referrer and X-Content-Type-Options: nosniff are set on all responses.

Data Visible to relay?
Who connected (JWT identity) Yes
Target host and port Yes
SSH traffic content No (encrypted)
Passwords typed in SSH No (encrypted)
SSH private keys No (never leave browser)

Building from source (optional)

The repo includes a pre-built dist/ directory with everything needed. If you want to modify the web client or update the nassh components:

# Prerequisites: Node.js 18+, libapps checkout
bash build/assemble.sh /path/to/libapps
cp ~/.config/google-chrome/Default/Extensions/iodihamcpbpeioajjeobimgagajmlibd/*/plugin/wasm/ssh.wasm dist/plugin/wasm/

Tech stack

Component Source
Terminal hterm
SSH client OpenSSH → WebAssembly
Relay protocol Corp Relay v4
Relay server nassh-proxy (FastAPI/Python)
Key encryption Web Crypto API (PBKDF2 + AES-256-GCM)

License

The web client wrapper (this repo) is MIT licensed. The underlying nassh/hterm/ssh_client components are BSD licensed by The Chromium Authors.

About

Browser-based SSH/SFTP client — repackages Google Secure Shell (nassh) as a standalone web page served by the same relay server

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors