Sign inSign up

hightemp/go_proxy_mux

By hightemp

•Updated 13 days ago

Image
0

434

hightemp/go_proxy_mux repository overview

⁠Go Proxy Mux

GitHub Repo Go Version GitHub release GitHub Downloads Docker Pulls Tests Release

An HTTP/HTTPS forward proxy that distributes client requests across HTTP, HTTPS, SOCKS4, and SOCKS5 upstream proxies. It supports Basic authentication, HTTP/1.1 and HTTP/2 CONNECT, and bounded failover.

⁠Features

  • HTTP or TLS-protected HTTPS listener; HTTP/2 CONNECT is available over TLS
  • Separate Basic authentication for clients and upstream proxies
  • Round-robin or random selection across HTTP, HTTPS, SOCKS4, and SOCKS5 upstreams
  • SOCKS4a for destination hostnames, and SOCKS5 for hostnames and IPv6 destinations
  • Temporary exclusion of an upstream after a network or CONNECT setup failure
  • One alternate attempt for bodyless GET/HEAD and for CONNECT before success; requests with bodies are not replayed
  • Global and per-IP limits for client TCP connections and CONNECT tunnels, configurable Go HTTP/TLS timeouts and connection pools, and graceful shutdown
  • Configuration through .env, process environment, or strict YAML

⁠Installation

⁠From release

Download a binary and SHA256SUMS from GitHub Releases⁠. On Linux, check the downloaded files in the same directory:

sha256sum --ignore-missing -c SHA256SUMS

Release assets include binaries for Linux, macOS, and Windows on amd64 and arm64. The env template is named env.example in the release assets; copy it to .env before editing. The assets contain examples but no working credentials or TLS keys.

⁠Docker

Images are published to Docker Hub⁠. To build the version from this checkout locally:

make docker-build

After preparing .env and certificate files, run the local image:

docker run -d --name go_proxy_mux -p 8380:8380 \
  --user "$(id -u):$(id -g)" --read-only --cap-drop ALL \
  --security-opt no-new-privileges \
  --env-file .env \
  -v "$PWD/certs:/app/certs:ro" \
  "hightemp/go_proxy_mux:$(cat VERSION)"

Use Docker Compose⁠ for either .env or config.yaml configuration.

⁠Build from source

Go 1.26.7 or newer is required.

git clone https://github.com/hightemp/go_proxy_mux.git
cd go_proxy_mux
make build

make build-static creates go_proxy_mux_static with CGO disabled. Both targets build for the current system. GitHub Actions builds the cross-platform release binaries.

⁠Project structure
cmd/go_proxy_mux/   application entry point
internal/config/    YAML, .env, environment overrides, and validation
internal/balancer/  upstream selection and cooldown
internal/proxy/     authentication, forwarding, tunnels, and server lifecycle
internal/socks/     SOCKS4/SOCKS4a and SOCKS5 TCP dialing

⁠Configuration

Copy .env.example⁠ to .env, replace all placeholder credentials and upstream addresses, and provide a certificate and key if the listener is exposed over the network:

cp .env.example .env
chmod 600 .env

Set MUX_UPSTREAM_COUNT to the number of upstream entries. Number their fields consecutively from MUX_UPSTREAM_1_* to MUX_UPSTREAM_N_*. The example intentionally fails validation until its placeholders and TLS files are replaced.

Settings take precedence in this order: process environment → .env → YAML → built-in defaults. The -env flag selects an env file (.env by default); -env "" disables it. The -config flag selects a YAML file (config.yaml by default). If YAML is absent, an env-only configuration must provide the upstream list.

.env uses literal KEY=VALUE lines. Do not wrap values in shell quotes. Values may contain #, $, or =; multiline values are not supported. MUX_PROXY_TIMEOUT is an integer number of seconds. Other timeout settings use Go duration syntax such as 15s or 2m.

⁠Environment variables

Every application setting is represented in .env.example⁠. MUX_PUBLISH_HOST, MUX_PUBLISH_PORT, PROXY_RUN_UID, and PROXY_RUN_GID affect only Docker Compose.

VariablePurpose
MUX_SERVER_HOST, MUX_SERVER_PORTListener address and port
MUX_SERVER_TLS_CERT_FILE, MUX_SERVER_TLS_KEY_FILETLS certificate and private key paths; both empty selects HTTP
MUX_SERVER_ALLOW_INSECURE_PUBLIC_HTTPExplicitly allow non-loopback HTTP
MUX_SERVER_ALLOW_UNAUTHENTICATED_PUBLIC_PROXYExplicitly allow a non-loopback listener without client auth
MUX_SERVER_MAX_CONNECTIONSMaximum active client TCP connections
MUX_SERVER_MAX_CONNECTIONS_PER_IPMaximum active client connections per source IP
MUX_SERVER_MAX_HEADER_BYTESIncoming HTTP request-header limit
MUX_SERVER_HTTP2_MAX_CONCURRENT_STREAMSHTTP/2 stream limit; 0 derives it from tunnel limits
MUX_SERVER_HTTP2_SEND_PING_TIMEOUT, MUX_SERVER_HTTP2_PING_TIMEOUT, MUX_SERVER_HTTP2_WRITE_BYTE_TIMEOUTHTTP/2 connection health and stalled-write timeouts
MUX_SERVER_READ_HEADER_TIMEOUT, MUX_SERVER_IDLE_TIMEOUT, MUX_SERVER_SHUTDOWN_TIMEOUTListener and shutdown timeouts
MUX_AUTH_ENABLED, MUX_AUTH_USERNAME, MUX_AUTH_PASSWORDClient Basic authentication
MUX_AUTH_MAX_FAILED_ATTEMPTS, MUX_AUTH_FAILURE_WINDOW, MUX_AUTH_BLOCK_DURATION, MUX_AUTH_MAX_TRACKED_IPSFailed login throttling per source IP
MUX_PROXY_ALGORITHMroundrobin or random
MUX_PROXY_TIMEOUTHTTP setup through response headers and CONNECT setup timeout, in seconds
MUX_PROXY_NETWORKOutbound auto, tcp4, or tcp6 dialing
MUX_PROXY_DIAL_TIMEOUT, MUX_PROXY_DIAL_KEEP_ALIVETCP dial and keep-alive durations
MUX_PROXY_TLS_HANDSHAKE_TIMEOUT, MUX_PROXY_RESPONSE_HEADER_TIMEOUTOutbound TLS and response-header deadlines
MUX_PROXY_RESPONSE_BODY_IDLE_TIMEOUTMaximum pause between bytes read from an HTTP response body
MUX_PROXY_IDLE_CONN_TIMEOUT, MUX_PROXY_EXPECT_CONTINUE_TIMEOUTHTTP transport idle and Expect: 100-continue timeouts
MUX_PROXY_MAX_IDLE_CONNS, MUX_PROXY_MAX_IDLE_CONNS_PER_HOST, MUX_PROXY_MAX_CONNS_PER_HOSTHTTP transport connection-pool limits
MUX_PROXY_MAX_TUNNELS, MUX_PROXY_TUNNEL_IDLE_TIMEOUTActive tunnel limit and idle timeout
MUX_PROXY_MAX_TUNNELS_PER_IPMaximum active tunnels per source IP
MUX_PROXY_FAILOVER_COOLDOWNTime to exclude a failed upstream
MUX_UPSTREAM_COUNTNumber of upstream entries
MUX_UPSTREAM_N_URLUpstream URL: http://, https://, socks4://, or socks5://
MUX_UPSTREAM_N_AUTH_ENABLED, MUX_UPSTREAM_N_AUTH_USERNAME, MUX_UPSTREAM_N_AUTH_PASSWORDCredentials for upstream N
MUX_UPSTREAM_N_TLS_CA_FILEOptional private CA file for an HTTPS upstream
MUX_PUBLISH_HOST, MUX_PUBLISH_PORTHost-side Compose binding only
PROXY_RUN_UID, PROXY_RUN_GIDContainer process identity in Compose only
⁠Go network stack tuning

