Webssh2
Web SSH Client using ssh2, socket.io, xterm.js, and express. webssh webssh2
Install / Use
/learn @billchurch/Webssh2README
WebSSH2 - Web SSH Client

WebSSH2 is an HTML5 web-based terminal emulator and SSH client with optional telnet support. It uses SSH2 as a client on a host to proxy a Websocket / Socket.io connection to an SSH2 server.

Quick Start
Requirements
- Node.js 22 LTS (Jod) or later
Installation
# Clone repository
git clone https://github.com/billchurch/webssh2.git
cd webssh2
# Install dependencies
npm install --production
# Start server
npm start
Access WebSSH2 at: http://localhost:2222/ssh (or http://localhost:2222/telnet if telnet is enabled)
Official Containers
- Preferred registry:
ghcr.io/billchurch/webssh2 - Docker Hub mirror:
docker.io/billchurch/webssh2 - Architectures:
linux/amd64,linux/arm64
Pull the latest build from GitHub Container Registry:
docker pull ghcr.io/billchurch/webssh2:latest
Run the container exposing the default port:
docker run --rm -p 2222:2222 ghcr.io/billchurch/webssh2:latest
To pin to a specific release (example: webssh2-server-v2.3.2):
docker run --rm -p 2222:2222 \
ghcr.io/billchurch/webssh2:2.3.2
The same tags are available on Docker Hub if you prefer the legacy namespace:
docker run --rm -p 2222:2222 docker.io/billchurch/webssh2:2.3.2
Configuration
WebSSH2 prefers environment variables for configuration (following 12-factor app principles):
# Basic configuration
export WEBSSH2_LISTEN_PORT=2222
export WEBSSH2_SSH_HOST=ssh.example.com
export WEBSSH2_HEADER_TEXT="My WebSSH2"
# Allow only password and keyboard-interactive authentication methods (default allows all)
export WEBSSH2_AUTH_ALLOWED=password,keyboard-interactive
npm start
For detailed configuration options, see Configuration Documentation.
Common Examples
Connect to a specific host using HTTP Basic Auth
http://localhost:2222/ssh/host/192.168.1.100
Custom port and terminal using interactive modal auth
http://localhost:2222/ssh?port=2244&sshterm=xterm-256color
Docker with environment variables
docker run --rm -it \
-p 2222:2222 \
-e WEBSSH2_SSH_HOST=ssh.example.com \
-e WEBSSH2_SSH_ALGORITHMS_PRESET=modern \
-e WEBSSH2_AUTH_ALLOWED=password,publickey \
ghcr.io/billchurch/webssh2:latest
Need the Docker Hub mirror instead? Use docker.io/billchurch/webssh2:latest.
Documentation
Getting Started
- Quick Start Guide - Get up and running in 5 minutes
- Installation Guide - Detailed installation instructions
- Docker Setup - Docker and Kubernetes deployment
- Migration Guide - Upgrading from older versions
Configuration Documentation
- Configuration Overview - Configuration methods and priority
- Environment Variables - Complete environment variable reference
- URL Parameters - Query string parameters
Feature Documentation
- Authentication Methods - Password, key-based, and SSO
- Private Key Authentication - SSH key setup and usage
- Exec Channel - Non-interactive command execution
- Environment Forwarding - Pass environment variables
- Host Key Verification - MITM protection and key management
- Telnet Support - Optional telnet for legacy devices
Development
- Contributing Guide - How to contribute
- Development Setup - Setting up development environment
- API Documentation - WebSocket and REST APIs
Release Artifacts
- Build & Packaging Guide - Reproducible release flow and manifest format
- Container Integration - Using the packaged bundle in images and CI
Reference
- Troubleshooting - Common issues and solutions
- Breaking Changes - Version migration notes
Features
- 🌐 Web-based SSH - No client software required
- 🔐 Multiple Auth Methods - Password, private key, keyboard-interactive
- 📱 Responsive Design - Works on desktop and mobile
- 🎨 Customizable - Themes, fonts, and terminal settings
- 🔌 WebSocket - Real-time bidirectional communication
- 🐳 Docker Ready - Official Docker images available
- 🔧 Exec Channel - Run commands without opening a shell
- 🌍 Environment Variables - Pass custom environment to SSH sessions
- 🛡️ Subnet Restrictions - IPv4/IPv6 CIDR subnet validation for access control
- 📁 SFTP Support - File transfer capabilities (v2.6.0+)
- 📡 Telnet Support - Optional telnet protocol for legacy devices (disabled by default)
Host Key Verification
Host key verification protects SSH connections against man-in-the-middle (MITM) attacks by validating the public key presented by the remote SSH server. When enabled, WebSSH2 compares the server's host key against a known-good key before allowing the connection to proceed. This is the same trust-on-first-use (TOFU) model used by OpenSSH.
The feature is disabled by default and must be explicitly enabled in configuration.
Configuration
Add the hostKeyVerification block under ssh in config.json:
{
"ssh": {
"hostKeyVerification": {
"enabled": true,
"mode": "hybrid",
"unknownKeyAction": "prompt",
"serverStore": {
"enabled": true,
"dbPath": "/data/hostkeys.db"
},
"clientStore": {
"enabled": true
}
}
}
}
Modes of Operation
The mode setting is a shorthand that controls which key stores are active. Explicit serverStore.enabled and clientStore.enabled flags override the mode defaults when set.
| Mode | Server Store | Client Store | Description |
|------|:---:|:---:|---|
| server | on | off | Keys are verified exclusively against the server-side SQLite database. The client is never prompted. Best for locked-down environments where an administrator pre-seeds all host keys. |
| client | off | on | The server delegates verification to the browser client. The client stores accepted keys locally (e.g. in IndexedDB). Useful when no server-side database is available. |
| hybrid | on | on | The server store is checked first. If the key is unknown there, the client is asked. Provides server-enforced trust with client-side fallback for new hosts. (default) |
Unknown Key Actions
When a host key is not found in any enabled store, the unknownKeyAction setting determines what happens:
| Action | Behavior |
|--------|----------|
| prompt | Emit a hostkey:verify event to the client and wait for the user to accept or reject the key. Connection is blocked until the user responds or the 30-second timeout expires. (default) |
| alert | Emit a hostkey:alert event to the client as a notification, but allow the connection to proceed. The key is not stored; the alert will appear again on the next connection. |
| reject | Emit a hostkey:rejected event and refuse the connection immediately. Only pre-seeded keys in the server store will be accepted. |
Environment Variables
All host key settings can be configured via environment variables. Environment variables override config.json values.
| Variable | Config Path | Type | Default | Description |
|----------|-------------|------|---------|-------------|
| WEBSSH2_SSH_HOSTKEY_ENABLED | ssh.hostKeyVerification.enabled | boolean | false | Enable or disable host key verification |
| WEBSSH2_SSH_HOSTKEY_MODE | ssh.hostKeyVerification.mode | string | hybrid | Verification mode: server, client, or hybrid |
| WEBSSH2_SSH_HOSTKEY_UNKNOWN_ACTION | ssh.hostKeyVerification.unknownKeyAction | string | prompt | Action for unknown keys: prompt, alert, or reject |
| WEBSSH2_SSH_HOSTKEY_DB_PATH | ssh.hostKeyVerification.serverStore.dbPath | string | /data/hostkeys.db | Path to the SQLite host key database |
| WEBSSH2_SSH_HOSTKEY_SERVER_ENABLED | ssh.hostKeyVerification.serverStore.enabled | boolean | true | Enable the server-side SQLite store |
| WEBSSH2_SSH_HOSTKEY_CLIENT_ENABLED | ssh.hostKeyVerification.clientStore.enabled | boolean | true | Enable the client-side (browser) store |
SQLite Server Store Setup
The server store uses a SQLite database that is opened in read-only mode at runtime. You must create and populate the database ahead of time using the seeding script (see below).
Creating the database:
# Probe a host to create and populate the database
npm run hostkeys -- --host ssh.example.com
The script automatically creates the database file (and parent directories) at the configured dbPath if it does not exist.
Docker volume mounting:
When running in Docker, mount a volume to the directory containing your database so it persists across container restarts. The mount path must match the dbPath value in your configuration:
docker run --rm -p 2222:2222 \
-v /path/to/local/hostkeys:/data \
-e WEBSSH2_SSH_HOSTKEY_ENABLED=true \
-e WEBSSH2_SSH_HOSTKEY_DB_PATH=/data/hostkeys.db \
ghcr.io/billchurch/webssh2:latest
