Build a Self-Hosted SSH Tunneling Server with Per-User Accounting
If you want a self-hosted SSH tunneling server that handles bandwidth limits, session control, and brute-force protection without you scripting any of it, this walkthrough is for you. We'll set up Abdal 4iProto Server — a Go-based tunnel by Ebrahim Shafiei (EbraSha) — from build to first connection, and I'll explain the config decisions along the way so you're not just copy-pasting blindly.
Heads up: this server is part of the broader Abdal 4iProto ecosystem (https://github.com/4iProto), so there are matching clients and tools once your server is up.
What you'll have at the end
A running tunnel server with per-user speed caps, total-traffic quotas, concurrent-session limits, automatic IP blocking on failed logins, and a SOCKS5 endpoint your apps can use. Let's go.
Step 1: Prerequisites
You need:
- Go 1.25.10 or higher
- An SSH private key (
id_rsa) - A Linux or Windows host
Step 2: Generate your host key
ssh-keygen -t rsa -b 4096 -f id_rsa
Drop the resulting id_rsa in the project directory.
Step 3: Write the server config
Create server_config.json. On Linux:
{
"ports": [64235, 64236, 64237],
"shell": "/bin/bash",
"max_auth_attempts": 3,
"server_version": "SSH-2.0-Abdal-4iProto-Server"
}
On Windows, swap the shell:
{
"ports": [64235, 64236, 64237],
"shell": "cmd.exe",
"max_auth_attempts": 3,
"server_version": "SSH-2.0-Abdal-4iProto-Server"
}
The ports array makes the server listen on several ports at once. max_auth_attempts: 3 means three strikes and the IP is auto-blocked. server_version customizes the banner.
Step 4: Define your users
Create users.json. Here's an admin and a rate-limited user:
[
{
"username": "ebrasha",
"password": "change-me",
"role": "admin",
"blocked_domains": [],
"blocked_ips": [],
"log": "no",
"max_sessions": 1,
"session_ttl_seconds": 300
},
{
"username": "user1",
"password": "change-me-too",
"role": "user",
"blocked_domains": ["facebook.com", "*.facebook.com"],
"blocked_ips": ["10.0.0.*", "172.16.*.*"],
"log": "yes",
"max_sessions": 2,
"session_ttl_seconds": 300
}
]
Key fields to understand:
-
role:admingets host shell access;useris tunnel-only. Keep day-to-day accounts asuser. -
blocked_domains/blocked_ips: support wildcards (*.facebook.com) and ranges (10.0.0.*). -
log: per-user visited-site tracking —"yes"or"no". -
max_sessions: concurrent session ceiling. -
session_ttl_seconds: hard session lifetime before auto-reap.
To add bandwidth control, set max_speed_kbps (e.g. 1024 = 1 MB/s, enforced via token bucket on both directions) and max_total_mb (e.g. 10240 = 10 GB total quota). Quotas are checked every 1–2 seconds, and over-quota sessions are disconnected immediately — not just refused at next login.
Step 5: Build and run
go mod tidy
go build -o abdal-4iproto-server
./abdal-4iproto-server
That's it — the server is listening.
Step 6: Connect
The quickest test is a standard dynamic-forwarding SSH client:
ssh -D 1080 username@server_ip -p 22
Now point any app at the local SOCKS5 proxy and your traffic tunnels through. On Linux you can route everything with sshuttle:
sshuttle --dns -r ebrasha@SERVER_IP:2222 0.0.0.0/0 -vv
For a GUI experience, grab the official clients: Windows and Android.
Optional: relay traffic through another host with iptables NAT
If you need a relay in front of your server, enable forwarding and NAT:
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p
iptables -t nat -A PREROUTING -p tcp --dport 22 -j DNAT --to-destination IRAN_IP
iptables -t nat -A PREROUTING -j DNAT --to-destination 4iProto_IP
iptables -t nat -A POSTROUTING -j MASQUERADE
⚠️ Gotcha: the SSH-port DNAT rule must come before the general PREROUTING rule, and confirm you have alternate SSH access before applying these — it's an easy way to lock yourself out.
What's happening under the hood
A few implementation notes worth knowing as you operate this:
- Rate limiting uses a token-bucket algorithm, which smooths throughput while allowing short bursts — friendlier to interactive sessions than a hard window.
- Traffic accounting flushes to
traffic_<username>.jsonevery 10 seconds, so a crash costs at most a few seconds of stats. - Failed logins go to
invalid_logins.logwith the attempted credentials and IP; blocked IPs persist inblocked_ips.json. - TCP and UDP forwarding are both supported, plus DNSTT for DNS tunneling on hostile networks.
The full ecosystem
| Component | Repository |
|---|---|
| 🔐 Server | abdal-4iproto-server |
| ⚙️ Web Panel | abdal-4iproto-panel |
| 💻 Windows Client | abdal-4iproto-client |
| 📱 Android Client | abdal-4iproto-client-android |
| 🖱️ CLI Installer | abdal-4iproto-cli |
| 🗝️ SSH KeyGen | abdal-4iproto-server-ssh-keygen |
Tip: the CLI installer automates OS/arch detection, SHA-256 verification, port config, key generation, and service registration if you'd rather skip the manual build.
🔗 Source: https://github.com/ebrasha/abdal-4iproto-server
FAQ
Do I need the GUI clients? No — any SSH client with -D dynamic forwarding works. The clients just make it nicer.
How do I cap a user's speed? Add max_speed_kbps to their users.json entry (1024 = 1 MB/s).
What happens when a user hits their data quota? They're disconnected mid-session within 1–2 seconds and refused at next login until the quota resets.
Top comments (0)