Да, я знаю. «Опять статья про WireGuard». Но это не очередное «how-to» на 5 строк. Это — история боли, паранойи и, наконец, автоматизации. Это история о том, как мы перестали «обслуживать» VPN и заставили его работать на нас.
Привет, Хабр! Меня зовут... впрочем, неважно. Важно то, что я 10 лет занимаюсь тем, что «делаю, чтобы работало». И последние лет 8 из них я, как и вы, страдал.
Мое утро начиналось не с кофе, а с тикета в Jira:
«У меня отвалился VPN, не могу подключиться» (протух сертификат)
«Почему так медленно открывается...» (привет, TCP-over-TCP)
«Я не могу выпустить сертификат для нового сотрудника» (опять сломали
easy-rsa)
Мы все через это проходили. OpenVPN — это как старый-добрый ВАЗ-2106. Он едет, он легенда, и у каждого админа в гараже есть ящик запчастей для него (PKI, скрипты отзыва, кастомные push route). Но в 2025 году ездить на этом каждый день — больно.
В какой-то момент мы просто сели и посчитали, сколько человеко-часов уходит на обслуживание VPN-инфраструктуры. И прослезились. Пришло время перемен.
Наша "Боль", или "Классика", которая устала
Проблема была не в том, что OpenVPN плохой. Он просто... сложный. И громоздкий.
Управление PKI (Инфраструктура открытых ключей): Это главный монстр. Управление центром сертификации (CA), выпуск ключей для ка��дого пользователя, отзыв скомпрометированных ключей (CRL), отслеживание сроков действия... Это отдельная, полноценная работа.
Производительность: OpenVPN работает в userspace. Это оверхед. На шифрование тратится много CPU. При плохом канале (особенно мобильном) постоянные переподключения превращали жизнь «удаленщиков» в ад.
Кодовая база: Сотни тысяч строк кода. С точки зрения «здоровой паранойи» (а я фанат ИБ), это гигантская поверхность для атаки. Чем больше кода — тем больше багов.
Конфигурация: Гибкость OpenVPN — это и его проклятие. Файл конфига на 200 строк с
push,pull,tls-authи кастомными скриптами — это нормально. Но поддерживать это? Увольте.
Поиски "Серебряной пули"
Мы, инженеры, ленивы (в хорошем смысле). Нам нужен инструмент, который «один раз настроил и забыл».
Вариант А: Остаться на OpenVPN, но автоматизировать?
Почему нет: Это как пытаться прикрутить турбонаддув и ABS к тому самому ВАЗ-2106. Можно, но зачем? Мы автоматизируем не инструмент, а решение проблемы. Проблема сложности PKI никуда не денется.
Вариант Б: IPsec (StrongSwan/Libreswan)
Почему нет: Кто хоть раз дебажил IPsec «в поле», тот в цирке не смеется. Конфиги еще более зубодробительные, а несовместимость реализаций между вендорами — классика жанра.
Вариант В: Коммерческие "Zero Trust" решения (Tailscale, Zscaler, etc.)
Почему нет: Tailscale — прекрасен. Но он построен на... WireGuard. И он требует доверия к стороннему «control plane». Как админ-параноик, я хочу, чтобы ключи от моего «прода» лежали у меня в сейфе, а не в облаке у «дяди». Мы хотим свой VPN, полностью подконтрольный.
Вариант Г: WireGuard
Что это? Современный, быстрый, минималистичный VPN.
Ключевая фишка 1: Он работает в ядре Linux. Это делает его дьявольски быстрым.
Ключевая фишка 2: Никакого PKI. Вообще. Вся аутентификация построена на обмене простыми публичными ключами (как в SSH).
Ключевая фишка 3 (Паранойя ликует): Кодовая база — всего несколько тысяч строк. Аудировать ее можно «за выходные». Атаковать тут почти нечего.
Решено. Берем.
Решение "из окопов": Наш бастион на WireGuard
Наша задача: поднять центральный VPN-сервер (бастион), через который админы и DevOps-инженеры получают доступ во внутреннюю сеть (скажем, 10.10.0.0/16), где крутятся наши GitLab, Jenkins и тестовые стенды.
Disclaimer: Я фанат Arch... шучу. Берем обычную Ubuntu 22.04 LTS / Debian 12.
Шаг 1: Установка и генерация ключей (Сервер)
WireGuard уже есть в ядре, нужны только утилиты.
Bash
# На сервере
sudo apt update
sudo apt install wireguard -y
# Идем в папку конфигов
cd /etc/wireguard/
# Генерируем ПАРУ ключей: приватный и публичный
wg genkey | tee server_private.key | wg pubkey > server_public.key
# Генерируем ключи для нашего первого клиента (например, моего ноута)
wg genkey | tee client_maksim_private.key | wg pubkey > client_maksim_public.keyШаг 2: Конфиг сервера (/etc/wireguard/wg0.conf)
Это вся конфигурация сервера. ВСЯ.
Ini, TOML
[Interface]
# Приватный IP самого VPN-сервера
Address = 10.200.0.1/24
# Порт, который мы слушаем (UDP)
ListenPort = 51820
# Наш приватный ключ
PrivateKey = <содержимое файла server_private.key>
# Магия, которая "выпускает" клиентов в нашу сеть
# Включаем NAT при старте интерфейса
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# Выключаем NAT при остановке
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# --- ПЕРВЫЙ КЛИЕНТ ---
[Peer]
# Публичный ключ клиента
PublicKey = <содержимое файла client_maksim_public.key>
# IP, который мы ему выдаем в VPN-сети
AllowedIPs = 10.200.0.10/32Шаг 3: Включаем Сервер
Во-первых, разрешим ядру форвардить пакеты. Это самые частые грабли.
Bash
# Редактируем /etc/sysctl.conf
sudo nano /etc/sysctl.confНам нужна строка (раскомментируйте или добавьте): net.ipv4.ip_forward=1
Применяем: sudo sysctl -p
Теперь запускаем наш VPN-интерфейс:
Bash
# Запускаем и добавляем в автозагрузку
sudo systemctl enable --now wg-quick@wg0Проверяем: sudo wg show
Шаг 4: Конфиг клиента (Ноутбук админа)
Создаем файл client_maksim.conf на своем ноутбуке (или телефоне).
Ini, TOML
[Interface]
# IP, который нам "выдал" сервер
Address = 10.200.0.10/24
# Приватный ключ клиента
PrivateKey = <содержимое файла client_maksim_private.key>
# DNS (важно, чтобы работали внутренние имена)
DNS = 10.10.1.1
[Peer]
# Публичный ключ СЕРВЕРА
PublicKey = <содержимое файла server_public.key>
# Внешний IP:Порт нашего сервера
Endpoint = 95.217.XXX.XXX:51820
# САМАЯ ВАЖНАЯ СТРОКА
# Что мы заворачиваем в туннель?
# Вариант "Параноик" (весь трафик):
AllowedIPs = 0.0.0.0/0, ::/0
# Вариант "Split-tunnel" (только наша сеть):
# AllowedIPs = 10.10.0.0/16, 10.200.0.1/24Я, как параноик, предпочитаю 0.0.0.0/0. Так я уверен, что мой ноутбук из кофейни не светит в локальную сеть WiFi, а весь трафик (включая DNS-запросы) идет через наш защищенный бастион.
Клиент (в Windows/macOS/Linux) просто импортирует этот *.conf файл и нажимает "Connect". Все. Подключение — мгновенное.
Наши "Грабли" (Что пошло не так)
Звучит слишком просто? Так и есть. Но дьявол, как всегда, в деталях.
Грабли №1: "Не пингуется!"
Симптом: Клиент подключился (handshake есть), но не пингует даже сервер (10.200.0.1).
Лечение: 99% случаев — файрвол. Вы забыли открыть UDP-порт (в нашем примере
51820) на входе на сервере.sudo ufw allow 51820/udp
Грабли №2: "Сервер пингуется, а 'внутрянка' (10.10.0.0/16) — нет"
Симптом: 10.200.0.1 пингуется, а 10.10.5.12 (Jenkins) — нет.
Лечение: Две причины:
Вы забыли
net.ipv4.ip_forward=1иsudo sysctl -p.Ваши
iptablesправила для NAT (вPostUp) написаны с ошибкой и��и не для того интерфейса (eth0в моем примере, у вас может бытьens3или другой).
Грабли №3: "Все работает, но отваливается за NAT"
Симптом: Клиент (особенно мобильный) в сети с агрессивным NAT (некоторые отели, мобильные операторы) теряет соединение через пару минут простоя.
Лечение: Добавляем в клиентский конфиг (
[Peer]) одну строку:PersistentKeepalive = 25Это заставит клиента раз в 25 секунд слать "heartbeat", поддерживая NAT-трансляцию живой.
Грабли №4 (Самые главные): "Ручное управление ключами — это тот же PKI!"
Симптом: Мы это осознали, когда у нас стало 30+ инженеров. Добавлять каждого вручную (
wg genkey, копировать public, вставлять вwg0.conf, перезапускать...) — это больно. Мы вернулись к тому, с чего начали.Лечение (DevOps-way): Автоматизация. Мы не стали ставить Web-морды (хотя они есть, типа
wg-easy), потому что "мы же админы". Мы завели репозиторий в GitLab.Конфиг
/etc/wireguard/wg0.confпревратился в Ansible-шаблон (wg0.conf.j2).Информация о "пирах" (клиентах) хранится в
group_varsв виде YAML-словаря:YAML
wireguard_peers: - name: "maksim_laptop" public_key: "..." ip: "10.200.0.10/32" - name: "anna_phone" public_key: "..." ip: "10.200.0.11/32"Ansible-шаблон просто пробегается по этому списку циклом:
Django
... # --- ПИРЫ --- {% for peer in wireguard_peers %} [Peer] # {{ peer.name }} PublicKey = {{ peer.public_key }} AllowedIPs = {{ peer.ip }} {% endfor %}Процесс для нового сотрудника:
Он сам генерирует у себя пару ключей.
Создает Merge Request в наш репозиторий, добавляя свой
public_keyи имя в YAML.Старший инженер ревьюит MR, жмет "Merge".
CI/CD-пайплайн запускает Ansible-playbook, который раскатывает новый конфиг на VPN-сервер и бесшовно перезагружает его (
wg syncconf wg0).
ВСЁ. Мы получили GitOps-управляемый VPN. Админы больше не занимаются ключами, они просто аппрувят MR.
Заключение: Что мы получили?
Скорость: Подключение к VPN занимает 0.1 секунды. Не 10-30 секунд, как с OpenVPN.
Надежность: Связь не рвется при смене сети (с Wi-Fi на LTE).
Безопасность: Минимальная кодовая база и простая криптография.
Прозрачность: Вся конфигурация VPN-клиентов лежит в Git. Мы видим, кто и когда получил доступ.
Свободное время: Самое главное. У нас больше нет тикетов «VPN не работает». Мы занимаемся делом, а не «обслуживанием».
Так что да, если вы еще сидите на OpenVPN и страдаете — хватит это терпеть. Путь займет один вечер, а сэкономит сотни часов в будущем.