# 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 |