Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tokenbel-proxy

Small Go web service that proxies outbound GET requests through an upstream proxy with TLS client fingerprinting. Two upstream transports are supported, each with its own endpoint:

  • GET /socks5 (also available as GET /proxy) — SOCKS5 upstream, local DNS resolution
  • GET /https — HTTP(S) CONNECT upstream, remote DNS at the proxy exit (optional route)

and one technical endpoint:

  • GET /health

Business Logic

/socks5 accepts a target URL via query params, enforces Bearer token auth, forwards the request through SOCKS5, and returns upstream response as-is.

Flow:

  1. Validate Authorization header: Bearer <token>.
  2. Read query params:
    • url (required if target not set): full HTTPS URL used as-is
    • target (required if url not set): host/path without protocol, service prepends https://
    • method (required): only get supported (case-insensitive)
    • data_type (optional): currently ignored, kept for compatibility
  3. Only HTTPS targets are allowed. HTTP URLs are rejected with 400.
  4. If TOKENBEL_PROXY_ALLOWED_DOMAINS is configured, only requests to listed domains (including subdomains) are allowed.
  5. Execute request through SOCKS5 proxy with per-request timeout.
  6. Return upstream status code, headers, and body.

Route differences

Both proxy routes share the same contract (query params, auth, allowlist, error mapping). They differ only in upstream transport:

/socks5, /proxy /https
Upstream proxy URL TOKENBEL_PROXY_SOCKS5_URL (socks5/socks5h) TOKENBEL_PROXY_HTTPS_URL (http/https)
DNS resolution local — service resolves IPv4 and sends the IP through SOCKS remote — CONNECT carries the hostname, DNS resolves at the proxy exit
Caller User-Agent dropped forwarded verbatim; profile-matched Chrome UA when the caller sends none
Typical use gov.by JSON APIs anti-bot-protected sites (e.g. pravo.by) that reject datacenter DNS resolution and non-browser UAs

/https requires TOKENBEL_PROXY_HTTPS_URL. Without it the route returns 503 {"error":"endpoint_not_configured"} while /socks5 and /proxy keep working.

Error behavior:

  • Missing/invalid Bearer token -> 401
  • Missing url/target -> 400
  • HTTP (non-HTTPS) target -> 400
  • Disallowed domain -> 403
  • Invalid method -> 400
  • Rate limit exceeded -> 429
  • Upstream timeout -> 504
  • Proxy/upstream network error -> 502

Error response shape:

{
    "error": "bad_gateway",
    "message": "proxy connection failed",
    "details": "connection refused"
}

Configuration

Configuration is read from environment variables:

Variable Required Description
TOKENBEL_PROXY_BEARER_TOKEN Yes Bearer token for endpoint authentication
TOKENBEL_PROXY_SOCKS5_URL Yes SOCKS5 proxy URL (e.g., socks5://user:pass@host:port)
TOKENBEL_PROXY_HTTPS_URL No HTTP(S) CONNECT proxy URL (e.g., http://user:pass@geo.iproyal.com:12321); enables the /https route
TOKENBEL_PROXY_PORT No Server port (default: 8080)
TOKENBEL_PROXY_TIMEOUT No Upstream request timeout in seconds (default: 120)
TOKENBEL_PROXY_CLIENT_PROFILE No TLS client profile: chrome_120 or firefox_117 (default: chrome_120)
TOKENBEL_PROXY_INSECURE_TLS No Skip TLS verification: true or false (default: true)
TOKENBEL_PROXY_ALLOWED_DOMAINS No Comma-separated domain allowlist (default: empty = all allowed)
TOKENBEL_PROXY_RATE_LIMIT No Requests per second per IP (default: 0 = unlimited)
TOKENBEL_PROXY_RATE_BURST No Burst size for rate limiter (default: same as rate limit)

Example

export TOKENBEL_PROXY_BEARER_TOKEN="your-secret-token"
export TOKENBEL_PROXY_SOCKS5_URL="socks5://user:password@geo.iproyal.com:12321"
export TOKENBEL_PROXY_HTTPS_URL="http://user:password@geo.iproyal.com:12321"  # optional, enables /https
export TOKENBEL_PROXY_ALLOWED_DOMAINS="egr.gov.by,example.com"
export TOKENBEL_PROXY_RATE_LIMIT=5

The service will exit at startup if required variables are missing.

API Contract

1) Health endpoint

GET /health

Checks TCP reachability of every configured proxy (SOCKS5 always; HTTPS when TOKENBEL_PROXY_HTTPS_URL is set). Returns 200 if all are reachable, 503 otherwise.

Success response:

{ "status": "ok" }

Unhealthy response:

{ "status": "unhealthy", "details": "proxy unreachable" }

2) Proxy endpoint

