Skip to content

Repository files navigation

🔐 Telegram SSH Gateway Bot

A multi-user SSH proxy built with aiogram 3.x and asyncssh.
Deploy this bot on any internet-accessible server and let users SSH into their own remote machines through Telegram — even under strict network censorship.


Architecture Overview

User A ──► Telegram ──► [Bot Server] ──► SSH ──► Server A
User B ──► Telegram ──► [Bot Server] ──► SSH ──► Server B
  • Each user gets a fully isolated session (keyed by user_id).
  • A persistent shell channel (not exec_command) keeps working directory, env variables, and aliases alive across messages.
  • All credentials live only in RAM and are wiped on disconnect or timeout.

File Structure

tg-ssh-gateway/
├── bot.py              # Entry point, aiogram dispatcher, startup/shutdown hooks
├── config.py           # All settings from environment variables
├── session_manager.py  # Per-user FSM, credential lifecycle, inactivity timer
├── ssh_client.py       # asyncssh wrapper: persistent PTY shell, sentinel trick, ANSI strip
├── handlers.py         # aiogram command + message handlers
├── utils.py            # Telegram output chunking / file-attachment strategy
├── requirements.txt
└── .env.example

Quick Start

1. Install dependencies

pip install -r requirements.txt

2. Configure the bot

cp .env.example .env
# Edit .env and set BOT_TOKEN to your token from @BotFather

3. Run

python bot.py

Bot Commands

Command Description
/start Show welcome message and quick-start guide
/connect Begin 4-step SSH credential collection and connect
/disconnect Gracefully close SSH connection and wipe credentials
/status Show current session state
(any text) Execute as a shell command on the remote server

Configuration Reference

All settings are read from environment variables (or a .env file):

Variable Default Description
BOT_TOKEN required Telegram bot token from @BotFather
CMD_TIMEOUT 30 Seconds before a hanging command is killed with SIGINT
SESSION_TIMEOUT 1800 Seconds of inactivity before auto-disconnect (30 min)
CHUNK_SIZE 4000 Max chars per Telegram message
MAX_OUTPUT_CHARS 100000 Threshold above which output is sent as a .txt file

Security Design

  • No local execution — zero calls to subprocess, os.system, eval, or exec.
  • Credentials in RAM only — never written to disk, DB, or logs.
  • Password auto-deletion — the password message is deleted from the Telegram chat immediately after reading.
  • Per-user isolation — strict user_id keying; no session data is ever shared.
  • Inactivity auto-disconnect — idle sessions are wiped after SESSION_TIMEOUT seconds.
  • Graceful shutdown — all SSH connections are closed and credentials wiped on SIGINT/SIGTERM.

How the Persistent Shell Works (Sentinel Trick)

User types: cd /var/log && ls

Bot sends to remote shell stdin:
    cd /var/log && ls
    echo "§EXIT§:$?:§END§"

Bot reads stdout until it sees "§END§":
    [command output lines]
    §EXIT§:0:§END§

Bot parses exit code = 0, strips sentinel line, sends output to user.

Shell state (current directory, env vars, etc.) is preserved because we never close the channel.


Output Strategy

Output size Telegram delivery
≤ 4 000 chars Single code-block message
4 001 – 100 000 chars Sequential chunked messages
> 100 000 chars output.txt file attachment

Deployment Tips

  • Run with systemd or screen/tmux to keep the bot alive.
  • The bot uses long-polling (no webhook setup needed).
  • No external database required — all state is in RAM.
  • To run as a systemd service, create /etc/systemd/system/tg-ssh-bot.service:
[Unit]
Description=Telegram SSH Gateway Bot
After=network.target

[Service]
WorkingDirectory=/opt/tg-ssh-gateway
ExecStart=/usr/bin/python3 bot.py
EnvironmentFile=/opt/tg-ssh-gateway/.env
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

About

A tool that makes access easier

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages