โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โโโโโ โ
โ โ โฌก โ SSH GUARD โ
โ โโโโโ โ
โ โโโโโ โโโโโ โโโโโ โ
โ โ U โโโโ P โโค T โ โ T โ โ
โ โโโโโ โ โโโโโ โโโโโ โ
โ AUDIT โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
A high-performance SSH proxy gateway built in Rust for security auditing, access control, and session recording.
Every keystroke recorded. Every connection authorized. Every session auditable.
In modern infrastructure, direct SSH access to production servers is a security risk. SSH Guard Proxy solves this by acting as a single point of entry โ a bastion host that enforces authentication, authorization, and full audit logging for every SSH session.
- Developers SSH directly into production servers with no oversight
- No centralized record of who did what and when
- Shared credentials make accountability impossible
- Revoking access requires touching every server
Developer โ SSH Guard Proxy โ Target Server
โ
Audit Log (every keystroke)
| Feature | Description |
|---|---|
| ๐ Unified Authentication | Password (Argon2id) and public key auth at the gateway |
| ๐๏ธ Access Control (ACL) | Per-user host access policies โ who can access what |
| ๐ Full Audit Logging | Every input/output recorded in JSON Lines format |
| ๐ฌ Session Recording | Asciicast v2 format โ replay any session with asciinema |
| ๐ซ Command Filtering | Blacklist/whitelist mode โ block dangerous commands in real-time |
| ๐ SCP/SFTP Auditing | Full file transfer logging with filenames, sizes, and direction |
| ๐๏ธ Session Sharing | Multiple admins can watch a live session in real-time (read-only) |
| โก High Performance | Built on Rust + Tokio async runtime โ minimal overhead |
| ๐๏ธ Zero Target Changes | No agent or modification needed on target servers |
| ๐ Host Key Auto-generation | Ed25519 host keys generated on first run |
| ๐ฆ Connection Limiting | Max session control and auth failure lockout |
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SSH Guard Proxy โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ User โโSSHโโโ [SSH Server] โโโ [Auth & ACL] โโโ [Session Mgr] โ
โ โ โ
โ โผ โ
โ [Audit Logger] โโโโโโโโโโโโโโโ [SSH Client] โโโ Target
โ โ โ
โ โผ โ
โ logs/audit.jsonl โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- User connects:
ssh admin@proxy -p 2222 - Proxy authenticates the user (password or public key)
- User selects a target host from the authorized list
- Proxy establishes SSH connection to target
- All data is bidirectionally forwarded and logged
- Session ends โ audit record finalized
Download the latest release from the Releases page:
# Download and extract (example for macOS arm64)
curl -L https://github.com/mac119/ssh_proxy/releases/download/v0.1.0/ssh-guard-proxy-v0.1.0-darwin-arm64.tar.gz | tar xz
# Or manually download, extract, and set permissions
chmod +x ssh_proxy hash_passwordThe release package includes:
ssh_proxyโ Main proxy binaryhash_passwordโ Password hash generator toolconfig/โ Configuration templates
Requires Rust 1.70+ (install via rustup):
git clone https://github.com/mac119/ssh_proxy.git
cd ssh_proxy
cargo build --release
# Binaries at: target/release/ssh_proxy, target/release/hash_password[server]
listen_address = "0.0.0.0"
listen_port = 2222
host_key_path = "config/host_key"
[session]
idle_timeout_secs = 1800
max_sessions = 100
[audit]
log_dir = "logs"
record_session = true
[security]
max_auth_attempts = 3
lockout_duration_secs = 300Generate a password hash first:
./hash_password 'YourSecurePassword'
# Output: $argon2id$v=19$m=19456,t=2,p=1$...Then add to config:
[[users]]
name = "admin"
password_hash = "$argon2id$v=19$m=19456,t=2,p=1$..."
public_keys = []
allowed_hosts = ["*"] # Access to all hosts
[[users]]
name = "developer"
password_hash = "$argon2id$v=19$m=19456,t=2,p=1$..."
public_keys = ["ssh-ed25519 AAAAC3Nza..."]
allowed_hosts = ["web-01", "web-02"] # Restricted access[[hosts]]
name = "web-01"
address = "192.168.1.10"
port = 22
username = "deploy"
auth_method = "key"
private_key_path = "config/keys/web-01"
[[hosts]]
name = "db-01"
address = "10.0.0.50"
port = 22
username = "dbadmin"
auth_method = "password"
password = "encrypted:your_password_here"# Foreground (for testing)
./ssh_proxy
# Background with nohup
nohup ./ssh_proxy > /var/log/ssh_proxy.log 2>&1 &
# Background with output to file (recommended for debugging)
./ssh_proxy >> logs/proxy.log 2>&1 &
echo $! > ssh_proxy.pid # Save PID for later stop
# Stop the proxy
kill $(cat ssh_proxy.pid)Note: In production, use systemd (see Production Deployment below) for auto-restart, log management, and proper signal handling.
ssh admin@your-proxy-host -p 2222You'll see:
Welcome, admin! Available hosts:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[1] web-01 (192.168.1.10:22)
[2] web-02 (192.168.1.11:22)
[3] db-01 (10.0.0.50:22)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Select host number:
Select a host and you're in โ fully transparent, fully audited.
SSH Guard Proxy supports SCP and SFTP file transfers with full audit logging. All file transfers are recorded โ including filenames, sizes, direction, and timestamps.
# Upload to the default target host (first allowed host)
scp -P 2222 myfile.txt admin@proxy-host:/tmp/
# Upload to a specific target host (use user%host format)
scp -P 2222 myfile.txt admin%db-server-01@proxy-host:/tmp/
# Recursive directory upload
scp -r -P 2222 ./my-folder admin%web-server-01@proxy-host:/opt/# Download from the default target host
scp -P 2222 admin@proxy-host:/etc/hosts ./
# Download from a specific target host
scp -P 2222 admin%db-server-01@proxy-host:/var/log/app.log ./Modern OpenSSH (9.0+) uses SFTP by default for scp commands. Both modes are fully supported:
# Default (SFTP mode) โ works out of the box
scp -P 2222 file.txt admin@proxy-host:/tmp/
# Force legacy SCP protocol (if needed)
scp -O -P 2222 file.txt admin@proxy-host:/tmp/| Method | Example | Description |
|---|---|---|
| Default | admin@proxy |
Uses the first allowed host from ACL |
| Explicit | admin%db-server-01@proxy |
Specifies exact target host by name |
All file transfers generate audit entries:
{"event":"scp_session_start","session_id":"...","user":"admin","direction":"upload","target_host":"db-server-01","remote_path":"/tmp/"}
{"event":"scp_file_transfer","session_id":"...","user":"admin","direction":"upload","filename":"myfile.txt","size":10240,"mode":"0644"}Admins with watch permission can observe another user's active session in real-time (read-only).
# config/users.toml
[[users]]
name = "admin"
can_watch_sessions = true
watch_allowed_users = ["*"] # "*" = all users, or specific names- Connect to the proxy:
ssh admin@proxy-host -p 2222 - At the host selection menu, enter
w:Welcome, admin! Available hosts: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ [1] web-server-01 (192.168.1.10:22) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ [w] Watch active session โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Select host number: w - Select a session to watch:
Active sessions: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ [1] user=developer target=web-server-01 (5m ago, 0 watchers) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Select session number (q to cancel): 1 - You now see the session output in real-time. Press
Ctrl+Cto stop watching.
- Watchers are read-only โ no input is sent to the watched session
- Multiple admins can watch the same session simultaneously
- All watch events are audit-logged (
session_watch_start,session_watch_end)
Demo
- log in to the remote server
- then execute
lscommand
- Duplicate the terminal and log in to the ssh-proxy server. We will notice a
lscommand is being executed
All audit data is stored in logs/audit.jsonl in append-only JSON Lines format.
| Event | Description |
|---|---|
auth_success |
Successful authentication |
auth_failure |
Failed authentication attempt |
session_start |
User connected to a target host |
session_end |
Session terminated |
data (input) |
User keystrokes / commands |
data (output) |
Server responses |
command_blocked |
Command rejected by filter |
scp_session_start |
SCP/SFTP transfer session initiated |
scp_file_transfer |
File transferred (name, size, direction) |
session_watch_start |
Admin started watching a session |
session_watch_end |
Admin stopped watching (with duration) |
{"event":"auth_success","timestamp":"2026-05-05T07:10:00Z","user":"admin","peer_addr":"10.0.1.5:54321","method":"password"}
{"event":"session_start","timestamp":"2026-05-05T07:10:05Z","session_id":"a1b2c3d4","user":"admin","peer_addr":"10.0.1.5:54321","target_host":"web-01","target_addr":"192.168.1.10"}
{"event":"data","timestamp":"2026-05-05T07:10:10Z","session_id":"a1b2c3d4","direction":"input","data_base64":"bHMgLWxhCg==","data_len":7}
{"event":"data","timestamp":"2026-05-05T07:10:10Z","session_id":"a1b2c3d4","direction":"output","data_base64":"dG90YWwgNDgK...","data_len":256}
{"event":"session_end","timestamp":"2026-05-05T07:45:00Z","session_id":"a1b2c3d4"}# View all input commands from a session
grep '"direction":"input"' logs/audit.jsonl | \
jq -r '.data_base64' | \
while read line; do echo "$line" | base64 -d; done
# Find who connected today
grep '"event":"session_start"' logs/audit.jsonl | \
grep "$(date +%Y-%m-%d)" | \
jq '{user, target_host, timestamp}'
# Count failed logins
grep '"event":"auth_failure"' logs/audit.jsonl | wc -lSessions are recorded in asciicast v2 format:
# Replay a recorded session
asciinema play logs/sessions/<session_id>.castCreate /etc/systemd/system/ssh-guard-proxy.service:
[Unit]
Description=SSH Guard Proxy
After=network.target
[Service]
Type=simple
User=sshproxy
Group=sshproxy
WorkingDirectory=/opt/ssh_proxy
ExecStart=/opt/ssh_proxy/ssh_proxy
Restart=always
RestartSec=5
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/opt/ssh_proxy/logs
[Install]
WantedBy=multi-user.targetsudo systemctl enable ssh-guard-proxy
sudo systemctl start ssh-guard-proxychmod 600 config/host_key
chmod 600 config/keys/*
chmod 644 config/*.toml
chmod 700 logs/Add to /etc/logrotate.d/ssh-guard-proxy:
/opt/ssh_proxy/logs/audit.jsonl {
daily
rotate 90
compress
delaycompress
missingok
notifempty
copytruncate
}
# Only expose the proxy port
ufw allow 2222/tcp
# Block direct SSH to target hosts from outside
ufw deny from any to 192.168.1.0/24 port 22ssh_proxy/
โโโ Cargo.toml # Dependencies & build config
โโโ config/
โ โโโ proxy.toml # Main proxy configuration
โ โโโ users.toml # User accounts & ACL
โ โโโ hosts.toml # Target host definitions
โ โโโ host_key # Auto-generated Ed25519 host key
โโโ src/
โ โโโ main.rs # Entry point
โ โโโ config.rs # Configuration loading
โ โโโ server/
โ โ โโโ mod.rs # TCP listener & session spawning
โ โ โโโ handler.rs # SSH protocol handler (auth, data relay)
โ โโโ client/
โ โ โโโ mod.rs # SSH client (connects to targets)
โ โโโ auth/
โ โ โโโ mod.rs # Authentication (Argon2id, pubkey)
โ โ โโโ acl.rs # Access control logic
โ โโโ session/
โ โ โโโ mod.rs # Session lifecycle management
โ โโโ audit/
โ โโโ mod.rs # Audit event logger
โ โโโ recorder.rs # Asciicast session recorder
โโโ src/bin/
โ โโโ hash_password.rs # CLI tool to generate password hashes
โโโ logs/ # Audit output directory
| Component | Choice | Rationale |
|---|---|---|
| Language | Rust | Memory safety, zero-cost abstractions, fearless concurrency |
| SSH Protocol | russh | Native async SSH implementation (server + client) |
| Async Runtime | Tokio | Industry-standard, battle-tested async runtime |
| Password Hashing | Argon2id | Winner of Password Hashing Competition |
| Logging | tracing | Structured, async-aware instrumentation |
| Config | TOML + serde | Human-readable, type-safe configuration |
- Web management UI (live sessions, replay, user management)
- Database backend (PostgreSQL/SQLite for config & logs)
- Multi-factor authentication (TOTP/WebAuthn)
- Command blacklist/whitelist filtering
- SCP/SFTP file transfer auditing
- Cluster mode with load balancing
- Real-time alerting (Slack/webhook on suspicious activity)
- Session sharing (multiple admins watching one session)
Contributions are welcome! Please open an issue first to discuss what you'd like to change.
This project is licensed under the MIT License โ see the LICENSE file for details.
Built with ๐ฆ Rust for maximum performance and safety.
SSH Guard Proxy โ Because security shouldn't be an afterthought.