Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MultiMicroWARP

N independent Cloudflare WARP exits as local SOCKS5 proxies — one command.

Runs MicroWARP containers on podman: each exit is one tiny container with its own WARP identity, its own SOCKS5 port, and its own podman volume.

Useful for browser farms, multi-account tooling, and anything that needs several clean egress IPs without buying residential proxies.

Your app / browser / farm
        │  round-robin
        ▼
┌────────────────────────────────┐
│  warp-n1  → socks5h://…:40001  │──► WARP exit IP A
│  warp-n2  → socks5h://…:40002  │──► WARP exit IP B
│  ...                           │
│  warp-n10 → socks5h://…:40010  │──► WARP exit IP J
└────────────────────────────────┘
  podman + MicroWARP: 1 container + 1 volume = 1 WARP identity

Why this design

Single WARP (or single VPS IP) gets rate-limited fast when you open many sessions, and heavyweight warp-cli daemon images (~201 MB image, ~150 MB RAM per node) don't scale to a farm. MicroWARP's engine is kernel WireGuard + C microsocks: ~9–15 MB image and under 1 MB of RAM per node, so a 1 GB host runs dozens of exits and the whole stack starts in seconds. The real limit is Cloudflare's IP diversity, not memory.

How it works:

Property Behavior
Orchestration one podman run per node
Fleet discovery mmw.* labels (podman ps --filter label=mmw.managed=true)
Identity 1 named volume per node (mmw-data-n<N>) → separate WARP registration, atomic wipe
Isolation Default bind 127.0.0.1 only (not public)
Persistence optional quadlet systemd user units — survives reboots
Ops ./mmw run → parallel probe → recreate-until-unique → warp-pool.txt

Requirements

  • Linux, x86_64 / arm64
  • Podman ≥ 4.4 (rootless works and is the primary target; rootful works too)
  • curl, bash ≥ 4.3; nothing else
  • Containers allowed NET_ADMIN + SYS_MODULE; outbound UDP/TCP for WireGuard

Quick start

git clone https://github.com/elliottophellia/MultiMicroWARP.git
cd MultiMicroWarp

./mmw run -n 10        # start 10 exits, recreate bad ones until unique
./mmw status           # probe the fleet (no -n needed; labels find it)
./mmw stop             # remove the fleet (identities are kept)

run starts N nodes, probes every port through its SOCKS5 exit, and recreates bad nodes (FAIL, warp≠on, or duplicate exit IP) until every exit is unique, MAX_ROUNDS is hit, or the distinct-IP count stalls for two healthy rounds. It then reports your host's ceiling instead of grinding. Success writes warp-pool.txt and prints the port → IP map.

Commands

Command Meaning
./mmw run [-n N] Start stack → probe → recreate until unique + warp=on → write warp-pool.txt
./mmw probe [-n N] Wipe identities, start N fresh nodes, one probe pass → this host's distinct-exit ceiling
./mmw status List fleet (label-discovered) + probe all ports
./mmw pool [-o FILE] [-H HOST] [--scheme socks5h|socks5] [--stdout] Export the SOCKS pool
./mmw logs <n|warp-nN> [lines] Node logs
./mmw wipe <n…|all> Drop node container + identity volume → fresh registration on next run
./mmw stop Remove all managed containers; identity volumes are kept
./mmw restart stop, then run
./mmw install [-n N] Install the fleet as quadlet systemd user units
./mmw uninstall [--purge] Remove the units; --purge also drops identities

Options: -n/--count (1–64), -p/--base-port (default 40001), --bind (default 127.0.0.1, use 0.0.0.0 only behind a firewall/Tailscale), --image. Tuning: --max-rounds, --recreate-batch, --recreate-wait, --initial-wait. Everything is also settable in mmw.env; copy mmw.env.example for a documented starting point.

Survive reboots: quadlet install

Rootless podman restart policies don't survive a host reboot; systemd does. The intended flow is to converge first, then persist (existing identities and exit IPs are reused):

./mmw run -n 10          # converge to a healthy unique pool
./mmw stop               # remove containers; volumes (identities) kept
./mmw install -n 10      # write quadlet units + start via systemctl --user
./mmw status             # fleet is still discovered by labels

This writes ~/.config/containers/systemd/mmw-warp-n*.container units (perms 600; they may contain your SOCKS password) and starts them. Per node: systemctl --user status mmw-warp-n3. Remove everything with ./mmw uninstall (keep identities) or ./mmw uninstall --purge (fresh registrations next time).

If install warns about linger, run once: sudo loginctl enable-linger $USER; otherwise the units stop when you log out.

Node configuration (MicroWARP env)

Each node runs with caps NET_ADMIN+SYS_MODULE, four sysctls (src_valid_mark, IPv6 enable ×2, IPv6 forwarding), log rotation (max-size=3m), and BIND_ADDR=0.0.0.0 inside the container; the published host port scopes access. Every MicroWARP setting is reachable:

