- Per-listener `retries` overrides global default (0 = inherit) - Pool-level `allowed_protos` filters proxies during merge - Connection pooling documented in CHEATSHEET.md - Both features exposed in /config and /status API responses - 12 new tests (config parsing, API exposure, merge filtering) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
351 lines
9.6 KiB
Markdown
351 lines
9.6 KiB
Markdown
# s5p -- Cheatsheet
|
|
|
|
## CLI
|
|
|
|
```
|
|
s5p # direct, listen :1080
|
|
s5p -C socks5://127.0.0.1:9050 # through Tor
|
|
s5p -C socks5://tor:9050,http://px:8080 # Tor + HTTP proxy
|
|
s5p -c config/s5p.yaml # from config file
|
|
s5p -l 0.0.0.0:9999 # custom listen address
|
|
s5p -t 30 # 30s per-hop timeout
|
|
s5p -v # debug logging
|
|
s5p -q # errors only
|
|
s5p -S http://api:8081/proxies # proxy source API
|
|
s5p -r 5 # retry up to 5 proxies
|
|
s5p -m 512 # max concurrent connections
|
|
s5p --api 127.0.0.1:1081 # enable control API
|
|
s5p --cprofile # profile to s5p.prof
|
|
s5p --cprofile out.prof # profile to custom file
|
|
s5p --tracemalloc # memory profile (top 10)
|
|
s5p --tracemalloc 20 # memory profile (top 20)
|
|
```
|
|
|
|
## Systemd
|
|
|
|
```
|
|
make install-service # install unit + reload
|
|
sudo systemctl enable --now s5p # enable + start
|
|
sudo systemctl status s5p # check status
|
|
sudo systemctl restart s5p # restart
|
|
sudo systemctl stop s5p # stop
|
|
journalctl -u s5p -f # follow logs
|
|
journalctl -u s5p --since "5 min ago" # recent logs
|
|
```
|
|
|
|
## Container
|
|
|
|
```
|
|
make build # podman-compose build
|
|
make up # podman-compose up -d
|
|
make logs # podman-compose logs -f
|
|
make down # podman-compose down
|
|
```
|
|
|
|
Volumes: `./config/s5p.yaml` (ro), `~/.cache/s5p` → `/data` (pool state + profiles)
|
|
Dev override: compose.yaml mounts `./src` (ro) over the baked-in source.
|
|
|
|
## CI
|
|
|
|
Gitea Actions runs on push to `main`:
|
|
|
|
1. `ruff check` + `pytest` (test)
|
|
2. `gitleaks detect` (secrets scan)
|
|
3. Build + push `harbor.mymx.me/s5p/s5p:latest`
|
|
|
|
Secrets: `HARBOR_USER` / `HARBOR_PASS` (configured in Gitea repo settings).
|
|
|
|
## Config
|
|
|
|
```bash
|
|
cp config/example.yaml config/s5p.yaml # create live config (gitignored)
|
|
```
|
|
|
|
## Multi-Listener (config)
|
|
|
|
```yaml
|
|
listeners:
|
|
- listen: 0.0.0.0:1080
|
|
pool: clean # default for bare "pool"
|
|
chain:
|
|
- socks5://127.0.0.1:9050
|
|
- pool # Tor + 2 clean hops
|
|
- pool
|
|
- listen: 0.0.0.0:1081
|
|
pool: clean
|
|
chain:
|
|
- socks5://127.0.0.1:9050
|
|
- pool:clean # per-hop: explicit clean
|
|
- pool:mitm # per-hop: explicit mitm
|
|
- listen: 0.0.0.0:1082
|
|
chain:
|
|
- socks5://127.0.0.1:9050 # Tor only
|
|
- listen: 0.0.0.0:1083
|
|
chain:
|
|
- socks5://127.0.0.1:9050
|
|
- [pool:clean, pool:mitm] # random choice per connection
|
|
- [pool:clean, pool:mitm] # independent random choice
|
|
```
|
|
|
|
Per-hop pool: `pool` = listener default, `pool:name` = explicit pool,
|
|
`[pool:a, pool:b]` = random choice from candidates.
|
|
|
|
## Bypass Rules (config)
|
|
|
|
```yaml
|
|
listeners:
|
|
- listen: 0.0.0.0:1080
|
|
bypass:
|
|
- 127.0.0.0/8 # CIDR
|
|
- 10.0.0.0/8 # CIDR
|
|
- 192.168.0.0/16 # CIDR
|
|
- localhost # exact hostname
|
|
- .local # domain suffix
|
|
chain:
|
|
- socks5://127.0.0.1:9050
|
|
- pool
|
|
```
|
|
|
|
| Pattern | Type | Matches |
|
|
|---------|------|---------|
|
|
| `10.0.0.0/8` | CIDR | IPs in network |
|
|
| `127.0.0.1` | Exact IP | That IP only |
|
|
| `localhost` | Exact host | String equal |
|
|
| `.local` | Suffix | `*.local` and `local` |
|
|
|
|
## Listener Authentication (config)
|
|
|
|
```yaml
|
|
listeners:
|
|
- listen: 0.0.0.0:1080
|
|
auth:
|
|
alice: s3cret
|
|
bob: hunter2
|
|
chain:
|
|
- socks5://127.0.0.1:9050
|
|
- pool
|
|
```
|
|
|
|
```bash
|
|
curl -x socks5h://alice:s3cret@127.0.0.1:1080 https://example.com
|
|
```
|
|
|
|
No `auth:` key = no authentication required (default).
|
|
|
|
## Listener Retry Override (config)
|
|
|
|
```yaml
|
|
listeners:
|
|
- listen: 0.0.0.0:1080
|
|
retries: 5 # override global retries
|
|
chain:
|
|
- socks5://127.0.0.1:9050
|
|
- pool
|
|
- listen: 0.0.0.0:1082
|
|
chain:
|
|
- socks5://127.0.0.1:9050 # 0 = use global default
|
|
```
|
|
|
|
Per-listener `retries` overrides the global `retries` setting. Set to 0 (or
|
|
omit) to inherit the global value.
|
|
|
|
## Pool Protocol Filter (config)
|
|
|
|
```yaml
|
|
proxy_pools:
|
|
socks_only:
|
|
allowed_protos: [socks5] # reject http proxies
|
|
sources:
|
|
- url: http://api:8081/proxies/all
|
|
```
|
|
|
|
When set, proxies not matching `allowed_protos` are silently dropped during
|
|
merge. Useful when a source returns mixed protocols but the pool should
|
|
only serve a specific type.
|
|
|
|
## Multi-Tor Round-Robin (config)
|
|
|
|
```yaml
|
|
tor_nodes: # overrides first hop in all listeners
|
|
- socks5://10.200.1.1:9050
|
|
- socks5://10.200.1.254:9050
|
|
- socks5://10.200.1.250:9050
|
|
- socks5://10.200.1.13:9050
|
|
```
|
|
|
|
## Performance Tuning (config)
|
|
|
|
```yaml
|
|
max_connections: 256 # concurrent connection cap
|
|
pool_size: 8 # pre-warmed TCP conns to first hop (0 = off)
|
|
pool_max_idle: 30 # evict idle pooled conns (seconds)
|
|
```
|
|
|
|
## Connection Pool (config)
|
|
|
|
```yaml
|
|
pool_size: 8 # pre-warmed TCP connections per first hop (0 = off)
|
|
pool_max_idle: 30 # evict idle connections after N seconds
|
|
```
|
|
|
|
Pre-warms TCP connections to the first hop in the chain. Only the raw TCP
|
|
connection is pooled -- SOCKS/HTTP negotiation consumes it. One pool is
|
|
created per unique first hop (shared across listeners). Requires at least
|
|
one hop in `chain`.
|
|
|
|
| Setting | Default | Notes |
|
|
|---------|---------|-------|
|
|
| `pool_size` | 0 (off) | Connections per first hop |
|
|
| `pool_max_idle` | 30 | Idle eviction in seconds |
|
|
|
|
## Named Proxy Pools (config)
|
|
|
|
```yaml
|
|
proxy_pools:
|
|
clean:
|
|
sources:
|
|
- url: http://10.200.1.250:8081/proxies/all
|
|
mitm: false # adds ?mitm=0
|
|
state_file: /data/pool-clean.json
|
|
refresh: 300
|
|
test_interval: 120
|
|
max_fails: 3
|
|
mitm:
|
|
sources:
|
|
- url: http://10.200.1.250:8081/proxies/all
|
|
mitm: true # adds ?mitm=1
|
|
state_file: /data/pool-mitm.json
|
|
refresh: 300
|
|
test_interval: 120
|
|
max_fails: 3
|
|
```
|
|
|
|
Singular `proxy_pool:` still works (becomes pool "default").
|
|
|
|
## Source Filters (proxy_pool sources)
|
|
|
|
| Filter | Values | Query param |
|
|
|--------|--------|-------------|
|
|
| `proto` | socks5/socks4/http | `?proto=...` |
|
|
| `country` | ISO alpha-2 | `?country=...` |
|
|
| `limit` | integer | `?limit=...` |
|
|
| `mitm` | true/false | `?mitm=1` / `?mitm=0` |
|
|
|
|
## Tor Control Port (config)
|
|
|
|
```yaml
|
|
tor:
|
|
control_port: 9051
|
|
password: "" # or cookie_file: /path/to/cookie
|
|
newnym_interval: 60 # auto-rotate every 60s (0 = manual)
|
|
```
|
|
|
|
```bash
|
|
curl -s http://127.0.0.1:1081/tor | jq . # status
|
|
curl -s -X POST http://127.0.0.1:1081/tor/newnym | jq . # new circuit
|
|
```
|
|
|
|
## Hot Reload
|
|
|
|
```bash
|
|
kill -HUP $(pidof s5p) # reload config
|
|
podman kill --signal HUP s5p # container reload
|
|
```
|
|
|
|
## Proxy File Format
|
|
|
|
```
|
|
# one proxy URL per line
|
|
socks5://1.2.3.4:1080
|
|
socks5://user:pass@5.6.7.8:1080
|
|
http://proxy.example.com:8080
|
|
```
|
|
|
|
## Proxy URLs
|
|
|
|
```
|
|
socks5://host:port
|
|
socks5://user:pass@host:port
|
|
socks4://host:port
|
|
http://host:port
|
|
http://user:pass@host:port
|
|
```
|
|
|
|
## Control API
|
|
|
|
```bash
|
|
s5p --api 127.0.0.1:1081 -c config/s5p.yaml # enable API
|
|
|
|
curl -s http://127.0.0.1:1081/status | jq . # runtime status
|
|
curl -s http://127.0.0.1:1081/metrics # prometheus metrics
|
|
curl -s http://127.0.0.1:1081/pool | jq . # all proxies
|
|
curl -s http://127.0.0.1:1081/pool/alive | jq . # alive only
|
|
curl -s http://127.0.0.1:1081/config | jq . # current config
|
|
curl -s -X POST http://127.0.0.1:1081/reload # reload config
|
|
curl -s -X POST http://127.0.0.1:1081/pool/test # health test now
|
|
```
|
|
|
|
## Testing
|
|
|
|
```bash
|
|
# Check exit IP
|
|
curl -x socks5h://127.0.0.1:1080 https://httpbin.org/ip
|
|
|
|
# Tor check
|
|
curl -x socks5h://127.0.0.1:1080 https://check.torproject.org/api/ip
|
|
|
|
# Verbose curl
|
|
curl -v -x socks5h://127.0.0.1:1080 https://example.com
|
|
|
|
# With timeout
|
|
curl --max-time 30 -x socks5h://127.0.0.1:1080 https://example.com
|
|
```
|
|
|
|
## Profiling
|
|
|
|
```bash
|
|
# Enable in compose.yaml: uncomment the command line
|
|
python -m pstats s5p.prof # interactive stats viewer
|
|
python -m pstats ~/.cache/s5p/s5p.prof # container profile output
|
|
```
|
|
|
|
## Metrics Log
|
|
|
|
```
|
|
metrics: conn=1842 ok=1790 fail=52 retries=67 active=3 in=50.0M out=1.0G rate=4.72/s p50=198.3ms p95=890.1ms up=1h01m01s pool=42/65
|
|
```
|
|
|
|
## Prometheus Metrics (`/metrics`)
|
|
|
|
```bash
|
|
curl -s http://127.0.0.1:1081/metrics
|
|
```
|
|
|
|
```
|
|
# TYPE s5p_connections counter
|
|
s5p_connections_total 1842
|
|
# TYPE s5p_active_connections gauge
|
|
s5p_active_connections 3
|
|
# TYPE s5p_pool_proxies_alive gauge
|
|
s5p_pool_proxies_alive{pool="clean"} 30
|
|
# TYPE s5p_chain_latency_seconds summary
|
|
s5p_chain_latency_seconds{quantile="0.5"} 0.198300
|
|
s5p_chain_latency_seconds{quantile="0.95"} 0.890100
|
|
# EOF
|
|
```
|
|
|
|
OpenMetrics format. Use `/status` for JSON equivalent.
|
|
|
|
## Troubleshooting
|
|
|
|
| Symptom | Check |
|
|
|---------|-------|
|
|
| Connection refused | Is Tor running? `ss -tlnp \| grep 9050` |
|
|
| Timeout | Increase `-t`, check proxy reachability |
|
|
| DNS leak | Use `socks5h://` (not `socks5://`) in client |
|
|
| Auth failed | Verify credentials in proxy URL |
|
|
| Port in use | `fuser -k 1080/tcp` to free the port |
|
|
| Proxy keeps failing | Backoff penalizes for 60s; check `pool=` in metrics |
|
|
| "static chain unreachable" | Tor/upstream hop is down; pool tests skipped |
|
|
| Slow startup | Normal on cold start; warm restarts serve instantly from state |
|