Цифровой дворник

network

Цикл «Не светим лишнего». Выпуск 2.

Firewall часто воспринимают как стену с кучей дырок-портов. На практике полезнее думать о нём как о списке решений: этот пакет относится к такой-то сессии, пришёл отсюда, идёт туда, значит его пропускаем или выбрасываем.

Начальная позиция для защищаемого контура простая: если разрешающего правила нет, соединение не должно пройти. Это и есть default deny. Не «мы запретили несколько плохих адресов», а «мы разрешили только то, что понимаем».

Сначала не перепутаем input и forward

На MikroTik и большинстве других маршрутизаторов есть принципиальная разница.

Input — трафик к самому маршрутизатору. WinBox, SSH RouterOS, API, DNS на роутере, ICMP к его адресу. Если мы защищаем управление MikroTik, работаем здесь.

Forward — трафик, проходящий через маршрутизатор. Например, внешний клиент идёт через DNAT на RDP-сервер или внутреннюю веб-панель. Пакет адресован не самому MikroTik, он только проходит через него.

Типовая ошибка выглядит так: администратор добавляет красивое правило в input, проверяет, что WinBox закрыт, и считает, что опубликованный сервер тоже защищён. А DNAT спокойно продолжает работать через forward.

NAT не является разрешением сам по себе

DNAT отвечает на вопрос «куда переписать адрес назначения». Firewall отвечает на вопрос «пустить ли пакет дальше». Лучше держать эту логику раздельно.

Допустим, внешний 203.0.113.10:10443 переводится на внутренний 10.20.30.15:443. Это ещё не значит, что доступ должен быть разрешён всем. В forward можно потребовать одновременно:

  • вход с WAN;
  • состояние нового соединения;
  • нужный внутренний адрес и порт после DNAT;
  • наличие источника в группе access-monitoring;
  • логирование начала соединения.

Тогда NAT остаётся постоянным, но без членства в разрешённой группе ресурс выглядит закрытым.

Группы лучше отдельных исключений

Если ресурсов больше двух, не стоит строить правила вокруг фамилий и разовых адресов. Удобнее завести сущности по смыслу:

  • назначения protected-monitoring, protected-dev, protected-cctv;
  • источники access-admin, access-developer, access-contractor;
  • отдельные цепочки или правила для HTTP, SSH, RDP и управления.

Тогда пользователь или портал добавляет адрес в готовую группу. Он не получает возможность придумать себе произвольный destination. Это сильно снижает шанс, что ошибка административной панели превратится в «открыли человеку всю серверную сеть».

Плюсы обычного firewall

  • работает с любыми IP-протоколами и не зависит от приложения;
  • почти везде уже есть;
  • поведение можно увидеть счётчиками и логами;
  • хорошо масштабируется через группы адресов;
  • не требует установки чего-либо на клиент.

Минусы

Главный минус — firewall обычно не знает человека. Он знает IP-адрес, иногда интерфейс, VLAN, сертификат туннеля или метку соединения. Если два пользователя выходят через один NAT, для обычного L3/L4-фильтра они выглядят одинаково.

Вторая проблема — правила статичны, пока кто-то или что-то их не изменит. Человеческие исключения имеют свойство оставаться навсегда.

Где можно больно ошибиться

Порядок правил. Firewall идёт сверху вниз. Широкий accept раньше точного drop делает точный drop декоративным.

Established, related. Состояние соединений удобно: не приходится заново проверять каждый ответный пакет. Но если правило accept established,related стоит выше проверки актуального access-list, уже открытый SSH или RDP может продолжить работу после удаления адреса из списка.

FastTrack. Он ускоряет обработку established-соединений, пропуская часть обычного пути firewall. Для трафика, который должен отключаться немедленно при отзыве разрешения, FastTrack нужно либо обходить, либо продумывать очистку connection tracking.

IPv6. Можно идеально закрыть IPv4 и оставить тот же сервер доступным по глобальному IPv6. Проверяем оба стека или сознательно отключаем неиспользуемый.

Управление самим маршрутизатором. REST API, WinBox и SSH не должны торчать в Интернет просто потому, что «там сложный пароль». Управление лучше вынести в отдельный адрес/VRF/VLAN и ограничить источники.

Слишком широкая группа назначения. Открывать /16, потому что нужный сервер находится где-то внутри, — плохая экономия строк конфигурации.

Когда этого достаточно

Обычный firewall отлично решает задачу, если источники стабильны и хорошо известны: офисы, площадки, внешние сервисы, собственный VPN-шлюз. Для людей с динамическими адресами ему нужен ещё один слой: временный список, портал, knocking или другая система, которая будет решать, кого и на сколько добавлять.

Ранее в цикле

Документация и источники

#network #mikrotik

Цикл «Не светим лишнего». Выпуск 2.

Firewall часто воспринимают как стену с кучей дырок-портов. На практике полезнее думать о нём как о списке решений: этот пакет относится к такой-то сессии, пришёл отсюда, идёт туда, значит его пропускаем или выбрасываем.

Начальная позиция для защищаемого контура простая: если разрешающего правила нет, соединение не должно пройти. Это и есть default deny. Не «мы запретили несколько плохих адресов», а «мы разрешили только то, что понимаем».

Сначала не перепутаем input и forward

На MikroTik и большинстве других маршрутизаторов есть принципиальная разница.

Input — трафик к самому маршрутизатору. WinBox, SSH RouterOS, API, DNS на роутере, ICMP к его адресу. Если мы защищаем управление MikroTik, работаем здесь.

Forward — трафик, проходящий через маршрутизатор. Например, внешний клиент идёт через DNAT на RDP-сервер или внутреннюю веб-панель. Пакет адресован не самому MikroTik, он только проходит через него.

Типовая ошибка выглядит так: администратор добавляет красивое правило в input, проверяет, что WinBox закрыт, и считает, что опубликованный сервер тоже защищён. А DNAT спокойно продолжает работать через forward.

NAT не является разрешением сам по себе

