Some checks failed
PR Checks / tofu-checks (pull_request) Failing after 2s
1/1 projects applied successfully.
Reusable OpenTofu module for creating isolated tenant VMs with: - Public IP on vmbr1 (bridged, firewall=true) - Cloud-init: password auth, fail2ban, UFW hardening - Per-VM Proxmox firewall (IN: SSH+ICMP, OUT: allow, block SMTP) Includes test-tenant VM (185.47.204.227) for verification. Changes: - modules/tenant-vm/ — reusable module (VM + FW + cloud-init) - environments/production/tenant-vms.tf — tenant VM definitions - policies/security.rego — require firewall=true on vmbr1 - atlantis.yaml — trigger on module file changes - main.tf — updated host prerequisites comment Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
134 lines
3.4 KiB
HCL
134 lines
3.4 KiB
HCL
# Tenant VM module — creates an isolated VM with public IP on vmbr1
|
|
#
|
|
# Resources created:
|
|
# 1. Cloud-init snippet (user, password, fail2ban, UFW)
|
|
# 2. VM with public IP on vmbr1 (firewall=true)
|
|
# 3. Per-VM Proxmox firewall (IN: SSH+ICMP, OUT: allow, block SMTP)
|
|
|
|
terraform {
|
|
required_providers {
|
|
proxmox = {
|
|
source = "bpg/proxmox"
|
|
version = "~> 0.90"
|
|
}
|
|
}
|
|
}
|
|
|
|
# ─── Cloud-init snippet ──────────────────────────────────────────────────────
|
|
|
|
resource "proxmox_virtual_environment_file" "cloud_init" {
|
|
content_type = "snippets"
|
|
datastore_id = "local"
|
|
node_name = var.node_name
|
|
|
|
source_raw {
|
|
data = templatefile("${path.module}/cloud-init.yaml.tftpl", {
|
|
hostname = var.name
|
|
username = var.username
|
|
password = var.password
|
|
ssh_key = var.ssh_public_key
|
|
})
|
|
file_name = "ci-${var.name}.yaml"
|
|
}
|
|
}
|
|
|
|
# ─── VM ───────────────────────────────────────────────────────────────────────
|
|
|
|
resource "proxmox_virtual_environment_vm" "tenant" {
|
|
depends_on = [proxmox_virtual_environment_file.cloud_init]
|
|
|
|
name = var.name
|
|
node_name = var.node_name
|
|
vm_id = var.vm_id
|
|
tags = ["tenant", "tofu", "ubuntu"]
|
|
|
|
stop_on_destroy = true
|
|
started = var.started
|
|
on_boot = true # tenant VMs auto-start on host reboot
|
|
|
|
cpu {
|
|
cores = var.cpu_cores
|
|
type = "x86-64-v2-AES"
|
|
}
|
|
|
|
memory {
|
|
dedicated = var.ram_mb
|
|
}
|
|
|
|
disk {
|
|
datastore_id = "local"
|
|
file_id = "local:iso/ubuntu-24.04-cloudimg-amd64.img"
|
|
interface = "virtio0"
|
|
size = var.disk_gb
|
|
file_format = "qcow2"
|
|
discard = "on"
|
|
iothread = true
|
|
}
|
|
|
|
network_device {
|
|
bridge = "vmbr1"
|
|
firewall = true # Per-VM Proxmox firewall on bridged interface
|
|
}
|
|
|
|
initialization {
|
|
datastore_id = "local"
|
|
user_data_file_id = proxmox_virtual_environment_file.cloud_init.id
|
|
|
|
ip_config {
|
|
ipv4 {
|
|
address = "${var.public_ip}/${var.subnet_mask}"
|
|
gateway = var.gateway
|
|
}
|
|
}
|
|
|
|
dns {
|
|
servers = ["188.93.16.19", "188.93.17.19"]
|
|
}
|
|
}
|
|
}
|
|
|
|
# ─── Proxmox Firewall — per-VM options ───────────────────────────────────────
|
|
|
|
resource "proxmox_virtual_environment_firewall_options" "tenant" {
|
|
depends_on = [proxmox_virtual_environment_vm.tenant]
|
|
|
|
node_name = var.node_name
|
|
vm_id = var.vm_id
|
|
|
|
enabled = true
|
|
input_policy = "DROP"
|
|
output_policy = "ACCEPT"
|
|
}
|
|
|
|
# ─── Proxmox Firewall — per-VM rules ─────────────────────────────────────────
|
|
|
|
resource "proxmox_virtual_environment_firewall_rules" "tenant" {
|
|
depends_on = [proxmox_virtual_environment_vm.tenant]
|
|
|
|
node_name = var.node_name
|
|
vm_id = var.vm_id
|
|
|
|
rule {
|
|
type = "in"
|
|
action = "ACCEPT"
|
|
proto = "tcp"
|
|
dport = "22"
|
|
comment = "Allow SSH"
|
|
}
|
|
|
|
rule {
|
|
type = "in"
|
|
action = "ACCEPT"
|
|
proto = "icmp"
|
|
comment = "Allow ICMP"
|
|
}
|
|
|
|
rule {
|
|
type = "out"
|
|
action = "DROP"
|
|
proto = "tcp"
|
|
dport = "25"
|
|
comment = "Block SMTP (anti-spam)"
|
|
}
|
|
}
|