c4aead7f8c24190b3eee252759b5ef8888fd0073
All checks were successful
Build Admin / Build image (push) Successful in 17s
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
# On a fresh VM (as root):
sudo bash scripts/install.sh
Configuration
# 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
# 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:
upstream anthropic_backend {
server api.anthropic.com:443;
keepalive 32;
}
2. Add location block in the server section:
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:
cd /opt/proxy/proxy-vm
docker compose up -d --build
K8s Side: How to Call the Proxy
Environment Variables (in your Deployment/ConfigMap)
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
// In tts-worker, replace ELEVENLABS_BASE_URL:
// Before: https://api.elevenlabs.io
// After: http://<proxy-vm-ip>: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_SECRETgenerated viaopenssl rand -hex 32ALLOWED_CIDRrestricted 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:
# 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:
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:
volumes:
- ./nginx/ssl:/etc/nginx/ssl:ro
Monitoring
Tail logs in JSON format
docker compose logs -f proxy | jq .
Count 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
Check container health
docker inspect --format='{{.State.Health.Status}}' api-proxy
How It Works
- A k8s pod sends an HTTP request to
http://<proxy-vm>:8080/elevenlabs/v1/text-to-speech/...with theX-Proxy-Tokenheader and the original API key (xi-api-key). - Nginx checks the source IP against the configured
ALLOWED_CIDRgeo block and validates theX-Proxy-TokenagainstPROXY_SECRET— rejecting with 403 if either fails. - The
/elevenlabs/prefix is stripped via rewrite, and the request is forwarded over HTTPS toapi.elevenlabs.iowith SNI enabled and keepalive connections. - The
X-Proxy-Tokenheader is removed before forwarding; all other headers (includingxi-api-key) pass through unchanged. - The response streams back unbuffered to the pod — critical for binary audio data from ElevenLabs TTS.
Description
Languages
Shell
87.5%
Dockerfile
12.5%