An HTTP reverse proxy that routes requests to backend ports specified in the URL path.
Overview
portPxy extracts a port number from the first segment of the request path and proxies the request to that port on a backend host. For example, a request to /8081/api/endpoint is forwarded to backend:8081/api/endpoint.
This solves the problem of dynamically exposing multiple backend ports through a single proxy endpoint, particularly useful in Kubernetes environments where applications create HTTP listeners on various ports at runtime.
Use Case
Applications like Apache NiFi allow users to configure HTTP listeners on arbitrary ports at runtime (e.g., ListenHTTP processors, webhook receivers). In Kubernetes, these dynamic ports cannot be easily exposed through standard Service or Ingress configurations without constant reconfiguration.
portPxy provides a single ingress endpoint that routes to backend ports based on the URL path:
- External request:
https://app.example.com/8081/webhook - Ingress routes to portPxy on port 8080
- portPxy proxies to
backend:8081/webhook
How It Works
- Extract port from the first path segment (e.g.,
/8081/path→ port 8081) - Validate port is within the allowed range
- Proxy request to
BACKEND_PROTO://BACKEND_HOST:8081/path - Return backend response to client
The proxy records Prometheus metrics and structured logs for each request.
Configuration
All settings are configured via environment variables or command-line flags:
| Variable | Flag | Default | Description |
|---|---|---|---|
IP |
-ip |
127.0.0.1 |
Server bind address |
PORT |
-port |
8080 |
Proxy server port |
METRICS_PORT |
-metricsPort |
2112 |
Prometheus metrics port |
DEBUG |
-debug |
false |
Enable debug logging |
BACKEND_HOST |
-backendHost |
httpbin.org |
Target backend hostname |
BACKEND_PROTO |
-backendProto |
https |
Backend protocol (http/https) |
ALLOW_PORT_BEGIN |
-allowRangeBegin |
443 |
Start of allowed port range |
ALLOW_PORT_END |
-allowRangeEnd |
443 |
End of allowed port range |
Usage
Binary
# Build go build -o portpxy ./cmd/portpxy.go # Run with defaults (proxies to httpbin.org:443) ./portpxy # Run with custom backend ./portpxy -ip=0.0.0.0 -backendHost=localhost -backendProto=http -allowRangeBegin=8080 -allowRangeEnd=8090 # Or use environment variables IP=0.0.0.0 BACKEND_HOST=localhost BACKEND_PROTO=http ALLOW_PORT_BEGIN=8080 ALLOW_PORT_END=8090 ./portpxy
Docker
# Run with Docker docker run --rm -p 8080:8080 -e IP=0.0.0.0 txn2/portpxy:latest # Custom configuration docker run --rm -p 8080:8080 \ -e IP=0.0.0.0 \ -e BACKEND_HOST=localhost \ -e BACKEND_PROTO=http \ -e ALLOW_PORT_BEGIN=8080 \ -e ALLOW_PORT_END=8090 \ txn2/portpxy:latest
Kubernetes Sidecar
Deploy as a sidecar container alongside your application:
apiVersion: v1 kind: Pod metadata: name: app-with-proxy spec: containers: - name: app image: your-app:latest # Application creates listeners on ports 8080-8090 - name: portpxy image: txn2/portpxy:latest ports: - containerPort: 8080 name: proxy - containerPort: 2112 name: metrics env: - name: IP value: "0.0.0.0" - name: BACKEND_HOST value: "localhost" - name: BACKEND_PROTO value: "http" - name: ALLOW_PORT_BEGIN value: "8080" - name: ALLOW_PORT_END value: "8090"
Testing
# Start the proxy with local backend IP=0.0.0.0 BACKEND_HOST=localhost BACKEND_PROTO=http ALLOW_PORT_BEGIN=8080 ALLOW_PORT_END=8090 ./portpxy # In another terminal, start a test server on port 8081 python3 -m http.server 8081 # Test proxying curl http://localhost:8080/8081/ # Test port validation (should return error) curl http://localhost:8080/9999/ # Check metrics curl http://localhost:2112/metrics
Metrics
Prometheus metrics are exposed on the metrics port (default 2112):
portpxy_total_requests: Total number of requests processedportpxy_response_time: Request latency distribution
Security
The ALLOW_PORT_BEGIN and ALLOW_PORT_END settings restrict which ports can be proxied. This prevents unauthorized access to arbitrary ports on the backend host. Configure these values to match your application's port range.
Development
Build
go build -o portpxy ./cmd/portpxy.go
Test Release
goreleaser --skip-publish --rm-dist --skip-validate
Release
GITHUB_TOKEN=$GITHUB_TOKEN goreleaser --rm-distLicense
MIT