mmw flag MicroWARP env
--protocol wireguard|masque TUNNEL_PROTOCOL
--endpoint "162.159.192.1 188.114.96.3:2408 …" ENDPOINT_IP — rotated across nodes, re-rotated each recreate round (bare IPs get port 2408)
--socks-user U --socks-pass P SOCKS_USER / SOCKS_PASS (must come as a pair)
--mtu, --keepalive MTU, KEEPALIVE
--ipv6 / --no-ipv6 ENABLE_IPV6=1 / 0
--gh-proxy URL GH_PROXY (when github.com is blocked where nodes boot)
--set KEY=VAL (repeatable) anything else: MASQUE_PROXY_MODE, MASQUE_HTTP2, MASQUE_SNI, MASQUE_MTU, WARP_JWT, WARP_LICENSE, TAILSCALE_CIDR, GOMEMLIMIT, …

The same variables work as plain env vars or in mmw.env (EXTRA_ENV=( "MASQUE_PROXY_MODE=socks" )). MicroWARP 0.3.0 registers dual-stack (IPv4 + IPv6) by default; force IPv4-only with --no-ipv6.

Handy for CI / dry runs: upstream's --set MICROWARP_TEST_MODE=1 starts containers that skip all initialization.

Use the pool

warp-pool.txt (from run, or ./mmw pool):

socks5h://127.0.0.1:40001
socks5h://127.0.0.1:40002
...
curl --proxy socks5h://127.0.0.1:40001 https://api.ipify.org
from itertools import cycle
POOL = [f"socks5h://127.0.0.1:{40001+i}" for i in range(10)]
proxy_cycle = cycle(POOL)
def next_proxy():
    u = next(proxy_cycle)
    return {"http": u, "https": u}

socks5h:// vs socks5://

socks5h resolves DNS through the proxy, which keeps clients off your local resolver. socks5 resolves locally; since the WARP tunnel is dual-stack by default both schemes work, but if you forced --no-ipv6, local resolution can hand the proxy an unroutable IPv6 target (curl: (97)). Switch with ./mmw pool --scheme socks5 only if a client demands it. (Upstream MicroWARP's README notes socks5h can feel slower on resolves; if you see resolve timeouts, try --scheme socks5.)

More examples in examples/.

Pool size expectations (read this)

Target size Realistic outcome
10–15 exits Normal to attempt. On a network that hands out diverse egress you may land close to N unique — but on one host behind a single NAT/VPN path, Cloudflare often reuses a few exits and unique can cap well below N.
~20 exits Hard to get 20 unique. Anycast overlaps; expect duplicate-IP nodes the recreate loop must churn through.
30+ Not practical for all-unique on one host. Use multiple hosts (each contributes its own egress) or accept heavy IP reuse.

A host behind a single NAT/VPN frequently routes every account to the same egress (unique=1). That is Cloudflare's assignment, not a bug here. Measure your host in about a minute with fresh accounts:

./mmw probe -n 10

Levers when you want more diversity: --endpoint rotation, patience (MAX_ROUNDS=12 RECREATE_WAIT=90 ./mmw run …), smaller recreate batches (--recreate-batch 1), or more hosts.

Troubleshooting

./mmw status                     # fleet + per-port probe
./mmw logs 1                     # node logs (or: ./mmw logs warp-n1)
podman inspect warp-n1 | less    # full container state
  • Containers start then exit / never go healthy — check ./mmw logs n. Usual causes: missing NET_ADMIN/SYS_MODULE in your podman setup, a locked-down kernel refusing wg0 creation, or first boot needing outbound HTTPS to github.com (wgcf download) and Cloudflare. If GitHub is blocked, set --gh-proxy.
  • All healthy but same public IP — mild overlap is normal (see above). ./mmw run already recreates duplicates; a manual single-node reset is ./mmw wipe 3 && ./mmw run -n 10.
  • First boot all FAIL — registration takes a few seconds per node, more under load. Wait a minute and re-./mmw status.
  • Port already allocatedss -lptn | grep 4000, then ./mmw run -n 10 -p 41001.
  • Remote host needs the pool — bind to a private interface (--bind 0.0.0.0 behind a firewall, or a Tailscale IP if your podman supports binding to it), then ./mmw pool -H <tailscale-ip>.
  • Quadlet units didn't survive reboot — enable linger: sudo loginctl enable-linger $USER.
  • Start over clean./mmw stop && ./mmw wipe all (or ./mmw uninstall --purge for a quadlet fleet).

Legal / ToS

You are responsible for Cloudflare WARP ToS and local law. This repo only orchestrates containers.

Credits

  • ccbkkb/MicroWARP — the minimal kernel-WireGuard WARP engine + microsocks SOCKS5 server inside every node
  • Micolaabdi/multi-warp — reference for the orchestration pattern (N independent identities, recreate-until-unique loop)

License

MIT

About

N independent Cloudflare WARP exits as local SOCKS5 proxies — one command.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Contributors

Languages