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
+3 -2
View File
@@ -1,6 +1,7 @@
# Secret token — k8s pods must send this in X-Proxy-Token header # Secret token — k8s pods must send this in X-Proxy-Token header (optional)
# If empty or unset — token auth is disabled (open mode, protected by IP allowlist only)
# Generate with: openssl rand -hex 32 # Generate with: openssl rand -hex 32
PROXY_SECRET=change-me-generate-with-openssl-rand-hex-32 PROXY_SECRET=
# Allowed source CIDRs (comma-separated), leave empty to allow all (dev mode) # Allowed source CIDRs (comma-separated), leave empty to allow all (dev mode)
# Example: "10.0.0.0/8,172.16.0.0/12" # Example: "10.0.0.0/8,172.16.0.0/12"
+1
View File
@@ -0,0 +1 @@
.env
+5 -4
View File
@@ -4,17 +4,18 @@ FROM nginx:1.27-alpine
# Remove default config # Remove default config
RUN rm -f /etc/nginx/conf.d/default.conf RUN rm -f /etc/nginx/conf.d/default.conf
# Copy nginx config # Copy nginx configs
COPY nginx/nginx.conf /etc/nginx/nginx.conf COPY nginx/nginx.conf /etc/nginx/nginx.conf
COPY nginx/locations.conf /etc/nginx/locations.conf
# Copy entrypoint script # Copy entrypoint script
COPY docker-entrypoint.sh /docker-entrypoint.sh COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh RUN chmod +x /docker-entrypoint.sh
# Create conf.d directory for generated configs # Create directories for generated configs and optional SSL certs
RUN mkdir -p /etc/nginx/conf.d RUN mkdir -p /etc/nginx/conf.d /etc/nginx/ssl
EXPOSE 8080 EXPOSE 8080 8443
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget -qO- http://localhost:8080/health || exit 1 CMD wget -qO- http://localhost:8080/health || exit 1
+85 -89
View File
@@ -1,38 +1,35 @@
# API Proxy — HTTPS Reverse Proxy # 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 ## Quick Start
### Prerequisites ### Prerequisites
- Ubuntu 24.04 VM - 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 ### Installation
```bash ```bash
# On a fresh VM (as root):
sudo bash scripts/install.sh sudo bash scripts/install.sh
``` ```
### Configuration ### Configuration
```bash ```bash
# Edit environment variables: vi .env
vi /opt/proxy/proxy-vm/.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 ALLOWED_CIDR=10.0.0.0/8,172.16.0.0/12
# Restart to apply: docker compose up -d --build
cd /opt/proxy/proxy-vm
docker compose restart
``` ```
### Verify ### Verify
```bash ```bash
# Health check (no auth required): # Health check (no auth):
curl http://localhost:8080/health curl http://localhost:8080/health
# Test with auth: # 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 ## How to Add a New Upstream
Example: adding `api.anthropic.com``/anthropic/*` Example: adding `api.anthropic.com``/anthropic/*`
### 1. Add upstream block in `nginx/nginx.conf`: Add this block to `nginx/locations.conf`:
```nginx
upstream anthropic_backend {
server api.anthropic.com:443;
keepalive 32;
}
```
### 2. Add location block in the `server` section:
```nginx ```nginx
location /anthropic/ { location /anthropic/ {
@@ -65,21 +88,28 @@ location /anthropic/ {
return 403 '{"error":"invalid_token"}'; return 403 '{"error":"invalid_token"}';
} }
set $anthropic_upstream https://api.anthropic.com;
rewrite ^/anthropic/(.*) /$1 break; rewrite ^/anthropic/(.*) /$1 break;
proxy_pass https://anthropic_backend; proxy_pass $anthropic_upstream;
proxy_ssl_server_name on; proxy_ssl_server_name on;
proxy_ssl_name api.anthropic.com; proxy_ssl_name api.anthropic.com;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_set_header Host 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 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_set_header X-Proxy-Token "";
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_buffering off; proxy_buffering off;
proxy_request_buffering off; proxy_request_buffering off;
proxy_read_timeout 120s; proxy_read_timeout 120s;
@@ -87,23 +117,18 @@ location /anthropic/ {
} }
``` ```
### 3. Rebuild and restart: Then rebuild: `docker compose up -d --build`
```bash
cd /opt/proxy/proxy-vm
docker compose up -d --build
```
--- ---
## K8s Side: How to Call the Proxy ## K8s Side: How to Call the Proxy
### Environment Variables (in your Deployment/ConfigMap) ### Environment Variables
```yaml ```yaml
env: env:
- name: PROXY_BASE_URL - 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 - name: PROXY_SECRET
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
@@ -111,27 +136,36 @@ env:
key: token key: token
``` ```
### TypeScript Example ### TypeScript Examples
```typescript ```typescript
// In tts-worker, replace ELEVENLABS_BASE_URL: // ElevenLabs TTS
// Before: https://api.elevenlabs.io
// After: http://<proxy-vm-ip>:8080/elevenlabs
const response = await fetch( const response = await fetch(
`${process.env.PROXY_BASE_URL}/elevenlabs/v1/text-to-speech/${voiceId}`, `${process.env.PROXY_BASE_URL}/elevenlabs/v1/text-to-speech/${voiceId}`,
{ {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'xi-api-key': process.env.ELEVENLABS_API_KEY, // passed through to upstream 'xi-api-key': process.env.ELEVENLABS_API_KEY,
'X-Proxy-Token': process.env.PROXY_SECRET, // validated by proxy 'X-Proxy-Token': process.env.PROXY_SECRET,
}, },
body: JSON.stringify(payload), body: JSON.stringify(payload),
} }
); );
// Response is binary MP3, identical to direct ElevenLabs call
const buffer = Buffer.from(await response.arrayBuffer()); 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` - [ ] `PROXY_SECRET` generated via `openssl rand -hex 32`
- [ ] `ALLOWED_CIDR` restricted to cluster CIDR only - [ ] `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 - [ ] VM has no public IP or is behind NAT
- [ ] Logs rotate (Docker logging driver configured: 50m x 5 files) - [ ] Logs rotate (Docker logging driver configured: 50m x 5 files)
- [ ] TLS certs have restricted permissions (`chmod 600 ssl/tls.key`)
---
## 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 ## Monitoring
### Tail logs in JSON format
```bash ```bash
# Tail logs
docker compose logs -f proxy | jq . docker compose logs -f proxy | jq .
```
### Count requests per upstream per minute # Requests per upstream per minute
```bash
docker compose logs --since=1m proxy --no-log-prefix \ docker compose logs --since=1m proxy --no-log-prefix \
| jq -r '.uri' \ | jq -r '.uri' \
| cut -d'/' -f2 \ | cut -d'/' -f2 \
| sort | uniq -c | sort -rn | sort | uniq -c | sort -rn
```
### Check container health # Container health
```bash
docker inspect --format='{{.State.Health.Status}}' api-proxy docker inspect --format='{{.State.Health.Status}}' api-proxy
``` ```
@@ -205,8 +201,8 @@ docker inspect --format='{{.State.Health.Status}}' api-proxy
## How It Works ## 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`). 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 the configured `ALLOWED_CIDR` geo block and validates the `X-Proxy-Token` against `PROXY_SECRET` — rejecting with 403 if either fails. 2. Nginx checks the source IP against `ALLOWED_CIDR` and validates `X-Proxy-Token` — 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. 3. The prefix (`/elevenlabs/`, `/openai/`, `/telegram/`) is stripped via rewrite, and the request is forwarded over HTTPS to the upstream with SNI enabled.
4. The `X-Proxy-Token` header is removed before forwarding; all other headers (including `xi-api-key`) pass through unchanged. 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 from ElevenLabs TTS. 5. The response streams back unbuffered to the pod — critical for binary audio data and long-polling (Telegram).
+5 -3
View File
@@ -1,6 +1,3 @@
# proxy-vm/docker-compose.yml
version: "3.9"
services: services:
proxy: proxy:
build: . build: .
@@ -8,7 +5,12 @@ services:
restart: unless-stopped restart: unless-stopped
ports: ports:
- "8080:8080" - "8080:8080"
- "8443:8443"
env_file: .env env_file: .env
volumes:
# Mount TLS certs (optional — HTTPS disabled if files are absent)
# Place tls.crt and tls.key in ./ssl/ directory
- ./ssl:/etc/nginx/ssl:ro
healthcheck: healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"] test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
interval: 30s interval: 30s
+37 -6
View File
@@ -10,7 +10,6 @@ mkdir -p "${CONF_DIR}"
ALLOWLIST_FILE="${CONF_DIR}/allowlist.conf" ALLOWLIST_FILE="${CONF_DIR}/allowlist.conf"
if [ -z "${ALLOWED_CIDR}" ]; then if [ -z "${ALLOWED_CIDR}" ]; then
# Dev mode: allow all IPs
cat > "${ALLOWLIST_FILE}" <<'GEO' cat > "${ALLOWLIST_FILE}" <<'GEO'
geo $allowed_ip { geo $allowed_ip {
default 1; default 1;
@@ -18,12 +17,11 @@ geo $allowed_ip {
GEO GEO
echo "[entrypoint] ALLOWED_CIDR is empty — allowing all IPs (dev mode)" echo "[entrypoint] ALLOWED_CIDR is empty — allowing all IPs (dev mode)"
else else
# Build geo block from comma-separated CIDRs
{ {
echo 'geo $allowed_ip {' echo 'geo $allowed_ip {'
echo ' default 0;' echo ' default 0;'
echo "${ALLOWED_CIDR}" | tr ',' '\n' | while read -r cidr; do echo "${ALLOWED_CIDR}" | tr ',' '\n' | while read -r cidr; do
cidr=$(echo "${cidr}" | xargs) # trim whitespace cidr=$(echo "${cidr}" | xargs)
[ -n "${cidr}" ] && echo " ${cidr} 1;" [ -n "${cidr}" ] && echo " ${cidr} 1;"
done done
echo '}' echo '}'
@@ -35,10 +33,10 @@ fi
AUTH_FILE="${CONF_DIR}/auth.conf" AUTH_FILE="${CONF_DIR}/auth.conf"
if [ -z "${PROXY_SECRET}" ]; then if [ -z "${PROXY_SECRET}" ]; then
echo "[entrypoint] WARNING: PROXY_SECRET is not set — all requests will be rejected!" echo "[entrypoint] PROXY_SECRET is not set — token auth disabled (open mode)"
cat > "${AUTH_FILE}" <<'MAP' cat > "${AUTH_FILE}" <<'MAP'
map $http_x_proxy_token $auth_ok { map $http_x_proxy_token $auth_ok {
default 0; default 1;
} }
MAP MAP
else else
@@ -51,6 +49,39 @@ MAP
echo "[entrypoint] Token auth configured" echo "[entrypoint] Token auth configured"
fi fi
# --- 3. Start nginx --- # --- 3. Copy locations.conf ---
cp /etc/nginx/locations.conf "${CONF_DIR}/locations.conf"
echo "[entrypoint] Locations config copied"
# --- 4. Generate HTTPS server block (if certs exist) ---
HTTPS_FILE="${CONF_DIR}/https_server.conf"
SSL_CERT="/etc/nginx/ssl/tls.crt"
SSL_KEY="/etc/nginx/ssl/tls.key"
if [ -f "${SSL_CERT}" ] && [ -f "${SSL_KEY}" ]; then
cat > "${HTTPS_FILE}" <<HTTPS
server {
listen 8443 ssl;
server_name _;
ssl_certificate ${SSL_CERT};
ssl_certificate_key ${SSL_KEY};
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
include /etc/nginx/conf.d/locations.conf;
}
HTTPS
echo "[entrypoint] HTTPS enabled on port 8443 (cert: ${SSL_CERT})"
else
# Empty file so nginx include does not fail
: > "${HTTPS_FILE}"
echo "[entrypoint] No TLS certs found at ${SSL_CERT} — HTTPS disabled"
fi
# --- 5. Start nginx ---
echo "[entrypoint] Starting nginx..." echo "[entrypoint] Starting nginx..."
exec nginx -g 'daemon off;' exec nginx -g 'daemon off;'
+160
View File
@@ -0,0 +1,160 @@
# proxy-vm/nginx/locations.conf
# Shared location blocks — included by both HTTP and HTTPS server blocks.
# --- Health check (no auth) ---
location = /health {
access_log off;
default_type application/json;
return 200 '{"status":"ok","version":"1.0"}';
}
# --- ElevenLabs ---
location /elevenlabs/ {
if ($allowed_ip = 0) {
return 403 '{"error":"ip_not_allowed"}';
}
if ($auth_ok = 0) {
return 403 '{"error":"invalid_token"}';
}
set $elevenlabs_upstream https://api.elevenlabs.io;
rewrite ^/elevenlabs/(.*) /$1 break;
proxy_pass $elevenlabs_upstream;
proxy_ssl_server_name on;
proxy_ssl_name api.elevenlabs.io;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_set_header Host api.elevenlabs.io;
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;
}
# --- OpenAI ---
location /openai/ {
if ($allowed_ip = 0) {
return 403 '{"error":"ip_not_allowed"}';
}
if ($auth_ok = 0) {
return 403 '{"error":"invalid_token"}';
}
set $openai_upstream https://api.openai.com;
rewrite ^/openai/(.*) /$1 break;
proxy_pass $openai_upstream;
proxy_ssl_server_name on;
proxy_ssl_name api.openai.com;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_set_header Host api.openai.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;
}
# --- Telegram Bot API ---
location /telegram/ {
if ($allowed_ip = 0) {
return 403 '{"error":"ip_not_allowed"}';
}
if ($auth_ok = 0) {
return 403 '{"error":"invalid_token"}';
}
set $telegram_upstream https://api.telegram.org;
rewrite ^/telegram/(.*) /$1 break;
proxy_pass $telegram_upstream;
proxy_ssl_server_name on;
proxy_ssl_name api.telegram.org;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_set_header Host api.telegram.org;
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;
}
# --- openrouter Bot API ---
location /openrouter/ {
if ($allowed_ip = 0) {
return 403 '{"error":"ip_not_allowed"}';
}
set $openrouter_upstream https://openrouter.ai/api/v1;
rewrite ^/openrouter/(.*) /$1 break;
proxy_pass $openrouter_upstream;
proxy_ssl_server_name on;
proxy_ssl_name openrouter.ai;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_set_header Host openrouter.ai;
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;
}
# --- Catch-all ---
location / {
default_type application/json;
return 404 '{"error":"unknown_upstream","hint":"use /elevenlabs/, /openai/ or /telegram/" or /openrouter/}';
}
+4 -100
View File
@@ -45,109 +45,13 @@ http {
# --- Token auth --- # --- Token auth ---
include /etc/nginx/conf.d/auth.conf; include /etc/nginx/conf.d/auth.conf;
# --- HTTP server ---
server { server {
listen 8080; listen 8080;
server_name _; server_name _;
include /etc/nginx/conf.d/locations.conf;
# --- Health check (no auth) ---
location = /health {
access_log off;
default_type application/json;
return 200 '{"status":"ok","version":"1.0"}';
} }
# --- ElevenLabs --- # --- HTTPS server (generated at container start if certs exist) ---
location /elevenlabs/ { include /etc/nginx/conf.d/https_server.conf;
# Auth checks
if ($allowed_ip = 0) {
return 403 '{"error":"ip_not_allowed"}';
}
if ($auth_ok = 0) {
return 403 '{"error":"invalid_token"}';
}
# Variable forces runtime DNS resolution (not cached at startup)
set $elevenlabs_upstream https://api.elevenlabs.io;
# Strip /elevenlabs/ prefix and proxy
rewrite ^/elevenlabs/(.*) /$1 break;
proxy_pass $elevenlabs_upstream;
proxy_ssl_server_name on;
proxy_ssl_name api.elevenlabs.io;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
# Host header must match upstream for Cloudflare
proxy_set_header Host api.elevenlabs.io;
proxy_set_header Connection "";
# Scrub all headers that leak the original client IP
# Cloudflare reads these to determine "real" client geo
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 "";
# Remove proxy token before forwarding to upstream
proxy_set_header X-Proxy-Token "";
# HTTP/1.1 for keepalive
proxy_http_version 1.1;
# Streaming / performance
proxy_buffering off;
proxy_request_buffering off;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}
# --- OpenAI ---
location /openai/ {
if ($allowed_ip = 0) {
return 403 '{"error":"ip_not_allowed"}';
}
if ($auth_ok = 0) {
return 403 '{"error":"invalid_token"}';
}
set $openai_upstream https://api.openai.com;
rewrite ^/openai/(.*) /$1 break;
proxy_pass $openai_upstream;
proxy_ssl_server_name on;
proxy_ssl_name api.openai.com;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_set_header Host api.openai.com;
proxy_set_header Connection "";
# Scrub all headers that leak the original client IP
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;
}
# --- Catch-all ---
location / {
default_type application/json;
return 404 '{"error":"unknown_upstream","hint":"use /elevenlabs/ or /openai/"}';
}
}
} }
+6
View File
@@ -0,0 +1,6 @@
# Ignore actual TLS certificates — never commit to git
*.crt
*.key
*.pem
!.gitkeep
!.gitignore
View File