torproxy
by hapheus
README.md
<div align="center">
# 🧅 torproxy
[](https://github.com/hapheus/torproxy/releases)
[](https://hub.docker.com/r/hapheus/torproxy)
[](https://hub.docker.com/r/hapheus/torproxy)
[](https://github.com/hapheus/torproxy/stargazers)
[](LICENSE)
[](https://github.com/hapheus/torproxy/actions/workflows/pr-gate.yml)
[](https://github.com/hapheus/torproxy/actions/workflows/daily-image-verification.yml)
[](https://github.com/hapheus/torproxy/actions/workflows/osv-scanner.yml)
[](https://github.com/hapheus/torproxy/actions/workflows/codeql.yml)
[](https://scorecard.dev/viewer/?uri=github.com/hapheus/torproxy)
[](https://www.bestpractices.dev/projects/15828)
[Quick Start](#quick-start) · [Transparent Gateway](#transparent-tor-gateway) · [Model Context Protocol (MCP)](#-model-context-protocol-mcp) · [Use Cases](#-when-to-use-torproxy) · [Architecture](#architecture) · [REST API](docs/api.md) · [Configuration](docs/configuration.md) · [Examples](examples/) · [Contributing](CONTRIBUTING.md)
</div>
**A production-grade, leak-proof Tor HTTP and SOCKS5 proxy packaged for Docker.**
Route entire container stacks without touching application code, equip AI coding assistants (Claude Desktop, Cursor) with native Tor tools via MCP, and rotate circuits dynamically with a single REST call.
- 🛡️ **Zero Clearnet Leaks:** Fail-closed netfilter kill-switch & Privoxy fail-safe — never leaks your real IP on connection drops.
- 🚀 **Transparent Tor Gateway:** Tunnel any container's TCP & DNS via `network_mode: "service:torproxy"` — zero app config changes.
- 🤖 **Native AI Agent MCP Server:** Built-in Model Context Protocol server (SSE & Stdio) for Claude Desktop, Cursor, and Antigravity.
- 🔄 **Dynamic Circuit & Country Control:** Switch identities (`SIGNAL NEWNYM`) and select exit node countries on the fly via REST.
- 🔒 **Defense in Depth:** Runs strictly as an unprivileged user (`torproxy`), with ephemeral cookie authentication for Tor control.
---
## Quick Start
Get up and running with a verified Tor circuit in seconds:
### 1. Launch torproxy
Clone and run via Docker Compose:
```sh
docker compose up -d
```
*Or launch directly via Docker Run without cloning:*
```sh
docker run -d --name torproxy --restart unless-stopped \
-p 127.0.0.1:8118:8118 -p 127.0.0.1:9050:9050 -p 127.0.0.1:8080:8080 \
hapheus/torproxy:latest
```
### 2. Verify Tor connectivity
```sh
# Verify HTTP proxy (returns {"IsTor": true, "IP": "..."})
curl -x http://127.0.0.1:8118 https://check.torproject.org/api/ip
# Verify SOCKS5 proxy with remote DNS resolution
curl --socks5-hostname 127.0.0.1:9050 https://check.torproject.org/api/ip
# Check live connection status via REST API
curl -s http://127.0.0.1:8080/status | jq .
```
> [!IMPORTANT]
> **Leak-Proof Guarantee (Fail-Closed):** If Tor disconnects or crashes, Privoxy immediately returns `503 Service Unavailable` and the Netfilter kill switch drops outgoing packets. Your real IP will **never** silently leak to clearnet under network failures.
> **Security Note:** In `docker-compose.yml`, all ports bind strictly to `127.0.0.1` by default. Neither the proxy listeners nor the management API require authentication; do not expose them to untrusted networks.
<details>
<summary><b>⚡ Advanced: Dynamic Identity Rotation & Country Routing (click to expand)</b></summary>
```sh
# Request a fresh Tor identity without restarting the container:
curl -X POST http://127.0.0.1:8080/reconnect
# Switch exit country dynamically on reconnect:
curl -X POST "http://127.0.0.1:8080/reconnect?country=DE"
# Switch with strict exit country enforcement via JSON payload:
curl -X POST http://127.0.0.1:8080/reconnect \
-H "Content-Type: application/json" \
-d '{"country": "AT", "strict_nodes": true}'
# Or launch with a default country at startup:
TORPROXY_COUNTRY=DE docker compose up -d
```
</details>
---
## 🎯 When to Use torproxy
Understanding where `torproxy` excels helps you pick the right tool for the job:
- ✅ **Ideal For:**
- **Automated Web Scraping & Data Extraction:** Distribute requests and bypass IP rate limits with dynamic circuit rotation.
- **AI Agent Automation (MCP):** Allow Claude Desktop, Cursor, and agent frameworks to fetch web and `.onion` pages safely.
- **Zero-Friction Container Privacy:** Tunnel third-party containers (crawlers, search engines, microservices) with zero code modifications.
- **Geolocation Verification:** Test localized web experiences and CDN geo-routing across different exit countries (`TORPROXY_COUNTRY=DE,AT,US`).
- **Self-Hosted Privacy Stacks:** Route upstream search queries (e.g. SearXNG) privately.
- ⚠️ **Not Designed For:**
- **High-Bandwidth / Latency-Critical Traffic:** Tor is not designed for 4K video streaming, voice calls, or real-time gaming.
- **Bypassing Dedicated Anti-Bot Protection:** Major CDNs (e.g., Cloudflare, Akamai) often challenge or block known public Tor exit nodes.
---
## Architecture
```text
Host Client / Dependent Containers
│
├── HTTP Requests ────────► Privoxy (:8118)
│ │ (forward-socks5t)
├── SOCKS5 Requests ──────────┼───────────────► Tor Daemon (:9050)
│ │ │
└── REST Control API ─────► API Server (:8080) │ (Cookie Auth)
│ │
└─ ControlPort ─────┘ (:9051 loopback)
│
▼
Tor Onion Network
▼
Target Website
```
---
<a id="transparent-tor-gateway"></a>
## 🚀 Transparent Tor Gateway
Route **any** existing Docker container's entire outbound TCP and DNS traffic automatically through Tor — **without modifying your applications or adding proxy flags**.
```yaml
services:
torproxy:
image: hapheus/torproxy:latest
pull_policy: always
cap_add:
- NET_ADMIN
environment:
TORPROXY_GATEWAY_MODE: "true"
TORPROXY_COUNTRY: ${TORPROXY_COUNTRY:-}
restart: unless-stopped
# Attach any application container directly to torproxy's network namespace:
my-app:
image: curlimages/curl:latest
network_mode: "service:torproxy"
depends_on:
torproxy:
condition: service_healthy
command: ["-s", "https://check.torproject.org/api/ip"]
```
### Why use Transparent Gateway mode?
- **Zero Application Changes:** No need to configure `HTTP_PROXY`, `ALL_PROXY`, or browser arguments.
- **Tor DNSPort (:9053):** All DNS requests on port 53 (UDP & TCP) are automatically resolved via Tor to prevent DNS leaks.
- **Fail-Closed Netfilter Kill Switch:** All direct clearnet traffic is blocked. If Tor stops or disconnects, connections fail closed immediately.
- **Process-Scoped WAN Bypass:** Only the dedicated unprivileged `tor` user may reach the WAN (`owner --uid-owner`); the API, Privoxy, and attached app containers cannot bypass the kill switch.
- **IPv6:** Tor may bootstrap over IPv6 (its own exception); every other process remains blocked — no IPv6 clearnet leaks.
- **UDP Protection:** Unsupported non-DNS UDP traffic (e.g. QUIC/HTTP-3) is blocked with ICMP port-unreachable, ensuring fast fallback to TCP without hangs.
- See the ready-to-run [Transparent Gateway Example](examples/transparent-tor-gateway/) for details.
---
## 🤖 Model Context Protocol (MCP)
`torproxy` includes a built-in, zero-dependency **Model Context Protocol (MCP)** server on port `2501` (SSE) and via Stdio (`torproxy mcp`), allowing AI agents (Claude Desktop, Cursor, Antigravity) to manage Tor circuits and execute anonymized web requests directly.
### Claude Desktop Integration (`claude_desktop_config.json`)
Connect Claude Desktop to your running `torproxy` container via Stdio without exposing host ports:
```json
{
"mcpServers": {
"torproxy": {
"command": "docker",
"args": ["exec", "-i", "torproxy", "torproxy", "mcp"]
}
}
}
```
### Cursor & Antigravity Integration (`.cursor/mcp.json`)
Connect via HTTP/SSE:
```json
{
"mcpServers": {
"torproxy": {
"url": "http://127.0.0.1:2501/sse"
}
}
}
```
Or use the root-level `.mcp.json` manifest auto-detected by Cursor and [cursor.directory](https://cursor.directory) on workspace open.
### Smithery.ai Integration (`smithery.yaml`)
Install directly with zero configuration via [Smithery.ai](https://smithery.ai):
```bash
npx -y @smithery/cli install hapheus/torproxy --client claude
```
### Key Tools Available to Agents
- `torproxy_fetch`: **Zero-config fetch** — execute HTTP/HTTPS or `.onion` requests strictly routed through Tor (no local curl or host proxy setup needed).
- `torproxy_status`: Retrieve live bootstrap progress, egress IP, and GeoIP metadata.
- `torproxy_get_ip`: Query the current verified Tor exit IP.
- `torproxy_reconnect`: Request a new Tor identity (`SIGNAL NEWNYM`), optionally changing exit country (`country="DE"`).
- `torproxy_health` & `torproxy_diagnostics`: Automated operational health and circuit diagnostics.
See [docs/mcp.md](docs/mcp.md) for full configuration details, authentication options, and tool schemas.
---
## Available Interfaces and Ports
| Interface | Default Port | Internal Service | Description |
| --------- | ------------ | ---------------- | ----------- |
| **HTTP Proxy** | `8118` | Privoxy | HTTP/HTTPS forward proxy routing traffic to Tor. Fails closed (500/503) if Tor is disconnected. |
| **SOCKS5 Proxy** | `9050` | Tor | Direct Tor SOCKS5 listener. Supports remote DNS resolution via `socks5h://`. |
| **REST API** | `8080` | Python REST API | JSON management and health endpoint. Circuit rotation and connection management. |
| **MCP Server (SSE)** | `2501` | Python MCP Server | Model Context Protocol server (SSE) for AI agents (Cursor, Antigravity). |
| **TransPort** *(Gateway Mode)* | `9040` | Tor | Transparent TCP proxy redirect listener when `TORPROXY_GATEWAY_MODE=true`. |
| **DNSPort** *(Gateway Mode)* | `9053` | Tor | Transparent DNS resolver listener when `TORPROXY_GATEWAY_MODE=true`. |
---
## REST API Reference
The lightweight API server on port `8080` manages circuits and monitors health without requiring clients to implement the Tor Control Protocol.
| Endpoint | Method | Status Code | Purpose | Example Response |
| -------- | ------ | ----------- | ------- | ---------------- |
| `/status`, `/` | `GET`, `POST` | `200` | Connection status and current exit IP. | `{"status":"connected","ip":"198.51.100.42"}` |
| `/ip` | `GET` | `200`, `503` | Current verified Tor exit IP. Returns 503 while bootstrapping. | `{"ip":"198.51.100.42"}` |
| `/health`, `/v1/health` | `GET` | `200`, `503` | Structured diagnostic health state across Tor, proxies, and egress. | `{"status":"healthy","tor":{"bootstrapped":true,"progress":100},"proxies":{"socks5":true},"egress":{"verified":true,"ip":"..."}}` |
| `/diagnostics`, `/v1/diagnostics` | `GET` | `200` | Operational telemetry (uptime, reconnect counts, bootstrap progress). | `{"uptime_seconds":120,"reconnect_total":2,"tor":{"bootstrapped":true}}` |
| `/metrics`, `/v1/metrics` | `GET` | `200` | Prometheus exposition format metrics for scrapers. | Plaintext Prometheus metrics. |
| `/reconnect` | `POST` (or `GET`) | `200`, `400` | Request new circuit (`SIGNAL NEWNYM`). Supports `?country=DE` or JSON body. Clears IP cache. | `{"action":"reconnect","status":"success","exit_country":"DE"}` |
| `/disconnect` | `POST` (or `GET`) | `200` | Pause Tor network traffic (`DisableNetwork=1`). Fails closed. | `{"action":"disconnect","status":"success"}` |
| `/connect` | `POST` (or `GET`) | `200`, `400` | Resume Tor network traffic (`DisableNetwork=0`). Supports `?country=DE` or JSON body. | `{"action":"connect","status":"success"}` |
> **HTTP Methods:** `POST` is recommended for state mutations (`/reconnect`, `/disconnect`, `/connect`). Legacy `GET` calls remain fully supported for backward compatibility. Mutations presented with a foreign browser `Origin`/`Referer` are rejected with `403 Forbidden` (CSRF protection); `curl`/script calls without those headers are unaffected.
> **Authentication (Recommended):** Unauthenticated by default for backward compatibility. Setting `TORPROXY_API_TOKEN` (or `TORPROXY_API_KEY`) protects all endpoints with `Authorization: Bearer <token>` or `X-API-Key: <token>`.
> Detailed schema definitions are documented in [docs/api.md](docs/api.md).
---
## Container Health Checks
`torproxy` includes a ready-aware Docker health check (`docker/healthcheck.sh`). A container reports `healthy` only when:
1. Privoxy and Tor daemons are running under non-root user.
2. Tor bootstrap is 100% complete (`bootstrapped=done`).
3. SOCKS5 listener on port 9050 passes greeting handshake.
4. Egress IP is confirmed through Tor (`IsTor: true`).
In Docker Compose, dependent services can wait for genuine Tor readiness using `service_healthy`:
```yaml
services:
app:
image: my-scraper:latest
depends_on:
torproxy:
condition: service_healthy
```
---
## Configuration
Default settings work out-of-the-box with zero configuration. You can customize runtime behavior using environment variables in `.env` or Docker Compose:
| Variable | Default | Purpose |
| -------- | ------- | ------- |
| `TORPROXY_COUNTRY` | *(unset)* | Restrict exit nodes to specific countries (e.g. `DE`, `AT`, `US`, `DE,AT`). |
| `TORPROXY_STRICT_NODES` | `0` | When `1`/`true`, reject circuits if configured country exit nodes are unavailable. |
| `TORPROXY_GATEWAY_MODE` | `0` | Enable Transparent Tor Gateway mode (`TransPort`, `DNSPort`, netfilter kill switch). Requires `cap_add: [NET_ADMIN]`. |
| `TORPROXY_API_TOKEN` | *(unset)* | Bearer token / API key securing REST API port `8080`. Recommended for production. |
| `TORPROXY_GEOIP_AUTO_UPDATE` | `0` | Opt-in: periodically download and refresh Tor's GeoIP database from Tor Project GitLab. |
> 📖 **Full Configuration Reference:** See [docs/configuration.md](docs/configuration.md) for custom listening ports (`TORPROXY_LISTEN_PORT`, `TORPROXY_SOCKS_PORT`, `TORPROXY_API_PORT`), address binding rules, and GeoIP update parameters.
---
## Shipped Examples
Ready-to-run use cases with hardened configurations:
| Example | Purpose | Guide |
| ------- | ------- | ----- |
| **Transparent Tor Gateway** | Route any container's entire TCP & DNS traffic automatically through Tor without proxy settings. | [View Example](examples/transparent-tor-gateway/) |
| **Hardened Production** | Read-only rootfs, dropped capabilities, and memory-backed tmpfs. | [View Example](examples/hardened-production/) |
| **Browserless Chromium** | Headless browser automation and scraping routed through Tor. | [View Example](examples/browserless-chromium-via-tor/) |
| **CRW Web Scraper** | High-performance webpage-to-Markdown scraper via Tor proxy. | [View Example](examples/crw-web-scraper-via-tor/) |
| **SearXNG Metasearch** | Private, self-hosted search engine routing upstream queries via Tor. | [View Example](examples/searxng-via-tor/) |
---
## Security Model and Trust Boundaries
- **Non-Root Execution**: The container init starts as root only to drop privileges — every daemon runs as an unprivileged user (`tor`, `privoxy`, `torproxy`). In gateway mode the netfilter WAN bypass is scoped to the dedicated `tor` UID, so the API/Privoxy cannot reach the WAN directly (ISSUE-03).
- **Authenticated ControlPort**: Tor's control port (`127.0.0.1:9051`) requires ephemeral filesystem cookie authentication (`CookieAuthentication 1`).
- **Fail-Closed Guarantee**: If Tor disconnects or crashes, Privoxy drops traffic and returns HTTP 503 instead of leaking to clearnet.
- **DNS Leak Prevention**: To prevent local DNS leaks, always use remote DNS resolution with SOCKS5 (`curl --socks5-hostname 127.0.0.1:9050` or `socks5h://`).
- **Threat Model**: See [SECURITY.md](SECURITY.md) for vulnerability reporting procedures, threat boundaries, and anonymity limitations.
- **Hardening Guide**: See [docs/hardening.md](docs/hardening.md) for read-only rootfs and dropped Linux capabilities.
---
## Project Documentation
- [docs/compatibility.md](docs/compatibility.md) — Backward compatibility contract and frozen interfaces.
- [docs/api.md](docs/api.md) — Exhaustive REST API specification and response schemas.
- [docs/configuration.md](docs/configuration.md) — Configuration guide and startup validation rules.
- [docs/hardening.md](docs/hardening.md) — Container runtime hardening, read-only rootfs, and security recommendations.
- [docs/releases.md](docs/releases.md) — Semantic versioning policy, upgrade paths, and rollback procedures.
---
## Contributing
We welcome contributions! Please review our [Contributing Guidelines](CONTRIBUTING.md) and [Code of Conduct](CONTRIBUTING.md#core-engineering-principles).
All PRs must maintain backward compatibility, include hermetic unit tests, and maintain >= 90% test coverage.
---
## License
Released under the [MIT License](LICENSE).
---
## 💝 Support the Project
If you find this project helpful, consider showing some support:
- ☕ Buy me a coffee: Send a tip via [PayPal](https://paypal.me/hapheus).
- ❤️ Charity Donation: Donate to [Österreichische Krebshilfe (Austrian Cancer Aid)](https://www.krebshilfe.net/) or any local animal welfare organization.
- 🎯 Dream Support: I would love some tickets for the World Darts Championship at Ally Pally (Alexandra Palace) – hopefully, I will make it there one day!
This server cannot be deployed
Maintenance
ActivityActive
ResponsivenessLimited data