Files
elevenlabs.io/README.md
2026-03-21 10:47:11 +07:00

213 lines
4.9 KiB
Markdown

# 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://<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_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://<proxy-vm>: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.