Skip to content

mac119/ssh_proxy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

16 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚         ╔═══╗                   β”‚
    β”‚         β•‘ ⬑ β•‘  SSH GUARD        β”‚
    β”‚         β•šβ•β•β•β•                   β”‚
    β”‚    β”Œβ”€β”€β”€β”       β”Œβ”€β”€β”€β”  β”Œβ”€β”€β”€β”    β”‚
    β”‚    β”‚ U β”œβ”€β”€β†’ P ── T β”‚  β”‚ T β”‚    β”‚
    β”‚    β””β”€β”€β”€β”˜  ↕    β””β”€β”€β”€β”˜  β””β”€β”€β”€β”˜    β”‚
    β”‚         AUDIT                   β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ›‘οΈ SSH Guard Proxy

A high-performance SSH proxy gateway built in Rust for security auditing, access control, and session recording.

Rust License: MIT tokio

Every keystroke recorded. Every connection authorized. Every session auditable.


🎯 Why SSH Guard Proxy?

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.

The Problem

  • 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

The Solution

Developer β†’ SSH Guard Proxy β†’ Target Server
                ↓
          Audit Log (every keystroke)

✨ Features

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

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      SSH Guard Proxy                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                   β”‚
β”‚   User ──SSH──→ [SSH Server] ──→ [Auth & ACL] ──→ [Session Mgr]  β”‚
β”‚                                                        β”‚          β”‚
β”‚                                                        β–Ό          β”‚
β”‚                  [Audit Logger] ◀────────────── [SSH Client] ──→ Target
β”‚                       β”‚                                           β”‚
β”‚                       β–Ό                                           β”‚
β”‚                 logs/audit.jsonl                                   β”‚
β”‚                                                                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Data Flow

  1. User connects: ssh admin@proxy -p 2222
  2. Proxy authenticates the user (password or public key)
  3. User selects a target host from the authorized list
  4. Proxy establishes SSH connection to target
  5. All data is bidirectionally forwarded and logged
  6. Session ends β†’ audit record finalized

πŸš€ Quick Start

Installation

Option 1: Download Pre-built Binary (Recommended)

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_password

The release package includes:

  • ssh_proxy β€” Main proxy binary
  • hash_password β€” Password hash generator tool
  • config/ β€” Configuration templates

Option 2: Build from Source

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

Configure

1. Proxy Settings (config/proxy.toml)

[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 = 300

2. Add Users (config/users.toml)

Generate 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

3. Add Target Hosts (config/hosts.toml)

[[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"

Run

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

Connect

ssh admin@your-proxy-host -p 2222

You'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.

File Transfer (SCP/SFTP)

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 a File

# 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 a File

# 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 ./

Legacy SCP Mode

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/

Target Host Selection

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

SCP Audit Log Events

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"}

Session Sharing (Live Watch)

Admins with watch permission can observe another user's active session in real-time (read-only).

Enable Watch Permission

# config/users.toml
[[users]]
name = "admin"
can_watch_sessions = true
watch_allowed_users = ["*"]  # "*" = all users, or specific names

Usage

  1. Connect to the proxy: ssh admin@proxy-host -p 2222
  2. 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
    
  3. 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
    
  4. You now see the session output in real-time. Press Ctrl+C to stop watching.

Notes

  • 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
image
  • then execute ls command
image
  • Duplicate the terminal and log in to the ssh-proxy server. We will notice a ls command is being executed
image

πŸ“Š Audit Logs

All audit data is stored in logs/audit.jsonl in append-only JSON Lines format.

Log Events

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)

Example Log Entries

{"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"}

Decoding Commands from Logs

# 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 -l

Session Replay

Sessions are recorded in asciicast v2 format:

# Replay a recorded session
asciinema play logs/sessions/<session_id>.cast

🏒 Production Deployment

Systemd Service

Create /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.target
sudo systemctl enable ssh-guard-proxy
sudo systemctl start ssh-guard-proxy

File Permissions

chmod 600 config/host_key
chmod 600 config/keys/*
chmod 644 config/*.toml
chmod 700 logs/

Log Rotation

Add to /etc/logrotate.d/ssh-guard-proxy:

/opt/ssh_proxy/logs/audit.jsonl {
    daily
    rotate 90
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}

Firewall

# 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 22

πŸ“ Project Structure

ssh_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

πŸ”§ Technology Stack

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

πŸ—ΊοΈ Roadmap

  • 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)

🀝 Contributing

Contributions are welcome! Please open an issue first to discuss what you'd like to change.


πŸ“„ License

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.

About

πŸ›‘οΈ A high-performance SSH proxy gateway built in Rust β€” unified authentication, per-user access control (ACL), full session audit logging, and terminal replay. Secure your infrastructure without modifying target servers.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages