Terminal Gateway

September 11, 2025 ยท View on GitHub

A web-based terminal that can connect to remote systems via SSH or Telnet using xterm.js and a Node.js proxy server.

Features

  • ๐Ÿ–ฅ๏ธ 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

Installation

  1. Clone or download this project
  2. Install dependencies:
npm install

Configuration

Before running the server, you need to configure the allowed hosts and security settings.

Gateway Configuration

  1. Copy the example configuration:
cp config/gateway.json.example config/gateway.json
  1. Edit config/gateway.json to 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"
  }
}

Configuration Options

allowedHosts

  • 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

security

  • 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

logging

  • logConnections: Whether to log connection attempts
  • logLevel: Logging level ("info", "warn", "error")

โš ๏ธ Important: The server must be restarted after changing the configuration file.

PHP Client Configuration

The php/index.php file provides a standalone client that can be used independently. To configure it:

  1. 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.)
  1. 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.

Usage

  1. Configure allowed hosts in config/gateway.json

  2. Start the server:

npm start
  1. Open your browser and go to http://localhost:3000

  2. 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.

Development

For development with auto-restart:

npm run dev

Example Connections

SSH Examples

  • Linux Server: user@example.com:22
  • Raspberry Pi: pi@192.168.1.100:22
  • Cloud Instance: ubuntu@ec2-xxx.amazonaws.com:22

Telnet Examples

  • Star Wars ASCII: towel.blinkenlights.nl:23
  • Weather Service: rainmaker.wunderground.com:3000
  • Local Router: 192.168.1.1:23

Production Deployment

For production use, it's recommended to run the Terminal Gateway behind a reverse proxy for TLS termination and additional security.

Caddy Configuration

Create a Caddyfile:

terminal.yourdomain.com {
    reverse_proxy localhost:3000
    
    # Optional: Add authentication
    # basicauth {
    #     user \$2a\$14$...hashed-password...
    # }
}

Start Caddy:

caddy run --config Caddyfile

Nginx Configuration

Create /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 nginx

Apache Configuration

Create /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 apache2

Environment Variables

For production, set these environment variables:

export NODE_ENV=production
export PORT=3000
export HOST=localhost

Automatic Startup

Option 1: Cron with @reboot (Simple)

  1. Make the startup script executable:
chmod +x start-gateway.sh
  1. Edit the script to set the correct path:
nano start-gateway.sh
# Change /path/to/terminalgateway to /home/webterminal
  1. Add to crontab:
crontab -e
# Add this line:
@reboot /home/webterminal/start-gateway.sh
  1. Create the user systemd directory and copy the service file:
mkdir -p ~/.config/systemd/user
cp terminal-gateway.service ~/.config/systemd/user/
  1. Edit the service file to set the correct path:
nano ~/.config/systemd/user/terminal-gateway.service
# Update WorkingDirectory=/home/webterminal
  1. Enable lingering (allows service to run without user login):
sudo loginctl enable-linger $USER
  1. Enable and start the user service:
systemctl --user daemon-reload
systemctl --user enable terminal-gateway
systemctl --user start terminal-gateway
  1. Check service status:
systemctl --user status terminal-gateway
journalctl --user -u terminal-gateway -f

Benefits 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

Option 3: PM2 Process Manager (Cross-platform)

  1. Install PM2 globally:
npm install -g pm2
  1. Start the application:
pm2 start ecosystem.config.js --env production
  1. Save PM2 configuration:
pm2 save
pm2 startup
# Follow the instructions to enable startup on boot
  1. Manage the process:
pm2 status
pm2 logs terminal-gateway
pm2 restart terminal-gateway
pm2 stop terminal-gateway

Option 4: Docker (Containerized)

Create 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-gateway

With Docker Compose (docker-compose.yml):

version: '3.8'
services:
  terminal-gateway:
    build: .
    ports:
      - "3000:3000"
    restart: unless-stopped
    volumes:
      - ./config:/app/config:ro

Security Notes

โš ๏ธ Important: This tool requires additional security considerations for production use:

  • Configure the gateway.json allowedHosts 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

Architecture

  • Frontend: HTML5 + xterm.js + Socket.IO client
  • Backend: Node.js + Express + Socket.IO + SSH2 + Telnet-client
  • Communication: WebSocket for real-time terminal data

Browser Support

  • Chrome/Chromium 60+
  • Firefox 55+
  • Safari 11+
  • Edge 79+

Dependencies

  • express: Web server framework
  • socket.io: Real-time WebSocket communication
  • ssh2: SSH client implementation
  • telnet-client: Telnet client implementation
  • xterm: Terminal emulator for the web

License

MIT License