package main # Import the raw input instead of using tfplan alias import input # Helper function to get VM resources get_vms = vms { planned_values := input.planned_values vms := [r | r := planned_values.root_module.resources[_]; r.type == "proxmox_vm_qemu"] } # Helper function to check if a value is empty or undefined is_empty(value) { value == "" } { value == null } { not value } # Helper function to check minimum memory requirements (in MB) min_memory = 512 # Deny if VM allows password authentication deny[msg] { vm := get_vms[_] not is_empty(vm.values.cipassword) msg := sprintf("VM '%s' uses password authentication. Use SSH keys only.", [vm.name]) } # Deny if VM allows root login deny[msg] { vm := get_vms[_] vm.values.ciuser == "root" msg := sprintf("VM '%s' allows root login. Use a non-root user.", [vm.name]) } # Deny if qemu-agent is not enabled deny[msg] { vm := get_vms[_] vm.values.agent != 1 msg := sprintf("VM '%s' does not have qemu-agent enabled (agent = 1).", [vm.name]) } # Deny if VM uses insecure network bridge deny[msg] { vm := get_vms[_] net := vm.values.network[_] net.bridge != "vmbr2" msg := sprintf("VM '%s' uses insecure network bridge '%s'. Use 'vmbr2'.", [vm.name, net.bridge]) } # Deny if IPv6 is not disabled deny[msg] { vm := get_vms[_] not vm.values.skip_ipv6 msg := sprintf("VM '%s' does not have IPv6 disabled (skip_ipv6 = true).", [vm.name]) } # Deny if TLS verification is disabled deny[msg] { provider := input.configuration.provider_config.proxmox provider.expressions.pm_tls_insecure.constant_value == true msg := "TLS verification must be enabled (pm_tls_insecure = false)" } # Deny if provider version is not pinned deny[msg] { provider := input.configuration.terraform.required_providers.proxmox not startswith(provider.version_constraint, "=") msg := "Provider version must be pinned with '=' constraint" } # Deny if VM memory is below minimum requirement deny[msg] { vm := get_vms[_] memory := to_number(vm.values.memory) memory < min_memory msg := sprintf("VM '%s' has insufficient memory (%dMB). Minimum required: %dMB.", [vm.name, memory, min_memory]) } # Deny if VM does not have a description deny[msg] { vm := get_vms[_] is_empty(vm.values.desc) msg := sprintf("VM '%s' must have a description for documentation purposes.", [vm.name]) } # Deny if VM uses default SCSI controller deny[msg] { vm := get_vms[_] vm.values.scsihw == "lsi" msg := sprintf("VM '%s' uses default SCSI controller. Use virtio-scsi-pci for better performance.", [vm.name]) } # Test rule to verify policy is loaded test_policy_loaded { true }