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>
136 lines
4.2 KiB
HCL
136 lines
4.2 KiB
HCL
# Production environment — managed by Claude AI via Atlantis
|
|
# Changes to this file go through PR → plan → approve → apply
|
|
|
|
terraform {
|
|
required_version = ">= 1.6.0"
|
|
|
|
backend "s3" {
|
|
bucket = "tofu-state"
|
|
key = "production/terraform.tfstate"
|
|
endpoints = { s3 = "http://minio:9000" }
|
|
region = "us-east-1"
|
|
|
|
skip_credentials_validation = true
|
|
skip_metadata_api_check = true
|
|
skip_requesting_account_id = true
|
|
use_path_style = true
|
|
}
|
|
|
|
required_providers {
|
|
proxmox = {
|
|
source = "bpg/proxmox"
|
|
version = "~> 0.90"
|
|
}
|
|
}
|
|
}
|
|
|
|
provider "proxmox" {
|
|
endpoint = "https://185.47.204.226:8006/"
|
|
insecure = true # self-signed cert
|
|
|
|
# api_token read from PROXMOX_VE_API_TOKEN env var
|
|
# Decrypted from SOPS by Atlantis custom workflow
|
|
|
|
ssh {
|
|
agent = false
|
|
username = "root"
|
|
private_key = file("/secrets/ssh-key")
|
|
}
|
|
}
|
|
|
|
# Verify Proxmox connectivity — read cluster nodes
|
|
data "proxmox_virtual_environment_nodes" "nodes" {}
|
|
|
|
output "proxmox_nodes" {
|
|
description = "Proxmox cluster node names"
|
|
value = data.proxmox_virtual_environment_nodes.nodes.names
|
|
}
|
|
|
|
# ─── Cloud Images ─────────────────────────────────────────────────────────────
|
|
# Managed by OpenTofu — no manual wget needed
|
|
|
|
resource "proxmox_virtual_environment_download_file" "ubuntu_2404_cloud" {
|
|
content_type = "iso"
|
|
datastore_id = "local"
|
|
node_name = "georgeops"
|
|
url = "https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img"
|
|
file_name = "ubuntu-24.04-cloudimg-amd64.img"
|
|
overwrite_unmanaged = true # adopt existing manually-downloaded file
|
|
}
|
|
|
|
# ─── Host Prerequisites (not manageable via Proxmox API) ─────────────────────
|
|
# vmbr1 bridge: 185.47.204.226/28, bridge-ports eth0 (public IP + tenant VMs)
|
|
# vmbr0 bridge: 10.10.10.1/24, bridge-ports none (NAT for internal VMs)
|
|
# NAT: iptables MASQUERADE 10.10.10.0/24 → vmbr1 (post-up)
|
|
# Host protect: iptables DROP .227-.236 → host INPUT (post-up on vmbr1)
|
|
# ip_forward: /etc/sysctl.d/99-ip-forward.conf (net.ipv4.ip_forward = 1)
|
|
# Snippets: pvesm set local --content iso,vztmpl,backup,snippets
|
|
# Reason: Proxmox API does not support post-up/post-down (bpg/proxmox #1454)
|
|
# See: proxmox-patterns.md in Claude memory
|
|
|
|
# ─── Test VM (Step 4.6) ──────────────────────────────────────────────────────
|
|
|
|
resource "proxmox_virtual_environment_vm" "test_vm_01" {
|
|
depends_on = [proxmox_virtual_environment_download_file.ubuntu_2404_cloud]
|
|
|
|
name = "test-vm-01"
|
|
node_name = "georgeops"
|
|
vm_id = 100
|
|
tags = ["test", "tofu", "ubuntu"]
|
|
|
|
stop_on_destroy = true
|
|
started = true
|
|
on_boot = false # test VM, no auto-start on host reboot
|
|
|
|
cpu {
|
|
cores = 2
|
|
type = "x86-64-v2-AES"
|
|
}
|
|
|
|
memory {
|
|
dedicated = 2048
|
|
}
|
|
|
|
disk {
|
|
datastore_id = "local"
|
|
# NOTE: Using hardcoded path (not resource reference) because file_id forces VM replacement.
|
|
# The download_file resource above ensures the image exists via depends_on.
|
|
file_id = "local:iso/ubuntu-24.04-cloudimg-amd64.img"
|
|
interface = "virtio0"
|
|
size = 20
|
|
file_format = "qcow2"
|
|
discard = "on"
|
|
iothread = true
|
|
}
|
|
|
|
network_device {
|
|
bridge = "vmbr0"
|
|
firewall = false # NAT bridge — isolation via NAT + host firewall
|
|
}
|
|
|
|
initialization {
|
|
datastore_id = "local"
|
|
|
|
ip_config {
|
|
ipv4 {
|
|
address = "10.10.10.100/24"
|
|
gateway = "10.10.10.1"
|
|
}
|
|
}
|
|
|
|
dns {
|
|
servers = ["8.8.8.8", "1.1.1.1"]
|
|
}
|
|
|
|
user_account {
|
|
username = "ubuntu"
|
|
keys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO+Y8ns0RgUfR21POlIVsHD+Lp+x7cUBupqXsyMeVNZ claude@control-plane"]
|
|
}
|
|
}
|
|
}
|
|
|
|
output "test_vm_01_ip" {
|
|
description = "Test VM IP address (NAT behind bare_srv_1)"
|
|
value = "10.10.10.100"
|
|
}
|