From 0cb0b27f2066578d27dd1e6a1e6a59e79f2212ae Mon Sep 17 00:00:00 2001 From: Fovway Date: Sun, 26 Oct 2025 15:20:04 +0700 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=D0=A3=D0=BB=D1=83=D1=87=D1=88?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20IP-?= =?UTF-8?q?=D0=B0=D0=B4=D1=80=D0=B5=D1=81=D0=B0=20=D0=BA=D0=BB=D0=B8=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=20=D0=B8=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D0=B9=20=D1=8D=D0=BD=D0=B4=D0=BF=D0=BE=D0=B8=D0=BD=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Расширена логика получения IP-адреса с учетом новых заголовков, таких как CF-Connecting-IP и Forwarded. - Добавлено детальное логирование заголовков для упрощения отладки. - Внедрен тестовый эндпоинт для проверки извлечения IP-адресов и отображения заголовков запроса. --- NGINX_PROXY_SETUP.md | 149 +++++++++++++++++++++++++++++++++++++++++++ server.js | 85 ++++++++++++++++++++---- 2 files changed, 222 insertions(+), 12 deletions(-) create mode 100644 NGINX_PROXY_SETUP.md diff --git a/NGINX_PROXY_SETUP.md b/NGINX_PROXY_SETUP.md new file mode 100644 index 0000000..e6a51cb --- /dev/null +++ b/NGINX_PROXY_SETUP.md @@ -0,0 +1,149 @@ +# Настройка nginx Proxy Manager для корректной передачи IP-адресов + +## Проблема + +При использовании nginx Proxy Manager в режиме bridge, реальный IP-адрес клиента может не передаваться корректно, и вместо него всегда показывается IP-адрес прокси-сервера (например, 90.189.198.107). + +## Решение + +### 1. Настройка nginx Proxy Manager + +В настройках прокси-хоста в nginx Proxy Manager добавьте следующие заголовки: + +#### В разделе "Advanced" → "Custom Nginx Configuration": + +```nginx +# Передача реального IP-адреса клиента +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 X-Forwarded-Host $host; +proxy_set_header X-Forwarded-Port $server_port; + +# Дополнительные заголовки для лучшей совместимости +proxy_set_header X-Client-IP $remote_addr; +proxy_set_header X-Forwarded $scheme://$host; + +# Установка доверенного прокси +proxy_set_header Host $host; +proxy_set_header X-Forwarded-Server $host; +``` + +#### Или в разделе "Custom Nginx Configuration" (если доступен): + +```nginx +location / { + proxy_pass http://your-app-container:3000; + + # Передача реального IP-адреса + 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 X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Client-IP $remote_addr; + + # Дополнительные настройки + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Server $host; + + # Таймауты + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + + # Буферизация + proxy_buffering off; + proxy_request_buffering off; +} +``` + +### 2. Настройка Docker Compose (если используется) + +Если ваш nginx Proxy Manager работает в Docker, убедитесь, что в docker-compose.yml правильно настроена сеть: + +```yaml +version: "3.8" +services: + nginx-proxy-manager: + image: jc21/nginx-proxy-manager:latest + container_name: nginx-proxy-manager + ports: + - "80:80" + - "443:443" + - "81:81" + volumes: + - ./data:/data + - ./letsencrypt:/etc/letsencrypt + networks: + - npm-network + restart: unless-stopped + + your-app: + image: your-app-image + container_name: your-app + networks: + - npm-network + restart: unless-stopped + +networks: + npm-network: + driver: bridge +``` + +### 3. Проверка работы + +После настройки используйте тестовый эндпоинт для проверки: + +```bash +curl -H "X-Forwarded-For: 192.168.1.100" https://your-domain.com/api/debug/ip +``` + +Или откройте в браузере: `https://your-domain.com/api/debug/ip` + +### 4. Ожидаемый результат + +В ответе должно быть: + +```json +{ + "detected_ip": "192.168.1.100", // Реальный IP клиента + "headers": { + "x-forwarded-for": "192.168.1.100", + "x-real-ip": "192.168.1.100", + "x-client-ip": "192.168.1.100" + } +} +``` + +### 5. Отладка + +Если IP-адрес все еще неправильный: + +1. Проверьте логи nginx Proxy Manager +2. Убедитесь, что заголовки передаются (используйте `/api/debug/ip`) +3. Проверьте настройки сети Docker +4. Убедитесь, что nginx Proxy Manager не находится за дополнительными прокси + +### 6. Дополнительные настройки + +#### Для Cloudflare + +Если вы используете Cloudflare, добавьте: + +```nginx +proxy_set_header CF-Connecting-IP $http_cf_connecting_ip; +``` + +#### Для AWS Load Balancer + +```nginx +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; +``` + +## Примечания + +- После изменения настроек nginx Proxy Manager перезапустите контейнер +- Убедитесь, что ваш Node.js сервер настроен с `app.set("trust proxy", true)` +- Тестовый эндпоинт `/api/debug/ip` можно удалить после настройки diff --git a/server.js b/server.js index 882551b..a844e73 100644 --- a/server.js +++ b/server.js @@ -304,29 +304,67 @@ function getClientIP(req) { console.log("x-forwarded-for:", req.headers["x-forwarded-for"]); console.log("x-real-ip:", req.headers["x-real-ip"]); console.log("x-client-ip:", req.headers["x-client-ip"]); + console.log("cf-connecting-ip:", req.headers["cf-connecting-ip"]); // Cloudflare + console.log("x-forwarded:", req.headers["x-forwarded"]); + console.log("forwarded:", req.headers["forwarded"]); console.log("req.connection.remoteAddress:", req.connection?.remoteAddress); console.log("req.socket.remoteAddress:", req.socket?.remoteAddress); console.log("ALL HEADERS:", JSON.stringify(req.headers, null, 2)); console.log("========================"); - // Используем req.socket.remoteAddress напрямую, так как nginx передает внутренний IP контейнера - let ip = req.socket?.remoteAddress; + // Приоритет заголовков для nginx proxy manager: + // 1. X-Real-IP (часто используется nginx) + // 2. X-Forwarded-For (стандартный заголовок) + // 3. CF-Connecting-IP (если используется Cloudflare) + // 4. X-Client-IP + // 5. req.ip (Express с trust proxy) + // 6. req.socket.remoteAddress (прямое соединение) - // Если remoteAddress недоступен, пробуем заголовки - if (!ip || ip === "::1") { - ip = - req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || - req.headers["x-real-ip"] || - req.headers["x-client-ip"] || - req.ip || - req.connection?.remoteAddress || - "unknown"; + let ip = null; + + // Проверяем X-Real-IP (nginx часто использует этот заголовок) + if (req.headers["x-real-ip"]) { + ip = req.headers["x-real-ip"]; + } + // Проверяем X-Forwarded-For (берем первый IP из списка) + else if (req.headers["x-forwarded-for"]) { + ip = req.headers["x-forwarded-for"].split(",")[0].trim(); + } + // Проверяем CF-Connecting-IP (Cloudflare) + else if (req.headers["cf-connecting-ip"]) { + ip = req.headers["cf-connecting-ip"]; + } + // Проверяем X-Client-IP + else if (req.headers["x-client-ip"]) { + ip = req.headers["x-client-ip"]; + } + // Проверяем Forwarded заголовок (RFC 7239) + else if (req.headers["forwarded"]) { + const forwarded = req.headers["forwarded"]; + const forMatch = forwarded.match(/for=([^;,\s]+)/); + if (forMatch) { + ip = forMatch[1]; + } + } + // Используем req.ip (Express с trust proxy) + else if (req.ip && req.ip !== "::1" && req.ip !== "127.0.0.1") { + ip = req.ip; + } + // В крайнем случае используем socket remoteAddress + else if ( + req.socket?.remoteAddress && + req.socket.remoteAddress !== "::1" && + req.socket.remoteAddress !== "127.0.0.1" + ) { + ip = req.socket.remoteAddress; } - // Очищаем IP от скобок IPv6 и портов + // Очищаем IP от скобок IPv6, портов и кавычек if (ip && ip !== "unknown") { // Убираем скобки IPv6 ip = ip.replace(/[[\]]/g, ""); + // Убираем кавычки если есть + ip = ip.replace(/['"]/g, ""); // Обрабатываем IPv6 адреса if (ip.startsWith("::ffff:")) { @@ -710,6 +748,29 @@ app.get("/api/user", requireApiAuth, (req, res) => { }); }); +// Тестовый эндпоинт для проверки IP-адресов (только для отладки) +app.get("/api/debug/ip", (req, res) => { + const clientIP = getClientIP(req); + + res.json({ + detected_ip: clientIP, + headers: { + "x-forwarded-for": req.headers["x-forwarded-for"], + "x-real-ip": req.headers["x-real-ip"], + "x-client-ip": req.headers["x-client-ip"], + "cf-connecting-ip": req.headers["cf-connecting-ip"], + "x-forwarded": req.headers["x-forwarded"], + forwarded: req.headers["forwarded"], + }, + express: { + req_ip: req.ip, + socket_remoteAddress: req.socket?.remoteAddress, + connection_remoteAddress: req.connection?.remoteAddress, + }, + all_headers: req.headers, + }); +}); + // Страница с заметками (требует аутентификации) app.get("/notes", requireAuth, (req, res) => { // Получаем цвет пользователя для предотвращения FOUC