MUX_PROXY_TIMEOUT limits forwarded HTTP requests until the response headers arrive, and limits CONNECT setup. It does not cap the total duration of an HTTP response body. MUX_PROXY_RESPONSE_BODY_IDLE_TIMEOUT defaults to 2 minutes and closes a response only when no body bytes arrive during that interval. Large downloads can run for hours while data keeps arriving. The separate dial, TLS handshake, and response-header timeouts apply within the setup deadline. MUX_PROXY_NETWORK selects automatic, IPv4-only, or IPv6-only TCP dialing to an upstream proxy; SOCKS upstreams still resolve destination hostnames themselves.

The transport settings control idle connection reuse and limits per destination host. MUX_PROXY_MAX_CONNS_PER_HOST=0 leaves the total per-host limit unlimited. The HTTP/2 SETTINGS stream limit defaults to the lower of the global and per-IP tunnel limits; ping and stalled-write timeouts use Go's net/http.HTTP2Config.

⁠YAML configuration

YAML is also supported; see config.example.yaml⁠. A minimal TLS example is:

server:
  host: "0.0.0.0"
  port: 8380
  tls:
    cert_file: "./certs/fullchain.pem"
    key_file: "./certs/privkey.pem"
auth:
  enabled: true
  username: "<set-username>"
  password: "<set-strong-password>"
  max_failed_attempts: 10
  failure_window: 1m
  block_duration: 5m
  max_tracked_ips: 4096
proxy:
  algorithm: roundrobin
  timeout: 30
  response_body_idle_timeout: 2m
upstreams:
  - url: "socks5://socks.example.net:1080"
    auth:
      enabled: true
      username: "<upstream-username>"
      password: "<upstream-password>"

Replace the placeholders before use. Unknown YAML keys and invalid settings stop startup. To run only from YAML when .env is present in the project directory:

./go_proxy_mux -config config.yaml -env ""
⁠Listener security and TLS

For local HTTP, bind 127.0.0.1 and leave both TLS paths empty. A listener on another address requires TLS unless allow_insecure_public_http is explicitly enabled. Public access without client authentication separately requires allow_unauthenticated_public_proxy. The shipped examples use TLS and Basic authentication.

The certificate must match the hostname or IP used by clients. Certificate and key files are validated at startup. Renew them externally and restart the proxy to load replacements. The Docker image contains neither configuration secrets nor TLS keys.

By default, the tenth failed Basic authentication attempt from one source IP within a one-minute window blocks that IP for 5 minutes. The window begins with its first failure. Earlier failures receive 407; blocked requests receive 429 with Retry-After, including requests with correct credentials until the block expires. A successful login before the block clears the failure count. Clients sharing one public IP also share its limit. max_tracked_ips bounds in-memory failure records (4096 by default); when full, the oldest record is evicted. Counters reset when the process restarts.

⁠Upstream proxies and failover

Each upstream is selected by its URL scheme. HTTP and HTTPS upstreams can use Basic authentication. An HTTPS upstream validates its certificate against system roots; set tls_ca_file only for a private CA.

SOCKS4 supports an optional USERID in auth.username and no password. Domain destinations use SOCKS4a; IPv6 destinations require SOCKS5. SOCKS5 supports either no authentication or a username/password. Destination hostnames are sent to the SOCKS upstream for resolution. SOCKS5 username/password is sent to that upstream without encryption. Keep upstream credentials in auth, not in the URL.

roundrobin and random choose among available upstreams. A network or CONNECT setup failure excludes an upstream for failover_cooldown (30 seconds by default). Before responding to the client, a bodyless GET/HEAD or CONNECT may try one alternate upstream. POST and other requests with bodies are never replayed. An upstream 407 becomes a client-facing 502, so clients are not asked for the upstream's credentials. There is no automatic direct connection if all upstreams fail.

⁠Resource limits