DNAT отвечает на вопрос «куда переписать адрес назначения». Firewall отвечает на вопрос «пустить ли пакет дальше». Лучше держать эту логику раздельно.

Допустим, внешний 203.0.113.10:10443 переводится на внутренний 10.20.30.15:443. Это ещё не значит, что доступ должен быть разрешён всем. В forward можно потребовать одновременно:

  • вход с WAN;
  • состояние нового соединения;
  • нужный внутренний адрес и порт после DNAT;
  • наличие источника в группе access-monitoring;
  • логирование начала соединения.

Тогда NAT остаётся постоянным, но без членства в разрешённой группе ресурс выглядит закрытым.

Группы лучше отдельных исключений

Если ресурсов больше двух, не стоит строить правила вокруг фамилий и разовых адресов. Удобнее завести сущности по смыслу:

  • назначения protected-monitoring, protected-dev, protected-cctv;
  • источники access-admin, access-developer, access-contractor;
  • отдельные цепочки или правила для HTTP, SSH, RDP и управления.

Тогда пользователь или портал добавляет адрес в готовую группу. Он не получает возможность придумать себе произвольный destination. Это сильно снижает шанс, что ошибка административной панели превратится в «открыли человеку всю серверную сеть».

Плюсы обычного firewall

  • работает с любыми IP-протоколами и не зависит от приложения;
  • почти везде уже есть;
  • поведение можно увидеть счётчиками и логами;
  • хорошо масштабируется через группы адресов;
  • не требует установки чего-либо на клиент.

Минусы

Главный минус — firewall обычно не знает человека. Он знает IP-адрес, иногда интерфейс, VLAN, сертификат туннеля или метку соединения. Если два пользователя выходят через один NAT, для обычного L3/L4-фильтра они выглядят одинаково.

Вторая проблема — правила статичны, пока кто-то или что-то их не изменит. Человеческие исключения имеют свойство оставаться навсегда.

Где можно больно ошибиться

Порядок правил. Firewall идёт сверху вниз. Широкий accept раньше точного drop делает точный drop декоративным.

Established, related. Состояние соединений удобно: не приходится заново проверять каждый ответный пакет. Но если правило accept established,related стоит выше проверки актуального access-list, уже открытый SSH или RDP может продолжить работу после удаления адреса из списка.

FastTrack. Он ускоряет обработку established-соединений, пропуская часть обычного пути firewall. Для трафика, который должен отключаться немедленно при отзыве разрешения, FastTrack нужно либо обходить, либо продумывать очистку connection tracking.

IPv6. Можно идеально закрыть IPv4 и оставить тот же сервер доступным по глобальному IPv6. Проверяем оба стека или сознательно отключаем неиспользуемый.

Управление самим маршрутизатором. REST API, WinBox и SSH не должны торчать в Интернет просто потому, что «там сложный пароль». Управление лучше вынести в отдельный адрес/VRF/VLAN и ограничить источники.

Слишком широкая группа назначения. Открывать /16, потому что нужный сервер находится где-то внутри, — плохая экономия строк конфигурации.

Когда этого достаточно

Обычный firewall отлично решает задачу, если источники стабильны и хорошо известны: офисы, площадки, внешние сервисы, собственный VPN-шлюз. Для людей с динамическими адресами ему нужен ещё один слой: временный список, портал, knocking или другая система, которая будет решать, кого и на сколько добавлять.

Ранее в цикле

Документация и источники

#network

Обложка

Есть такой классический сетевой ритуал: зайти на железку, снять конфиг, сохранить в файл, потом когда-нибудь использовать его для восстановления или сверки.

Звучит просто.
Но есть один нюанс, который регулярно портит людям жизнь: пагинация в терминале.

Это когда устройство не вываливает весь конфиг сразу, а показывает его кусками:

--More--

или:

....press ENTER to next line, CTRL_C to break, other key to next page....

На экране это выглядит нормально: нажал Enter или Space, поехали дальше.
А вот если в этот момент логируется сессия или копируется вывод, эта служебная строка может попасть прямо в файл с конфигом.

Потом кто-то берёт этот файл, вставляет обратно в устройство, и железка начинает честно выполнять вот это:

....press ENTER to next line....

Спойлер: такой команды обычно нет.

В итоге получаем:

% Unrecognized command, and error detected at '^' marker.

и небольшую экскурсию в мир грусти.


Главное правило

Перед снятием большого конфига нужно отключить постраничный вывод.

Пагинация — это не часть конфигурации.
Это мусор интерфейса терминала, который очень убедительно притворяется командами.


Huawei / Huawei-like GPON / коммутаторы

Для Huawei и похожих CLI обычно помогает:

screen-length 0 temporary
display current-configuration

или, если на конкретной модели команда короче:

screen-length 0 temporary
display current-config

Ключевое слово здесь — temporary.
Оно отключает пагинацию только на текущую сессию, без изменения постоянных настроек.


Cisco IOS / IOS-XE

terminal length 0
show running-config

После этого конфиг выводится целиком, без --More--.


Cisco ASA

terminal pager 0
show running-config

На ASA используется именно terminal pager 0, а не terminal length 0.


Juniper JunOS

Можно отключить длину экрана в CLI:

set cli screen-length 0
show configuration

Или использовать вариант на одну команду:

show configuration | no-more

Второй способ удобен, когда не хочется менять параметры текущей CLI-сессии.


MikroTik RouterOS

Обычно экспорт нормально уходит без классической терминальной пагинации:

/export

Для более компактного вывода:

/export terse

Если надо сохранить сразу в файл на самом устройстве:

/export file=backup

Потом файл можно скачать с роутера.


Тут всё зависит от модели и версии прошивки. Часто встречаются варианты:

terminal length 0
terminal datadump
disable clipaging

После этого уже:

show running-config

или аналогичная команда просмотра конфигурации.

Если команда не сработала — лучше посмотреть ? в CLI, потому что у одного и того же вендора синтаксис может отличаться от линейки к линейке.


Linux-серверы и системные команды

На Linux проблема обычно не в сетевом CLI, а в pager’ах вроде less.

Для systemctl:

systemctl --no-pager status nginx

Для journalctl:

journalctl --no-pager -u nginx

Можно принудительно заменить pager на cat:

PAGER=cat команда

Что чистить, если уже попало

Если конфиг уже снят с мусором, ищем и удаляем строки вида:

--More--
---- More ----
....press ENTER to next line, CTRL_C to break, other key to next page....

А также управляющие хвосты терминала, которые могут выглядеть как странные символы или ANSI-последовательности.

Перед применением такого конфига лучше хотя бы быстро поискать:

grep -Ei "more|press ENTER|next page|CTRL_C" backup.cfg

Если что-то нашлось — это не конфиг, это привет от терминала.


Мини-чек-лист перед сохранением конфига

  • [ ] Отключили пагинацию.
  • [ ] Сняли конфиг.
  • [ ] Проверили, что нет --More--, press ENTER, next page.
  • [ ] Сохранили оригинальный файл.
  • [ ] Для восстановления используем очищенный файл.
  • [ ] Не удаляем служебные команды типа commit / quit, если они являются частью CLI-логики устройства.

Особенно это важно на устройствах, где конфиг состоит из профилей. Например, на Huawei GPON внутри line-profile, vlan-profile, rule-profile, specific-profile команды commit и quit могут быть не мусором, а нормальной частью применения настроек.


Короткий вывод

Отключить пагинацию — это 5 секунд.

Не отключить — это потом полчаса смотреть на:

% Unrecognized command

и думать, почему железка пытается выполнить человеческую просьбу «press ENTER to next line».


#network #lifehacks

Обложка

Цикл «Не светим лишнего». Выпуск 1.

Если у вас есть хоть какая-то корпоративная инфраструктура, то довольно быстро появляется список ресурсов, которые вроде бы внутренние, но иногда нужны снаружи. Причём «иногда» постепенно превращается в «каждый день, но разным людям и по разным причинам».

Самый очевидный пример — корпоративные веб-системы: мониторинг, helpdesk, Wiki, Git, панели виртуализации, внутренние отчёты. Дальше идут SSH и RDP, WinBox, интерфейсы камер, контроллеры Wi-Fi, IPMI, iDRAC и iLO. Разработчикам нужны dev- и test-контуры, registry, CI/CD и временные стенды. Подрядчику надо на два часа попасть к конкретной камере или серверу. Дежурному инженеру — ночью зайти с домашнего Интернета и починить аварию. С удалённой площадки требуется доступ к одной служебной системе, но тащить ради неё всю сеть в туннель не хочется.

Есть и менее очевидные точки входа. Например, технологический Wi-Fi на объекте: обычный сотрудник получает только Интернет и почту, а инженер после дополнительной авторизации — доступ к оборудованию. Или временный демонстрационный контур, который должен открываться заказчику на время показа и снова исчезать. Или админская панель нового проекта, которую разработчики выставили «на недельку», а нашли через полгода.

Три разные двери

Тут полезно разделить три вещи, которые в разговоре часто смешивают.

Первая дверь — сетевая достижимость. Есть ли вообще маршрут до ресурса, опубликован ли он через NAT или reverse proxy, отвечает ли его адрес из Интернета. Если порт закрыт молча, сканер видит одно. Если отвечает SSH-баннер, веб-сервер или страница входа — уже совсем другое.

Вторая дверь — решение firewall. Даже если маршрут и DNAT существуют, firewall может пропускать соединение только с определённых адресов, в определённое время или после специального действия. На этом уровне обычно видны IP, протокол, порт и состояние соединения. Имя пользователя firewall чаще всего не знает.

Третья дверь — авторизация самого ресурса. Логин в Grafana, SSH-ключ, пароль Windows, сертификат, TOTP, роль в корпоративной системе. Именно здесь приложение понимает, кто пришёл и что ему разрешено делать внутри.

Эти двери не заменяют друг друга. Если ресурс имеет отличный пароль, это ещё не повод круглосуточно показывать всему Интернету его версию, баннер, TLS-стек и форму входа. И наоборот: если доступ ограничен по IP, нельзя считать, что конкретный пользователь уже опознан. За одним разрешённым NAT может сидеть целый офис.

Почему не оставить только VPN

VPN — нормальная и привычная технология. Когда человеку действительно нужна приватная L3-связность с несколькими сетями и произвольными протоколами, он часто остаётся лучшим выбором.

Но VPN не всегда удобен как единственный ответ. На машине уже может работать другой корпоративный или личный VPN. Могут пересекаться адресные пространства, меняться DNS и default route, включаться kill switch. Для разового подрядчика приходится заводить профиль, выдавать инструкцию, потом не забыть всё отозвать. А иногда ради одной веб-панели пользователь получает возможность ходить в целую подсеть.

Поэтому дальше мы будем смотреть не на «замену VPN», а на набор инструментов. Где-то достаточно статического allow-list. Где-то лучше выдать временную запись на час. Где-то пригодится port knocking. Для веб-систем логичен авторизующий reverse proxy. Для RDP и SSH — web-bastion. А если хочется открывать firewall только при живой браузерной сессии, можно сделать TOTP-портал с heartbeat.

Что мы хотим получить в итоге

Нормальная схема должна отвечать на несколько простых вопросов:

  • какой именно ресурс или группа ресурсов открывается;
  • кому и на каком основании;
  • с какого адреса или устройства;
  • на какой срок;
  • что произойдёт, если пользователь закроет сессию;
  • оборвутся ли уже установленные соединения;
  • кто потом сможет разобраться по журналам, что происходило.

И главное — доступ должен быть минимальным. Подрядчику по камерам не нужна сеть гипервизоров. Разработчику тестового проекта не нужен IPMI. Дежурному админу может понадобиться много, но только после сильной авторизации и на ограниченное время.

С чего начнём

В следующих выпусках пойдём от простого к сложному. Сначала обычный firewall и VPN, затем статические и временные списки, port knocking и SPA. Дальше — captive portal, reverse proxy, TOTP и наша любимая идея с живым WebSocket heartbeat. В финале соберём всё в одну комбинированную архитектуру и прикинем лабораторный стенд на MikroTik.

Смысл цикла не в том, чтобы объявить один метод правильным. Задача проще: перестать светить наружу всё подряд и научиться выдавать ровно тот доступ, который нужен человеку сейчас.

Документация и источники

#network

Обложка

Боль – Proxmox-сервер живёт в чужой сети, за чужим NAT. Шлюз не мой, портов не даёт, статику режет (только DHCP). Белого IP нет вообще. Классика «сервер лежит непонятно где».

Но снаружи у меня всё работает: – HTTPS для lab.example (Caddy, сертификаты Let's Encrypt) – SSH-доступ к роутерам – Мониторинг, фото, локальные LLM — открываются из любого интернета

Решение Три кита, на которых вывез всю историю: 1) Виртуальная сеть, изолированная от внешней шизофрении – Поднял вторую сеть br1 (198.51.100.0/24) без IP на хосте. Внутри неё живут сервисы: Beszel, Uptime Kuma, Immich, Ollama, Hermes. – Шлюз и NAT — отдельный LXC-контейнер с Caddy (198.51.100.1): он делает masquerade из внутренней сети в наружу, ip_forward=1.

Врезка: схема NAT/туннель– Зачем? Сервисы получают стабильную адресацию и жизнь, независимую от капризов внешнего мира.

2) Туннель, который сам уходит из-под NAT – Контейнер с Caddy инициирует SSH reverse-туннель к моему MikroTik с белым IP (203.0.113.20). – Внешние порты 443 и 80 на роутере редиректятся на 8443 и 8080, которые поднял SSH-туннель, — и уже по туннелю трафик попадает в Caddy. – Схема: интернет → MikroTik (белый IP) → SSH reverse → Caddy → сервисы в br1. – Снаружи всё приходит на 443/80, внутри туннеля — на 8443/8080.

3) Один вход — одна дверь – Весь внешний трафик встречает Caddy: раздаёт по доменам, сам продлевает TLS-сертификаты, на нём же ACL для админок. Одна точка входа — меньше боли и хаоса.

Бонус: SSH reverse-туннель, или «костыль, который стал фичей» Суть – Обычный SSH-туннель (-L) тянет удалённый порт к тебе. Reverse (-R) делает наоборот: машина за NAT сама стучится наружу и «пробрасывает» свой локальный порт на стороне сервера. – Натурально подходит для железок за CGNAT, подвалов и «дач».

Правильная картинка в моём кейсе:

Caddy (за NAT)  ──ssh -R──>  MikroTik (белый IP)
      │                           │
      └── на MikroTik открыты 8443/8080 → идут в мой localhost:443/80

Команды — как у меня Шаг 1. На MikroTik — разрешить SSH-форвардинг:

/ip ssh forwarding-enabled=both

Шаг 2. На MikroTik — локально перенаправить внешние 443/80 на 8443/8080 (куда слушает sshd для -R):

/ip firewall nat add chain=dstnat protocol=tcp dst-port=443 action=redirect to-port=8443
/ip firewall nat add chain=dstnat protocol=tcp dst-port=80  action=redirect to-port=8080

Шаг 3. На MikroTik — пропустить эти порты в firewall (до drop-правила):

/ip firewall filter add chain=input protocol=tcp dst-port=8443,8080 action=accept

Шаг 4. На Caddy (Linux) — поднять сам reverse-туннель:

ssh -R 8443:localhost:443 -R 8080:localhost:80 \
    -N -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes \
    admin@203.0.113.20
  • -R 8443:localhost:443 — «порт 8443 на MikroTik идёт в мой localhost:443»
  • -N — только туннель
  • ServerAliveInterval=30 — keepalive, чтобы NAT не рвал соединение
  • ExitOnForwardFailure — падаем, если порт не поднялся

Шаг 5. Завернул это в systemd-сервис (mt-tunnel.service), чтобы жил после ребута и сам переподключался.

Итог: открываю https://router.lab.example — попадаю в Winbox роутера за NAT. Один вход — и весь дом за ним.

Почему это «костыль», но рабочий – SSH — это TCP поверх TCP: на потере пакетов ведёт себя хуже UDP-туннелей. – Одно длинное соединение, которое надо держать живым. – Ключ от роутера лежит на Caddy: его компрометация = доступ к роутеру.

Почему не WireGuard – Пробовал — не завёлся в непривилегированном LXC (нет /dev/net/tun). – SSH уже был настроен, ключи были — 5 минут до рабочего результата. – «Правильный» вариант — WG с TUN:

# на хосте PVE, в конфиге контейнера
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file 0 0

потом mknod /dev/net/tun c 10 200 внутри — и WG поднимается как надо.

Цифры и железки – Proxmox PVE 9.2: – br0 (192.0.2.0/24, DHCP, шлюз 192.0.2.1 — MAC от VMware, статику режут) – br1 (198.51.100.0/24, без IP на хосте) – Caddy lxc-gw: 198.51.100.1 (шлюз br1), NAT masquerade br1→wan, ip_forward=1 – SSH reverse-туннель: Caddy → admin@203.0.113.20 (MikroTik), 443→8443, 80→8080, keepalive=30 c, systemd-сервис mt-tunnel – MikroTik: /ip ssh forwarding-enabled=both; dstnat redirect 443→8443, 80→8080; firewall input accept tcp 8443/8080 (до drop) – Сертификаты: Let's Encrypt для lab.example (валиден до ноября 2026) – Сервисы в br1: – Beszel — 198.51.100.12 – Uptime Kuma — 198.51.100.14 – Immich — 198.51.100.15 – Hermes — 198.51.100.16 – Ollama — 198.51.100.17

Вывод Сервер может жить за любым NAT — хоть у соседа, хоть на «даче». Пока есть исходящий интернет, все сервисы доступны снаружи через один туннель и один шлюз. Костыль? Да. Рабочая фича? Тоже да.

#network #selfhosting

Обложка

Вводные и исходные данные