GET /socks5?url=<https-url>&method=get GET /socks5?target=<host/path>&data_type=json&method=get

Required header:

Authorization: Bearer $TOKENBEL_PROXY_BEARER_TOKEN

A X-Request-ID header is set on every response. If the incoming request includes one, it is forwarded; otherwise a random ID is generated.

Example:

curl -i \
  -H "Authorization: Bearer $TOKENBEL_PROXY_BEARER_TOKEN" \
  "http://localhost:8080/socks5?target=egr.gov.by/api/v2/egr/getAllJurNamesByRegNum/191179355&data_type=json&method=get"

3) HTTPS proxy endpoint

GET /https?url=<https-url>&method=get GET /https?target=<host/path>&method=get

Same contract as /socks5 (auth header, query params, allowlist, error mapping), but the request is tunneled through the HTTP(S) CONNECT proxy configured via TOKENBEL_PROXY_HTTPS_URL. Two behavioral differences:

  • the target hostname travels in the CONNECT request, so DNS resolves at the proxy exit (socks5h-style remote resolution);
  • the caller's User-Agent is forwarded upstream (anti-bot front-ends reject Go/curl defaults); when the caller sends none, a Chrome UA matching the default TLS profile is applied.

Without TOKENBEL_PROXY_HTTPS_URL the route returns 503 {"error":"endpoint_not_configured","details":"set TOKENBEL_PROXY_HTTPS_URL"}.

Example:

curl -i \
  -H "Authorization: Bearer $TOKENBEL_PROXY_BEARER_TOKEN" \
  -H "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" \
  -H "Accept-Language: ru" \
  "http://localhost:8080/https?target=pravo.by/ofitsialnoe-opublikovanie/novye-postupleniya/&method=get"

Local Build and Run

From project root:

cd src
go mod tidy
go build -o proxy .
./proxy

The service starts on :8080.

Quick checks:

curl -s http://localhost:8080/health
curl -i "http://localhost:8080/socks5?target=example.com&method=get"
# expected: 401 (missing auth header)

Testing

Run all checks:

cd src
go test ./...
go vet ./...
go build ./...

Current unit tests are in:

  • handler/proxy_test.go
  • handler/health_test.go
  • handler/notconfigured_test.go
  • handler/ratelimit_test.go
  • handler/requestid_test.go
  • client/client_test.go
  • config/config_test.go

Covered scenarios:

  • unauthorized requests
  • missing target
  • invalid method
  • HTTPS-only enforcement
  • domain allowlist (allowed, blocked, subdomains)
  • per-request timeout (504)
  • request ID generation and forwarding
  • rate limiting (under/over limit, separate IPs)
  • health check (proxy reachable, unreachable, no proxy configured, multiple proxies)
  • client constructor scheme validation (socks5 vs http/https)
  • User-Agent forwarding rules (dropped on /socks5, forwarded/defaulted on /https)
  • not-configured route stub (503)
  • timeout mapping
  • bad gateway mapping
  • success response forwarding
  • config env var parsing

Docker Build and Deployment

Service includes a multi-stage Dockerfile:

  • Builder: golang:alpine
  • Runtime: alpine:latest

Build image

From repository root:

docker build -t tokenbel-proxy:latest ./src

Run container

docker run --rm -p 8080:8080 \
  -e TOKENBEL_PROXY_BEARER_TOKEN="$TOKENBEL_PROXY_BEARER_TOKEN" \
  -e TOKENBEL_PROXY_SOCKS5_URL="$TOKENBEL_PROXY_SOCKS5_URL" \
  -e TOKENBEL_PROXY_HTTPS_URL="$TOKENBEL_PROXY_HTTPS_URL" \
  tokenbel-proxy:latest

Validate container

curl -s http://localhost:8080/health

Deployment Notes

For production-like deployment, at minimum:

  1. Restrict inbound access (private network, gateway, ACLs).
  2. Set TOKENBEL_PROXY_ALLOWED_DOMAINS to limit upstream targets.
  3. Enable rate limiting via TOKENBEL_PROXY_RATE_LIMIT.
  4. Set TOKENBEL_PROXY_INSECURE_TLS=false if upstream certificates are valid.
  5. Monitor logs (structured JSON via log/slog) and HTTP status distribution.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages