user 3e2c431f49
Some checks failed
ci / test (push) Failing after 2s
ci / secrets (push) Failing after 2s
ci / build (push) Has been skipped
feat: switch CI to linux runner with podman containers
Replace ubuntu-latest runner with linux label and migrate all
container operations from docker to podman. Add requirements.txt
as single source of truth for runtime dependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 06:33:32 +01:00

s5p

SOCKS5 proxy server with Tor and proxy-chain support. Routes connections through configurable chains of SOCKS4, SOCKS5, and HTTP CONNECT proxies.

Features

  • SOCKS5 server (RFC 1928)
  • Proxy chaining: tunnel through multiple hops in sequence
  • Supported hop protocols: SOCKS5, SOCKS4/4a, HTTP CONNECT
  • Per-hop authentication (username/password)
  • DNS leak prevention (domain names forwarded to proxies, never resolved locally)
  • Tor integration (SOCKS5 hop + control port NEWNYM for circuit rotation)
  • Multi-Tor round-robin (tor_nodes distributes traffic across Tor instances)
  • Multi-listener: different ports with different chain depths and pool assignments
  • Named proxy pools: independent sources, health testing, and state per pool
  • MITM source filter (mitm: true/false adds ?mitm=0/1 to API requests)
  • Per-proxy failure backoff (60s cooldown), stale proxy expiry, chain pre-flight
  • Fast warm start (seconds on restart vs minutes on cold start)
  • Connection retry with proxy rotation (configurable attempts)
  • Dead proxy reporting to upstream API (optional report_url)
  • SIGHUP hot reload (timeout, retries, log_level, pool config)
  • Connection metrics with per-listener latency and pool stats
  • Concurrent connection limit with backpressure (max_connections)
  • Async HTTP client for proxy source fetching (parallel, no threads)
  • First-hop TCP connection pool (pre-warmed, stale-evicted)
  • Built-in control API (runtime metrics, pool state, config reload via HTTP)
  • Container-ready (Alpine-based, podman/docker)
  • Graceful shutdown (SIGTERM/SIGINT)
  • Pure Python, asyncio-based, minimal dependencies

Quick Start

# Install locally
cd ~/git/s5p
python -m venv .venv && source .venv/bin/activate
pip install -e .

# Run with Tor
s5p -C socks5://127.0.0.1:9050

# Run with config file
cp config/example.yaml config/s5p.yaml  # edit with your proxies
s5p -c config/s5p.yaml

# Test it
curl --proxy socks5h://127.0.0.1:1080 https://check.torproject.org/api/ip

Container

make build   # podman-compose build
make up      # podman-compose up -d
make logs    # podman-compose logs -f
make down    # podman-compose down

Production images bake source into the image. Config and data are mounted at runtime. Pool state and profile output persist in ~/.cache/s5p/ (/data inside container). The compose.yaml volume mount overrides source for local dev.

CI (Gitea Actions) runs lint + tests on push to main, then builds and pushes harbor.mymx.me/s5p/s5p:latest.

Configuration

Copy the example and edit with your proxy chain:

cp config/example.yaml config/s5p.yaml
timeout: 10
retries: 3
max_connections: 256                  # concurrent connection limit
pool_size: 8                          # pre-warmed connections to first hop
api_listen: 127.0.0.1:1081           # control API (disabled by default)

# Named proxy pools (each with independent sources and health testing)
proxy_pools:
  clean:
    sources:
      - url: http://10.200.1.250:8081/proxies/all
        mitm: false                   # filter: ?mitm=0
    refresh: 300
    test_interval: 120
    max_fails: 3
  mitm:
    sources:
      - url: http://10.200.1.250:8081/proxies/all
        mitm: true                    # filter: ?mitm=1
    refresh: 300
    test_interval: 120
    max_fails: 3

# Multi-listener: each port gets a chain depth and pool assignment
# Use "pool" for listener default, "pool:name" for explicit pool per hop,
# or [pool:a, pool:b] for random choice from candidates per connection
listeners:
  - listen: 0.0.0.0:1080
    pool: clean
    chain:
      - socks5://127.0.0.1:9050
      - pool                           # Tor + 2 clean proxies
      - pool
  - listen: 0.0.0.0:1081
    chain:
      - socks5://127.0.0.1:9050
      - [pool:clean, pool:mitm]        # random choice per connection
      - [pool:clean, pool:mitm]        # independent random choice
  - listen: 0.0.0.0:1082
    chain:
      - socks5://127.0.0.1:9050       # Tor only

# Singular proxy_pool: still works (becomes pool "default")

tor:
  control_port: 9051                  # Tor control port (NEWNYM)
  password: ""                        # or cookie_file for auth
  newnym_interval: 0                  # periodic circuit rotation (0 = manual)

config/s5p.yaml is gitignored; config/example.yaml is the tracked template.

CLI Reference

s5p [-c FILE] [-l [HOST:]PORT] [-C URL[,URL,...]] [-S URL] [-t SEC] [-r N] [-m N] [--api [HOST:]PORT] [-v|-q]

Options:
  -c, --config FILE        YAML config file
  -l, --listen [HOST:]PORT Listen address (default: 127.0.0.1:1080)
  -C, --chain URL[,URL]    Comma-separated proxy chain
  -S, --proxy-source URL   Proxy source API URL
  -t, --timeout SEC        Per-hop timeout (default: 10)
  -r, --retries N          Max attempts per connection (default: 3, proxy_source only)
  -m, --max-connections N  Max concurrent connections (default: 256)
  --api [HOST:]PORT        Enable control API (e.g. 127.0.0.1:1081)
  -v, --verbose            Debug logging
  -q, --quiet              Errors only
  --cprofile [FILE]        Enable cProfile, dump to FILE (default: s5p.prof)
  --tracemalloc [N]        Enable tracemalloc, show top N allocators on exit (default: 10)
  -V, --version            Show version

How Chaining Works

:1080  Client -> s5p -> Tor -> [clean] -> [clean]       -> Dest  (2 clean hops)
:1081  Client -> s5p -> Tor -> [clean|mitm] -> [clean|mitm] -> Dest  (random)
:1082  Client -> s5p -> Tor -> Dest                              (Tor only)

s5p connects to Hop1 via TCP, negotiates the hop protocol (SOCKS5/4/HTTP), then over that tunnel negotiates with Hop2, and so on. Each listener draws from its assigned named pool -- alive proxies are appended per-connection (one per pool entry), weighted toward those with the most recent successful health test. Each hop only sees its immediate neighbors.

Description
No description provided
Readme 769 KiB
Languages
Python 99.6%
Makefile 0.2%
Dockerfile 0.2%