VM Bot: create vm-202 for user 223747162 #70

Merged
claude merged 1 commits from vm/create-223747162 into main 2026-02-15 12:57:05 +01:00
Owner

Automated VM creation by Telegram bot.

  • User: 223747162 (@WorkHappens)
  • VM: vm-202
  • IP: 185.47.204.228
  • VMID: 202
Automated VM creation by Telegram bot. - User: 223747162 (@WorkHappens) - VM: vm-202 - IP: 185.47.204.228 - VMID: 202
claude added 1 commit 2026-02-15 12:56:23 +01:00
vm-bot: create vm-202
Some checks failed
AI Review / AI Code Review (pull_request) Successful in 2s
PR Checks / OpenTofu Validate & Policy (pull_request) Failing after 8s
Security Scan / Security Scan (pull_request) Successful in 10s
1/1 projects applied successfully.
9240655882
claude added 1 commit 2026-02-15 12:56:23 +01:00
vm-bot: create vm-202
Some checks failed
AI Review / AI Code Review (pull_request) Successful in 2s
PR Checks / OpenTofu Validate & Policy (pull_request) Failing after 8s
Security Scan / Security Scan (pull_request) Successful in 10s
1/1 projects applied successfully.
9240655882
Author
Owner

AI Code Review

Обзор PR: Создание VM-202

🔴 Критические проблемы

1. Пароль в открытом виде в Terraform

  • Пароль uLy3oyOAeqIZ3zeM хранится в plaintext в .tf файле
  • Риск: попадает в git history, видим в logs, Atlantis output, state file
  • Решение: использовать SOPS + age для encryption, как в других местах инфры

2. IP адрес за границей выделенного диапазона

  • Требуемый диапазон: 185.47.204.227-230 (4 слота, VMID 201-204)
  • Выделенный IP: 185.47.204.228 ✓ верный
  • Но: нужно проверить, свободен ли IP и не занят ли VMID 202 в Proxmox

3. Отсутствие lifecycle правила

  • Нужно добавить lifecycle { ignore_changes = [password] } или перенести в random_password
  • Иначе при повторном apply пароль может перегенериться

🟡 Проблемы среднего приоритета

4. Hardcoded пароль вместо random_password

# Должно быть:
resource "random_password" "vm_202" {
  length  = 16
  special = true
}

# И в locals:
password = random_password.vm_202.result

5. Отсутствие проверки лимитов ресурсов

  • 4GB RAM + 50GB disk для VMID 202 — проверьте, хватает ли свободных ресурсов на bare_srv_1
  • Если лимит достигнут, apply упадёт в Atlantis

6. Нет валидации IP адреса

  • Проверьте в state/fleet/bare_srv_1/network.yaml, что .228 действительно свободен

Что хорошо

  • Структура locals соответствует существующему паттерну
  • RAM/disk в пределах разумного для tenant VM
  • Slot (VMID 202) не конфликтует с K8s (300-302) или другими VMs

🔧 Обязательные исправления перед merge

  1. Зашифровать пароль через SOPS:

    sops -e -i environments/production/tenant-vms.tf
    
  2. Использовать random_password в root module или data source, не hardcode

  3. Добавить pre-apply проверку:

    • terraform plan должен показать, что IP свободен
    • Atlantis должен заблокировать apply если есть конфликты
  4. Проверить свободное место на bare_srv_1:

    # На bare_srv_1:
    pvesh get /nodes/pve/qemu | grep vmid
    

Итог: ПР заблокирован до зашифровки пароля. Раскрытые учётные данные — critical security issue. После фиксации — перепроверить IP и VMID, merge в Atlantis.


Automated review by Claude Haiku | advisory, does not block merge

## AI Code Review ## Обзор PR: Создание VM-202 ### 🔴 Критические проблемы **1. Пароль в открытом виде в Terraform** - Пароль `uLy3oyOAeqIZ3zeM` хранится в plaintext в `.tf` файле - **Риск:** попадает в git history, видим в logs, Atlantis output, state file - **Решение:** использовать SOPS + age для encryption, как в других местах инфры **2. IP адрес за границей выделенного диапазона** - Требуемый диапазон: `185.47.204.227-230` (4 слота, VMID 201-204) - Выделенный IP: `185.47.204.228` ✓ верный - **Но:** нужно проверить, свободен ли IP и не занят ли VMID 202 в Proxmox **3. Отсутствие lifecycle правила** - Нужно добавить `lifecycle { ignore_changes = [password] }` или перенести в `random_password` - Иначе при повторном apply пароль может перегенериться ### 🟡 Проблемы среднего приоритета **4. Hardcoded пароль вместо random_password** ```hcl # Должно быть: resource "random_password" "vm_202" { length = 16 special = true } # И в locals: password = random_password.vm_202.result ``` **5. Отсутствие проверки лимитов ресурсов** - 4GB RAM + 50GB disk для VMID 202 — проверьте, хватает ли свободных ресурсов на bare_srv_1 - Если лимит достигнут, apply упадёт в Atlantis **6. Нет валидации IP адреса** - Проверьте в `state/fleet/bare_srv_1/network.yaml`, что `.228` действительно свободен ### ✅ Что хорошо - Структура locals соответствует существующему паттерну - RAM/disk в пределах разумного для tenant VM - Slot (VMID 202) не конфликтует с K8s (300-302) или другими VMs ### 🔧 Обязательные исправления перед merge 1. **Зашифровать пароль через SOPS:** ```bash sops -e -i environments/production/tenant-vms.tf ``` 2. **Использовать `random_password` в root module или data source**, не hardcode 3. **Добавить pre-apply проверку:** - `terraform plan` должен показать, что IP свободен - Atlantis должен заблокировать apply если есть конфликты 4. **Проверить свободное место на bare_srv_1:** ```bash # На bare_srv_1: pvesh get /nodes/pve/qemu | grep vmid ``` --- **Итог:** ПР **заблокирован до зашифровки пароля**. Раскрытые учётные данные — critical security issue. После фиксации — перепроверить IP и VMID, merge в Atlantis. --- _Automated review by Claude Haiku | advisory, does not block merge_
Author
Owner

Ran Plan for project: production dir: environments/production workspace: default

Show Output
OpenTofu used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
+ create
~ update in-place (current -> planned)

OpenTofu will perform the following actions:

  # module.k8s_node["k8s-worker-01"].proxmox_virtual_environment_vm.k8s_node will be updated in-place
~ resource "proxmox_virtual_environment_vm" "k8s_node" {
        id                                   = "301"
        name                                 = "k8s-worker-01"
        tags                                 = [
            "k8s",
            "tofu",
            "ubuntu",
        ]
        # (30 unchanged attributes hidden)

      ~ cpu {
          ~ cores      = 10 -> 6
            # (7 unchanged attributes hidden)
        }

      ~ memory {
          ~ dedicated      = 40960 -> 24576
            # (3 unchanged attributes hidden)
        }

        # (3 unchanged blocks hidden)
    }

  # module.k8s_node["k8s-worker-02"].proxmox_virtual_environment_vm.k8s_node will be updated in-place
~ resource "proxmox_virtual_environment_vm" "k8s_node" {
        id                                   = "302"
        name                                 = "k8s-worker-02"
        tags                                 = [
            "k8s",
            "tofu",
            "ubuntu",
        ]
        # (30 unchanged attributes hidden)

      ~ cpu {
          ~ cores      = 10 -> 6
            # (7 unchanged attributes hidden)
        }

      ~ memory {
          ~ dedicated      = 40960 -> 24576
            # (3 unchanged attributes hidden)
        }

        # (3 unchanged blocks hidden)
    }

  # module.tenant_vm["vm-202"].proxmox_virtual_environment_file.cloud_init will be created
+ resource "proxmox_virtual_environment_file" "cloud_init" {
      + content_type           = "snippets"
      + datastore_id           = "local"
      + file_modification_date = (known after apply)
      + file_name              = (known after apply)
      + file_size              = (known after apply)
      + file_tag               = (known after apply)
      + id                     = (known after apply)
      + node_name              = "georgeops"
      + overwrite              = true
      + timeout_upload         = 1800

      + source_raw {
          + data      = (sensitive value)
          + file_name = "ci-vm-202.yaml"
          + resize    = 0
        }
    }

  # module.tenant_vm["vm-202"].proxmox_virtual_environment_firewall_options.tenant will be created