У вас бывало: телефон орёт «без интернета», на ноуте Windows уныло пишет «подключение без доступа», а пинги — как на подбор, зелёные, 0% потерь? У меня это расцвело на две недели. Причём не «упал провайдер», не «роутер сдох», а что-то совсем точечное: то игры не логинятся, то AI молчит, то push'и у умных ламп залипают, зато сайт районной библиотеки бодро открывается. Живой канал, но местами как будто ножницами выстрижено.

Фон: обычный homelab-дворик. Встаю утром, варю кофе, иду VLAN’ы кормить:

  • Домашний Wi‑Fi → сегмент 10.0.10.0/24, роутер 10.0.10.1.
  • Гостевая сеть → своя подсеть 10.0.20.0/24, NAT на тот же WAN.
  • IoT-живность → 10.0.30.0/24, без выхода в локалку, только наружу.
  • Аплинк один, серый NAT, никаких белых адресов не светим.
  • Роутер от производителя «которого нельзя называть», с нормальным firewall'ом.
  • Для проверки связности — самописная утилита connect-check: ~500 тестов в один прогон (кептив-порталы ОС, DNS/DoT/DoH, QUIC, игры, AI-платформы, облака, банки, IoT-порты, CDN, скорость). Отчёт — один HTML-файл.

Врезка: карта VLAN и подключений

До 16 июля всё было как в рекламе провайдеров (которых мы тут не называем). А вот дальше пошла странная пила: устройства вроде в сети, но «интернета нет». Я достал свой connect-check и начал вести дневник замеров.

Как и что делал — хроника, инструменты, команды

Как появился connect-check (спойлер: не сразу)

Сначала никакой утилиты не было. Я просто реверсил трафик телефонов: смотрел, куда они вообще стучатся, когда решают «есть ли интернет». Поднял tcpdump, отфильтровал по устройству — и увидел, что к части ресурсов SYN уходит, а ответа нет вообще: ни RST, ни SYN-ACK. Просто тишина. Так впервые вылезли кептив-порталы — connectivitycheck.gstatic.com и его соседи.

Дальше — проще: раз режут эти, наверняка режут и похожие. Накидал список «канареек» (connectivitycheck.android.com, msftconnecttest.com, captive.apple.com и прочие) и стал проверять их руками, пачками. А потом из этого выросла маленькая консольная утилитка, которая гоняет адреса пачками и отдаёт один HTML-отчёт. Со временем список ресурсов и сервисов разросся до 500+ проверок — туда попали игры, AI-платформы, облака, IoT-порты, банки, DNS/DoT/DoH, QUIC.

16.07. Старт. Симптом: телефоны «без интернета», браузер кое-как живёт, игры не логинятся

  • Прогон connect-check с Android в домашнем Wi‑Fi:
    • 40 OK, 7 предупреждений, 8 провалов.
  • Первое, что проверяю при «интернета нет»:
    • Кептив-порталы ОС. На Android это connectivitycheck.gstatic.com/generate204 и connectivitycheck.android.com/generate204.
    • curl -I http://connectivitycheck.gstatic.com/generate_204
    • curl -I http://connectivitycheck.android.com/generate_204
    • Ожидается HTTP/204 No Content. Фактически: таймауты (WinINet на Windows выдаёт 12002), а ping connectivitycheck.gstatic.com — 0% потерь, DNS резолвит. Значит, кто-то душит именно эти URL.
  • DNS и пинги:
    • ping 1.1.1.1 → ок.
    • ping 8.8.8.8 → ок.
    • dig connectivitycheck.gstatic.com A @1.1.1.1 → A-запись есть.
    • А вот HTTP к generate_204 — молчит. Сразу ловушка: Android и Windows считают, что интернет отсутствует, потому что их «проверки доступа» не отвечают.

Вечер 16.07. «Может, это Wi‑Fi шалит?» Перепрыгнул на второй SSID (5 ГГц, DFS, канал 52)

  • Прогон connect-check:
    • 14 OK, 6 warn, 37 fail — стало хуже.
  • Возвращаюсь на основной SSID — снова «нормально плохое»:
    • 41 OK, 6 warn, 8 fail.
  • Вывод: не в радио. DFS может добавлять спецэффектов, но главная беда где-то за радиоинтерфейсом.

18.07 утро. Первая волна нарастает

  • Прогон:
    • 120 OK, 18 warn, 19 fail.
  • Бьём DoT (DNS-over-TLS) — подозрение на фильтрацию порта 853:
    • echo | openssl s_client -connect dns.google:853 -servername dns.google -brief → зависает/таймаут.
    • echo | openssl s_client -connect 8.8.8.8:853 -servername dns.google → таймаут.
    • echo | openssl s_client -connect 9.9.9.9:853 -servername dns.quad9.net → таймаут.
    • echo | openssl s_client -connect 1.1.1.1:853 -servername cloudflare-dns.com → соединяется.
  • На Android в настройках частного DNS «Автоматически» → телефон объявляет сеть «без интернета». Ставлю «Выключено» — статус возвращается, браузер оживает. Диагноз: порт 853 фильтруется точечно (Cloudflare пропускают), Android при этом падает в обморок.

20–21.07. Пик. Резко ложится почти всё иностранное, задевает и часть наших

  • 20.07 утро:
    • 228 OK, 12 warn, 5 fail — пока терпимо.
  • 20.07 (гостевая сеть 10.0.20.0/24):
    • 154 OK, 25 warn, 61 fail — гостям сегодня лучше книжку.
  • 20.07 полдень:
    • 220 OK, 17 warn, 2 fail — как будто «кто-то» отпустил.
  • 20.07 вечер:
    • 116 OK, 31 warn, 93 fail — опять душит.
  • 21.07 утро:
    • 114 OK, 27 warn, 99 fail — держат под водой.
  • 21.07 день:
    • 105 OK, 61 warn, 80 fail — в строю лишь самое житейское.

Что именно резалось в пик (проверял точечными коннектами):

