DEV Community

Ebrahim Shafiei
Ebrahim Shafiei

Posted on

Build a Self-Hosted SSH Tunneling Server with Per-User Accounting (Abdal 4iProto)

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
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

On Windows, swap the shell:

{
  "ports": [64235, 64236, 64237],
  "shell": "cmd.exe",
  "max_auth_attempts": 3,
  "server_version": "SSH-2.0-Abdal-4iProto-Server"
}
Enter fullscreen mode Exit fullscreen mode

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
  }
]
Enter fullscreen mode Exit fullscreen mode

Key fields to understand:

  • role: admin gets host shell access; user is tunnel-only. Keep day-to-day accounts as user.
  • 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

⚠️ 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>.json every 10 seconds, so a crash costs at most a few seconds of stats.
  • Failed logins go to invalid_logins.log with the attempted credentials and IP; blocked IPs persist in blocked_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)