βββββββββββββββββββββββββββββββββββ
β βββββ β
β β ⬑ β 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 |
| β‘ 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
- Rust 1.70+ (install via rustup)
- Target servers accessible via SSH from the proxy host
git clone https://github.com/mac119/ssh_proxy.git
cd ssh_proxy
cargo build --release[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:
cargo run --bin 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"# Development
cargo run
# Production
./target/release/ssh_proxyssh 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.
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 |
{"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.