Перейти к содержанию

Архитектура 2.0

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

Решение — пересмотреть все. Не отдельные компоненты — а принципы, на которых построена архитектура.


Основные ограничения:

Дефицит выходных узлов. Покупать серверы за рубежом становится сложнее. Я начал привлекать участников, которые поднимают Core-ноды самостоятельно — но механизм подключения оставался ручным: SSH-доступ, настройка вручную, ручной контроль.

Дефицит доменных имен. Старая архитектура требовала отдельного домена для каждого узла — входного и выходного. При масштабировании это стало операционным ограничением: не хватало доменов, чтобы принимать новых участников даже при наличии серверных ресурсов.

Доверие между узлами ядра. Узлы ядра были объединены в доверенную SSH-сеть. Удобно для управления — опасно в эксплуатации. Компрометация одного узла открывала доступ ко всем остальным: именно это и было использовано в инциденте.

Жесткая древовидная топология. Каждый узел ядра держал вокруг себя свою группу входных узлов. При масштабировании каждый такой узел становился потенциальным бутылочным горлышком. Слои не масштабировались независимо.


Что изменилось.

От дерева — к двум независимым кластерам.

Входной слой — однородный Kubernetes-кластер. Любой Entry-под может обслужить любого клиента. Новый участник добавляет worker node в кластер — поды распределяются автоматически. Выходной слой — федеративный: независимые Core-ноды участников. Слои масштабируются независимо друг от друга, количество входных и выходных узлов не связано.

Решает: жесткую топологию, проблемы при масштабировании.

Нулевое доверие между узлами.

Узлы не доверяют друг другу по умолчанию. Все взаимодействие — только через взаимную аутентификацию по сертификатам (mTLS). Управляющий кластер — единственный удостоверяющий центр. Никакого SSH-доверия, никаких доверенных сетей между нодами ядра.

Решает: горизонтальное распространение при компрометации одного узла.

Один домен на кластер.

Входной слой живет под единым доменом ячейки. Выходной — тоже. Entry-под при подключении к Core-ноде указывает SNI ячейки — независимо от реального IP конкретного сервера. Для наблюдателя весь трафик ячейки выглядит как взаимодействие с одним сервисом.

Решает: дефицит доменных имен; один домен теперь обслуживает произвольное количество узлов.

Subscription вместо статической конфигурации.

Клиент держит не конкретный адрес, а URL подписки. При обновлении — приложение само забирает актуальную конфигурацию. Смена узла, ротация gRPC-пути, переключение ячейки — для пользователя все это происходит прозрачно.

Решает: операционную нагрузку при ротации узлов и конфигураций.

Онбординг через Docker.

Участник устанавливает Docker, получает одноразовый join-токен, запускает контейнер. Контейнер генерирует ключевую пару локально, отправляет CSR на управляющий кластер, получает сертификат и регистрируется в сети. Приватный ключ не покидает сервер участника. SSH-доступа к серверу не передается.

Решает: операционную сложность онбординга; подключение новых участников — через вступительный токен и запуск контейнера.

Выделенный управляющий кластер.

Отдельный независимый кластер управляющих серверов с Kubernetes control plane, HA etcd и Root CA — отдельно от плоскости данных. Здесь же сервисная инфраструктура: Telegram-бот, резервное копирование, subscription-сервис. Управляющий кластер не участвует в передаче трафика — только управление и PKI.

Решает: единую точку отказа; отказоустойчивость за распределенного кластера серверов.


В итоге сеть масштабируется горизонтально в обоих слоях — быстро и без ручных операций. Компрометация узла или целой ячейки — изолированный инцидент, не затрагивающий остальных. Участники восстанавливают инфраструктуру с нуля через тот же механизм онбординга.

Подробная техническая документация Архитектуры 2.0 — в разделе архитектуры.