Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SimpleProtocol Server

A lightweight, single-binary encrypted proxy server written in Go. Designed to work with SimpleProtocol-compatible clients (Android / iOS).

Features

  • Zero-config — auto-generates a 256-bit PSK on first run, persists to a stable file (~/.simpleproxy/psk.hex by default)
  • Single binary — no dependencies, no config files needed
  • QR code — prints a scannable QR code containing the connection URI on startup
  • AES-256-GCM encrypted framed transport with HKDF-SHA256 key derivation
  • HMAC-SHA256 handshake authentication with timestamp verification (±30s window)
  • Anti-replay nonce cache to prevent replay attacks
  • Anti-probe — on auth failure, sends random data before closing (doesn't reveal it's a proxy)
  • Bidirectional relay — supports any TCP traffic (HTTP, HTTPS, etc.)
  • Foreground / daemon mode — run with -fg for console logging, or daemonise automatically
  • Smart IP detection — tries overseas + CN-accessible IP services with automatic fallback
  • Environment variable override — set SP_SERVER_IP to skip auto-detection (useful for NAT/CDN/domain)

Protocol

Client → Server Handshake:
  [nonce 32B] [timestamp 8B BE] [HMAC-SHA256 32B] [pad_len 2B BE] [padding 32-256B]

Key Derivation (HKDF-SHA256):
  c2s_key = HKDF(psk, nonce, "simple-c2s", 32)
  s2c_key = HKDF(psk, nonce, "simple-s2c", 32)

Encrypted Frame Format:
  [encrypted_length (2+16 bytes)] [encrypted_payload (N+16 bytes)]

Nonce Construction (12 bytes):
  [4-byte prefix from handshake nonce] [8-byte big-endian counter]
  Length frame uses counter*2, payload frame uses counter*2+1

First encrypted frame = SOCKS5-style target address:
  Type 0x01 (IPv4): [0x01] [4B IP] [2B port BE]
  Type 0x03 (Domain): [0x03] [1B len] [domain] [2B port BE]
  Type 0x04 (IPv6): [0x04] [16B IP] [2B port BE]

Connection URI

simple://BASE64URL(port:hex_psk:host)#ServerName

Example: simple://MjMzMzM6YWJjZDEyMzQuLi46MzUuMTkyLjU4LjIxNA#MyServer

Clients scan this URI via QR code or import directly.

Requirements

  • Go 1.20+ (for building)
  • Linux / macOS / Windows (any platform Go supports)

Build

# Clone and build
cd simpleserver
go mod tidy
go build -o simpleserver .

# Cross-compile for Linux
GOOS=linux GOARCH=amd64 go build -o simpleserver-linux-amd64 .

Usage

# First run — generates PSK, prints QR code, starts daemon
./simpleserver -p 23333

# Foreground mode (recommended for debugging, logs to stdout)
./simpleserver -fg -p 23333

# Custom port
./simpleserver -p 8443

# Custom PSK file path (recommended for scripted deployments)
./simpleserver -p 23333 -psk-file /etc/simpleproxy/psk.hex

Command-line flags

