cloud-init: автонастройка VPN при создании сервера
Обычный сценарий: вы создаёте VPS, ждёте загрузки, заходите по SSH, ставите WireGuard или OpenVPN, генерируете ключи, правите конфиг, включаете форвардинг — и только через 10-15 минут ручной работы сервер готов раздавать VPN. Если серверы нужны регулярно (тестовые окружения, серверы под конкретную поездку, замена заблокированного IP), эта рутина утомляет и допускает ошибки от невнимательности. cloud-init убирает этот шаг: вы описываете конфигурацию один раз в user-data, и VPN поднимается сам в первые секунды жизни сервера — ещё до того, как вы успеете открыть терминал.
Содержание
Что такое cloud-init и что происходит при первом старте VM
cloud-init — это сервис, который есть почти во всех облачных образах Ubuntu, Debian, AlmaLinux и других дистрибутивов. При создании сервера через панель управления или API провайдер передаёт в VM текстовый блок — user-data. cloud-init читает его на самом первом старте и выполняет описанные там действия: устанавливает пакеты, создаёт файлы, запускает команды.
Важная деталь: user-data выполняется один раз — при первой загрузке инстанса, определяемой по уникальному instance-id. Перезагрузка сервера cloud-init заново не запускает. Этим он отличается от systemd-юнита или крон-задачи: не про постоянную работу, а про начальную инициализацию — ровно то, что нужно для разового поднятия VPN при создании сервера.
Формат user-data определяется первой строкой файла. Самый частый и удобный для VPN-задач — #cloud-config, YAML-подобный декларативный формат с модулями packages, write_files, runcmd. Альтернатива — обычный shell-скрипт с шебангом #!/bin/bash, который просто выполняется как есть. Для сложных сценариев их можно объединить через multi-part MIME, но для одного VPN-сервера хватает одного #cloud-config.
Какой VPS выбрать для собственного VPN
Для личного VPN на телефоне, компьютере и домашних устройствах начните с Lite: 1 vCPU, 2 ГБ памяти и 40 ГБ NVMe. Это стартовый вариант для небольшого числа одновременно подключённых устройств. Если VPN будет обслуживать команду и другие приложения, рассмотрите Старт с 2 vCPU и 4 ГБ памяти.
Для личного VPN
Lite · 1 vCPU · 2 ГБ RAM
40 ГБ NVMe
Начальный тариф для своего VPN-сервера.
Для VPN и дополнительных сервисов
Старт · 2 vCPU · 4 ГБ RAM
80 ГБ NVMe
Больше памяти для нескольких приложений на одном сервере.
Аренда VPS для VPN: после заказа переходите к настройке по этой инструкции. Скорость зависит также от канала и маршрута до сервера.
В кнопках заказа выбрана локация в Великобритании. Другую страну можно выбрать при оформлении.
Оплата VPS картой РФ или по СБП доступна в личном кабинете. Для безналичной оплаты от юридического лица или ИП запросите счёт у support@maatrix.io, указав тариф и реквизиты.
Как передать user-data серверу
У большинства провайдеров VPS есть поле User Data прямо в форме создания сервера в панели — обычно на шаге выбора образа или в разделе «Advanced options». Туда вставляется содержимое файла целиком.
Через CLI или API это тоже одна опция:
# пример для облака с cloud-init API (синтаксис условный,
# у каждого провайдера свои названия параметров)
curl -X POST https://api.example-cloud.com/v1/servers \
-H "Authorization: Bearer $API_TOKEN" \
-d image=ubuntu-24-04 \
-d size=2vcpu-4gb \
-d region=fra1 \
--data-urlencode "user_data@cloud-init.yaml"
Если вы уже используете Terraform для создания VPS, user-data передаётся через параметр ресурса (обычно называется user_data) с функцией templatefile(), которая подставляет в шаблон реальные значения — например, публичный ключ клиента — перед отправкой:
resource "cloud_server" "vpn" {
name = "vpn-01"
image = "ubuntu-24-04"
size = "1vcpu-1gb"
region = "fra1"
user_data = templatefile("${path.module}/cloud-init-wg.yaml.tpl", {
server_private_key = var.wg_server_private_key
client_public_key = var.wg_client_public_key
})
}
Так весь процесс — от terraform apply до готового поднятого туннеля — укладывается в одну команду без единого ручного захода на сервер.
Готовый user-data для WireGuard
Ключи WireGuard лучше сгенерировать заранее на своей машине (wg genkey, wg pubkey), а не пытаться делать это внутри cloud-init — так проще контролировать, какой ключ куда попадает, и не завязываться на порядок выполнения модулей. Вот рабочий #cloud-config, который ставит WireGuard и поднимает интерфейс сразу после установки пакетов:
#cloud-config
package_update: true
packages:
- wireguard
- wireguard-tools
write_files:
- path: /etc/wireguard/wg0.conf
permissions: '0600'
owner: root:root
content: |
[Interface]
PrivateKey = SERVER_PRIVATE_KEY_HERE
Address = 10.10.0.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = CLIENT_PUBLIC_KEY_HERE
AllowedIPs = 10.10.0.2/32
runcmd:
- sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf
- sysctl -p
- systemctl enable wg-quick@wg0
- systemctl start wg-quick@wg0
Пара нюансов, которые часто ломают этот сценарий на практике:
- Имя внешнего интерфейса в
PostUp/PostDown(eth0) нужно проверить под конкретный образ — у части провайдеров этоens3илиenp1s0. Если ошибиться,wg-quickне упадёт, но NAT работать не будет, а трафик из туннеля наружу не пойдёт. - Модуль
runcmdвыполняется послеwrite_filesиpackages, порядок в файле cloud-config гарантирован — можно не бояться, что сервис попытается стартовать раньше, чем появится конфиг. - Если вместо
sedвы предпочитаете системный способ, вариант чище — положитьnet.ipv4.ip_forward=1отдельным файлом в/etc/sysctl.d/99-wireguard.confчерез тот жеwrite_files.
Это тот же сервер, который получился бы после ручной настройки по шагам из статьи про установку WireGuard на Ubuntu 24.04 — просто без вашего участия и без риска что-то забыть на середине.
Вариант для OpenVPN: осторожнее с PKI
С OpenVPN автоматизация первого старта сложнее, потому что классическая схема требует полноценного центра сертификации (easy-rsa): генерация CA, серверного и клиентских сертификатов. Генерировать CA прямо в cloud-init — плохая идея: приватный ключ CA окажется в user-data в открытом виде, а user-data часто остаётся доступным через API/метаданные сервера ещё долго после создания.
Практичнее два подхода. Первый — сгенерировать PKI заранее (например, локально или на отдельном защищённом сервере) и передать через cloud-init уже готовые серверный сертификат, ключ и dh.pem:
#cloud-config
package_update: true
packages:
- openvpn
write_files:
- path: /etc/openvpn/server.conf
content: |
port 1194
proto udp
dev tun
ca /etc/openvpn/ca.crt
cert /etc/openvpn/server.crt
key /etc/openvpn/server.key
dh /etc/openvpn/dh.pem
server 10.20.0.0 255.255.255.0
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 1.1.1.1"
keepalive 10 120
persist-key
persist-tun
- path: /etc/openvpn/ca.crt
content: |
-----BEGIN CERTIFICATE-----
...ваш CA-сертификат...
-----END CERTIFICATE-----
- path: /etc/openvpn/server.key
permissions: '0600'
content: |
-----BEGIN PRIVATE KEY-----
...приватный ключ сервера...
-----END PRIVATE KEY-----
runcmd:
- sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf
- sysctl -p
- iptables -t nat -A POSTROUTING -s 10.20.0.0/24 -o eth0 -j MASQUERADE
- systemctl enable openvpn@server
- systemctl start openvpn@server
Блок server.crt в примере опущен для краткости — в реальном файле он идёт тем же write_files-блоком, что и ca.crt. Второй подход — вообще не класть сертификаты в user-data, а скачать их при загрузке с защищённого эндпоинта по одноразовому токену (об этом ниже). Для полноценной установки с нуля, если автоматизация не нужна с первой секунды, разумнее ориентироваться на пошаговую установку OpenVPN на Ubuntu 24.04 и переносить в cloud-init только те шаги, которые действительно повторяются от сервера к серверу.
Ключи и секреты: чего не стоит класть в user-data напрямую
user-data — не защищённое хранилище секретов. На большинстве провайдеров его содержимое доступно через API аккаунта, а изнутри самой VM — через сервис метаданных (curl http://169.254.169.254/...) без какой-либо авторизации, кроме факта, что запрос идёт с самого сервера. Для приватного ключа WireGuard одного личного VPN-сервера это приемлемый риск: утечка требует либо доступа к панели, либо root на самой VM, которые и так критичны. Но масштабировать подход на что-то чувствительнее — CA-ключ, сертификаты для десятков клиентов — не стоит.
Более безопасная схема — не embedding, а fetch-at-boot: user-data не содержит секрет, а только команду на его получение с одноразовым или короткоживущим токеном:
#cloud-config
packages:
- wireguard
runcmd:
- curl -sSf -H "Authorization: Bearer ${ONE_TIME_TOKEN}" \
https://secrets.example.internal/vpn/wg0.conf \
-o /etc/wireguard/wg0.conf
- chmod 600 /etc/wireguard/wg0.conf
- systemctl enable --now wg-quick@wg0
Токен в этом случае живёт ровно до первого использования (сервер секретов его отзывает), а сам приватный ключ никогда не сохраняется в логах API-запроса на создание сервера. Для одного домашнего или рабочего VPN-сервера это избыточно, но если вы автоматизируете создание серверов регулярно и через общий CI/CD — это уже разумная гигиена, а не паранойя.
Проверка и отладка, если VPN не поднялся
Первое, что стоит сделать после создания сервера — не пытаться сразу подключиться клиентом, а проверить, что сам cloud-init отработал без ошибок:
cloud-init status --long
Статус done без ошибок в поле errors означает, что все модули завершились штатно. Если что-то пошло не так, детальный лог выполнения — в двух местах:
cat /var/log/cloud-init-output.log # stdout/stderr всех команд из runcmd
cat /var/log/cloud-init.log # подробный лог самого cloud-init построчно
Типичные причины, по которым VPN не поднимается, несмотря на «зелёный» статус cloud-init:
- Опечатка в имени внешнего интерфейса в
PostUp/PostDown— сервис стартует, но трафик не маршрутизируется. - IP-forwarding не применился, потому что
sysctl -pвыполнился раньше, чем изменения записались в файл (редко, но случается при нестандартном порядке модулей) — проверяется черезsysctl net.ipv4.ip_forward, должно быть1. - Провайдер режет исходящий UDP на нестандартный порт по умолчанию — стоит явно проверить правила firewall/security group на стороне панели, cloud-init их не трогает.
- В
write_filesконфиг записался с неверными правами доступа, и сервис отказался стартовать из-за слишком открытого файла с приватным ключом — WireGuard и OpenVPN оба чувствительны к правам600на файлы с ключами.
Для отладки шаблона на уже существующей машине можно принудительно прогнать модули заново без пересоздания сервера:
sudo cloud-init clean --logs
sudo cloud-init init
sudo cloud-init modules --mode=config
sudo cloud-init modules --mode=final
Это не полный аналог первого старта (часть вещей привязана к первой загрузке ядра), но для отладки write_files и runcmd достаточно — и заметно быстрее, чем пересоздавать VM на каждую правку YAML.
Частые вопросы
Все ли провайдеры VPS поддерживают user-data через cloud-init?
Практически все крупные площадки — да, для образов на базе Ubuntu, Debian, AlmaLinux, Rocky Linux. Поддержка обычно не заявлена явно как «фича VPN», а идёт как общая возможность облачных образов — ищите поле «User Data» или «Cloud-init» на шаге создания сервера или в разделе Advanced.
Что если нужен не Ubuntu/Debian, а другой дистрибутив?
cloud-init есть и в облачных образах CentOS/AlmaLinux/Rocky, но набор доступных модулей и путей к пакетным менеджерам отличается — например, вместо apt нужен dnf, и в некоторых сборках cloud-init предустановлен не по умолчанию. Перед переносом скрипта на другой дистрибутив стоит проверить cloud-init --version и доступность нужных модулей.
Безопасно ли класть приватный ключ WireGuard прямо в user-data, как в примере?
Для личного или рабочего VPN-сервера — приемлемый компромисс: риск ограничен доступом к панели провайдера и root-доступом на самой VM, которые и так критичны для безопасности. Для чувствительных сценариев (CA-ключи, десятки клиентов) лучше схема с получением секрета по токену при загрузке, описанная выше.
Можно ли использовать один и тот же user-data для нескольких серверов?
Технически да, но для WireGuard и OpenVPN так делать не стоит — одинаковый приватный ключ или общий CA без разделения по клиентским сертификатам ломает саму модель безопасности VPN. Используйте шаблонизацию (Terraform templatefile или подстановку переменных перед отправкой) даже для похожих серверов.
cloud-init выполнится повторно, если развернуть сервер из snapshot?
Зависит от того, как провайдер обрабатывает instance-id при клонировании: часто новый сервер получает новый ID и user-data выполнится заново, но иногда ID сохраняется, и cloud-init решает, что уже всё сделал. Стоит проверить один раз на конкретном провайдере, прежде чем полагаться на снапшоты как способ клонирования VPN-серверов.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Готовы перейти к настройке?
Закажите Lite с конфигурацией, предложенной для старта, или сравните все тарифы. Оплата картой РФ и через СБП — в личном кабинете.
Заказать Lite Все тарифы и цены