1) Кептив-порталы ОС — системно. Это и делает «нет интернета» при живом канале. – curl -I http://connectivitycheck.gstatic.com/generate_204 → таймаут. – curl -I http://www.msftconnecttest.com/connecttest.txt → таймаут. – curl -I http://captive.apple.com/hotspot-detect.html → таймаут. – Статистика по gstatic: 0/8 успешных подряд — стабильный ноль.

Врезка: кептив-порталы режут точечно

2) DoT :853 — чёткая политика: – dns.google, 8.8.8.8, 9.9.9.9 → не коннектится. – 1.1.1.1 → коннектится. – tcpdump -i wan -n host 8.8.8.8 and port 853 → SYN уходит, ответа нет. Строгое «нет» на SYN/ACK.

Врезка: DoT режут выборочно

3) Игры — входы и CDN/CM: – Battle.net, включая login :1119 на *.actual.battle.net → нет рукопожатия. – nc -vz eu.actual.battle.net 1119 → Operation timed out. – Steam CDN/CM, Epic, Riot, Ubisoft, GOG, Xbox Live, PlayStation Network, Nintendo, EA, Roblox, Minecraft: – Формально это TCP/443, но TLS-рукопожатие залипает на ClientHello. – curl -sI https://store.steampowered.com → может открыться, а лаунчер — не логинится: SNI/DPI по доменам и шаблонам. – Трассы выглядят как обычно до границы, дальше молчание: – mtr -T -P 443 battle.net → маршруты до 198.51.100.x, затем тишина.

4) AI-платформы — отрезаны подчистую: – OpenAI (включая ChatGPT и API), Claude/Anthropic, Gemini, AI Studio, Grok/xAI, DeepSeek, Mistral, Hugging Face, Groq, Together, Poe, Notion AI, Cursor. – Браузер: WinINet 12002 (таймаут) или 12057 (TLS/DNS). – curl -sS https://api.openai.com/v1/models → дожидается полминуты и роняет коннект.

5) Облака: – AWS S3 CDN (в частности, репы amazonlinux) — регулярно в fail. – curl -I https://some-bucket.s3.amazonaws.com/… → периодические таймауты.

6) IoT-порты — push/шины: – MQTT :8883 (Tuya, Aqara) → conn. timeout. – Google mtalk :5228/:5229 → timeout. – Apple push :5223 → timeout. – XMPP :5222 → timeout. – В логах умного дома: «облако недоступно», девайсы падают в оффлайн. Локальные сценарии работают, удалённые уведомления — нет.

7) QUIC (UDP/443): – curl —http3 -I https://www.google.com → долго, потом откат на HTTP/2. – curl —http3 -I https://www.cloudflare.com → аналогично. – tcpdump -i wan udp port 443 and host 203.0.113.10 → Handshake не складывается, пакеты отваливаются. DPI глушит HTTP/3.

8) DNS-имена точечно: – dig +time=2 +retry=1 iot.yandex.ru @1.1.1.1 → ок. – dig iot.yandex.ru @10.0.10.1 (локальный резолвер, форвард к аплинку) → SERVFAIL/timeout. Значит не весь DNS умер, а выборочно по именам у аплинка. – Аналогично: api.alice.yandex.net, ok.ru, copilot.microsoft.com, clouddrive.nabucasa.com — то не резолвит, то резолвит, но дальше 443 молчит.

9) NTP: – time.google.com, ntp.yandex.ru — временами не отвечают по UDP/123. На графике — ступеньки дрейфа часов. В другие минуты — живут.

22.07 10:57 — будто тумблер щёлкнули

  • Прогон:
    • 286 OK, 17 warn, 2 fail (AWS S3 + Росбанк). Всё остальное — отпустило.
  • Проверяю DoT:
    • dns.google:853 — снова нет.
    • 1.1.1.1:853 — по-прежнему да.
  • Кептив-порталы:
    • gstatic generate_204 → начал стабильно давать 204. Android и Windows радостно вернули «интернет есть».

28.07 ночь — вторая волна, на этот раз российские сервисы в прицеле

  • Прогон:
    • 257 OK, 15 warn, 30 fail.
  • В fail — банки и маркетплейсы:
    • Сбер, Т‑Банк, ВТБ, Альфа, РСХБ, ПСБ, Росбанк.
    • Ozon, РЖД, Дзен, OK.ru, Okko, Почта России.
  • Параллельно опять хромает игры и немного CDN для магазинов. Картина будто «настроить другое правило».

29.07 00:09 — полное восстановление

  • Прогон:
    • 264 OK, 40 warn, 0 fail. Впервые за эти две недели — идеально по фейлам.
  • QUIC по-прежнему местами не любит Google/Cloudflare, но уже в warn — с откатом на TCP/443 всё открывается.

05.08 постскриптум — остаточные артефакты

  • Прогон:
    • 423 OK, 79 warn, 3 fail.
  • Fail:
    • Ведомости — HTTP 502 (их косяк, похоже).
    • Rambler Почта, Proton Mail — TCP :443/:80 закрыт/фильтруется.
  • Warn: 23 сайта с недогруженными ассетами с CDN:
    • fonts.gstatic.com, counter.yadro.ru, static.criteo.net, www.gstatic.com — часть запросов не уходит/не доходит, но основные страницы открываются.

И ещё детали, которые я докручивал в процессе

  • Трассировки:
    • mtr -T -P 443 api.openai.com → доходит до 198.51.100.x, дальше тишина. С ICMP — зелёный.
  • Сравнение сетей:
    • Домашний Wi‑Fi и гостевая сеть (разные VLAN’ы, один WAN) ловят одинаковую пилу. Значит, не межсетевик и не NAT в моём роутере.
  • Локальный DNS:
    • unbound на 10.0.10.2, форвардил на аплинк → та же точечная резка по именам.
    • Прямой dig @1.1.1.1 — чаще ок, чем через аплинк-резолвер. Но DoT к dns.google стабильно не работает.
  • Режимы браузера:
    • HTTP/2 по TCP — чаще жив.
    • HTTP/3 (QUIC) — регулярно отправляют «в угол».
  • Игровые лаунчеры:
    • Локальный браузер может открыть маркетплейс, а логин/патч — нет. Смотрел в tcpdump — ClientHello уходит, ответа нет. DPI по SNI и/или набору JA3-отпечатков.

