Skip to content

crystade/proxy-server-go

Repository files navigation

Proxy Server (Go implementation)

An implementation of Crystade Proxy Server.

Features

  • ✅ Custom header-based proxying (X-Proxy-Upstream)
  • ✅ IP blacklist (RFC 1918 private networks, loopback, link-local)
  • ✅ DNS resolution with fresh lookups per request
  • ✅ Loop detection via X-Proxy-State header
  • ✅ Optional authentication via X-Proxy-Auth
  • ✅ Optional redirect following (X-Proxy-Follow-Redirects)
  • ✅ TLS certificate validation
  • ✅ Connection lifecycle management (client/upstream disconnection handling)
  • ✅ Proper error responses with X-Proxy-State: ERROR

Configuration

All configuration is via environment variables:

Variable Required Default Description
HOST No localhost Host/interface to bind to (e.g., 0.0.0.0, localhost)
PORT No 8080 Port to listen on (e.g., 8080)
AUTH_SECRET No "" If set, requires X-Proxy-Auth header with this value
UPSTREAM_TIMEOUT No 30s Timeout for upstream connections (e.g., 10s, 1m)
DISABLE_TLS_VERIFY No false Disable TLS certificate verification (dev only)

Usage

Build

go build -o proxy-server .

Run

# Basic usage (defaults to localhost:8080)
./proxy-server

# Custom port
PORT=9000 ./proxy-server

# Bind to all interfaces
HOST=0.0.0.0 PORT=8080 ./proxy-server

# With authentication
PORT=8080 AUTH_SECRET=my-secret-token ./proxy-server

# With custom timeout
PORT=8080 UPSTREAM_TIMEOUT=10s ./proxy-server

Making Requests

# Basic request
curl -H "X-Proxy-Upstream: https://api.example.com/data" \
     http://localhost:8080/

# With authentication
curl -H "X-Proxy-Upstream: https://api.example.com/data" \
     -H "X-Proxy-Auth: my-secret-token" \
     http://localhost:8080/

# POST request with body
curl -X POST \
     -H "X-Proxy-Upstream: https://api.example.com/submit" \
     -H "Content-Type: application/json" \
     -d '{"key":"value"}' \
     http://localhost:8080/

# With redirect following (same-origin only)
curl -X POST \
     -H "X-Proxy-Upstream: https://api.example.com/submit" \
     -H "X-Proxy-Follow-Redirects: same-origin" \
     -H "Content-Type: application/json" \
     -d '{"key":"value"}' \
     http://localhost:8080/

Redirect Handling

By default, the proxy forwards 3xx redirect responses to the client unchanged. To have the proxy follow redirects automatically:

# Follow same-origin redirects only
# (redirect must have same scheme, host, and port as original URL)
curl -H "X-Proxy-Upstream: https://api.example.com/redirect" \
     -H "X-Proxy-Follow-Redirects: same-origin" \
     http://localhost:8080/

# Follow cross-origin redirects
# (cookies are stripped when origin changes for security)
curl -H "X-Proxy-Upstream: https://api.example.com/redirect" \
     -H "X-Proxy-Follow-Redirects: cross-origin" \
     http://localhost:8080/

Redirect Following Behavior:

  • Follows up to 5 consecutive redirects (returns 508 Loop Detected if exceeded)
  • Only follows 301, 302, 307, and 308 status codes with Location header
  • Methods are transformed per HTTP semantics:
    • 301/302: Changes to GET (except HEAD remains HEAD)
    • 307/308: Preserves original method
  • In same-origin mode: Blocks redirects to different origins
  • In cross-origin mode: Strips Cookie header when origin changes
  • All redirect targets are validated against IP blacklist
  • Only the final response is returned to the client

Response Headers

Every response includes X-Proxy-State:

  • X-Proxy-State: FORWARDED - Response is from the upstream server
  • X-Proxy-State: ERROR - Response is a proxy-generated error

Error Codes

Status Reason
400 Bad Request Missing or invalid X-Proxy-Upstream, invalid URL scheme, or auth over plain HTTP
401 Unauthorized Missing or invalid X-Proxy-Auth
403 Forbidden Upstream IP is blacklisted (private/loopback/link-local)
501 Not Implemented Protocol upgrade requested (WebSocket, etc.)
502 Bad Gateway Upstream unreachable (DNS failure, connection refused, etc.)
504 Gateway Timeout Upstream connection timeout
508 Loop Detected Upstream response contains X-Proxy-State header, or redirect chain exceeds 5 hops

Security

⚠️ This proxy is designed for internal use only. Do not expose it to the public internet without:

  • Strong authentication (AUTH_SECRET)
  • TLS/HTTPS on the client-proxy connection
  • Network-layer access controls (firewall, VPC, etc.)
  • Additional security measures (mTLS, IP allowlisting, etc.)

IP Blacklist

The proxy blocks connections to:

  • RFC 1918 private networks: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
  • Loopback: 127.0.0.0/8, ::1/128
  • Link-local: 169.254.0.0/16, fe80::/10
  • IPv6 unique local: fc00::/7
  • Current network: 0.0.0.0/8

Examples

Successful Request

$ curl -v -H "X-Proxy-Upstream: https://httpbin.org/get" http://localhost:8080/
< HTTP/1.1 200 OK
< X-Proxy-State: FORWARDED
< Content-Type: application/json
...

Blocked Private IP

$ curl -v -H "X-Proxy-Upstream: http://192.168.1.1/" http://localhost:8080/
< HTTP/1.1 403 Forbidden
< X-Proxy-State: ERROR
< Content-Type: application/json
{"reason":"upstream IP is forbidden","detail":"target ip 192.168.1.1 is blocked by 192.168.0.0/16"}

Missing Header

$ curl -v http://localhost:8080/
< HTTP/1.1 400 Bad Request
< X-Proxy-State: ERROR
< Content-Type: application/json
{"reason":"missing X-Proxy-Upstream header"}

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Crystade Proxy Server implementation in Go

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors