Виртуальная машина с инфраструктурой
Terraform создаёт виртуальную машину в Proxmox полным клонированием шаблона
Ubuntu 24.04 с VM ID 9000. Ansible устанавливает Docker Engine и Docker
Compose, после чего разворачивает Traefik, Gitea, Prometheus, cAdvisor, Grafana
и Portainer. На этой же VM настраивается WireGuard для безопасного доступа к
локальной сети через VPN.
Базовая настройка VM также включает часовой пояс Europe/Moscow, синхронизацию
времени через Chrony и установку QEMU Guest Agent для взаимодействия гостевой
системы с Proxmox.
Репозиторий не выполняет terraform apply или запуск Ansible автоматически.
Требования
- В шаблоне Proxmox
9000настроен cloud-init, установлен и включён QEMU Guest Agent. - Указанный в Terraform публичный SSH-ключ существует на локальной машине.
- DNS-записи A/AAAA для сервисов указывают на публичный адрес, с которого TCP
80 и 443 перенаправляются на
192.168.1.109. - Публичный TCP-порт 80 доступен Traefik для прохождения Let's Encrypt HTTP-01 challenge.
- Установлены Terraform, Ansible, SOPS и настроен ключ
ageлибо другой поддерживаемый SOPS KMS.
Параметры виртуальной машины по умолчанию:
- 4 vCPU;
- 8 ГБ RAM;
- диск 100 ГБ;
- адрес
192.168.1.109/24.
Перед использованием проверьте
terraform/terraform.tfvars.example, особенно имя узла Proxmox, VM ID новой
машины, gateway, datastore, SSH-пользователя и путь к публичному ключу.
Terraform
Создайте локальный файл с параметрами и передайте API token через переменную окружения:
cp terraform/terraform.tfvars.example terraform/terraform.tfvars
export TF_VAR_proxmox_api_token='user@pam!terraform=TOKEN_SECRET'
make tf-init
make tf-plan
Команда применения намеренно не добавлена в Makefile. После проверки плана её нужно выполнить самостоятельно:
terraform -chdir=terraform apply
Файл terraform.tfvars, state-файлы и сохранённые планы добавлены в
.gitignore. Не сохраняйте API token или Terraform state в Git.
Секреты SOPS
Скопируйте .sops.yaml.example в .sops.yaml и укажите публичный ключ-получатель
age. При использовании KMS настройте соответствующее правило SOPS.
Затем создайте и заполните файл с секретами:
make secrets-init
$EDITOR ansible/inventory/group_vars/all/secrets.sops.yml
make secrets-encrypt
sops ansible/inventory/group_vars/all/secrets.sops.yml
В файле должны быть заданы:
gitea_admin_password— пароль администратора Gitea;gitea_db_password— пароль PostgreSQL для Gitea;grafana_admin_password— пароль администратора Grafana.
Каждый пароль должен содержать не менее 12 символов. Playbook остановится до развёртывания сервисов, если файл отсутствует или пароль не прошёл проверку.
Не добавляйте secrets.sops.yml в Git, пока команда sops filestatus не
подтверждает, что файл зашифрован. Зашифрованный SOPS-файл можно хранить в Git;
secrets.example.yml секретов не содержит.
Ansible
Перед запуском проверьте файл
ansible/inventory/group_vars/all/main.yml:
- замените
letsencrypt_emailна настоящий адрес; - проверьте доменные имена всех сервисов;
- задайте в
admin_allowlistдоверенные CIDR-сети; - при необходимости обновите закреплённые версии образов контейнеров;
- проверьте SSH-пользователя
ansible_user. - при необходимости измените
timezoneи списокntp_pools.
Версии образов закреплены явно и были сверены с официальными registry
12 июля 2026 года. Для Portainer используется стабильная LTS-ветка, а не более
коротко поддерживаемая STS. Образ cAdvisor публикуется в официальном GitHub
Container Registry ghcr.io/google/cadvisor.
Установка зависимостей и запуск:
make ansible-deps
make ansible-check
make ansible-run
make ansible-check выполняет Ansible в режиме --check --diff и не должен
изменять состояние машины. Некоторые Docker-модули могут сообщать о неполной
поддержке check mode, поэтому перед настоящим запуском нужно проверить вывод.
Сеть и открытые порты
Ansible включает UFW с запретом входящих подключений по умолчанию и открывает:
| Порт | Назначение |
|---|---|
22/tcp |
SSH-доступ к виртуальной машине |
80/tcp |
HTTP и Let's Encrypt HTTP-01 challenge |
443/tcp |
HTTPS через Traefik |
2222/tcp |
Git по SSH в Gitea |
51820/udp |
WireGuard VPN |
Web-интерфейс Gitea и встроенный OCI/package registry доступны через
https://git.4vkamnyam.ru. Для Git по SSH используется порт 2222.
Самостоятельная регистрация в Gitea отключена. Новых пользователей создаёт
только администратор через Site Administration → User Accounts. Анонимный
просмотр /explore, репозиториев и API также запрещён: неавторизованные
пользователи перенаправляются на страницу входа.
Traefik Dashboard, Grafana, Prometheus и Portainer доступны только с адресов из
admin_allowlist. По умолчанию разрешена локальная сеть 192.168.1.0/24.
При первом открытии Portainer предложит создать локального администратора.
WireGuard VPN
WireGuard работает на infra-VM, Proxmox-хост при этом остаётся без дополнительных пакетов и сетевых настроек. Используется следующая схема:
- публичный endpoint:
78.138.184.244:51820; - VPN-подсеть:
10.20.0.0/24; - адрес сервера в VPN:
10.20.0.1; - доступная через VPN локальная сеть:
192.168.1.0/24; - режим split tunnel — обычный интернет клиента не проходит через VPN.
На роутере необходимо создать перенаправление порта:
UDP 51820 → 192.168.1.109:51820
Роль включает IPv4 forwarding и NAT между 10.20.0.0/24 и
192.168.1.0/24. Поэтому добавлять обратный статический маршрут на домашнем
роутере не требуется. Устройства локальной сети будут видеть VPN-подключения
как трафик от 192.168.1.109.
Первый запуск
При первом запуске роль создаёт приватный ключ сервера непосредственно в
/etc/wireguard/server_private.key. Файл имеет права 0600, его нельзя
копировать в репозиторий или передавать клиентам. Публичный ключ выводится в
результате запуска Ansible и сохраняется здесь:
ssh alexander@192.168.1.109 \
'sudo cat /etc/wireguard/server_public.key'
Запустить только роль WireGuard после общего развёртывания можно командой:
cd ansible
ansible-playbook site.yml --tags wireguard
Обратите внимание: pre-tasks с загрузкой SOPS-секретов выполняются и при запуске
по тегу, поэтому зашифрованный secrets.sops.yml всё равно должен существовать.
Создание ключей клиента
Ключи каждого клиента создаются на его устройстве. На Linux или macOS с установленным WireGuard:
umask 077
wg genkey | tee client-private.key | wg pubkey > client-public.key
Содержимое client-private.key остаётся только у владельца устройства.
Содержимое client-public.key добавляется в
ansible/inventory/group_vars/all/main.yml:
wireguard_peers:
- name: alexander-laptop
address: 10.20.0.10/32
public_key: "ПУБЛИЧНЫЙ_КЛЮЧ_КЛИЕНТА"
- name: user-1
address: 10.20.0.11/32
public_key: "ПУБЛИЧНЫЙ_КЛЮЧ_КЛИЕНТА"
Адреса клиентов должны быть уникальными, иметь маску /32 и находиться в
подсети 10.20.0.0/24.
После добавления или удаления клиента повторно запустите роль WireGuard. Ansible пересоберёт конфигурацию и перезапустит интерфейс.
Конфигурация клиента
[Interface]
PrivateKey = ПРИВАТНЫЙ_КЛЮЧ_КЛИЕНТА
Address = 10.20.0.10/32
[Peer]
PublicKey = ПУБЛИЧНЫЙ_КЛЮЧ_СЕРВЕРА
Endpoint = 78.138.184.244:51820
AllowedIPs = 10.20.0.0/24, 192.168.1.0/24
PersistentKeepalive = 25
После подключения должны быть доступны, например:
https://192.168.1.81:8006 # Proxmox
https://192.168.1.109 # infra-VM
Порт Proxmox 8006 публиковать в интернет не нужно.
Динамические маршруты Traefik
Traefik следит за каталогом /opt/stacks/traefik/dynamic на виртуальной машине.
Роль Ansible создаёт в нём security.yml с общими middleware.
В этот каталог можно добавлять отдельные YAML-файлы с динамическими:
- routers;
- middlewares;
- services;
- TLS options.
Traefik обнаруживает изменения автоматически, перезапуск контейнера не нужен. Для добавляемых вручную маршрутов используйте отдельные имена файлов — тогда Ansible не перезапишет их при следующем запуске.
Адреса сервисов
- Gitea и registry:
https://git.4vkamnyam.ru - Traefik Dashboard:
https://traefik.4vkamnyam.ru - Grafana:
https://grafana.4vkamnyam.ru - Prometheus:
https://prometheus.4vkamnyam.ru - Portainer:
https://portainer.4vkamnyam.ru