Что в итоге вышло — сухие цифры и факты

  • 16.07 (Android, домашний Wi‑Fi): 40 OK, 7 warn, 8 fail.
  • 16.07 вечер (Android, второй SSID 5 ГГц DFS ch52): 14 OK, 6 warn, 37 fail.
  • 16.07 вечер (Android, обратно): 41 OK, 6 warn, 8 fail.
  • 18.07 утро: 120 OK, 18 warn, 19 fail.
  • 20.07 утро: 228 OK, 12 warn, 5 fail.
  • 20.07 (гостевая сеть): 154 OK, 25 warn, 61 fail.
  • 20.07 полдень: 220 OK, 17 warn, 2 fail.
  • 20.07 вечер: 116 OK, 31 warn, 93 fail.
  • 21.07 утро: 114 OK, 27 warn, 99 fail.
  • 21.07 день: 105 OK, 61 warn, 80 fail.
  • 22.07 утро: 241 OK, 17 warn, 39 fail.
  • 22.07 (ещё сеть): 109 OK, 30 warn, 73 fail.
  • 22.07 10:57: 286 OK, 17 warn, 2 fail.
  • 28.07 ночь: 257 OK, 15 warn, 30 fail.
  • 29.07 00:09: 264 OK, 40 warn, 0 fail.
  • 05.08: 423 OK, 79 warn, 3 fail (+ 23 сайта с частично не грузящимися ассетами CDN).

Паттерны фильтрации, подтверждённые тестами:

  • Системные кептив-порталы (connectivitycheck.gstatic.com, connectivitycheck.android.com, www.msftconnecttest.com, captive.apple.com) — точечно в молоко. Поэтому ОС дружно помечают сети как «без интернета».
  • DoT :853 — фильтрация. dns.google/8.8.8.8/9.9.9.9 закрыты/таймаут. 1.1.1.1 открыт.
  • Игровые экосистемы — отрезает логин/патч:
    • Battle.net (включая :1119), Steam CDN/CM, Epic, Riot, Ubisoft, GOG, Xbox Live, PlayStation Network, Nintendo, EA, Roblox, Minecraft.
  • AI-платформы — таймауты/DNS: OpenAI, ChatGPT, OpenAI API, Claude/Anthropic, Gemini, AI Studio, Grok/xAI, DeepSeek, Mistral, Hugging Face, Groq, Together, Poe, Notion AI, Cursor.
  • Облака — регулярно AWS S3 CDN (например, amazonlinux).
  • IoT-порты — MQTT :8883, Google mtalk :5228/:5229, Apple push :5223, XMPP :5222.
  • QUIC (UDP/443) — глушится DPI, при живом TCP/443.
  • DNS по именам — точечные дыры: iot.yandex.ru, api.alice.yandex.net, ok.ru, copilot.microsoft.com, clouddrive.nabucasa.com.
  • NTP — time.google.com, ntp.yandex.ru иногда молчат по UDP/123.
  • Российские сервисы тоже ловили:
    • 22.07: Госуслуги/Президент/Правительство/Госдума, Wildberries, 2ГИС.
    • 28.07: банки (Сбер, Т‑Банк, ВТБ, Альфа, РСХБ, ПСБ, Росбанк), Ozon, РЖД, Дзен, OK.ru, Okko, Почта России.

Динамика по волнам:

Врезка: волны фильтрации

  • 16–18.07 — первая волна: телефоны «без интернета» (кептивы), DoT, игры.
  • 20–21.07 — пик: до 61–99 сбоев за прогон, ложится почти всё зарубежное, местами и РФ-сервисы.
  • 22.07 10:57 — резкий откат до 2 сбоев (AWS S3 + Росбанк).
  • 28.07 — вторая волна: 30 сбоев, в основном РФ‑сервисы и игры.
  • 29.07 — полное восстановление — 0 сбоев.
  • 05.08 — хвост: 3 сбоя + кучка CDN-ассетов в warn.

Важно: весь этот цирк проходил при зелёной базовой связности:

  • Пинги к 1.1.1.1/8.8.8.8 — без потерь.
  • DNS в целом резолвит (кроме точечной резки по именам).
  • Скорость по iperf к 198.51.100.50:5201 (мой тестовый узел за NAT’ом друга) — как обычно.
  • Приложения, которые не попадают под SNI/DPI-правила, работали.

Выводы и что делать

1) «Нет интернета» ≠ нет интернета. Если пинги зелёные и DNS в целом жив, а телефон/Windows врут — первым делом смотрите кептив-порталы:

Если тут таймауты — ОС решит, что «интернета нет», и начнёт творить дичь (не все приложения станут выходить в сеть).

2) DoT (порт 853) может фильтроваться точечно. На Android:

  • Настройки → Сеть и интернет → Частный DNS → Выключено.
  • «Автоматически» при заблокированном dns.google:853 превратит вашу сеть в «без интернета», хотя TCP/443 и DNS по UDP/53 живут.

Проверка с хоста:

  • echo | openssl s_client -connect dns.google:853 -servername dns.google -brief.
  • echo | openssl s_client -connect 1.1.1.1:853 -servername cloudflare-dns.com -brief.

3) Это очень похоже на SNI/DPI-фильтрацию и тестовые волны ТСПУ:

  • Маркируются конкретные домены/семейства (игры, AI, облака).
  • Режутся push-порты и QUIC.
  • Ломаются именно «проверочные» домены OS, чтобы техника считала сеть оффлайн.
  • Наблюдается волнами, с резкими включениями/откатами.

4) Как диагностировать самостоятельно (минимальный чек-лист):

