Короткий ответ
Маскарад и dst-nat полезны, но на инциденте вы видите адрес шлюза 203.0.113.10 или VIP прокси 10.0.30.10, а не клиента. Лечение: логировать original src на firewall (filter log до SNAT / conntrack), не SNAT'ить там, где можно маршрутизировать, на HTTP — доверенный X-Forwarded-For / Forwarded только от своего proxy. Не выключайте NAT «чтобы логи стали честными», сломав интернет офиса. Не выключайте фильтр.
LAN 10.0.10.0/24, DMZ 10.0.30.0/24, mgmt 10.0.99.0/24.
Симптомы и как отличить
access.log nginx на web01: все запросы 10.0.30.1 (шлюз) или один белый. Fail2ban банит 10.0.30.1 — падает весь офис. На SQL все сессии с адреса app-прокси. Firewall log drop показывает только post-NAT IP.
| Где смотрите | Видите шлюз | Где настоящий src |
|---|---|---|
| После masquerade WAN | 203.0.113.10 | conntrack original, log до SNAT |
| После DNAT на web | внутренний VIP | prerouting / XFF |
| HTTP за прокси | IP proxy | XFF если proxy свой |
| SMTP relay | IP релея | Received-заголовки |
Не путать с пробросом как риском: там угроза экспозиции, здесь — слепота расследования.
Возможные причины
masquerade/src-natLAN→DMZ (hairpin или «чтобы вернулось»), хотя достаточно маршрута.- Reverse proxy не передаёт XFF или приложение ему не верит.
- Приложение доверяет XFF с интернета (подделка).
- Логи filter смотрят post-DNAT dst, не original.
- Центральный SNAT для партнёрских туннелей.
- Нет хранения логов шлюза — логи не хранятся.
Диагностика
1. Где NAT
sudo nft list table ip nat
sudo nft list table inet nat
sudo conntrack -L -o extended | head/ip firewall nat print
/ip firewall connection print where src-address~"10.0.10.50"Ищите src-nat/masquerade не только на WAN, но LAN→DMZ.
2. Совпадает ли лог приложения с conntrack
Воспроизведите запрос с известного клиента 10.0.10.50. Сравните access.log, conntrack -L по dst 443, log firewall.
sudo conntrack -L -p tcp --dport 443 | grep 10.0.10.50Если conntrack знает 10.0.10.50, а nginx нет — чините XFF/прокси, не «отключайте NAT мира».
3. XFF
На proxy:
# схема, не слепой copypaste в прод без trust:
# set_real_ip_from 10.0.30.1;
# real_ip_header X-Forwarded-For;Проверьте запрос с WAN: если без своего proxy в цепочке XFF можно прислать любой — не доверяйте.
4. Журнал firewall
Включен ли log на правилах с original tuple. См. журналирование. Префикс должен содержать ip saddr до SNAT (в nft log на filter/forward обычно original для LAN→WAN).
Концепция pf: log на pass, states показывают orig. Не выключайте pf.
Решение
Сценарий A. Убрать лишний SNAT LAN→DMZ
Если шлюз — default gw для обеих сетей, web01 маршрутизирует ответ на 10.0.10.0/24 через 10.0.30.1, masquerade не нужен. Удалите src-nat LAN→DMZ. Клиентский IP дойдёт до web. Проверьте hairpin отдельно (пользователь ходит на публичное имя изнутри).
RouterOS:
/ip firewall nat print
# disable src-nat from 10.0.10.0/24 to 10.0.30.0/24 if presentСценарий B. WAN masquerade оставить, логировать original
Исходящие в интернет будут с 203.0.113.10. Это норма. Для инцидента исходящего C2 смотрите лог/conntrack на шлюзе, не на внешнем сервисе. Включите log new с серверов (sample) + syslog на отдельный узел.
sudo nft add rule inet filter forward ip saddr 10.0.10.20 oifname ens18 ct state new \
counter log prefix "SRC-ORIG " limit rate 10/secondСценарий C. HTTP: XFF только от своего proxy
Цепочка: клиент → proxy 10.0.30.10 → app. App set_real_ip_from только 10.0.30.10. Заголовок от WAN напрямую — игнор.
Не пишите секрет сессии в лог рядом с IP. IP+время+URL достаточно для тикета.
Сценарий D. DNAT входящий
В логе filter после dnat dst уже внутренний. Для WAN-инцидента логируйте в prerouting или правило WAN с 203.0.113.10:443 и saddr клиента интернета. Сохраняйте syslog.
nft log на iif wan tcp dport 443 ct state new.
Сценарий E. Партнёрский туннель с SNAT
Если без SNAT overlapping — документируйте. Для расследований: отдельный NAT pool на партнёра, не общий офисный masquerade, чтобы бан не бил всех.
Не сканируйте чужие сети, чтобы «найти, кто мы для них». Смотрите свой conntrack.
Как проверить, что проблема устранена
- Запрос с
10.0.10.50на внутренний HTTP (без лишнего SNAT): в access.log10.0.10.50. - Запрос с LTE на сайт: в логе proxy публичный адрес клиента (или XFF после своего proxy), не «случайный» спуф.
conntrackна шлюзе для исходящего с app01 показывает original10.0.10.20.- Ложные баны всего
10.0.30.1прекратились. - Firewall не disabled, NAT WAN для офиса жив.
curl -sS ifconfig.me # с клиента LAN: должен быть 203.0.113.10 — это норма WAN NATВнутренние логи при этом знают 10.0.10.50.
Если не помогло
- Несколько NAT (CGNAT провайдера) — внешний сервис видит ещё чужой адрес; ваши логи шлюза всё равно primary.
- UDP без conntrack timeout — увеличьте timeout осознанно, не any accept.
- Приложение за TLS-терминацией без XFF.
- Windows IIS за ARR — свой заголовок, проверьте trust.
- Логи ротируются за сутки — чините хранение.
Профилактика
- Запрет SNAT между внутренними зонами без RFC и владельца.
- Обязательный syslog 5-tuple new на публикациях WAN.
- real_ip только trust proxy.
- Учения IR: «найдите клиента по access.log» на стенде.
- Матрица NAT-правил рядом с матрицей портов.
FAQ
Отключить NAT полностью?
Нет, если нет провайдерских маршрутов на RFC1918. Снимите только внутренний лишний SNAT.
X-Real-IP vs XFF
Важно не имя, а trust. Один заголовок, список доверенных hop.
Нужен ли PROXY protocol
Если ваш proxy и backend это умеют (документация nginx/haproxy) — да, для TCP не-HTTP. Не выдумывайте поля.
Почему fail2ban банит офис
Бан по адресу прокси. Чините src, jail ignoreip своего proxy, не отключайте jail и не отключайте FW.
Логировать payload?
Нет. 5-tuple + allow/drop. Payload — PII и секреты.