+ resource "proxmox_virtual_environment_firewall_options" "tenant" {
      + dhcp          = false
      + enabled       = true
      + id            = (known after apply)
      + input_policy  = "DROP"
      + log_level_in  = "nolog"
      + log_level_out = "nolog"
      + macfilter     = true
      + ndp           = false
      + node_name     = "georgeops"
      + output_policy = "ACCEPT"
      + radv          = true
      + vm_id         = 202
    }

  # module.tenant_vm["vm-202"].proxmox_virtual_environment_firewall_rules.tenant will be created
+ resource "proxmox_virtual_environment_firewall_rules" "tenant" {
      + id        = (known after apply)
      + node_name = "georgeops"
      + vm_id     = 202

      + rule {
          + action  = "ACCEPT"
          + comment = "Allow SSH"
          + dport   = "22"
          + enabled = true
          + pos     = (known after apply)
          + proto   = "tcp"
          + type    = "in"
        }
      + rule {
          + action  = "ACCEPT"
          + comment = "Allow ICMP"
          + enabled = true
          + pos     = (known after apply)
          + proto   = "icmp"
          + type    = "in"
        }
      + rule {
          + action  = "ACCEPT"
          + comment = "Allow node_exporter from control plane"
          + dport   = "9100"
          + enabled = true
          + pos     = (known after apply)
          + proto   = "tcp"
          + source  = "78.109.17.180"
          + type    = "in"
        }
      + rule {
          + action  = "DROP"
          + comment = "Block SMTP (anti-spam)"
          + dport   = "25"
          + enabled = true
          + pos     = (known after apply)
          + proto   = "tcp"
          + type    = "out"
        }
    }

  # module.tenant_vm["vm-202"].proxmox_virtual_environment_vm.tenant will be created
+ resource "proxmox_virtual_environment_vm" "tenant" {
      + acpi                                 = true
      + bios                                 = "seabios"
      + boot_order                           = (known after apply)
      + delete_unreferenced_disks_on_destroy = true
      + hotplug                              = (known after apply)
      + id                                   = (known after apply)
      + ipv4_addresses                       = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + keyboard_layout                      = "en-us"
      + mac_addresses                        = (known after apply)
      + migrate                              = false
      + name                                 = "vm-202"
      + network_interface_names              = (known after apply)
      + node_name                            = "georgeops"
      + on_boot                              = true
      + protection                           = false
      + purge_on_destroy                     = true
      + reboot                               = false
      + reboot_after_update                  = true
      + scsi_hardware                        = "virtio-scsi-pci"
      + started                              = true
      + stop_on_destroy                      = true
      + tablet_device                        = true
      + tags                                 = [
          + "tenant",
          + "tofu",
          + "ubuntu",
        ]
      + template                             = false
      + timeout_clone                        = 1800
      + timeout_create                       = 1800
      + timeout_migrate                      = 1800
      + timeout_move_disk                    = 1800
      + timeout_reboot                       = 1800
      + timeout_shutdown_vm                  = 1800
      + timeout_start_vm                     = 1800
      + timeout_stop_vm                      = 300
      + vm_id                                = 202

      + cpu {
          + cores      = 1
          + hotplugged = 0
          + limit      = 1
          + numa       = false
          + sockets    = 1
          + type       = "x86-64-v2-AES"
          + units      = (known after apply)
        }

      + disk {
          + aio               = "io_uring"
          + backup            = true
          + cache             = "none"
          + datastore_id      = "local"
          + discard           = "on"
          + file_format       = "qcow2"
          + file_id           = "local:iso/ubuntu-24.04-cloudimg-amd64.img"
          + interface         = "virtio0"
          + iothread          = true
          + path_in_datastore = (known after apply)
          + replicate         = true
          + size              = 50
          + ssd               = false

          + speed {
              + iops_read            = 5000
              + iops_read_burstable  = 8000
              + iops_write           = 3000
              + iops_write_burstable = 5000
              + read                 = 200
              + read_burstable       = 400
              + write                = 100
              + write_burstable      = 200
            }
        }

      + initialization {
          + datastore_id         = "local"
          + file_format          = (known after apply)
          + meta_data_file_id    = (known after apply)
          + network_data_file_id = (known after apply)
          + type                 = (known after apply)
          + user_data_file_id    = (known after apply)
          + vendor_data_file_id  = (known after apply)

          + dns {
              + servers = [
                  + "188.93.16.19",
                  + "188.93.17.19",
                ]
            }

          + ip_config {
              + ipv4 {
                  + address = "185.47.204.228/28"
                  + gateway = "185.47.204.225"
                }
            }
        }

      + memory {
          + dedicated      = 4096
          + floating       = 0
          + keep_hugepages = false
          + shared         = 0
        }

      + network_device {
          + bridge      = "vmbr1"
          + enabled     = true
          + firewall    = true
          + mac_address = (known after apply)
          + model       = "virtio"
          + mtu         = 0
          + queues      = 0
          + rate_limit  = 12
          + vlan_id     = 0
        }

      + vga (known after apply)
    }

Plan: 4 to add, 2 to change, 0 to destroy.

Changes to Outputs:
~ tenant_vms    = {
      + vm-202 = {
          + public_ip = "185.47.204.228"
          + username  = "root"
          + vm_id     = 202
        }
        # (1 unchanged attribute hidden)
    }

=== Checkov IaC Security Scan ===
  • ▶️ To apply this plan, comment:
    atlantis apply -p production
    
  • 🚮 To delete this plan and lock, click here
  • 🔁 To plan this project again, comment:
    atlantis plan -p production
    

Plan: 4 to add, 2 to change, 0 to destroy.


  • To apply all unapplied plans from this Pull Request, comment:
    atlantis apply
    
  • 🚮 To delete all plans and locks from this Pull Request, comment:
    atlantis unlock
    
Ran Plan for project: `production` dir: `environments/production` workspace: `default` <details><summary>Show Output</summary> ```diff OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create ~ update in-place (current -> planned) OpenTofu will perform the following actions: # module.k8s_node["k8s-worker-01"].proxmox_virtual_environment_vm.k8s_node will be updated in-place ~ resource "proxmox_virtual_environment_vm" "k8s_node" { id = "301" name = "k8s-worker-01" tags = [ "k8s", "tofu", "ubuntu", ] # (30 unchanged attributes hidden) ~ cpu { ~ cores = 10 -> 6 # (7 unchanged attributes hidden) } ~ memory { ~ dedicated = 40960 -> 24576 # (3 unchanged attributes hidden) } # (3 unchanged blocks hidden) } # module.k8s_node["k8s-worker-02"].proxmox_virtual_environment_vm.k8s_node will be updated in-place ~ resource "proxmox_virtual_environment_vm" "k8s_node" { id = "302" name = "k8s-worker-02" tags = [ "k8s", "tofu", "ubuntu", ] # (30 unchanged attributes hidden) ~ cpu { ~ cores = 10 -> 6 # (7 unchanged attributes hidden) } ~ memory { ~ dedicated = 40960 -> 24576 # (3 unchanged attributes hidden) } # (3 unchanged blocks hidden) } # module.tenant_vm["vm-202"].proxmox_virtual_environment_file.cloud_init will be created + resource "proxmox_virtual_environment_file" "cloud_init" { + content_type = "snippets" + datastore_id = "local" + file_modification_date = (known after apply) + file_name = (known after apply) + file_size = (known after apply) + file_tag = (known after apply) + id = (known after apply) + node_name = "georgeops" + overwrite = true + timeout_upload = 1800 + source_raw { + data = (sensitive value) + file_name = "ci-vm-202.yaml" + resize = 0 } } # module.tenant_vm["vm-202"].proxmox_virtual_environment_firewall_options.tenant will be created + resource "proxmox_virtual_environment_firewall_options" "tenant" { + dhcp = false + enabled = true + id = (known after apply) + input_policy = "DROP" + log_level_in = "nolog" + log_level_out = "nolog" + macfilter = true + ndp = false + node_name = "georgeops" + output_policy = "ACCEPT" + radv = true + vm_id = 202 } # module.tenant_vm["vm-202"].proxmox_virtual_environment_firewall_rules.tenant will be created + resource "proxmox_virtual_environment_firewall_rules" "tenant" { + id = (known after apply) + node_name = "georgeops" + vm_id = 202 + rule { + action = "ACCEPT" + comment = "Allow SSH" + dport = "22" + enabled = true + pos = (known after apply) + proto = "tcp" + type = "in" } + rule { + action = "ACCEPT" + comment = "Allow ICMP" + enabled = true + pos = (known after apply) + proto = "icmp" + type = "in" } + rule { + action = "ACCEPT" + comment = "Allow node_exporter from control plane" + dport = "9100" + enabled = true + pos = (known after apply) + proto = "tcp" + source = "78.109.17.180" + type = "in" } + rule { + action = "DROP" + comment = "Block SMTP (anti-spam)" + dport = "25" + enabled = true + pos = (known after apply) + proto = "tcp" + type = "out" } } # module.tenant_vm["vm-202"].proxmox_virtual_environment_vm.tenant will be created + resource "proxmox_virtual_environment_vm" "tenant" { + acpi = true + bios = "seabios" + boot_order = (known after apply) + delete_unreferenced_disks_on_destroy = true + hotplug = (known after apply) + id = (known after apply) + ipv4_addresses = (known after apply) + ipv6_addresses = (known after apply) + keyboard_layout = "en-us" + mac_addresses = (known after apply) + migrate = false + name = "vm-202" + network_interface_names = (known after apply) + node_name = "georgeops" + on_boot = true + protection = false + purge_on_destroy = true + reboot = false + reboot_after_update = true + scsi_hardware = "virtio-scsi-pci" + started = true + stop_on_destroy = true + tablet_device = true + tags = [ + "tenant", + "tofu", + "ubuntu", ] + template = false + timeout_clone = 1800 + timeout_create = 1800 + timeout_migrate = 1800 + timeout_move_disk = 1800 + timeout_reboot = 1800 + timeout_shutdown_vm = 1800 + timeout_start_vm = 1800 + timeout_stop_vm = 300 + vm_id = 202 + cpu { + cores = 1 + hotplugged = 0 + limit = 1 + numa = false + sockets = 1 + type = "x86-64-v2-AES" + units = (known after apply) } + disk { + aio = "io_uring" + backup = true + cache = "none" + datastore_id = "local" + discard = "on" + file_format = "qcow2" + file_id = "local:iso/ubuntu-24.04-cloudimg-amd64.img" + interface = "virtio0" + iothread = true + path_in_datastore = (known after apply) + replicate = true + size = 50 + ssd = false + speed { + iops_read = 5000 + iops_read_burstable = 8000 + iops_write = 3000 + iops_write_burstable = 5000 + read = 200 + read_burstable = 400 + write = 100 + write_burstable = 200 } } + initialization { + datastore_id = "local" + file_format = (known after apply) + meta_data_file_id = (known after apply) + network_data_file_id = (known after apply) + type = (known after apply) + user_data_file_id = (known after apply) + vendor_data_file_id = (known after apply) + dns { + servers = [ + "188.93.16.19", + "188.93.17.19", ] } + ip_config { + ipv4 { + address = "185.47.204.228/28" + gateway = "185.47.204.225" } } } + memory { + dedicated = 4096 + floating = 0 + keep_hugepages = false + shared = 0 } + network_device { + bridge = "vmbr1" + enabled = true + firewall = true + mac_address = (known after apply) + model = "virtio" + mtu = 0 + queues = 0 + rate_limit = 12 + vlan_id = 0 } + vga (known after apply) } Plan: 4 to add, 2 to change, 0 to destroy. Changes to Outputs: ~ tenant_vms = { + vm-202 = { + public_ip = "185.47.204.228" + username = "root" + vm_id = 202 } # (1 unchanged attribute hidden) } === Checkov IaC Security Scan === ``` </details> * :arrow_forward: To **apply** this plan, comment: ```shell atlantis apply -p production ``` * :put_litter_in_its_place: To **delete** this plan and lock, click [here](http://atlantis:4141/lock?id=claude%252Finfrastructure%252Fenvironments%252Fproduction%252Fdefault%252Fproduction) * :repeat: To **plan** this project again, comment: ```shell atlantis plan -p production ``` Plan: 4 to add, 2 to change, 0 to destroy. --- * :fast_forward: To **apply** all unapplied plans from this Pull Request, comment: ```shell atlantis apply ``` * :put_litter_in_its_place: To **delete** all plans and locks from this Pull Request, comment: ```shell atlantis unlock ```
admin approved these changes 2026-02-15 12:56:44 +01:00
admin left a comment
Collaborator

Auto-approved by VM bot

Auto-approved by VM bot
Author
Owner

atlantis apply -p production

atlantis apply -p production
Author
Owner

AI Code Review

Обзор PR: VM Bot создание vm-202

🔴 КРИТИЧЕСКИЕ ПРОБЛЕМЫ

1. Пароль в plaintext в Terraform (SECURITY)

  • password = "uLy3oyOAeqIZ3zeM" — учётные данные хранятся в Git, доступны всем с доступом к репо
  • Требуется: использовать SOPS/age для шифрования или генерировать пароль через random_password + Vault
  • Это нарушает security.yaml — шифрование секретов обязательно

2. Недокументированное увеличение слотов VM (BLAST RADIUS)

  • Локали указывают 4 слота (vm-201..204, IPs .227-.230)
  • В PR добавлена vm-202 с IP .228 — это пересекается с планом
  • Вопрос: vm-201 уже существует? Если да — скопировать её конфиг в PR. Если нет — заполнить пробелы по порядку
  • Нет гарантии, что .228 не забронирована в другом месте

🟡 СЕРЬЁЗНЫЕ ПРОБЛЕМЫ

3. Отсутствует уникальность IP-адреса (NETWORK)

  • Нет validation-а на конфликты IPs в сети 185.47.204.224/28
  • Пример: если кто-то вручную создаст ещё одну VM с .228, будет collision
  • Требуется: добавить проверку в validate-state.sh или constraint в Terraform

4. VMID 202 может конфликтовать (PROXMOX)

  • Нет проверки, что VMID 202 свободен в Proxmox
  • K8s nodes занимают 300-302, tenant-VMs должны быть 201-204
  • Требуется: перед apply убедиться, что VMID не зарезервирован (pvesh get /nodes/pve/qemu)

🟠 ПРАКТИЧЕСКИЕ ЗАМЕЧАНИЯ

5. Пароль по-прежнему в plain-text логах (AUDIT TRAIL)

  • Даже если зашифровать в Git, пароль появится в terraform plan/apply логах
  • Лучше: не хранить пароли в Terraform вообще — генерировать в cloud-init, сразу отправлять юзеру в бот
  • Сейчас этот пароль будет видит в Atlantis PR comments (публично)

6. Отсутствует проверка на лимиты ресурсов (RESOURCE MGMT)

  • 4096 MB RAM + 50 GB disk — стандартные, но не указано:
    • CPU cores?
    • Сетевой лимит (12 MB/s по vmbot-improvements.md)?
    • Disk I/O лимиты (IOPS/bandwidth)?
  • В модуле modules/tenant-vm/ эти параметры должны быть явно

Рекомендации по фиксу

  1. Немедленно: удалить пароль из PR, добавить в SOPS-encrypted файл или использовать Vault
  2. Перед apply: запустить проверку VMID и IP уникальности
  3. В module: добавить dynamic "cpu" и dynamic "limits" для контроля ресурсов
  4. В CI: добавить шаг, который запрещает password = в plaintext (gitleaks уже должна ловить)

НЕ МЕРЖИТЬ до фиксов #1, #2, #4.


Automated review by Claude Haiku | advisory, does not block merge

## AI Code Review ## Обзор PR: VM Bot создание vm-202 ### 🔴 КРИТИЧЕСКИЕ ПРОБЛЕМЫ **1. Пароль в plaintext в Terraform** (SECURITY) - `password = "uLy3oyOAeqIZ3zeM"` — учётные данные хранятся в Git, доступны всем с доступом к репо - **Требуется:** использовать SOPS/age для шифрования или генерировать пароль через `random_password` + Vault - Это нарушает [security.yaml](file:///srv/server-manifest/state/security.yaml) — шифрование секретов обязательно **2. Недокументированное увеличение слотов VM** (BLAST RADIUS) - Локали указывают 4 слота (vm-201..204, IPs .227-.230) - В PR добавлена vm-202 с IP .228 — это пересекается с планом - **Вопрос:** vm-201 уже существует? Если да — скопировать её конфиг в PR. Если нет — заполнить пробелы по порядку - Нет гарантии, что .228 не забронирована в другом месте ### 🟡 СЕРЬЁЗНЫЕ ПРОБЛЕМЫ **3. Отсутствует уникальность IP-адреса** (NETWORK) - Нет validation-а на конфликты IPs в сети 185.47.204.224/28 - Пример: если кто-то вручную создаст ещё одну VM с .228, будет collision - **Требуется:** добавить проверку в `validate-state.sh` или constraint в Terraform **4. VMID 202 может конфликтовать** (PROXMOX) - Нет проверки, что VMID 202 свободен в Proxmox - K8s nodes занимают 300-302, tenant-VMs должны быть 201-204 - **Требуется:** перед apply убедиться, что VMID не зарезервирован (`pvesh get /nodes/pve/qemu`) ### 🟠 ПРАКТИЧЕСКИЕ ЗАМЕЧАНИЯ **5. Пароль по-прежнему в plain-text логах** (AUDIT TRAIL) - Даже если зашифровать в Git, пароль появится в terraform plan/apply логах - **Лучше:** не хранить пароли в Terraform вообще — генерировать в cloud-init, сразу отправлять юзеру в бот - Сейчас этот пароль будет видит в Atlantis PR comments (публично) **6. Отсутствует проверка на лимиты ресурсов** (RESOURCE MGMT) - 4096 MB RAM + 50 GB disk — стандартные, но не указано: - CPU cores? - Сетевой лимит (12 MB/s по [vmbot-improvements.md](vmbot-improvements.md))? - Disk I/O лимиты (IOPS/bandwidth)? - В модуле `modules/tenant-vm/` эти параметры должны быть явно --- ## Рекомендации по фиксу 1. **Немедленно:** удалить пароль из PR, добавить в SOPS-encrypted файл или использовать Vault 2. **Перед apply:** запустить проверку VMID и IP уникальности 3. **В module:** добавить `dynamic "cpu"` и `dynamic "limits"` для контроля ресурсов 4. **В CI:** добавить шаг, который запрещает `password =` в plaintext (gitleaks уже должна ловить) **⛔ НЕ МЕРЖИТЬ** до фиксов #1, #2, #4. --- _Automated review by Claude Haiku | advisory, does not block merge_
Author
Owner

Ran Apply for project: production dir: environments/production workspace: default

Show Output
module.tenant_vm["vm-202"].proxmox_virtual_environment_file.cloud_init: Creating...
module.k8s_node["k8s-worker-01"].proxmox_virtual_environment_vm.k8s_node: Modifying... [id=301]
module.k8s_node["k8s-worker-02"].proxmox_virtual_environment_vm.k8s_node: Modifying... [id=302]
module.tenant_vm["vm-202"].proxmox_virtual_environment_file.cloud_init: Creation complete after 1s [id=local:snippets/ci-vm-202.yaml]
module.tenant_vm["vm-202"].proxmox_virtual_environment_vm.tenant: Creating...
module.k8s_node["k8s-worker-01"].proxmox_virtual_environment_vm.k8s_node: Modifications complete after 3s [id=301]
module.k8s_node["k8s-worker-02"].proxmox_virtual_environment_vm.k8s_node: Modifications complete after 3s [id=302]
module.tenant_vm["vm-202"].proxmox_virtual_environment_vm.tenant: Creation complete after 7s [id=202]
module.tenant_vm["vm-202"].proxmox_virtual_environment_firewall_options.tenant: Creating...
module.tenant_vm["vm-202"].proxmox_virtual_environment_firewall_rules.tenant: Creating...
module.tenant_vm["vm-202"].proxmox_virtual_environment_firewall_options.tenant: Creation complete after 0s [id=options-3614604432]
module.tenant_vm["vm-202"].proxmox_virtual_environment_firewall_rules.tenant: Creation complete after 1s [id=rule-2763983303]

Apply complete! Resources: 4 added, 2 changed, 0 destroyed.

Outputs:

k8s_nodes = {
  "k8s-master" = {
    "ip_address" = "10.10.10.200"
    "vm_id" = 300
  }
  "k8s-worker-01" = {
    "ip_address" = "10.10.10.201"
    "vm_id" = 301
  }
  "k8s-worker-02" = {
    "ip_address" = "10.10.10.202"
    "vm_id" = 302
  }
}
proxmox_nodes = tolist([
  "georgeops",
])
tenant_vms = {
  "vm-201" = {
    "public_ip" = "185.47.204.227"
    "username" = "root"
    "vm_id" = 201
  }
  "vm-202" = {
    "public_ip" = "185.47.204.228"
    "username" = "root"
    "vm_id" = 202
  }
}
Ran Apply for project: `production` dir: `environments/production` workspace: `default` <details><summary>Show Output</summary> ```diff module.tenant_vm["vm-202"].proxmox_virtual_environment_file.cloud_init: Creating... module.k8s_node["k8s-worker-01"].proxmox_virtual_environment_vm.k8s_node: Modifying... [id=301] module.k8s_node["k8s-worker-02"].proxmox_virtual_environment_vm.k8s_node: Modifying... [id=302] module.tenant_vm["vm-202"].proxmox_virtual_environment_file.cloud_init: Creation complete after 1s [id=local:snippets/ci-vm-202.yaml] module.tenant_vm["vm-202"].proxmox_virtual_environment_vm.tenant: Creating... module.k8s_node["k8s-worker-01"].proxmox_virtual_environment_vm.k8s_node: Modifications complete after 3s [id=301] module.k8s_node["k8s-worker-02"].proxmox_virtual_environment_vm.k8s_node: Modifications complete after 3s [id=302] module.tenant_vm["vm-202"].proxmox_virtual_environment_vm.tenant: Creation complete after 7s [id=202] module.tenant_vm["vm-202"].proxmox_virtual_environment_firewall_options.tenant: Creating... module.tenant_vm["vm-202"].proxmox_virtual_environment_firewall_rules.tenant: Creating... module.tenant_vm["vm-202"].proxmox_virtual_environment_firewall_options.tenant: Creation complete after 0s [id=options-3614604432] module.tenant_vm["vm-202"].proxmox_virtual_environment_firewall_rules.tenant: Creation complete after 1s [id=rule-2763983303] Apply complete! Resources: 4 added, 2 changed, 0 destroyed. Outputs: k8s_nodes = { "k8s-master" = { "ip_address" = "10.10.10.200" "vm_id" = 300 } "k8s-worker-01" = { "ip_address" = "10.10.10.201" "vm_id" = 301 } "k8s-worker-02" = { "ip_address" = "10.10.10.202" "vm_id" = 302 } } proxmox_nodes = tolist([ "georgeops", ]) tenant_vms = { "vm-201" = { "public_ip" = "185.47.204.227" "username" = "root" "vm_id" = 201 } "vm-202" = { "public_ip" = "185.47.204.228" "username" = "root" "vm_id" = 202 } } ``` </details>
claude merged commit 51054f5c25 into main 2026-02-15 12:57:05 +01:00
Author
Owner

Locks and plans deleted for the projects and workspaces modified in this pull request:

  • dir: environments/production workspace: default
Locks and plans deleted for the projects and workspaces modified in this pull request: - dir: `environments/production` workspace: `default`
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: claude/infrastructure#70
No description provided.