5) Что делать, кроме «подождать, пока отпустит»:

  • Собирать фактуру: даты/время, домены, порты, IP назначения (dstIP), скриншоты ошибок (WinINet 12002/12057), логи утилит.
  • Отправлять письмо в ЦМУ ССОП: info@noc.gov.ru.
    • Указать:
    • Номер ТСПУ (если известен; его провайдер обычно светит в договорах/ЛК).
    • Оператора (название не пишу здесь, но в письме — указывать).
    • Примеры недоступных сайтов/сервисов (с точными доменами).
    • Время и длительность каждого инцидента (волны).
    • Трассировки (traceroute/mtr) и результаты повторных попыток не реже 1 раза в 5 минут.
    • dstIP/порты, на которых фиксировались отказы.
    • Скриншоты ответов/таймаутов, желателен pcap-файл.
  • Важно: показывайте, что это не «у вас роутер», а репродуцируется на разных VLAN/сетях/устройствах (домашний Wi‑Fi, гостевая сеть, мобильный).
  • На смартфонах до разбора симптомов отключайте «Частный DNS» и не пугайтесь «без интернета» — это диагноз ОС по сломанным кептив-порталам, а не факт отсутствия канала.

6) Для гигиены лабы:

  • Разведите мониторинг системных URL ОС (gstatic/msftconnecttest/captive.apple) отдельной метрикой — это золотой индикатор.
  • Храните эталонный набор доменов по категориям: игры, AI, облака, банки, IoT. Один прогон — и картинка дня ясна.
  • Автоматизируйте записи о волнах: таймстемпы, OK/warn/fail. У меня это делает connect-check, отчёт — один HTML, открывается даже оффлайн.

Мой личный срез по «что это было»

  • По совокупности: точечная SNI/DPI-фильтрация, вероятно, волны настройки/тестов ТСПУ. Это объясняет, почему одновременно падали:
    • Кептив-порталы (ОС теряют уверенность, заявляют «без интернета»).
    • DoT :853 (частично), QUIC (UDP/443).
    • Семейства доменов (игры, AI, облака) и отдельные DNS-имена.
    • IoT-порты для уведомлений/шины.
  • Наличие «окна» 22.07 10:57 (практически полное восстановление) и второго всплеска 28.07 по РФ-сервисам выглядит как переключение профилей/политик, а не «сломался провод».
  • Полное восстановление 29.07 в 00:09 — характерный «сменили правило/откатили конфиг».

Вопрос к вам

Кто ещё ловил такие волны? С той же симптоматикой: живые пинги/DNS, но «без интернета» из‑за кептивов, закрытый DoT (кроме 1.1.1.1), отключенный QUIC, массовые таймауты игр/AI/облаков, и потом резкий откат? Чем мерили, есть ли у вас числа по дням/часам? Писали ли в ЦМУ ССОП на info@noc.gov.ru и что ответили?

🔧 Утилита диагностики: https://github.com/cooler58/connect-check/releases

#monitoring #network

Обложка

Вводные. Лаба на Proxmox, всё в LXC. Внутрянка на br1 (198.51.100.0/24). Снаружи — один вход через Caddy: обратный прокси, TLS сам. Админки и мониторинг — за ACL: только мой IP, остальным 403. ComfyUI (SD 1.5) живёт в lxc-comfy (198.51.100.18), раньше пыхтел на CPU, в этот день переехал на GPU: Radeon 760M, ROCm-торч, HSAOVERRIDEGFX_VERSION=11.0.0, ollama временно стопнул, чтобы не бодалась за GPU. ComfyUI слушает 8188, в мир — comfy.example-lab.ru. Параллельно ставил Frigate в свежем lxc-frigate.

Симптом. «Комфи 403, остальное открывается». photos.example-lab.ru — ок, beszel/uptime — ок, а comfy.example-lab.ru — 403, позже 502. Подозрение на ACL: «может, IP не тот?»

Хроника.

1) Логи Caddy: с телефона (открывал из Telegram) подряд два respond “Access denied” 403. Потом — 502: connect refused к 198.51.100.18:8188.

2) Внутри lxc-comfy: – ss -tlnp | grep 8188 → LISTEN 0.0.0.0:8188 (python) – curl http://127.0.0.1:8188/ → 200, UI отдался – curl http://198.51.100.18:8188/ -I → 200

Значит, жив и слушает на всех интерфейсах.

3) Снаружи (с хоста/через Caddy/с соседнего контейнера): – bash -lc '>/dev/tcp/198.51.100.18/8188' → Connection refused – tcpdump -i any -n port 8188 внутри lxc-comfy → 0 пакетов – tcpdump -i br1 -n host 198.51.100.18 and port 8188 → SYN доходит и прилетает RST

Если бы отвечал ComfyUI, был бы SYN-ACK. Значит, откликается кто-то другой.

4) Исключаем банальное: PVE firewall — пусто, ebtables — пусто, nft/iptables в контейнерах — ACCEPT, ip_forward=0 (но у нас L2-мост). Чисто.

Разгадка — ARP. – ip neigh show 198.51.100.18 → lladdr «чужой MAC» – А у lxc-comfy MAC другой. – bridge fdb show | grep «чужой MAC» → этот MAC висит на порту lxc-frigate.

Бинго: у lxc-frigate оказался тот же IP 198.51.100.18. ARP резолвит адрес в «чужой MAC», трафик уезжает во Frigate, там 8188 не слушается — оттого RST. 403 здесь просто шум от ACL на первых попытках, корень — дубль IP.

Фикс. – Перекинул lxc-frigate на 198.51.100.19: pct set <frigate_id> --net0 name=eth0,bridge=br1,ip=198.51.100.19/24,gw=198.51.100.1 pct reboot <frigate_id> – ip neigh flush 198.51.100.18 – Проверка: 198.51.100.18 снова указывает на MAC lxc-comfy. – comfy.example-lab.ru → 200, UI летает на GPU.

Мораль. Когда «не открывается», сперва проверь, твой ли это вообще адрес. Дубль IP — тихий убийца: ICMP может отвечать «кто-то один», а TCP будет резать RST с чужого контейнера. Диагностика по тропинке: ss (слушает?) → tcpdump (доходит?) → ARP/fdb (чей MAC?).

А вы чем страхуете лабу от дублей: DHCP-резервы, IPAM, arpwatch на br1 или что-то своё?

#network #lifehacks