max_connections and max_tunnels bound active client TCP connections and CONNECT tunnels globally; their *_per_ip counterparts also limit each source IP and cannot exceed the global limits. max_header_bytes bounds incoming request headers. The HTTP/2 stream limit defaults to the lower tunnel limit and cannot exceed either tunnel limit; ping and stalled-write timeouts can be tuned separately. A tunnel closes after tunnel_idle_timeout without traffic. SIGINT and SIGTERM stop new requests and close tracked tunnels within shutdown_timeout.

⁠Docker Compose

Two Compose files provide separate configuration paths:

ConfigurationCommandNotes
.envdocker compose up --builddocker-compose.yml⁠ passes .env as raw values; Docker Compose 2.30+ required. MUX_PUBLISH_HOST:MUX_PUBLISH_PORT maps to MUX_SERVER_PORT.
config.yamldocker compose -f docker-compose.config.yml up --builddocker-compose.config.yml⁠ mounts YAML read-only and disables .env loading in the application. It publishes port 8380; update the mapping if server.port differs.

Both variants mount certs/ read-only and run with a read-only root filesystem, no Linux capabilities, and no privilege escalation. The image defaults to UID/GID 1000; Compose uses PROXY_RUN_UID and PROXY_RUN_GID (also 1000 by default). Use a non-root UID that can read your mode-0600 configuration and key files, or grant the chosen group read access. Set server.host (or MUX_SERVER_HOST) to 0.0.0.0 inside the container. The certificate and key paths in the examples resolve under /app/certs/. Base images are pinned by digest and checked for updates weekly by Dependabot.

⁠Usage

Start with .env:

./go_proxy_mux -env .env

A local HTTP listener with client authentication disabled can proxy an HTTPS website using CONNECT:

curl --proxy http://127.0.0.1:8380 https://example.com

For a TLS listener, configure clients with an HTTPS proxy URL, the certificate's hostname, and client credentials. Both incoming modes can carry HTTPS site traffic; TLS on the listener protects the client-to-proxy connection.

⁠Makefile commands

CommandDescription
make buildBuild a normal binary for the current system
make build-staticBuild go_proxy_mux_static with CGO disabled
make runRun from the project directory; .env is loaded when present
make ciCheck formatting, vet, lint, and run race-enabled tests
make load-testRun concurrent local HTTP and CONNECT benchmarks
make docker-buildBuild hightemp/go_proxy_mux:<VERSION> locally
make docker-pushPush that local image to Docker Hub
make cleanRemove local normal and static binaries
make releaseCheck, build, commit, tag VERSION, and atomically push main and the tag

⁠Testing

make ci

CI validates both Compose variants and builds the Docker image. Integration tests cover HTTP forwarding, HTTP/1.1 and HTTP/2 CONNECT, upstream TLS, SOCKS4/SOCKS5, authentication, failover, cancellation, and shutdown.

make load-test measures authenticated small and 64 KiB HTTP responses plus HTTP/1.1, HTTP/2, and SOCKS5 CONNECT echo tunnels through local upstreams at 32 workers (-cpu=4, parallelism 8). The regular test suite checks that slow client tunnels release their slots. The benchmarks report throughput and allocations for this machine; they do not measure remote upstream latency or Internet bandwidth.

⁠Release

  1. Set the plain semantic version in VERSION⁠.
  2. Configure the repository secrets DOCKERHUB_USERNAME and DOCKERHUB_TOKEN for hightemp/go_proxy_mux.
  3. From an up-to-date main branch, run make release.

The Make target runs CI and builds locally, commits project changes when present, creates an annotated v<version> tag on the resulting commit, then pushes the branch and tag atomically. A clean checkout tags the current commit. It refuses to overwrite an existing tag. A failed push can be retried with the same command if the local commit and tag are intact.

The tag triggers GitHub Actions release⁠ after CI⁠ passes. The workflow publishes six binaries, examples, and SHA256SUMS to GitHub Releases; it also publishes linux/amd64 and linux/arm64 images to Docker Hub. Stable releases update latest. The workflow checks the published image platforms and release asset checksums.

Tag summary

Content type

Image

Digest

sha256:1c7207bb2…

Size

9.5 MB

Last updated

13 days ago

docker pull hightemp/go_proxy_mux