Да, я знаю. «Опять статья про WireGuard». Но это не очередное «how-to» на 5 строк. Это — история боли, паранойи и, наконец, автоматизации. Это история о том, как мы перестали «обслуживать» VPN и заставили его работать на нас.

Привет, Хабр! Меня зовут... впрочем, неважно. Важно то, что я 10 лет занимаюсь тем, что «делаю, чтобы работало». И последние лет 8 из них я, как и вы, страдал.

Мое утро начиналось не с кофе, а с тикета в Jira:

  • «У меня отвалился VPN, не могу подключиться» (протух сертификат)

  • «Почему так медленно открывается...» (привет, TCP-over-TCP)

  • «Я не могу выпустить сертификат для нового сотрудника» (опять сломали easy-rsa)

Мы все через это проходили. OpenVPN — это как старый-добрый ВАЗ-2106. Он едет, он легенда, и у каждого админа в гараже есть ящик запчастей для него (PKI, скрипты отзыва, кастомные push route). Но в 2025 году ездить на этом каждый день — больно.

В какой-то момент мы просто сели и посчитали, сколько человеко-часов уходит на обслуживание VPN-инфраструктуры. И прослезились. Пришло время перемен.

Наша "Боль", или "Классика", которая устала

Проблема была не в том, что OpenVPN плохой. Он просто... сложный. И громоздкий.

  1. Управление PKI (Инфраструктура открытых ключей): Это главный монстр. Управление центром сертификации (CA), выпуск ключей для ка��дого пользователя, отзыв скомпрометированных ключей (CRL), отслеживание сроков действия... Это отдельная, полноценная работа.

  2. Производительность: OpenVPN работает в userspace. Это оверхед. На шифрование тратится много CPU. При плохом канале (особенно мобильном) постоянные переподключения превращали жизнь «удаленщиков» в ад.

  3. Кодовая база: Сотни тысяч строк кода. С точки зрения «здоровой паранойи» (а я фанат ИБ), это гигантская поверхность для атаки. Чем больше кода — тем больше багов.

  4. Конфигурация: Гибкость 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) — нет.

  • Лечение: Две причины:

    1. Вы забыли net.ipv4.ip_forward=1 и sudo sysctl -p.

    2. Ваши 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.

    1. Конфиг /etc/wireguard/wg0.conf превратился в Ansible-шаблон (wg0.conf.j2).

    2. Информация о "пирах" (клиентах) хранится в 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"
    3. Ansible-шаблон просто пробегается по этому списку циклом:

      Django

      ...
      # --- ПИРЫ ---
      {% for peer in wireguard_peers %}
      [Peer]
      # {{ peer.name }}
      PublicKey = {{ peer.public_key }}
      AllowedIPs = {{ peer.ip }}
      {% endfor %}
    4. Процесс для нового сотрудника:

      • Он сам генерирует у себя пару ключей.

      • Создает Merge Request в наш репозиторий, добавляя свой public_key и имя в YAML.

      • Старший инженер ревьюит MR, жмет "Merge".

      • CI/CD-пайплайн запускает Ansible-playbook, который раскатывает новый конфиг на VPN-сервер и бесшовно перезагружает его (wg syncconf wg0).

ВСЁ. Мы получили GitOps-управляемый VPN. Админы больше не занимаются ключами, они просто аппрувят MR.

Заключение: Что мы получили?

  1. Скорость: Подключение к VPN занимает 0.1 секунды. Не 10-30 секунд, как с OpenVPN.

  2. Надежность: Связь не рвется при смене сети (с Wi-Fi на LTE).

  3. Безопасность: Минимальная кодовая база и простая криптография.

  4. Прозрачность: Вся конфигурация VPN-клиентов лежит в Git. Мы видим, кто и когда получил доступ.

  5. Свободное время: Самое главное. У нас больше нет тикетов «VPN не работает». Мы занимаемся делом, а не «обслуживанием».

Так что да, если вы еще сидите на OpenVPN и страдаете — хватит это терпеть. Путь займет один вечер, а сэкономит сотни часов в будущем.