add host
Build Admin / Build image (push) Successful in 37s

This commit is contained in:
2026-07-09 14:41:17 +07:00
parent c4aead7f8c
commit b2c7e7ba80
10 changed files with 307 additions and 205 deletions
+85 -89
View File
@@ -1,38 +1,35 @@
# 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.)
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 open **only** to k8s cluster CIDR (firewall rule)
- Port 8080 (HTTP) and optionally 8443 (HTTPS) open **only** to k8s cluster CIDR
### Installation
```bash
# On a fresh VM (as root):
sudo bash scripts/install.sh
```
### Configuration
```bash
# Edit environment variables:
vi /opt/proxy/proxy-vm/.env
vi .env
# Set your k8s cluster CIDR:
# Set your secret and cluster CIDR:
PROXY_SECRET=<generated-token>
ALLOWED_CIDR=10.0.0.0/8,172.16.0.0/12
# Restart to apply:
cd /opt/proxy/proxy-vm
docker compose restart
docker compose up -d --build
```
### Verify
```bash
# Health check (no auth required):
# Health check (no auth):
curl http://localhost:8080/health
# Test with auth:
@@ -41,20 +38,46 @@ 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/*`
### 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:
Add this block to `nginx/locations.conf`:
```nginx
location /anthropic/ {
@@ -65,21 +88,28 @@ location /anthropic/ {
return 403 '{"error":"invalid_token"}';
}
set $anthropic_upstream https://api.anthropic.com;
rewrite ^/anthropic/(.*) /$1 break;
proxy_pass https://anthropic_backend;
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 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-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;
@@ -87,23 +117,18 @@ location /anthropic/ {
}
```
### 3. Rebuild and restart:
```bash
cd /opt/proxy/proxy-vm
docker compose up -d --build
```
Then rebuild: `docker compose up -d --build`
---
## K8s Side: How to Call the Proxy
### Environment Variables (in your Deployment/ConfigMap)
### Environment Variables
```yaml
env:
- name: PROXY_BASE_URL
value: "http://10.0.1.50:8080" # proxy VM internal IP
value: "http://10.0.1.50:8080" # or https://10.0.1.50:8443
- name: PROXY_SECRET
valueFrom:
secretKeyRef:
@@ -111,27 +136,36 @@ env:
key: token
```
### TypeScript Example
### TypeScript Examples
```typescript
// In tts-worker, replace ELEVENLABS_BASE_URL:
// Before: https://api.elevenlabs.io
// After: http://<proxy-vm-ip>:8080/elevenlabs
// 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, // passed through to upstream
'X-Proxy-Token': process.env.PROXY_SECRET, // validated by proxy
'xi-api-key': process.env.ELEVENLABS_API_KEY,
'X-Proxy-Token': process.env.PROXY_SECRET,
},
body: JSON.stringify(payload),
}
);
// Response is binary MP3, identical to direct ElevenLabs call
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 }),
}
);
```
---
@@ -140,64 +174,26 @@ const buffer = Buffer.from(await response.arrayBuffer());
- [ ] `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)
- [ ] 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)
---
## 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
```
- [ ] TLS certs have restricted permissions (`chmod 600 ssl/tls.key`)
---
## Monitoring
### Tail logs in JSON format
```bash
# Tail logs
docker compose logs -f proxy | jq .
```
### Count requests per upstream per minute
```bash
# 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
```bash
# Container health
docker inspect --format='{{.State.Health.Status}}' api-proxy
```
@@ -205,8 +201,8 @@ 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.
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.
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).