# 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, Telegram Bot API). ## Quick Start ### Prerequisites - Ubuntu 24.04 VM - Port 8080 (HTTP) and optionally 8443 (HTTPS) open **only** to k8s cluster CIDR ### Installation ```bash sudo bash scripts/install.sh ``` ### Configuration ```bash vi .env # Set your secret and cluster CIDR: PROXY_SECRET= ALLOWED_CIDR=10.0.0.0/8,172.16.0.0/12 docker compose up -d --build ``` ### Verify ```bash # Health check (no auth): curl http://localhost:8080/health # Test with auth: curl -H "X-Proxy-Token: YOUR_SECRET" http://localhost:8080/elevenlabs/v1/voices ``` --- ## Available Endpoints | Prefix | Upstream | |--------------|-----------------------------| | /elevenlabs/ | https://api.elevenlabs.io | | /openai/ | https://api.openai.com | | /telegram/ | https://api.telegram.org | | /health | local health check (no auth)| --- ## TLS / HTTPS To enable HTTPS on port 8443, place your certificate and key in the `ssl/` directory: ```bash mkdir -p ssl # Option A: self-signed cert openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout ssl/tls.key \ -out ssl/tls.crt \ -subj "/CN=api-proxy" # Option B: copy existing certs cp /path/to/your/cert.pem ssl/tls.crt cp /path/to/your/key.pem ssl/tls.key docker compose up -d --build ``` The entrypoint auto-detects `ssl/tls.crt` and `ssl/tls.key`. If present — HTTPS is enabled on port 8443. If absent — only HTTP on 8080. --- ## How to Add a New Upstream Example: adding `api.anthropic.com` → `/anthropic/*` Add this block to `nginx/locations.conf`: ```nginx location /anthropic/ { if ($allowed_ip = 0) { return 403 '{"error":"ip_not_allowed"}'; } if ($auth_ok = 0) { return 403 '{"error":"invalid_token"}'; } set $anthropic_upstream https://api.anthropic.com; rewrite ^/anthropic/(.*) /$1 break; proxy_pass $anthropic_upstream; proxy_ssl_server_name on; proxy_ssl_name api.anthropic.com; proxy_ssl_protocols TLSv1.2 TLSv1.3; proxy_set_header Host api.anthropic.com; proxy_set_header Connection ""; proxy_set_header X-Forwarded-For ""; proxy_set_header X-Real-IP ""; proxy_set_header True-Client-IP ""; proxy_set_header CF-Connecting-IP ""; proxy_set_header X-Client-IP ""; proxy_set_header Forwarded ""; proxy_set_header Via ""; 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; } ``` Then rebuild: `docker compose up -d --build` --- ## K8s Side: How to Call the Proxy ### Environment Variables ```yaml env: - name: PROXY_BASE_URL value: "http://10.0.1.50:8080" # or https://10.0.1.50:8443 - name: PROXY_SECRET valueFrom: secretKeyRef: name: api-proxy key: token ``` ### TypeScript Examples ```typescript // ElevenLabs TTS 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, 'X-Proxy-Token': process.env.PROXY_SECRET, }, body: JSON.stringify(payload), } ); const buffer = Buffer.from(await response.arrayBuffer()); // Telegram Bot API const tgResponse = await fetch( `${process.env.PROXY_BASE_URL}/telegram/bot${process.env.TELEGRAM_BOT_TOKEN}/sendMessage`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Proxy-Token': process.env.PROXY_SECRET, }, body: JSON.stringify({ chat_id: chatId, text: message }), } ); ``` --- ## Security Checklist - [ ] `PROXY_SECRET` generated via `openssl rand -hex 32` - [ ] `ALLOWED_CIDR` restricted to cluster CIDR only - [ ] Port 8080/8443 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) - [ ] TLS certs have restricted permissions (`chmod 600 ssl/tls.key`) --- ## Monitoring ```bash # Tail logs docker compose logs -f proxy | jq . # Requests per upstream per minute docker compose logs --since=1m proxy --no-log-prefix \ | jq -r '.uri' \ | cut -d'/' -f2 \ | sort | uniq -c | sort -rn # Container health 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. 2. Nginx checks the source IP against `ALLOWED_CIDR` and validates `X-Proxy-Token` — rejecting with 403 if either fails. 3. The prefix (`/elevenlabs/`, `/openai/`, `/telegram/`) is stripped via rewrite, and the request is forwarded over HTTPS to the upstream with SNI enabled. 4. `X-Proxy-Token` and all IP-leaking headers are removed before forwarding; API keys pass through unchanged. 5. The response streams back unbuffered to the pod — critical for binary audio data and long-polling (Telegram).