Flag Default Description
-p 23333 Listen port
-fg false Run in foreground (don't daemonise)
-psk-file ~/.simpleproxy/psk.hex Persistent PSK file path

Environment variables

Variable Default Description
SP_SERVER_IP (empty) Manually specify server IP or domain. When set, skips auto-detection entirely. Useful for NAT/CDN/domain setups.
SP_VERBOSE_LOG 0 Set to 1 to enable verbose relay logging (per-connection byte counts, etc.)
# Example: manually set server IP (skips all auto-detection)
SP_SERVER_IP=my.domain.com ./simpleserver -fg -p 23333

# Example: use a specific IP behind NAT
SP_SERVER_IP=203.0.113.50 ./simpleserver -p 23333

# Example: enable verbose logging
SP_VERBOSE_LOG=1 ./simpleserver -fg -p 23333

IP auto-detection strategy

When SP_SERVER_IP is not set, the server automatically detects your public IP using multiple services with fallback:

Priority IPv4 Service IPv6 Service
1 (overseas) api.ipify.org api6.ipify.org
2 (overseas) ifconfig.me/ip
3 (CN-accessible) myip.ipip.net/ip 6.ipw.cn
4 (CN-accessible) 4.ipw.cn

IPv4 and IPv6 detection run in parallel. If all services fail, the server will interactively prompt you to enter your IP (or you can set SP_SERVER_IP to avoid the prompt).

First run output

  ╔═══════════════════════════════════════════╗
  ║         SimpleProtocol Server             ║
  ╚═══════════════════════════════════════════╝

  Endpoint : 35.192.58.214:23333
  PSK      : a1b2c3d4...
  PSK file : /home/user/.simpleproxy/psk.hex

  URI:
  simple://MjMzMzM6YTFiMmMz...#SimpleServer

  █████████████████████████████
  █ ▄▄▄▄▄ █ ▀▄▀▀█▀█ ▄▄▄▄▄ █  (QR Code)
  ...

  Daemon started (PID 12345)

Scan the QR code with the Android / iOS client to connect.

Logging

  • Foreground mode (-fg): logs to stdout
  • Daemon mode: logs to /tmp/simpleserver.log
# Monitor daemon logs
tail -f /tmp/simpleserver.log

Deploy as systemd service

# Copy binary
sudo cp simpleserver /usr/local/bin/

# Create service file
sudo tee /etc/systemd/system/simpleserver.service > /dev/null << 'EOF'
[Unit]
Description=SimpleProtocol Proxy Server
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/simpleserver -fg -p 23333
Restart=always
RestartSec=5
Environment="_SP_DAEMON=0"

[Install]
WantedBy=multi-user.target
EOF

# Enable and start
sudo systemctl enable simpleserver
sudo systemctl start simpleserver
sudo journalctl -u simpleserver -f

Architecture

┌──────────────────────────── Client Device ─────────────────────────┐
│                                                                     │
│  Apps (browser, etc.)                                               │
│       │  TCP / UDP                                                  │
│       ▼                                                             │
│  ┌─────────┐     IP packets      ┌──────────────────────────┐      │
│  │ TUN fd  │ ──────────────────→ │ Packet Reader (userspace │      │
│  │ 10.0.0.2│ ←──────────────── │  TCP stack + DNS proxy)   │      │
│  └─────────┘    crafted packets  └──────────┬───────────────┘      │
│                                              │ TCP connect          │
│                                              ▼                      │
│                                   ┌──────────────────────┐         │
│                                   │ sing-box (mixed-in)  │         │
│                                   │ 127.0.0.1:2080       │         │
│                                   │                      │         │
│                                   │ ┌──────────────────┐ │         │
│                                   │ │  Route decision  │ │         │
│                                   │ │  • intl → proxy  │ │         │
│                                   │ │  • domestic→direct│ │         │
│                                   │ │  • DNS → dns-out │ │         │
│                                   │ └──┬──────────┬────┘ │         │
│                                   │    │          │      │         │
│                                   │  proxy      direct   │         │
│                                   │  outbound   outbound │         │
│                                   └──┬──────────┬────────┘         │
│                                      │          │                   │
│                            ┌─────────┘          └──→ Physical NIC  │
│                            ▼                       (bypass TUN)     │
│                 ┌────────────────────────┐                          │
│                 │ SimpleProtocol Bridge   │                          │
│                 │ (SOCKS5 → Mux Tunnel)  │                          │
│                 │ 127.0.0.1:16080        │                          │
│                 └──────────┬─────────────┘                          │
│                            │ AES-256-GCM encrypted                  │
│                            │ Mux multiplexed TCP                    │
└────────────────────────────┼────────────────────────────────────────┘
                             │
                             ▼
          ┌──────────────────────────────────────────────────┐
          │            SimpleProtocol Server                  │
          │                                                  │
          │  ┌───────────────────────────────────────────┐   │
          │  │ Listener (:23333)                         │   │
          │  │                                           │   │
          │  │  1. Handshake verification                │   │
          │  │     [nonce 32B][ts 8B][HMAC 32B][padding] │   │
          │  │                                           │   │
          │  │  2. HKDF-SHA256 key derivation            │   │
          │  │     c2s_key, s2c_key (AES-256-GCM)        │   │
          │  │                                           │   │
          │  │  3. Mux demultiplex                       │   │
          │  │     Stream 1 ──→ dial target-a:443        │   │
          │  │     Stream 2 ──→ dial target-b:80         │   │
          │  │     Stream N ──→ dial target-n:port       │   │
          │  └───────────────────────────────────────────┘   │
          └──────────────────────────────────────────────────┘

Key Design Points

  • Single encrypted TCP — Client and server maintain one mux-multiplexed AES-256-GCM connection carrying all streams.
  • HMAC-SHA256 handshake — Timestamp-verified (±30s window) with anti-replay nonce cache.
  • Anti-probe — On auth failure, server sends random data before closing, making port scanning unable to identify the service.
  • HKDF key derivation — Each connection derives unique c2s/s2c session keys from a fresh 32-byte random nonce.
  • Mux demux — Server unpacks multiplexed streams and dials target servers independently, with async CONNECT to prevent head-of-line blocking.

Client Apps

Platform Repository
Android simple-proxy-client-android
iOS simple-proxy-client-ios

After starting the server, scan the QR code shown in your terminal with a client app, or manually import the connection URI.

Security

  • PSK storage: The 256-bit pre-shared key is stored in ~/.simpleproxy/psk.hex by default (or the -psk-file path you provide). Keep this file secure and backed up.
  • Timestamp window: Handshake requires client/server clocks within ±30 seconds (uses UTC Unix epoch, timezone-independent).
  • Nonce replay protection: Each nonce can only be used once within a 2-minute window.
  • Forward secrecy: Each connection derives unique session keys from a fresh random nonce via HKDF.
  • Anti-probing: Failed authentication connections receive random data, making port scanning unable to identify the service.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages