# API Proxy — HTTPS Reverse Proxy Production-ready nginx reverse proxy in Docker for forwarding requests from an internal Kubernetes cluster to external APIs (ElevenLabs, OpenAI, etc.) ## Quick Start ### Prerequisites - Ubuntu 24.04 VM - Port 8080 open **only** to k8s cluster CIDR (firewall rule) ### Installation ```bash # On a fresh VM (as root): sudo bash scripts/install.sh ``` ### Configuration ```bash # Edit environment variables: vi /opt/proxy/proxy-vm/.env # Set your k8s cluster CIDR: ALLOWED_CIDR=10.0.0.0/8,172.16.0.0/12 # Restart to apply: cd /opt/proxy/proxy-vm docker compose restart ``` ### Verify ```bash # Health check (no auth required): curl http://localhost:8080/health # Test with auth: curl -H "X-Proxy-Token: YOUR_SECRET" http://localhost:8080/elevenlabs/v1/voices ``` --- ## How to Add a New Upstream Example: adding `api.anthropic.com` → `/anthropic/*` ### 1. Add upstream block in `nginx/nginx.conf`: ```nginx upstream anthropic_backend { server api.anthropic.com:443; keepalive 32; } ``` ### 2. Add location block in the `server` section: ```nginx location /anthropic/ { if ($allowed_ip = 0) { return 403 '{"error":"ip_not_allowed"}'; } if ($auth_ok = 0) { return 403 '{"error":"invalid_token"}'; } rewrite ^/anthropic/(.*) /$1 break; proxy_pass https://anthropic_backend; proxy_ssl_server_name on; proxy_ssl_name api.anthropic.com; proxy_set_header Host api.anthropic.com; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Connection ""; proxy_set_header X-Proxy-Token ""; proxy_http_version 1.1; proxy_buffering off; proxy_request_buffering off; proxy_read_timeout 120s; proxy_send_timeout 120s; } ``` ### 3. Rebuild and restart: ```bash cd /opt/proxy/proxy-vm docker compose up -d --build ``` --- ## K8s Side: How to Call the Proxy ### Environment Variables (in your Deployment/ConfigMap) ```yaml env: - name: PROXY_BASE_URL value: "http://10.0.1.50:8080" # proxy VM internal IP - name: PROXY_SECRET valueFrom: secretKeyRef: name: api-proxy key: token ``` ### TypeScript Example ```typescript // In tts-worker, replace ELEVENLABS_BASE_URL: // Before: https://api.elevenlabs.io // After: http://:8080/elevenlabs const response = await fetch( `${process.env.PROXY_BASE_URL}/elevenlabs/v1/text-to-speech/${voiceId}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'xi-api-key': process.env.ELEVENLABS_API_KEY, // passed through to upstream 'X-Proxy-Token': process.env.PROXY_SECRET, // validated by proxy }, body: JSON.stringify(payload), } ); // Response is binary MP3, identical to direct ElevenLabs call const buffer = Buffer.from(await response.arrayBuffer()); ``` --- ## Security Checklist - [ ] `PROXY_SECRET` generated via `openssl rand -hex 32` - [ ] `ALLOWED_CIDR` restricted to cluster CIDR only - [ ] Port 8080 closed to the public (only k8s CIDR in firewall/NSG) - [ ] VM has no public IP or is behind NAT - [ ] Logs rotate (Docker logging driver configured: 50m x 5 files) --- ## Adding TLS (Self-Signed) If you need HTTPS between k8s and the proxy: ```bash # Generate self-signed cert: openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout /opt/proxy/proxy-vm/nginx/ssl/proxy.key \ -out /opt/proxy/proxy-vm/nginx/ssl/proxy.crt \ -subj "/CN=api-proxy" ``` Then in `nginx.conf`, change the server block: ```nginx server { listen 8443 ssl; ssl_certificate /etc/nginx/ssl/proxy.crt; ssl_certificate_key /etc/nginx/ssl/proxy.key; # ... rest of config } ``` Mount the certs in `docker-compose.yml`: ```yaml volumes: - ./nginx/ssl:/etc/nginx/ssl:ro ``` --- ## Monitoring ### Tail logs in JSON format ```bash docker compose logs -f proxy | jq . ``` ### Count requests per upstream per minute ```bash docker compose logs --since=1m proxy --no-log-prefix \ | jq -r '.uri' \ | cut -d'/' -f2 \ | sort | uniq -c | sort -rn ``` ### Check container health ```bash docker inspect --format='{{.State.Health.Status}}' api-proxy ``` --- ## How It Works 1. A k8s pod sends an HTTP request to `http://:8080/elevenlabs/v1/text-to-speech/...` with the `X-Proxy-Token` header and the original API key (`xi-api-key`). 2. Nginx checks the source IP against the configured `ALLOWED_CIDR` geo block and validates the `X-Proxy-Token` against `PROXY_SECRET` — rejecting with 403 if either fails. 3. The `/elevenlabs/` prefix is stripped via rewrite, and the request is forwarded over HTTPS to `api.elevenlabs.io` with SNI enabled and keepalive connections. 4. The `X-Proxy-Token` header is removed before forwarding; all other headers (including `xi-api-key`) pass through unchanged. 5. The response streams back unbuffered to the pod — critical for binary audio data from ElevenLabs TTS.