Привет! Меня зовут Даниил Донецков, я DevOps-инженер в KTS.
Аутсорс-разработка цифровых продуктов — одно из ключевых направлений нашей деятельности. Для доступа в собственные внутренние контуры и защищенные среды клиентов нашим сотрудникам приходилось каждый раз использовать разные связки логинов и паролей. С ростом числа клиентов это становилось неудобным, и перед нами встала непростая задача — обеспечить единую точку входа в разные среды.
Мы решили проблему с помощью сервиса Firezone, и в этой статье я хочу поделиться нашим опытом. Сегодня я расскажу о том, как DevOps-юнит KTS:
внедрил виртуальную сеть в существующую инфраструктуру, состоящую из двух k8s-кластеров и нескольких ВМ в разных облаках;
обеспечил бесперебойный доступ для более чем 150 сотрудников к веб-сервисам.
Оглавление
Почему Firezone
Наш выбор пал на Firezone по нескольким причинам.
Сервис имеет открытый исходный код. Он активно поддерживается, регулярно обновляется и улучшается.
Изначально Firezone спроектирован для горизонтального масштабирования. Следовательно, при работе с ним можно просто поднять новый сервер и запустить на нем дополнительный компонент частной сети.
Основан на протоколе WireGuard, что гарантирует скорость и безопасность.
Управляется и настраивается из веб-интерфейса, поэтому для настройки не приходится вручную менять конфиги на серверах.
Можно закрыть сеть от сторонних пользователей через интеграцию с сервисом Keycloak.
Можно включать по доменам. Если домен переедет, Firezone сам позаботится о том, что вы получите доступ к ресурсу.
Примечание: в документации Yandex Cloud есть инструкция по установке Firezone, но относится она к версиям Firezone 0.7.*. Мы же поговорим о том, как развернуть версии 1.*.*, у которых есть заметные преимущества (о них ниже).
Сборка
Сборку мы использовали из репозитория разработчиков на GitHub, но немного переделали ее под свои нужды.
По сути, мы допиливали только «голову» — API, Domain и Web. В Web мы исправляли скрипты для деплоя сетевых компонентов (Relay и Gateway, о них поговорим отдельно), фиксировали версию и игрались с параметрами.
Для самих сетевых компонентов мы решили не переделывать Docker-образы, поскольку для наших целей хватает и общедоступных.
Подготовка Helm Chart
В своем репозитории разработчики для тестирования запустили голову через Docker-Compose. Так же они форкнули репозиторий с чартом, но не поддерживают и не актуализируют его.
Мы же форкнули оригинальный репозиторий и добавили в него:
CronJob для вытягивания пользователей из Kecyloak: голова Firezone не умеет самостоятельно синхронизировать пользователей с сервисами аутентификации, и для решения этой проблемы мы добавили в наш Helm chart кронджобу, которая каждый час синхронизирует пользователей, добавляя их в основную группу;
джобу для инициализации организации, поскольку по умолчанию в Firezone подразумевается подключение внутрь контейнера и выполнение всех команд вручную на языке Elixir.
Запуск в Kubernetes
Мы запускаем Firezone в k8s через Terraform. Весь процесс инициализации занимает меньше часа, после чего Firezone уже готов к работе.
Вся прелесть версий 1.*.* в том, что голова (в документации называемая «Portal») не обязана быть развернута на сервере, через который будет выполняться подключение к сервисам. Таким образом, обслуживать голову становится заметно легче.
Инициализация организации
Для инициализации организации нужно выполнить следующие действия:
Подключиться к поду firezone-web:
kubectl exec -n firezone -it deployment/firezone-web -- bash
Запустить окружения Elixir:
bin/web remote
Создать аккаунт:
{:ok, account} = Domain.Accounts.create_account(%{name: "<ACCOUNT_NAME>", slug: "<ACCOUNT_SLUG>"})
Создать провайдера Keycloak:
{:ok, openid_connect} = Domain.Auth.create_provider(account, %{name: "<PROVIDER_NAME>", adapter: :openid_connect, adapter_config: %{"auto_create_users" => true, "client_id" => "firezone", "client_secret" => "<CLIENT_SECRET>,"response_type" => "code","scope" => "openid email profile offline_access","discovery_document_uri" => "<KEYCLOAK_DOCUMENT_URI>","redirect_uri" => "https://<FIREZONE_HOST>/auth/oidc/<PROVIDER_NAME>/callback/"}})
Создать первого админ-пользователя:
{:ok, actor} = Domain.Actors.create_actor(account, %{type: :account_admin_user, name: "<ADMIN NAME>"})
Создать идентификатор для первого админ-пользователя:
{:ok, identity} = Domain.Auth.upsert_identity(actor, openid_connect, %{provider_identifier: "<ADMIN_EMAIL>", provider_identifier_confirmation: "<ADMIN_EMAIL>"})
Разблокировать расширенную функциональность:
account |> Ecto.Changeset.change(features: %{flow_activities: true,policy_conditions: true,multi_site_resources: true,traffic_filters: true,self_hosted_relays: true,idp_sync: true,rest_api: true})|> Domain.Repo.update!()
После этого вы сможете войти в ваш аккаунт:
Авторизация через Keycloak будет выглядеть следующим образом:
Добавление ресурсов
Для начала надо создать ресурс Site. Это группа сервисов (сайтов, виртуальных машин, подсетей), в которые будет открыт доступ из конкретных шлюзов.
После создания сайта в интерфейсе откроется его вкладка. В нем можно добавить новые шлюзы и ресурсы (ip-адреса, подсети и домены).
Деплой Gateway-сервисов
Gateway-сервисы, или шлюзы — это двоичные файлы Linux, которые работают в вашей инфраструктуре. Их можно развернуть и в виде контейнеров Docker, и в виде служб systemd через дефолтный скрипт, или же взять минимально рекомендуемые настройки системы для запуска шлюза и сделать свой вариант запуска.
Шлюзы были разработаны как переносимые, чрезвычайно легкие и не требующие внешних зависимостей. Это упрощает их развертывание и управление ими в больших масштабах.
Работают шлюзы без необходимости в постоянном хранилище; вместо этого для их правильного функционирования требуется настроить лишь несколько переменных среды. Дополнительную информацию об их деплое см. в руководстве по развертыванию шлюзов.
Firezone предоставляет 4 готовых варианта развертывания шлюзов — через systemd, Docker, Terraform и кастомный. По сути это вкладки с готовым кодом, который можно просто скопировать и выполнить на нужной машине. Этот готовый код пришлось немного допилить, чтобы команды работали сразу, и любой сотрудник с необходимым доступом смог бы создать или масштабировать систему. В результате получилось довольно удобно.
Чаще всего мы пользуемся systemd. Надо признать, что это не самый удобный способ, поскольку при ошибке в запуске приходится вытаскивать скрипт, дебажить его и запускать корректно. Однако основная масса шлюзов у нас развернута именно через systemd, и работают они стабильно и без перебоев.
При этом помимо перечисленных способов есть еще один, неочевидный — деплой через Helm chart. В стандартных вариантах деплоя его нет, но Helm chart для Gateway можно найти на GitHub. Gateway-сервис, развернутый в k8s-кластере, позволяет обращаться к сервисам внутри Kubernetes по DNS кластера (к примеру, postgres.database.svc.cluster.local).
Комбинируя политики групп и разрешенные ресурсы, можно управлять доступом пользователей. Например:
пользователям группы DataScience доступны ресурсы **.database.svc.cluster.local (все сервисы в неймспейсе Database);
пользователям группы KubernetesAdmins доступны ресурсы **.svc.cluster.local (все сервисы в Kubernetes).
Деплой Relay-сервисов (отдельные STUN-серверы)
Relay (ретрансляторы) помогают клиентам устанавливать прямые соединения со шлюзами, используя метод обхода NAT, стандартизированный как STUN. Это хорошо работает в подавляющем большинстве случаев.
Однако иногда прямая связь не может быть установлена. Это может произойти по разным причинам, чаще всего — по одной из следующих:
клиент или шлюз находятся за особенно строгим брандмауэром, который иногда называют симметричным NAT. Такие брандмауэры чаще встречаются в корпоративных средах, но в последние годы их использование сокращается;
сетевая среда клиента или шлюза блокирует трафик WireGuard. Это случается редко, но может произойти в некоторых общедоступных сетях Wi-Fi и даже в некоторых странах.
В этих случаях Relay выступает в качестве посредника, реализуя протокол TURN для надежной передачи трафика между клиентом и шлюзом независимо от любых сетевых ограничений. Весь трафик в Firezone полностью зашифрован с помощью WireGuard.
Ретрансляторы не имеют возможности расшифровывать или иным образом изменять передаваемые данные. Они распределены в нескольких регионах, чтобы обеспечить низкую задержку и надежную связь, где бы ни находились сотрудники. К сожалению, и здесь не обошлось без ложки дегтя — поддержка systemd в оригинальном репозитории не выполняется уже давно, и скрипты в нем не работают.
Однако коммьюнити не дремлет: к примеру, тут можно найти инструкцию для поднятия Relay через systemd. Сразу оговорюсь: запуск двух ретрансляторов на одном сервере — подход сомнительный и нестабильный, они валятся с ошибкой занятого адреса. Лучший вариант — запускать по одному Relay-сервису на одной машине, и машин должно быть как минимум две.
Мы же запустили ретрансляторы в YC с помощью Terraform.
Закрытие ресурсов от Ethernet
Посмотрим на примерах, как закрыть ресурсы наших сервисов от общего доступа.
В k8s — отдельный ingress для админской консоли поднимается со следующей анотацией:
nginx.ingress.kubernetes.io/whitelist-source-range: "{gateway_ip}"
Для сервисов на ВМ с доступом через nginx:
Location /admin {
allow {gateway_ip};
deny all;
}
Заключение
Несмотря на некоторые пляски с бубном в процессе, в итоге мы получили рабочее решение и смогли обеспечить нашим сотрудникам стабильный доступ к защищенным ресурсам под одними кредами. Отдельно Firezone радует удобным управлением доступами и простым деплоем новых сетевых компонентов.
Кстати, у коммьюнити Firezone есть активный Discord-канал. Сам я неоднократно писал туда при возникновении проблем с деплоем, поддержкой и обслуживанием сервиса. В канале можно задать любой вопрос. Особенно приятно, что быстро отвечают не только активные пользователи, но и разработчики сервиса.
Надеюсь, наш опыт будет вам полезен. Если у вас возникнут вопросы — задавайте их в комментариях, буду рад обсудить.
Также рекомендую почитать статьи моих коллег для DevOps-инженеров:
JupyterHub на стероидах: реализация KubeFlow фич без масштабных интеграций
Поднимаем динамические окружения для stateless- и stateful-сервисов
И, наконец, напоминаю о том, что мы регулярно проводим DevOps-челленджи и награждаем победителей крутым мерчом. Доступ к архивным челленджам всегда открыт, так что вы можете начать готовиться к будущим испытаниям уже сейчас. Переходите в бота по ссылке, и он расскажет вам подробности.
Удачи!
chemtech
Рассматривали ли NetBird?