ä¸ć–‡ | English
SSH server and tunnel management system with user authentication, tunnel management, and Web UI.
- Features
- Technology Stack
- Quick Start
- Usage
- Environment Variables
- Security
- API Endpoints
- Project Structure
- Docker Deployment
- Development
- License
- Contributing
- SSH server with password authentication
- Reverse proxy/tunnel management
- User registration, login, and authentication
- Web UI for tunnel management (create, edit, delete)
- Multi-language support (English, Chinese, Spanish, French, German, Japanese, Russian, Arabic)
- RTL language support (Arabic)
- Two-factor authentication (2FA) with TOTP
- Bandwidth monitoring and statistics
- Real-time tunnel analytics
- GeoIP-based access logging
- SSH command interface (CUI) with PTY support
- Theme customization (dark/light mode)
- Strict TypeScript type checking
- Next.js SSR mode frontend
- Neo-brutalism UI design
- Node.js + TypeScript
- ssh2 (SSH server)
- better-sqlite3 (data storage)
- bcrypt (password hashing)
- JWT (session management)
- Next.js 16 (SSR mode)
- React 19
- TypeScript
- Tailwind CSS 4
- Radix UI components (shadcn/ui)
- React Hook Form with Zod validation
- React i18next for internationalization
npm installnpm run devThis will start both:
- SSH server (port 2222)
- Web UI (port 3000)
npm run build
npm startVisit http://localhost:3000 and register a new account
After logging in, create tunnels in the Web UI:
- Name: Descriptive name for the tunnel
- Target Host: Target server address to forward to
- Target Port: Port on the target server
- Local Port: Local port on the SSH server
Connect to the server using an SSH client:
ssh -L [local_port]:[target_host]:[target_port] username@server_address -p 2222WEB_PORT: Web UI port (default: 3000)SSH_PORT: SSH server port (default: 2222)JWT_SECRET: JWT secret (required for production)
- Change default JWT_SECRET in production
- Use strong passwords
- Enable 2FA for additional security
POST /api/auth/register- User registrationPOST /api/auth/login- User loginPOST /api/auth/logout- User logoutPOST /api/auth/enable-otp- Enable 2FAPOST /api/auth/disable-otp- Disable 2FAPOST /api/auth/verify-otp- Verify 2FA token
GET /api/tunnels- Get user tunnel listPOST /api/tunnels- Create new tunnelPUT /api/tunnels/[id]- Update tunnelDELETE /api/tunnels/[id]- Delete tunnelGET /api/tunnels/[id]/bandwidth- Get tunnel bandwidth usageGET /api/tunnels/[id]/access-logs- Get tunnel access logsGET /api/tunnels/[id]/access-stats- Get tunnel access statistics
SSHBridge/
├── src/ # Backend source code
│ ├── database.ts # Database management and models
│ ├── ssh-server.ts # SSH server implementation
│ ├── server.ts # Main server entry point
│ ├── components/ # Shared UI components
│ ├── lib/ # Utility functions
│ ├── types/ # TypeScript type definitions
│ ├── utils/ # Utility functions
│ └── cui/ # SSH CUI (PTY) interface
├── pages/ # Next.js pages
│ ├── api/ # API routes
│ │ ├── auth/ # Authentication endpoints
│ │ ├── tunnels/ # Tunnel management endpoints
│ │ ├── stats/ # Statistics endpoints
│ │ └── settings/ # Settings endpoints
│ ├── _app.tsx # Next.js app configuration
│ ├── _document.tsx # Document configuration
│ ├── index.tsx # Main page
│ ├── stats.tsx # Statistics page
│ ├── settings.tsx # Settings page
│ └── account.tsx # Account page
├── components/ # React components
│ ├── AuthContext.tsx # Authentication context
│ ├── AuthForm.tsx # Login/Register form
│ ├── TunnelManager.tsx # Tunnel management interface
│ ├── BandwidthMonitor.tsx # Bandwidth monitoring
│ ├── TunnelStats.tsx # Tunnel statistics
│ ├── Settings.tsx # Settings modal
│ ├── LanguageContext.tsx # Language context
│ └── ThemeContext.tsx # Theme context
├── styles/ # CSS files
│ ├── globals.css # Global styles
│ └── neo-brutalism.css # Custom UI theme
├── lib/ # Frontend library functions
│ ├── i18n.ts # i18n configuration
│ ├── locales/ # Translation files
│ └── apiErrors.ts # API error handling
├── docs/ # Documentation
│ ├── pty-error-handling.md
│ └── timezone-handling.md
└── scripts/ # Utility scripts
- Docker installed and running
- Server access (able to bind ports 2222 and 3000)
docker run -d \
--name sshbridge \
--network host \
-e BASE_TUNNEL_HOST=your-server-ip \
-e SSH_PORT=2222 \
ghcr.io/meowlynxsea/sshbridge:maindocker run -d \
--name sshbridge \
--network host \
-e BASE_TUNNEL_HOST=your-server-ip \
-e SSH_PORT=2222 \
-e JWT_SECRET=your-jwt-secret-key \
-e WEB_PORT=3000 \
-e DATABASE_PATH=/app/data/database.sqlite \
-e HOST_KEY_PATH=/app/data/host.key \
-e NEXT_PUBLIC_FOOTER_TEXT="Your Custom Footer" \
-v /path/to/data:/app/data \
ghcr.io/meowlynxsea/sshbridge:mainBASE_TUNNEL_HOST: Base tunnel host address (usually your server IP)SSH_PORT: SSH server port (default: 2222)
JWT_SECRET: JWT signing key (must be set for production)WEB_PORT: Web interface port (default: 3000)DATABASE_PATH: Database file path (default: ./database.sqlite)HOST_KEY_PATH: SSH host key path (default: ./host.key)NEXT_PUBLIC_FOOTER_TEXT: Custom footer text
Create a docker-compose.yml file:
version: '3.8'
services:
sshbridge:
image: ghcr.io/meowlynxsea/sshbridge:main
container_name: sshbridge
network_mode: host
restart: unless-stopped
environment:
- BASE_TUNNEL_HOST=your-server-ip
- SSH_PORT=2222
- JWT_SECRET=your-jwt-secret-key
- WEB_PORT=3000
- DATABASE_PATH=/app/data/database.sqlite
- HOST_KEY_PATH=/app/data/host.key
- NEXT_PUBLIC_FOOTER_TEXT=Your Custom Footer
volumes:
- ./sshbridge-data:/app/dataStart the service:
docker-compose up -dImportant: You must persist data files to avoid data loss
# Create data directory
mkdir -p ./sshbridge-data
# Start container with data volume mounted
docker run -d \
--name sshbridge \
--network host \
-e BASE_TUNNEL_HOST=your-server-ip \
-e SSH_PORT=2222 \
-e DATABASE_PATH=/app/data/database.sqlite \
-e HOST_KEY_PATH=/app/data/host.key \
-v $(pwd)/sshbridge-data:/app/data \
ghcr.io/meowlynxsea/sshbridge:mainBy default, data inside Docker containers is lost when the container is removed. SSHBridge requires persistence for the following critical files:
- Database file (
database.sqlite): Contains user accounts, tunnel configurations, and statistics - SSH host key (
host.key): SSH server authentication key
If these files are not persisted, each container restart will:
- Lose all user data
- Reset tunnel configurations
- Generate new SSH host keys (causing client connection warnings)
1. Using Data Volumes (Recommended)
# Create a Docker named volume
docker volume create sshbridge-data
docker run -d \
--name sshbridge \
--network host \
-e BASE_TUNNEL_HOST=your-server-ip \
-e SSH_PORT=2222 \
-e DATABASE_PATH=/app/data/database.sqlite \
-e HOST_KEY_PATH=/app/data/host.key \
-v sshbridge-data:/app/data \
ghcr.io/meowlynxsea/sshbridge:main2. Using Bind Mounts
# Create data directory on the host
mkdir -p /opt/sshbridge-data
chmod 755 /opt/sshbridge-data
docker run -d \
--name sshbridge \
--network host \
-e BASE_TUNNEL_HOST=your-server-ip \
-e SSH_PORT=2222 \
-e DATABASE_PATH=/app/data/database.sqlite \
-e HOST_KEY_PATH=/app/data/host.key \
-v /opt/sshbridge-data:/app/data \
ghcr.io/meowlynxsea/sshbridge:main3. Backup Strategy
# Backup data directory
tar -czf sshbridge-backup-$(date +%Y%m%d).tar.gz ./sshbridge-data
# Restore data directory
tar -xzf sshbridge-backup-YYYYMMDD.tar.gz-
Check container status:
docker ps | grep sshbridge -
View logs:
docker logs sshbridge
-
Access Web Interface: Open your browser and navigate to
http://your-server-ip:3000 -
Test SSH Connection:
ssh -p 2222 username@your-server-ip
-
Verify Data Persistence:
# Check if data files exist ls -la ./sshbridge-data/ # You should see database.sqlite and host.key files
-
Must set the
JWT_SECRETenvironment variable with a strong random string:JWT_SECRET=$(openssl rand -base64 32) -
Ensure data directory permissions are correct:
chown -R 1000:1000 ./sshbridge-data
-
Consider using firewall to restrict port access:
# Ubuntu/Debian ufw allow 2222/tcp ufw allow 3000/tcp # CentOS/RHEL firewall-cmd --permanent --add-port=2222/tcp firewall-cmd --permanent --add-port=3000/tcp firewall-cmd --reload
# Check container logs
docker logs sshbridge
# Check if ports are already in use
netstat -tulpn | grep -E ":(2222|3000)"- Verify firewall settings
- Check
WEB_PORTenvironment variable configuration - Confirm container is running
- Confirm
SSH_PORTenvironment variable configuration - Check network connectivity
- Verify user credentials
# Check data directory
docker exec sshbridge ls -la /app/data/
# Check mount points
docker inspect sshbridge | grep -A 5 -B 5 Mounts# Backup data (important!)
tar -czf sshbridge-backup-$(date +%Y%m%d).tar.gz ./sshbridge-data
# Stop and remove existing container
docker stop sshbridge && docker rm sshbridge
# Pull latest image
docker pull ghcr.io/meowlynxsea/sshbridge:main
# Restart with the same commandThe project uses strict TypeScript configuration. All code must pass type checking.
- ESLint with TypeScript rules
- Prettier for formatting
- Husky pre-commit hooks
- Conventional Git commits
The server automatically forwards incoming SSH connections to specified target hosts and ports based on user-configured tunnel information. Users don't need to specify forwarding parameters when connecting.
# Run type checking
npm run type-check
# Run linting
npm run lint
# Fix linting issues
npm run lint:fix
# Format code
npm run formatAGPL v3
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests and linting
- Submit a pull request