Adds Tor control port section to USAGE.md covering config, auth modes, rate limiting, and API endpoints. Updates README feature line and config example, CHEATSHEET with tor snippets, and marks the feature complete in TASKS.md and ROADMAP.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
483 lines
13 KiB
Markdown
483 lines
13 KiB
Markdown
# s5p -- Usage
|
|
|
|
## Basic Usage
|
|
|
|
```bash
|
|
# Direct proxy (no chain, just a SOCKS5 server)
|
|
s5p
|
|
|
|
# Through Tor
|
|
s5p -C socks5://127.0.0.1:9050
|
|
|
|
# Through Tor + another proxy
|
|
s5p -C socks5://127.0.0.1:9050,socks5://proxy:1080
|
|
|
|
# Custom listen address
|
|
s5p -l 0.0.0.0:9999 -C socks5://127.0.0.1:9050
|
|
|
|
# From config file
|
|
s5p -c config/s5p.yaml
|
|
|
|
# With proxy source API (rotate exit proxy per-connection)
|
|
s5p -C socks5://127.0.0.1:9050 -S http://10.200.1.250:8081/proxies
|
|
|
|
# Debug mode
|
|
s5p -v -C socks5://127.0.0.1:9050
|
|
```
|
|
|
|
## Configuration
|
|
|
|
Copy the tracked example to create your live config:
|
|
|
|
```bash
|
|
cp config/example.yaml config/s5p.yaml
|
|
```
|
|
|
|
| File | Tracked | Purpose |
|
|
|------|---------|---------|
|
|
| `config/example.yaml` | yes | Template with placeholder addresses |
|
|
| `config/s5p.yaml` | no (gitignored) | Live config with real proxy addresses |
|
|
|
|
```yaml
|
|
listen: 127.0.0.1:1080
|
|
timeout: 10
|
|
retries: 3
|
|
log_level: info
|
|
max_connections: 256 # concurrent connection limit (backpressure)
|
|
pool_size: 0 # pre-warmed TCP connections to first hop (0 = disabled)
|
|
pool_max_idle: 30 # max idle time for pooled connections (seconds)
|
|
api_listen: "" # control API bind address (empty = disabled)
|
|
|
|
chain:
|
|
- socks5://127.0.0.1:9050
|
|
|
|
proxy_pool:
|
|
sources:
|
|
- url: http://10.200.1.250:8081/proxies
|
|
proto: socks5
|
|
limit: 1000
|
|
- file: /etc/s5p/proxies.txt
|
|
refresh: 300
|
|
test_interval: 120
|
|
test_url: http://httpbin.org/ip
|
|
test_timeout: 15
|
|
test_concurrency: 5
|
|
max_fails: 3
|
|
state_file: "" # empty = ~/.cache/s5p/pool.json
|
|
```
|
|
|
|
## Proxy URL Format
|
|
|
|
```
|
|
protocol://[username:password@]host[:port]
|
|
```
|
|
|
|
| Protocol | Default Port | Auth Support |
|
|
|----------|-------------|-------------|
|
|
| socks5 | 1080 | username/password |
|
|
| socks4 | 1080 | none |
|
|
| http | 8080 | Basic |
|
|
|
|
## Container
|
|
|
|
```bash
|
|
make build # build image
|
|
make up # start container (detached)
|
|
make logs # follow logs
|
|
make down # stop and remove container
|
|
```
|
|
|
|
Source (`./src`) and config (`./config/s5p.yaml`) are mounted read-only
|
|
into the container. `~/.cache/s5p` is mounted as `/data` for pool state
|
|
and profile output. Edit locally, restart to pick up changes.
|
|
|
|
## Proxy Pool
|
|
|
|
Managed proxy pool with multiple sources, health testing, and persistence.
|
|
Appends an alive proxy after the static chain on each connection, weighted
|
|
by recency of last successful health test.
|
|
|
|
```yaml
|
|
proxy_pool:
|
|
sources:
|
|
- url: http://10.200.1.250:8081/proxies
|
|
proto: socks5 # optional: filter by protocol
|
|
country: US # optional: filter by country
|
|
limit: 1000 # max proxies to fetch from API
|
|
- file: /etc/s5p/proxies.txt # text file, one proxy URL per line
|
|
refresh: 300 # re-fetch sources every 300 seconds
|
|
test_interval: 120 # health test cycle every 120 seconds
|
|
test_url: http://httpbin.org/ip # URL for health checks
|
|
test_timeout: 15 # per-test timeout (seconds)
|
|
test_concurrency: 5 # parallel health tests
|
|
max_fails: 3 # evict after N consecutive failures
|
|
state_file: "" # empty = ~/.cache/s5p/pool.json
|
|
report_url: "" # POST dead proxies here (optional)
|
|
```
|
|
|
|
### Sources
|
|
|
|
| Type | Config key | Description |
|
|
|------|-----------|-------------|
|
|
| HTTP API | `url` | JSON: `{"proxies": [{"proto": "socks5", "proxy": "host:port"}, ...]}` |
|
|
| Text file | `file` | One proxy URL per line, `#` comments, blank lines ignored |
|
|
|
|
### Proxy file format
|
|
|
|
```
|
|
# Exit proxies
|
|
socks5://1.2.3.4:1080
|
|
socks5://user:pass@5.6.7.8:1080
|
|
http://proxy.example.com:8080
|
|
```
|
|
|
|
### Health testing
|
|
|
|
Each cycle tests all proxies through the full chain (static chain + proxy)
|
|
by sending an HTTP GET to `test_url`. Proxies are marked alive on `200` response.
|
|
After `max_fails` consecutive failures, a proxy is evicted.
|
|
|
|
Before each health test cycle, the static chain is tested without any pool
|
|
proxy. If the chain itself is unreachable (e.g., Tor is down), proxy tests
|
|
are skipped entirely and a warning is logged. This prevents false mass-failure
|
|
and unnecessary evictions.
|
|
|
|
Mass-failure guard: if >90% of tests fail in one cycle, eviction is skipped
|
|
(likely the static chain is broken, not the proxies).
|
|
|
|
### Selection weight
|
|
|
|
Alive proxies are selected with probability proportional to their recency
|
|
weight: `1 / (1 + age / 300)`, where `age` is seconds since the last
|
|
successful health test. This favors freshly-verified proxies over stale ones:
|
|
|
|
| Age | Weight |
|
|
|-----|--------|
|
|
| 0 s (just tested) | ~1.0 |
|
|
| 5 min | ~0.5 |
|
|
| 10 min | ~0.33 |
|
|
| 30 min | ~0.1 |
|
|
| Never tested | 0.01 |
|
|
|
|
### Failure backoff
|
|
|
|
When a proxy fails during an actual connection attempt (not just a health
|
|
test), its weight is penalized for 60 seconds. The penalty ramps linearly
|
|
from floor (0.01) back to normal over that window. This prevents retries
|
|
from repeatedly selecting a proxy that just failed.
|
|
|
|
### Stale proxy expiry
|
|
|
|
Proxies not returned by any source for 3 consecutive refresh cycles and
|
|
not currently alive are automatically evicted. This cleans up proxies
|
|
removed upstream faster than waiting for `max_fails` health test failures.
|
|
|
|
### Persistence
|
|
|
|
Pool state is saved to `state_file` (default: `~/.cache/s5p/pool.json`) after
|
|
each refresh/health cycle and on shutdown. On startup, previously-alive proxies
|
|
are loaded for fast warm starts.
|
|
|
|
### Warm start
|
|
|
|
When restarting with an existing state file, the server trusts the cached
|
|
alive state and begins accepting connections immediately. A full health test
|
|
of all proxies runs in the background. Startup takes seconds regardless of
|
|
pool size. Cold starts (no state file) test all proxies before serving.
|
|
|
|
### Dead proxy reporting
|
|
|
|
When `report_url` is set, evicted proxies are POSTed to the upstream API
|
|
after each health test cycle. This helps the source maintain list quality.
|
|
|
|
```yaml
|
|
proxy_pool:
|
|
report_url: http://10.200.1.250:8081/proxies/report
|
|
```
|
|
|
|
Payload format:
|
|
|
|
```json
|
|
{"dead": [{"proto": "socks5", "proxy": "1.2.3.4:1080"}, ...]}
|
|
```
|
|
|
|
Reporting is fire-and-forget; failures are logged at debug level only.
|
|
|
|
### CLI shorthand
|
|
|
|
```bash
|
|
s5p -C socks5://127.0.0.1:9050 -S http://10.200.1.250:8081/proxies
|
|
```
|
|
|
|
The `-S` flag creates a pool with a single API source (uses defaults for all
|
|
other pool settings).
|
|
|
|
### Legacy config
|
|
|
|
The old `proxy_source` key is still supported and auto-converts to `proxy_pool`
|
|
with a single API source. `proxy_pool` takes precedence if both are present.
|
|
|
|
## Control API
|
|
|
|
Built-in HTTP API for runtime inspection and management. Disabled by default;
|
|
enable with `api_listen` in config or `--api` on the command line.
|
|
|
|
```yaml
|
|
api_listen: 127.0.0.1:1081
|
|
```
|
|
|
|
```bash
|
|
s5p --api 127.0.0.1:1081 -c config/s5p.yaml
|
|
```
|
|
|
|
### Read endpoints
|
|
|
|
| Method | Path | Description |
|
|
|--------|------|-------------|
|
|
| `GET` | `/status` | Combined summary: uptime, metrics, pool stats, chain |
|
|
| `GET` | `/metrics` | Full metrics counters (connections, bytes, uptime) |
|
|
| `GET` | `/pool` | All proxies with per-entry state |
|
|
| `GET` | `/pool/alive` | Alive proxies only |
|
|
| `GET` | `/config` | Current runtime config (sanitized) |
|
|
|
|
### Write endpoints
|
|
|
|
| Method | Path | Description |
|
|
|--------|------|-------------|
|
|
| `POST` | `/reload` | Re-read config file (replaces SIGHUP) |
|
|
| `POST` | `/pool/test` | Trigger immediate health test cycle |
|
|
| `POST` | `/pool/refresh` | Trigger immediate source re-fetch |
|
|
|
|
All responses are `application/json`. Errors return `{"error": "message"}` with
|
|
appropriate status code (400, 404, 405, 500).
|
|
|
|
### Examples
|
|
|
|
```bash
|
|
# Runtime status
|
|
curl -s http://127.0.0.1:1081/status | jq .
|
|
|
|
# Full metrics
|
|
curl -s http://127.0.0.1:1081/metrics | jq .
|
|
|
|
# Pool state (all proxies)
|
|
curl -s http://127.0.0.1:1081/pool | jq .
|
|
|
|
# Alive proxies only
|
|
curl -s http://127.0.0.1:1081/pool/alive | jq '.proxies | length'
|
|
|
|
# Current config
|
|
curl -s http://127.0.0.1:1081/config | jq .
|
|
|
|
# Reload config (like SIGHUP)
|
|
curl -s -X POST http://127.0.0.1:1081/reload | jq .
|
|
|
|
# Trigger health tests now
|
|
curl -s -X POST http://127.0.0.1:1081/pool/test | jq .
|
|
|
|
# Re-fetch proxy sources now
|
|
curl -s -X POST http://127.0.0.1:1081/pool/refresh | jq .
|
|
```
|
|
|
|
Settings that require a restart: `listen`, `chain`, `pool_size`, `pool_max_idle`, `api_listen`.
|
|
|
|
## Tor Control Port
|
|
|
|
Optional integration with Tor's control protocol for circuit management.
|
|
When enabled, s5p connects to Tor's control port and can send NEWNYM signals
|
|
to request new circuits (new exit node) on demand or on a timer.
|
|
|
|
### Configuration
|
|
|
|
```yaml
|
|
tor:
|
|
control_host: 127.0.0.1 # Tor control address
|
|
control_port: 9051 # Tor control port
|
|
password: "" # HashedControlPassword (torrc)
|
|
cookie_file: "" # CookieAuthentication file path
|
|
newnym_interval: 0 # periodic NEWNYM in seconds (0 = manual only)
|
|
```
|
|
|
|
Requires Tor's `ControlPort` enabled in `torrc`:
|
|
|
|
```
|
|
ControlPort 9051
|
|
HashedControlPassword 16:... # or CookieAuthentication 1
|
|
```
|
|
|
|
### Authentication modes
|
|
|
|
| Mode | Config | torrc |
|
|
|------|--------|-------|
|
|
| Password | `password: "secret"` | `HashedControlPassword 16:...` |
|
|
| Cookie | `cookie_file: /var/run/tor/control.authcookie` | `CookieAuthentication 1` |
|
|
| None | (leave both empty) | No auth configured |
|
|
|
|
### Rate limiting
|
|
|
|
Tor enforces a minimum 10-second interval between NEWNYM signals. s5p
|
|
applies the same client-side rate limit to avoid unnecessary rejections.
|
|
|
|
### API endpoints
|
|
|
|
| Method | Path | Description |
|
|
|--------|------|-------------|
|
|
| `GET` | `/tor` | Controller status (enabled, connected, last NEWNYM) |
|
|
| `POST` | `/tor/newnym` | Trigger NEWNYM signal (new circuit) |
|
|
|
|
```bash
|
|
# Check tor controller status
|
|
curl -s http://127.0.0.1:1081/tor | jq .
|
|
|
|
# Request new circuit
|
|
curl -s -X POST http://127.0.0.1:1081/tor/newnym | jq .
|
|
```
|
|
|
|
### Periodic NEWNYM
|
|
|
|
Set `newnym_interval` to automatically rotate circuits:
|
|
|
|
```yaml
|
|
tor:
|
|
newnym_interval: 60 # new circuit every 60 seconds
|
|
```
|
|
|
|
Values below 10 are clamped to Tor's minimum interval.
|
|
|
|
## Connection Retry
|
|
|
|
When a proxy pool is active, s5p retries failed connections with a different
|
|
random proxy. Controlled by the `retries` setting (default: 3). Static-only
|
|
chains do not retry (retrying the same chain is pointless).
|
|
|
|
```yaml
|
|
retries: 5 # try up to 5 different proxies per connection
|
|
```
|
|
|
|
```bash
|
|
s5p -r 5 -C socks5://127.0.0.1:9050 -S http://api:8081/proxies
|
|
```
|
|
|
|
## Hot Reload
|
|
|
|
Send `SIGHUP` to reload the config file without restarting:
|
|
|
|
```bash
|
|
kill -HUP $(pidof s5p)
|
|
# or in a container:
|
|
podman kill --signal HUP s5p
|
|
```
|
|
|
|
Settings reloaded on SIGHUP:
|
|
|
|
| Setting | Effect |
|
|
|---------|--------|
|
|
| `timeout` | Per-connection timeout |
|
|
| `retries` | Max retry attempts |
|
|
| `log_level` | Logging verbosity |
|
|
| `max_connections` | Concurrent connection limit |
|
|
| `proxy_pool.*` | Sources, intervals, thresholds |
|
|
|
|
Settings that require a restart: `listen`, `chain`, `pool_size`, `pool_max_idle`, `api_listen`.
|
|
|
|
Requires `-c` / `--config` to know which file to re-read. Without a
|
|
config file, SIGHUP is ignored with a warning.
|
|
|
|
## Metrics
|
|
|
|
s5p tracks connection metrics and logs a summary every 60 seconds and on
|
|
shutdown:
|
|
|
|
```
|
|
metrics: conn=142 ok=98 fail=44 retries=88 active=3 in=1.2M out=4.5M up=0h05m12s pool=42/65
|
|
```
|
|
|
|
| Counter | Meaning |
|
|
|---------|---------|
|
|
| `conn` | Total incoming connections |
|
|
| `ok` | Successfully connected + relayed |
|
|
| `fail` | All retries exhausted |
|
|
| `retries` | Total retry attempts |
|
|
| `active` | Currently relaying |
|
|
| `in` | Bytes client -> remote |
|
|
| `out` | Bytes remote -> client |
|
|
| `up` | Server uptime |
|
|
| `pool` | Alive/total proxies (only when pool is active) |
|
|
|
|
## Profiling
|
|
|
|
```bash
|
|
# Run with cProfile enabled
|
|
s5p --cprofile -c config/s5p.yaml
|
|
|
|
# Custom output file
|
|
s5p --cprofile output.prof -c config/s5p.yaml
|
|
|
|
# Container: uncomment the command line in compose.yaml
|
|
# command: ["-c", "/app/config/s5p.yaml", "--cprofile", "/data/s5p.prof"]
|
|
# Profile output persists at ~/.cache/s5p/s5p.prof on the host.
|
|
|
|
# Analyze after stopping
|
|
python -m pstats s5p.prof
|
|
```
|
|
|
|
## Testing the Proxy
|
|
|
|
```bash
|
|
# Check exit IP via Tor
|
|
curl --proxy socks5h://127.0.0.1:1080 https://check.torproject.org/api/ip
|
|
|
|
# Fetch a page
|
|
curl --proxy socks5h://127.0.0.1:1080 https://example.com
|
|
|
|
# Use with Firefox: set SOCKS5 proxy to 127.0.0.1:1080, enable remote DNS
|
|
```
|
|
|
|
Note: use `socks5h://` (not `socks5://`) with curl to send DNS through the proxy.
|
|
|
|
## Connection Limit
|
|
|
|
s5p caps concurrent connections with an `asyncio.Semaphore`. When all
|
|
slots are taken, new clients backpressure at TCP accept (the connection
|
|
is accepted but the handler waits for a slot).
|
|
|
|
```yaml
|
|
max_connections: 256 # default
|
|
```
|
|
|
|
```bash
|
|
s5p -m 512 # override via CLI
|
|
```
|
|
|
|
The `max_connections` value is reloaded on SIGHUP. Connections already
|
|
in flight are not affected -- new limits take effect as active connections
|
|
close.
|
|
|
|
## First-Hop Connection Pool
|
|
|
|
Pre-warms TCP connections to the first hop in the chain, avoiding the
|
|
TCP handshake latency on each client request. Only the raw TCP
|
|
connection is pooled -- once SOCKS/HTTP CONNECT negotiation begins,
|
|
the connection is consumed.
|
|
|
|
```yaml
|
|
pool_size: 8 # 0 = disabled (default)
|
|
pool_max_idle: 30 # seconds before a pooled connection is evicted
|
|
```
|
|
|
|
Connections idle longer than `pool_max_idle` are discarded and replaced.
|
|
A background task tops up the pool at half the idle interval. Requires
|
|
at least one hop in `chain` -- ignored if chain is empty.
|
|
|
|
## Chain Order
|
|
|
|
Hops are traversed left-to-right:
|
|
|
|
```
|
|
-C hop1,hop2,hop3
|
|
|
|
Client -> s5p -> hop1 -> hop2 -> hop3 -> Destination
|
|
```
|
|
|
|
Each hop only sees its immediate neighbors.
|