Найти в Дзене

34 Подготовка облачной инфраструктуры

Все файлы создаются в контексте каталога /home/altlinux/bin, если не сказано иное vim network.tf # Обращаемся к источнику данных, чтобы узнать ID существующего виртуального маршрутизатора
data "openstack_networking_router_v2" "router" {
name = "cloud"
}
# Создаём сеть с именем "INTERNET" в соответствие с топологией
resource "openstack_networking_network_v2" "network" {
name = "INTERNET"
admin_state_up = "true"
}
# Создаём подсеть с именем "INTERNET" в ранее созданной сети с именем "INTERNET"
resource "openstack_networking_subnet_v2" "subnet" {
name = "INTERNET"
network_id = openstack_networking_network_v2.network.id
cidr = "192.168.200.0/24"
ip_version = 4
gateway_ip = "192.168.200.1"
dns_nameservers = [ "77.88.8.8" ]
enable_dhcp = true
allocation_pool {
start = "192.168.200.100"
end = "192.168.200.200"
}
}
# Добавляем в существующий маршрутизатор созданную подсеть с именем "INTERNET", чтобы в даль

Все файлы создаются в контексте каталога /home/altlinux/bin, если не сказано иное

  • Создаём файл network.tf и описываем последовательно сетевую часть для развёртывания данной инфраструктуры с поэтапным запуском и наблюдением созданных ресурсов:

vim network.tf

# Обращаемся к источнику данных, чтобы узнать ID существующего виртуального маршрутизатора
data "openstack_networking_router_v2" "router" {
name = "cloud"
}

# Создаём сеть с именем "INTERNET" в соответствие с топологией
resource "openstack_networking_network_v2" "network" {
name = "INTERNET"
admin_state_up = "true"
}

# Создаём подсеть с именем "INTERNET" в ранее созданной сети с именем "INTERNET"
resource "openstack_networking_subnet_v2" "subnet" {
name = "INTERNET"
network_id = openstack_networking_network_v2.network.id
cidr = "192.168.200.0/24"
ip_version = 4
gateway_ip = "192.168.200.1"
dns_nameservers = [ "77.88.8.8" ]
enable_dhcp = true

allocation_pool {
start = "192.168.200.100"
end = "192.168.200.200"
}
}

# Добавляем в существующий маршрутизатор созданную подсеть с именем "INTERNET", чтобы в дальнейшем ВМ подключённые к данной подсети имели доступ в Интернет
resource "openstack_networking_router_interface_v2" "router_interface" {
router_id = data.openstack_networking_router_v2.router.id
subnet_id = openstack_networking_subnet_v2.subnet.id
}

  • Выполняем проверку синтаксиса и структуры файлов конфигурации Terraform

terraform validate

terraform plan

terraform apply

  • В файл network.tf добавляем следующий код:Создаём порты в сети INTERNET в одноимённой подсети для каждого инстанса и балансировщика нагрузки
    Обращаясь уже к ранее созданным ресурсам, а именно к идентификаторам
    id ресурсов с именемами network и subnet

# Создадим порт для инстанса WebADM
resource "openstack_networking_port_v2" "port_webadm" {
name = "webadm"
network_id = openstack_networking_network_v2.network.id

fixed_ip {
subnet_id = openstack_networking_subnet_v2.subnet.id
ip_address = "192.168.200.20"
}
}

# Создадим порт для инстанса WEB1
resource "openstack_networking_port_v2" "port_web1" {
name = "web1"
network_id = openstack_networking_network_v2.network.id

fixed_ip {
subnet_id = openstack_networking_subnet_v2.subnet.id
ip_address = "192.168.200.21"
}
}

# Создадим порт для инстанса WEB2
resource "openstack_networking_port_v2" "port_web2" {
name = "web2"
network_id = openstack_networking_network_v2.network.id

fixed_ip {
subnet_id = openstack_networking_subnet_v2.subnet.id
ip_address = "192.168.200.22"
}
}

# Создадим порт для балансировщика нагрузки Load Balancer
resource "openstack_networking_port_v2" "port_loadbalancer" {
name = "Load Balancer"
network_id = openstack_networking_network_v2.network.id

fixed_ip {
subnet_id = openstack_networking_subnet_v2.subnet.id
ip_address = "192.168.200.23"
}
}

  • Выполняем проверку синтаксиса и структуры файлов конфигурации Terraform

terraform validate

terraform plan

terraform apply

  • В файл network.tf добавляем следующий код:Создаём плавающие IP-адреса ("публичные") для каждого инстанса и балансировщика нагрузки;
    Плавающие IP-адреса должны браться из "Публичной" сети указав её имя, в данном случае 
    public;

# Создадим плавающий IP для инстанса WebADM
resource "openstack_networking_floatingip_v2" "floatingip_webadm" {
pool = "public"
}

# Создадим плавающий IP для инстанса WEB1
resource "openstack_networking_floatingip_v2" "floatingip_web1" {
pool = "public"
}

# Создадим плавающий IP для инстанса WEB2
resource "openstack_networking_floatingip_v2" "floatingip_web2" {
pool = "public"
}

# Создадим плавающий IP для балансировщика нагрузки Load Balancer
resource "openstack_networking_floatingip_v2" "floatingip_loadbalancer" {
pool = "public"
}

  • Выполняем проверку синтаксиса и структуры файлов конфигурации Terraform

terraform validate

terraform plan

terraform apply

  • В файл network.tf добавляем следующий код:Создавая ассоциацию ранее созданного плавающего IP-адреса с ещё ранее созданным портов;
    Для каждого инстанса и балансировщика нагрузки;

# Создадим для WebADM ассоциацию плавающего IP и порт (публичного и приватного IP адресов)
resource "openstack_networking_floatingip_associate_v2" "association_webadm" {
port_id = openstack_networking_port_v2.port_webadm.id
floating_ip = openstack_networking_floatingip_v2.floatingip_webadm.address
}

# Создадим для WEB1 ассоциацию плавающего IP и порт (публичного и приватного IP адресов)
resource "openstack_networking_floatingip_associate_v2" "association_web1" {
port_id = openstack_networking_port_v2.port_web1.id
floating_ip = openstack_networking_floatingip_v2.floatingip_web1.address
}

# Создадим для WEB2 ассоциацию плавающего IP и порт (публичного и приватного IP адресов)
resource "openstack_networking_floatingip_associate_v2" "association_web2" {
port_id = openstack_networking_port_v2.port_web2.id
floating_ip = openstack_networking_floatingip_v2.floatingip_web2.address
}

# Создадим для Load Balancer ассоциацию плавающего IP и порт (публичного и приватного IP адресов)
resource "openstack_networking_floatingip_associate_v2" "association_loadbalancer" {
port_id = openstack_networking_port_v2.port_loadbalancer.id
floating_ip = openstack_networking_floatingip_v2.floatingip_loadbalancer.address
}

  • Выполняем проверку синтаксиса и структуры файлов конфигурации Terraform

terraform validate

terraform plan

terraform apply

  • Создаём файл security.tf и описываем последовательно часть касающуюся сетевой безопасности (с точки зрения требования задания) для развёртывания данной инфраструктуры с поэтапным запуском и наблюдением созданных ресурсов:

vim security.tf

  • Помещаем следующее содержимое:Создаём две группы безопасности для будущего контроля ICMP и SSH

# Создаём группу безопасности для ICMP
resource "openstack_networking_secgroup_v2" "secgroup_1" {
name = "ICMP"
description = "ICMP"
}

# Создаём группу безопасности для SSH
resource "openstack_networking_secgroup_v2" "secgroup_2" {
name = "SSH"
description = "SSH"
}

  • Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
  • В файл security.tf добавляем следующий код:Создавая правило, в ранее созданную группу безопасности обращаясь по идентификатору id созданного ресурса secgroup_1  для фильтрации по протоколу icmp с любых IP-адресов для входящего трафика;
    Создавая правило, в ранее созданную группу безопасности обращаясь по идентификатору
    id созданного ресурса secgroup_2 для фильтрации по протоколу tcp на порт 22 (SSH) с любых IP-адресов для входящего трафика;
    Ограничивая тем самом доступ к будущим инстансам по ICMP и SSH из внешних сетей;

# Добавляем правило в группу безопасности для ICMP
resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_icmp" {
direction = "ingress"
ethertype = "IPv4"
protocol = "icmp"
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.secgroup_1.id
}

# Добавляем правило в группу безопасности для SSH
resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_ssh" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 22
port_range_max = 22
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.secgroup_2.id
}

  • Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply
  • В файл security.tf добавляем следующий код:Поскольку в дальнейшем по заданию на инстансах WEB1 и WEB2 должно быть развёрнуто веб-приложение, а значит они должны принимать входящий трафик на порты 80/tcp и 443/tcp (HTTP и HTTPS)
    Поскольку в дальнейшем по заданию необходимо реализовать туннельное соединение между инстансами
    WebADM и WEB1, WEB2, а значит WebADM должен принимать входящий трафик на порт реализуемого VPN-соединения, например WireGuard (51820/udp);
    Таким образом необходимо создать ещё 1 или 2 группы безопасности с соответствующими правилами;
    В данном примере создаются
    две группы безопасности с именами WEB и VPN и три правила: два для группы WEB и одно для группы VPN;

# Создаём группу безопасность для WEB-трафика (HTTP/HTTPS)
resource "openstack_networking_secgroup_v2" "secgroup_3" {
name = "WEB"
description = "WEB for HTTP/HTTPS"
}

# Добавляем первое правило в группу безопасности для WEB (HTTP)
resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_http" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 80
port_range_max = 80
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.secgroup_3.id
}

# Добавляем второе правило в группу безопасности для WEB (HTTPS)
resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_https" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 443
port_range_max = 443
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.secgroup_3.id
}

# Создаём группу безопасность для VPN (WireGuard)
resource "openstack_networking_secgroup_v2" "secgroup_4" {
name = "VPN"
description = "VPN (Wireguard)"
}

# Добавляем правило в группу безопасности для VPN (WireGuard)
resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_vpn" {
direction = "ingress"
ethertype = "IPv4"
protocol = "udp"
port_range_min = 51820
port_range_max = 51820
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.secgroup_4.id
}

Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply

  • В файл network.tf добавляем следующий код:Назначая ранее созданные группы безопасности на ранее созданные порты для каждого инстанса, обращаясь по идентификаторам id созданных ранее ресурсов;
    На порт для инстанса 
    WebADM назначаем группы безопасности ICMP, SSH (явное требование задания) и VPN, т.к. в дальнейшем данный инстанс будет выступать в роле VPN-сервера и должен принимать соединения на порт указанный в правиле для данной группы безопасности;
    На порт для инстансов
    WEB1 и WEB2 назначаем группы безопасности ICMP, SSH (явное требование задания) и WEB, т.к. в дальнейшем на данных инстансах будет развёрнуто веб-приложение и должны приниматься соединения на порты указанные в правилах для данной группы безопасности;

# Назначим группы (ICMP, SSH, VPN) безопасности на порт для инстанса WebADM
resource "openstack_networking_port_secgroup_associate_v2" "security_group_associate_webadm" {
port_id = openstack_networking_port_v2.port_webadm.id
enforce = true
security_group_ids = [
openstack_networking_secgroup_v2.secgroup_1.id,
openstack_networking_secgroup_v2.secgroup_2.id,
openstack_networking_secgroup_v2.secgroup_4.id
]
}

# Назначим группы (ICMP, SSH, WEB) безопасности на порт для инстанса WEB1
resource "openstack_networking_port_secgroup_associate_v2" "security_group_associate_web1" {
port_id = openstack_networking_port_v2.port_web1.id
enforce = true
security_group_ids = [
openstack_networking_secgroup_v2.secgroup_1.id,
openstack_networking_secgroup_v2.secgroup_2.id,
openstack_networking_secgroup_v2.secgroup_3.id
]
}

# Назначим группы (ICMP, SSH, WEB) безопасности на порт для инстанса WEB2
resource "openstack_networking_port_secgroup_associate_v2" "security_group_associate_web2" {
port_id = openstack_networking_port_v2.port_web2.id
enforce = true
security_group_ids = [
openstack_networking_secgroup_v2.secgroup_1.id,
openstack_networking_secgroup_v2.secgroup_2.id,
openstack_networking_secgroup_v2.secgroup_3.id
]
}

  • Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply
  • Создаём файл instance.tf и описываем последовательно часть для развёртывания необходимых инстансов (виртуальных машин) с поэтапным запуском и наблюдением созданных ресурсов:

vim instance.tf

  • Помещаем следующее содержимое:Т.к. все ресурсы и сети должны быть созданы в соответствие с топологией по условиям задания, а на топологии ControlVM также должна быть подключена к сети INTERNET;
    То добавляем к инстансу 
    ControlVM ещё один сетевой интерфейс из подсети INTERNET и назначаем фиксированный IP-адрес;
    Необходимо указать идентификатор 
    id виртуальной машины ControlVM

# Подключаем в ControlVM интерфейс из подсети "INTERNET" в соответствие с топологией
resource "openstack_compute_interface_attach_v2" "controlvm" {
instance_id = "96306ddf-a488-4e35-8a8f-25696821efe5"
network_id = openstack_networking_network_v2.network.id
fixed_ip = "192.168.200.10"
depends_on = [ openstack_networking_subnet_v2.subnet ]
}

instance_id (идентификатор) можно получить средствами веб-интерфейса:\

-2

Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply

  • В файл instance.tf добавляем следующий код:Описывая конфигурацию инстансов WebADM, WEB1 и WEB2;
    См. ниже информацию о переменных
    var.* и файле cloud-init.yml;

# Создаём инстанс с именем "WebADM"
resource "openstack_compute_instance_v2" "webadm" {
name = "WebADM"
flavor_id = var.flavor_id
key_pair = var.key_pair
user_data = file("cloud-init.yml")

block_device {
uuid = var.image_id
source_type = "image"
volume_size = 10
boot_index = 0
destination_type = "volume"
delete_on_termination = true
}

network {
port = openstack_networking_port_v2.port_webadm.id
}
}

# Создаём инстанс с именами "WEB1"
resource "openstack_compute_instance_v2" "web1" {
name = "WEB1"
flavor_id = var.flavor_id
key_pair = var.key_pair
user_data = file("cloud-init.yml")

block_device {
uuid = var.image_id
source_type = "image"
volume_size = 10
boot_index = 0
destination_type = "volume"
delete_on_termination = true
}

network {
port = openstack_networking_port_v2.port_web1.id
}
}

# Создаём инстанс с именами "WEB2"
resource "openstack_compute_instance_v2" "web2" {
name = "WEB2"
flavor_id = var.flavor_id
key_pair = var.key_pair
user_data = file("cloud-init.yml")

block_device {
uuid = var.image_id
source_type = "image"
volume_size = 10
boot_index = 0
destination_type = "volume"
delete_on_termination = true
}

network {
port = openstack_networking_port_v2.port_web2.id
}
}

  • В файл variables.tf добавляем следующий код:Определяя значение переменных указывая идентификаторы id для соответствующих (существующих) ресурсов;
    См. ниже где брать идентификаторы;
    Учитывая основные требования задания для создаваемых инстансов:

# ID для образа "alt-p10-cloud-x86_64.qcow2" (Starterkit)
variable "image_id" {
type = string
default = "92e78753-4f88-40eb-a10a-5a7fb9bfc106"
}

# ID для шаблона 1 vCPU 1 RAM (openstack --insecure flavor list)
variable "flavor_id" {
type = string
default = "1f64883c-1cdd-45e4-ac8a-82ab57a12fdf"
}

# Имя ssh-ключа
variable "key_pair" {
type = string
default = "cloud"
}

Для переменной image_id значение можно получить:

-3

Для переменной flavor_id значение можно получить:

-4

Для переменной key_pair значение можно получить: cloud

  • Средствами Terraform передаём именно тот ключ, на основе которого осуществляется доступ с рабочего места до ControlVMЧтобы при необходимости можно было подключиться по SSH к каждому инстансу по "публичному" (Плавающему) IP-адресу, что также является требованиями задания;
  • Создаём файл cloud-init.yml:

vim cloud-init.yml

  • Помещаем следующее содержимое:Задавая пароль по требованиям задания P@ssw0rd для каждого создаваемого инстанса;
    Также передаём значение публичной части SSH-ключа (генерация ssh-ключей см. ниже), чтобы в дальнейшем был доступ с 
    ControlVM до каждого инстанса по SSH;
    И именно по ключам, т.к. по условиям задания подключение по SSH на основе открытых ключей;
    Доступ по SSH с 
    ControlVM до каждого инстанса необходим для дальнейшей работы Ansible;

#cloud-config
chpasswd:
expire: false
users:
- {name: altlinux, password: P@ssw0rd, type: text}
ssh_pwauth: false

users:
- name: altlinux
sudo: ALL=(ALL) NOPASSWD:ALL
groups: wheel
shell: /bin/bash
ssh_authorized_keys:
- ssh-rsa добавляем сюда сгенирированный ключ следующим образом

ssh-keygen -t rsa

cat -/. ssh/id_rsa.pub

Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply

  • Создаём файл loadbalancer.tf и описываем последовательно часть для развёртывания балансировщика нагрузки с поэтапным запуском и наблюдением созданных ресурсов:

vim loadbalancer.tf

  • Помещаем следующее содержимое:Создадим балансировщик нагрузки, который будет подключён в соответствие с топологией к подсети INTERNET;

# Создаём балансировщик нагрузки с именем "Load Balancer"
resource "openstack_lb_loadbalancer_v2" "loadbalancer" {
name = "Load Balancer"
vip_subnet_id = openstack_networking_subnet_v2.subnet.id

depends_on = [
openstack_compute_instance_v2.web1,
openstack_compute_instance_v2.web2
]
}

  • Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply
  • В файл loadbalancer.tf добавляем следующий код:Определяя правила в ранее созданном балансировщике нагрузки для HTTP и HTTPS трафика;

# Создаём правило в балансировщике нагрузки для HTTP
resource "openstack_lb_listener_v2" "listener_http" {
name = "HTTP"
protocol = "TCP"
protocol_port = "80"
loadbalancer_id = openstack_lb_loadbalancer_v2.loadbalancer.id
}

# Создаём правило в балансировщике нагрузки для HTTPS
resource "openstack_lb_listener_v2" "listener_https" {
name = "HTTPS"
protocol = "TCP"
protocol_port = "443"
loadbalancer_id = openstack_lb_loadbalancer_v2.loadbalancer.id
}

  • Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply
  • В файл loadbalancer.tf добавляем следующий код:Создавая целевые группы для HTTP и HTTPS указывая соответствующий протокол и алгоритм балансировки в соответствие с заданием round robin;

# Создаём целевую группу для HTTP с указанием алгоритма балансировки "ROUND ROBIN"
resource "openstack_lb_pool_v2" "pool_http" {
name = "HTTP"
protocol = "HTTP"
lb_method = "ROUND_ROBIN"
listener_id = openstack_lb_listener_v2.listener_http.id
}

# Создаём целевую группу для HTTPS с указанием алгоритма балансировки "ROUND ROBIN"
resource "openstack_lb_pool_v2" "pool_https" {
name = "HTTPS"
protocol = "HTTPS"
lb_method = "ROUND_ROBIN"
listener_id = openstack_lb_listener_v2.listener_https.id
}

Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply

  • В файл loadbalancer.tf добавляем следующий код:Добавляя инстансы WEB1 и WEB2 в ранее созданные целевые группы для HTTP и HTTPS;

# Добавляем инстанс WEB1 в целевую группу HTTP
resource "openstack_lb_member_v2" "member_web1_http" {
name = "WEB1"
subnet_id = openstack_networking_subnet_v2.subnet.id
pool_id = openstack_lb_pool_v2.pool_http.id
address = "192.168.200.21"
protocol_port = "80"
}

# Добавляем инстанс WEB2 в целевую группу HTTP
resource "openstack_lb_member_v2" "member_web2_http" {
name = "WEB2"
subnet_id = openstack_networking_subnet_v2.subnet.id
pool_id = openstack_lb_pool_v2.pool_http.id
address = "192.168.200.22"
protocol_port = "80"
}

# Добавляем инстанс WEB1 в целевую группу HTTPS
resource "openstack_lb_member_v2" "member_web1_https" {
name = "WEB1"
subnet_id = openstack_networking_subnet_v2.subnet.id
pool_id = openstack_lb_pool_v2.pool_https.id
address = "192.168.200.21"
protocol_port = "80"
}

# Добавляем инстанс WEB2 в целевую группу HTTPS
resource "openstack_lb_member_v2" "member_web2_https" {
name = "WEB2"
subnet_id = openstack_networking_subnet_v2.subnet.id
pool_id = openstack_lb_pool_v2.pool_https.id
address = "192.168.200.22"
protocol_port = "80"
}

Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply

  • В файл loadbalancer.tf добавляем следующий код:Добавляя проверку доступности инстансов в каждой целевой группе;

# Создаём проверку доступности инстансов в целевой группе HTTP
resource "openstack_lb_monitor_v2" "monitor_http" {
name = "monitor HTTP"
pool_id = openstack_lb_pool_v2.pool_http.id
type = "PING"
delay = "10"
timeout = "4"
max_retries = "5"
}

# Создаём проверку доступности инстансов в целевой группе HTTPS
resource "openstack_lb_monitor_v2" "monitor_https" {
name = "monitor HTTPS"
pool_id = openstack_lb_pool_v2.pool_https.id
type = "PING"
delay = "10"
timeout = "4"
max_retries = "5"
}

  • Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply
  • Создаём файл output.tf:

vim output.tf

  • Помещаем следующее содержимое:Для печати на экран "внешний (публичные)" плавающие IP-адреса всех инстансов;
    Понадобится в дальнейшем для сохранения вывода в файл по требованиям задания;

output "WebADM" {
value = openstack_networking_floatingip_v2.floatingip_webadm.address
depends_on = [ openstack_compute_instance_v2.webadm ]
}

output "WEB1" {
value = openstack_networking_floatingip_v2.floatingip_web1.address
depends_on = [ openstack_compute_instance_v2.web1 ]
}

output "WEB2" {
value = openstack_networking_floatingip_v2.floatingip_web2.address
depends_on = [ openstack_compute_instance_v2.web2 ]
}

output "LoadBalancer" {
value = openstack_networking_floatingip_v2.floatingip_loadbalancer.address
}

Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply

  • Создаём файл templates.tf:

vim templates.tf

  • Помещаем следующее содержимое:Поскольку нельзя предугадать какие IP-адреса будут выданы инстансам в роли "внешних" (Плавающих) реализуем возможность генерации автоматического инвентаря для Ansible средствами Terraform;
    Т.к. в конечном варианте ожидается запуск единого скрипта который развёрнёт инфраструктуру а затем её настроит;

data "template_file" "inventory" {
template = file("/home/altlinux/bin/_templates/inventory.tpl")

vars = {
user = "altlinux"
webadm = join("", [openstack_compute_instance_v2.webadm.name, " ansible_host=", openstack_networking_floatingip_v2.floatingip_webadm.address])
web1 = join("", [openstack_compute_instance_v2.web1.name, " ansible_host=", openstack_networking_floatingip_v2.floatingip_web1.address])
web2 = join("", [openstack_compute_instance_v2.web2.name, " ansible_host=", openstack_networking_floatingip_v2.floatingip_web2.address])
}
}

