190 lines
6.1 KiB
Plaintext
190 lines
6.1 KiB
Plaintext
# ZBrain Nginx Configuration
|
||
# ===========================
|
||
# Два server-блока:
|
||
# 1. Внутренний (brain.zetit.local) - полный доступ к UI и API
|
||
# 2. Публичный (brain.zetit.ru) - только /mcp/* и /oauth/*
|
||
#
|
||
# Положить в /etc/nginx/sites-available/zbrain
|
||
# и сделать symlink в /etc/nginx/sites-enabled/
|
||
|
||
# ============================================================
|
||
# Upstream'ы
|
||
# ============================================================
|
||
upstream brainhub_api {
|
||
server 127.0.0.1:3000;
|
||
keepalive 32;
|
||
}
|
||
|
||
upstream brainhub_api_public {
|
||
server 127.0.0.1:3010;
|
||
keepalive 32;
|
||
}
|
||
|
||
upstream brainhub_web {
|
||
server 127.0.0.1:8080;
|
||
keepalive 32;
|
||
}
|
||
|
||
# Rate limiting для публичного MCP
|
||
limit_req_zone $binary_remote_addr zone=mcp_public:10m rate=30r/s;
|
||
limit_req_zone $http_authorization zone=mcp_token:10m rate=100r/s;
|
||
|
||
# ============================================================
|
||
# Внутренний контур: brain.zetit.local
|
||
# ============================================================
|
||
server {
|
||
listen 80;
|
||
listen [::]:80;
|
||
server_name brain.zetit.local;
|
||
|
||
# Внутри сети можно без HTTPS, либо self-signed cert
|
||
# Если нужен HTTPS - раскомментировать listen 443 ниже и убрать listen 80
|
||
|
||
access_log /var/log/nginx/zbrain-internal.access.log;
|
||
error_log /var/log/nginx/zbrain-internal.error.log;
|
||
|
||
client_max_body_size 50M;
|
||
|
||
# Web UI (статика)
|
||
location / {
|
||
proxy_pass http://brainhub_web;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
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;
|
||
}
|
||
|
||
# API
|
||
location /api/ {
|
||
proxy_pass http://brainhub_api;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
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 "";
|
||
|
||
# Таймауты для долгих операций (sync, import)
|
||
proxy_read_timeout 300s;
|
||
proxy_send_timeout 300s;
|
||
}
|
||
|
||
# SSE events
|
||
location /api/events {
|
||
proxy_pass http://brainhub_api;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Connection "";
|
||
proxy_buffering off;
|
||
proxy_cache off;
|
||
proxy_read_timeout 24h;
|
||
|
||
# SSE headers
|
||
chunked_transfer_encoding off;
|
||
}
|
||
|
||
# MCP - внутренний доступ (тоже с токенами, но без rate limit)
|
||
location /mcp/ {
|
||
proxy_pass http://brainhub_api;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header Authorization $http_authorization;
|
||
|
||
proxy_read_timeout 120s;
|
||
proxy_buffering off;
|
||
}
|
||
}
|
||
|
||
# ============================================================
|
||
# Публичный контур: brain.zetit.ru
|
||
# ============================================================
|
||
server {
|
||
listen 80;
|
||
listen [::]:80;
|
||
server_name brain.zetit.ru;
|
||
|
||
# HTTP -> HTTPS redirect (кроме ACME challenge)
|
||
location /.well-known/acme-challenge/ {
|
||
root /var/www/certbot;
|
||
}
|
||
|
||
location / {
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl http2;
|
||
listen [::]:443 ssl http2;
|
||
server_name brain.zetit.ru;
|
||
|
||
# TLS
|
||
ssl_certificate /etc/letsencrypt/live/brain.zetit.ru/fullchain.pem;
|
||
ssl_certificate_key /etc/letsencrypt/live/brain.zetit.ru/privkey.pem;
|
||
ssl_session_timeout 1d;
|
||
ssl_session_cache shared:MozSSL:10m;
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
ssl_prefer_server_ciphers off;
|
||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||
|
||
# HSTS
|
||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||
|
||
# Security headers
|
||
add_header X-Content-Type-Options "nosniff" always;
|
||
add_header X-Frame-Options "DENY" always;
|
||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||
|
||
access_log /var/log/nginx/zbrain-public.access.log;
|
||
error_log /var/log/nginx/zbrain-public.error.log;
|
||
|
||
client_max_body_size 10M;
|
||
|
||
# ВАЖНО: на публичном контуре доступны только /mcp/* и /oauth/*
|
||
# Никакого UI, API админки и т.д.
|
||
|
||
# MCP proxy с rate limiting
|
||
location /mcp/ {
|
||
# Лимит по IP - защита от подбора токенов
|
||
limit_req zone=mcp_public burst=60 nodelay;
|
||
# Лимит по токену - защита от взбесившегося скрипта
|
||
limit_req zone=mcp_token burst=200 nodelay;
|
||
limit_req_status 429;
|
||
|
||
proxy_pass http://brainhub_api_public;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
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 Authorization $http_authorization;
|
||
|
||
proxy_read_timeout 120s;
|
||
proxy_buffering off;
|
||
}
|
||
|
||
# OAuth flow для удалённой авторизации
|
||
location /oauth/ {
|
||
limit_req zone=mcp_public burst=20 nodelay;
|
||
|
||
proxy_pass http://brainhub_api_public;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
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;
|
||
}
|
||
|
||
# Health check (без rate limit, для мониторинга)
|
||
location /health {
|
||
proxy_pass http://brainhub_api_public/health;
|
||
access_log off;
|
||
}
|
||
|
||
# Всё остальное - 404, чтобы публично ничего лишнего не светить
|
||
location / {
|
||
return 404;
|
||
}
|
||
}
|