A web-based terminal that can connect to remote systems via SSH or Telnet using xterm.js and a Node.js proxy server.
- 🖥️ Full terminal emulation using xterm.js
- 🔐 SSH connections with username/password authentication
- 📡 Telnet connections
- 🌐 WebSocket-based real-time communication
- 📱 Responsive design that works on desktop and mobile
- ⚡ Real-time terminal resizing
- 🎨 Dark theme terminal interface
- Clone or download this project
- Install dependencies:
npm installBefore running the server, you need to configure the allowed hosts and security settings.
- Copy the example configuration:
cp config/gateway.json.example config/gateway.json- Edit
config/gateway.jsonto configure allowed hosts:
{
"allowedHosts": [
{
"host": "your-server.example.com",
"description": "My SSH Server",
"protocols": ["ssh", "telnet"],
"defaultPort": {
"ssh": 22,
"telnet": 23
}
}
],
"security": {
"requireHostValidation": true,
"allowPrivateNetworks": false,
"maxConnectionTime": 3600000,
"connectionTimeout": 30000
},
"logging": {
"logConnections": true,
"logLevel": "info"
}
}- host: The hostname or IP address to allow connections to
- description: Human-readable description of the host
- protocols: Array of allowed protocols (
["ssh", "telnet"]) - defaultPort: Default ports for each protocol
- requireHostValidation: Whether to enforce the allowedHosts list (default:
true) - allowPrivateNetworks: Allow connections to private IP ranges (default:
false) - maxConnectionTime: Maximum connection time in milliseconds
- connectionTimeout: Connection timeout in milliseconds
- logConnections: Whether to log connection attempts
- logLevel: Logging level (
"info","warn","error")
The php/index.php file provides a standalone client that can be used independently. To configure it:
- Edit the connection settings in
php/index.php:
// Pre-configured proxy server settings
const PROXY_HOST = 'localhost'; // Your gateway server host
const PROXY_PORT = 3000; // Your gateway server port
// Remote SSH/Telnet host settings
const REMOTE_HOST = 'your-server.example.com'; // Target server
const REMOTE_PORT = 22; // Target port (22 for SSH, 1337 for Telnet, etc.)- Update the connection type (SSH or Telnet):
// For SSH connections
socket.emit('connect-ssh', {
host: REMOTE_HOST,
port: REMOTE_PORT,
username: username,
password: password
});
// For Telnet connections
socket.emit('connect-telnet', {
host: REMOTE_HOST,
port: REMOTE_PORT
});The PHP client includes all required assets locally (php/assets/) so it works without internet connectivity.
-
Configure allowed hosts in
config/gateway.json -
Start the server:
npm start-
Open your browser and go to
http://localhost:3000 -
Connect to a remote system:
- SSH: Enter host, port (default 22), username, and password
- Telnet: Enter host and port (default 23)
Note: Only hosts listed in allowedHosts with the appropriate protocols will be permitted to connect.
For development with auto-restart:
npm run dev- Linux Server:
user@example.com:22 - Raspberry Pi:
pi@192.168.1.100:22 - Cloud Instance:
ubuntu@ec2-xxx.amazonaws.com:22
- Star Wars ASCII:
towel.blinkenlights.nl:23 - Weather Service:
rainmaker.wunderground.com:3000 - Local Router:
192.168.1.1:23
For production use, it's recommended to run the Terminal Gateway behind a reverse proxy for TLS termination and additional security.
Create a Caddyfile:
terminal.yourdomain.com {
reverse_proxy localhost:3000
# Optional: Add authentication
# basicauth {
# user $2a$14$...hashed-password...
# }
}Start Caddy:
caddy run --config CaddyfileCreate /etc/nginx/sites-available/terminal-gateway:
server {
listen 443 ssl http2;
server_name terminal.yourdomain.com;
ssl_certificate /path/to/your/certificate.crt;
ssl_certificate_key /path/to/your/private.key;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name terminal.yourdomain.com;
return 301 https://$server_name$request_uri;
}Enable the site:
sudo ln -s /etc/nginx/sites-available/terminal-gateway /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxCreate /etc/apache2/sites-available/terminal-gateway.conf:
<VirtualHost *:443>
ServerName terminal.yourdomain.com
SSLEngine on
SSLCertificateFile /path/to/your/certificate.crt
SSLCertificateKeyFile /path/to/your/private.key
ProxyPreserveHost On
ProxyRequests Off
# WebSocket support
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://localhost:3000/$1" [P,L]
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/
# Set headers for WebSocket
ProxyPassReverse / http://localhost:3000/
ProxyPass /socket.io/ http://localhost:3000/socket.io/
ProxyPassReverse /socket.io/ http://localhost:3000/socket.io/
</VirtualHost>
# Redirect HTTP to HTTPS
<VirtualHost *:80>
ServerName terminal.yourdomain.com
Redirect permanent / https://terminal.yourdomain.com/
</VirtualHost>Enable required modules and site:
sudo a2enmod ssl rewrite proxy proxy_http proxy_wstunnel
sudo a2ensite terminal-gateway
sudo systemctl reload apache2For production, set these environment variables:
export NODE_ENV=production
export PORT=3000
export HOST=localhost- Make the startup script executable:
chmod +x start-gateway.sh- Edit the script to set the correct path:
nano start-gateway.sh
# Change /path/to/terminalgateway to /home/webterminal- Add to crontab:
crontab -e
# Add this line:
@reboot /home/webterminal/start-gateway.sh- Create the user systemd directory and copy the service file:
mkdir -p ~/.config/systemd/user
cp terminal-gateway.service ~/.config/systemd/user/- Edit the service file to set the correct path:
nano ~/.config/systemd/user/terminal-gateway.service
# Update WorkingDirectory=/home/webterminal- Enable lingering (allows service to run without user login):
sudo loginctl enable-linger $USER- Enable and start the user service:
systemctl --user daemon-reload
systemctl --user enable terminal-gateway
systemctl --user start terminal-gateway- Check service status:
systemctl --user status terminal-gateway
journalctl --user -u terminal-gateway -fBenefits of user service:
- Runs under your user account (no need for dedicated user)
- Survives user logout (with lingering enabled)
- Uses your home directory path automatically
- Easier to manage and debug
- Install PM2 globally:
npm install -g pm2- Start the application:
pm2 start ecosystem.config.js --env production- Save PM2 configuration:
pm2 save
pm2 startup
# Follow the instructions to enable startup on boot- Manage the process:
pm2 status
pm2 logs terminal-gateway
pm2 restart terminal-gateway
pm2 stop terminal-gatewayCreate a Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
USER node
CMD ["npm", "start"]Build and run:
docker build -t terminal-gateway .
docker run -d --name terminal-gateway -p 3000:3000 terminal-gatewayWith Docker Compose (docker-compose.yml):
version: '3.8'
services:
terminal-gateway:
build: .
ports:
- "3000:3000"
restart: unless-stopped
volumes:
- ./config:/app/config:ro- Configure the
gateway.jsonallowedHosts list appropriately - Use HTTPS/WSS connections (via reverse proxy)
- Implement proper authentication at the reverse proxy level
- Add rate limiting
- Consider using SSH keys instead of passwords
- Monitor and log connections
- Keep the server updated
- Frontend: HTML5 + xterm.js + Socket.IO client
- Backend: Node.js + Express + Socket.IO + SSH2 + Telnet-client
- Communication: WebSocket for real-time terminal data
- Chrome/Chromium 60+
- Firefox 55+
- Safari 11+
- Edge 79+
express: Web server frameworksocket.io: Real-time WebSocket communicationssh2: SSH client implementationtelnet-client: Telnet client implementationxterm: Terminal emulator for the web
MIT License