resource "local_file" "save_inventory" {
content = data.template_file.inventory.rendered
filename = "/home/altlinux/bin/ansible/inventory"
}

  • Создаём директорию "_templates" где будет хранить шаблоны:

mkdir _templates

  • Создаём сам файл шаблона для автоматической генерации инвентаря для Ansible:

vim _templates/inventory.tpl

  • Помещаем в него следующее содержимое:В результате Terraform должен будет генерировать инвентарь для Ansible по пути /home/altlinux/bin/ansible/inventory описанный в ini-формате;

${webadm}
${web1}
${web2}

[all:vars]
ansible_ssh_user = ${user}
ansible_ssh_extra_args = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
ansible_python_interpreter = /usr/bin/python3

  • Создаём директорию для Ansible:

mkdir ansible

  • Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply

sudo apt-get update && sudo apt-get install -y ansible

ansible -i ansible/inventory -m ping all

  • В файл templates.tf добавляем следующий код:Добавляя возможность Terraform автоматически генерировать конфигурационные файлы для WireGuard;
    Поскольку нет информации о "внешнем" (Плавающим) IP-адресе который будет назначем 
    WebADM при новом развёртывании проекта;
    Он необходим для конфигурационного файла 
    WireGuard для параметра Endpoint;

data "template_file" "web1_wg0" {
template = file("/home/altlinux/bin/_templates/web1_wg0.tpl")

vars = {
floatingip_webadm = join("",[openstack_networking_floatingip_v2.floatingip_webadm.address])
}
}

resource "local_file" "save_web1_wg0" {
content = data.template_file.web1_wg0.rendered
filename = "/home/altlinux/bin/ansible/wireguard/web1_wg0.conf"
}

data "template_file" "web2_wg0" {
template = file("/home/altlinux/bin/_templates/web2_wg0.tpl")

vars = {
floatingip_webadm = join("",[openstack_networking_floatingip_v2.floatingip_webadm.address])
}
}

resource "local_file" "save_web2_wg0" {
content = data.template_file.web2_wg0.rendered
filename = "/home/altlinux/bin/ansible/wireguard/web2_wg0.conf"
}

  • Создаём сам файл шаблона конфигурационного файла туннельного соединения для инстанса WEB1:

vim _templates/web1_wg0.tpl

  • Помещаем в него следующее содержимое:

[Interface]
Address = 10.20.30.2/29
PrivateKey = +OoP8y4QuTZL7P5Esr7YV7p/GqleqSTE5Mf6R1E6y38=

# WebAdm
[Peer]
PublicKey = Pn1bzrpZoBob/VeAvWDQfazwrZh18WarKSKzid2K1wc=
AllowedIPs = 10.20.30.0/29
PersistentKeepalive = 10
Endpoint = ${floatingip_webadm}:51820

  • Создаём сам файл шаблона конфигурационного файла туннельного соединения для инстанса WEB2:

vim _templates/web2_wg0.tpl

  • Помещаем в него следующее содержимое:

[Interface]
Address = 10.20.30.3/29
PrivateKey = UEN3Lbpm+NyXsw+XMEZPKHAaMocFRP0KfDZvIiu+BUk=

# WebAdm
[Peer]
PublicKey = Pn1bzrpZoBob/VeAvWDQfazwrZh18WarKSKzid2K1wc=
AllowedIPs = 10.20.30.0/29
PersistentKeepalive = 10
Endpoint = ${floatingip_webadm}:51820

  • Создаём директорию для куда Terraform сохранит сгенерированные конфигурационные файлы:

mkdir ansible/wireguard

  • Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply
  • Создаём конфигурационный файл туннельного соединения для инстанса WebADM:

vim ansible/wireguard/webadm_wg0.conf

  • Помещаем в него следующее содержимое:Т.к. с точки зрения WireGuard инстанс WebADM будет выступать сервером, то данный файл может бьть статическим и не требуем какой-либо динамически получаемой информации;

[Interface]
Address = 10.20.30.1/29
ListenPort = 51820
PrivateKey = wHpaZtEfAYGxVXhwzUa8OS1EbBXqrjDW8E1Z73pzTUI=

# WEB1
[Peer]
PublicKey = dZuyzPZVpJ4gyvMpJRCOI3PBGEIYe1x1MZuYyYHjfT0=
AllowedIPs = 10.20.30.2/32

# WEB2
[Peer]
PublicKey = ZSTT8CxQsxJ1KU9bO5PoSG6iLFBb8hQtIc4Kyox4AnU=
AllowedIPs = 10.20.30.3/32

  • Создаём playbook который будет реализовывать настройку VPN-соединения средствами WireGuard между WebADM и WEB1, WEB2:

vim ansible/wireguard_playbook.yml

  • Помещаем в него следующее содержимое:

---
- name: Install packages
hosts: all
become: true

tasks:
- name: Install Wireguard
community.general.apt_rpm:
name:
- wireguard-tools
- wireguard-tools-wg-quick
state: latest
update_cache: true

- name: Settings WebAdm Wireguard server
hosts: WebADM
become: true

tasks:
- name: Create directory '/etc/wireguard'
ansible.builtin.file:
path: /etc/wireguard
state: directory
mode: '0755'

- name: Copy file 'wg0.conf'
ansible.builtin.copy:
src: wireguard/webadm_wg0.conf
dest: /etc/wireguard/wg0.conf

- name: Started and enabled wg0
ansible.builtin.systemd:
name: wg-quick@wg0
state: started
enabled: true

- name: Settings WEB1 Wireguard client
hosts: WEB1
become: true

tasks:
- name: Create directory '/etc/wireguard'
ansible.builtin.file:
path: /etc/wireguard
state: directory
mode: '0755'

- name: Copy file 'wg0.conf'
ansible.builtin.copy:
src: wireguard/web1_wg0.conf
dest: /etc/wireguard/wg0.conf

- name: Started and enabled wg0
ansible.builtin.systemd:
name: wg-quick@wg0
state: started
enabled: true

- name: Settings WEB2 Wireguard client
hosts: WEB2
become: true

tasks:
- name: Create directory '/etc/wireguard'
ansible.builtin.file:
path: /etc/wireguard
state: directory
mode: '0755'

- name: Copy file 'wg0.conf'
ansible.builtin.copy:
src: wireguard/web2_wg0.conf
dest: /etc/wireguard/wg0.conf

- name: Started and enabled wg0
ansible.builtin.systemd:
name: wg-quick@wg0
state: started
enabled: true

  • Устанавливаем коллекцию необходимую для работы модуля community.general.apt_rpm:

ansible-galaxy collection install community.general

ansible-playbook -i ansible/inventory ansible/wireguard_playbook.yml

Проверяем наличие VPN-соединения:Подключаемся по SSH  с ControlVM на WebADM по Плавающему IP-адресу;
Смотрим туннельные соединения;
Проверяем связность

-5
  • Создаём playbook который будет реализовывать:WebADM так, чтобы она могла подключаться по SSH с использованием пользователя altlinux и пароля «P@ssw0rd» к инстансам WEB1 и WEB2 с помощью VPN туннеля

vim ansible/ssh_playbook.yml

  • Помещаем в него следующее содержимое:

---
- hosts: WEB1 WEB2
become: true

tasks:
- name: Setting 'sshd_config' file
ansible.builtin.lineinfile:
line: "{{ item }}"
path: /etc/openssh/sshd_config
state: present
with_items:
- "PasswordAuthentication no"
- "Match address 10.20.30.0/29"
- " PasswordAuthentication yes"

- name: Restarted sshd
ansible.builtin.systemd:
name: sshd
state: restarted

  • Запускаем playbook-сценарий для настройки SSH по VPN-туннелю:

ansible-playbook -i ansible/inventory ansible/ssh_playbook.yml

apt-get install tree

tree