diff --git a/ansible/roles/evidence_logger/tasks/main.yml b/ansible/roles/evidence_logger/tasks/main.yml new file mode 100644 index 0000000..2fa3820 --- /dev/null +++ b/ansible/roles/evidence_logger/tasks/main.yml @@ -0,0 +1,10 @@ +- name: Log evidence + include_role: + name: evidence_logger + vars: + log_file: "dns_check.log" + log_content: | + mail.example.com -> 203.0.113.10 + auth.example.com -> 203.0.113.11 + summary_entry: "✅ DNS resolution for example.com" + validation_entry: "DNS check completed and passed for fqdn_root: example.com" diff --git a/terraform/bootstrap.sh b/terraform/bootstrap.sh index abb9b3a..3881a34 100644 --- a/terraform/bootstrap.sh +++ b/terraform/bootstrap.sh @@ -1,8 +1,14 @@ #!/bin/bash -apt update && apt install -y git python3-pip curl ufw -pip3 install ansible -ufw allow OpenSSH -ufw --force enable -git clone https://github.com/mtkell/open-cmmc-stack.git /opt/open-cmmc-stack -cd /opt/open-cmmc-stack/ansible -ansible-playbook -i localhost, secure_ubuntu.yml +set -e + +echo "🔧 Initializing Terraform..." +terraform init + +echo "📐 Running Terraform plan..." +terraform plan -var-file=terraform.tfvars + +echo "🚀 Applying Terraform configuration..." +terraform apply -auto-approve -var-file=terraform.tfvars + +echo "✅ Done. Review outputs below:" +terraform output diff --git a/terraform/dns/azure_dns.tf b/terraform/dns/azure_dns.tf new file mode 100644 index 0000000..e69de29 diff --git a/terraform/dns/cloudflare.tf b/terraform/dns/cloudflare.tf new file mode 100644 index 0000000..e69de29 diff --git a/terraform/dns/digital_ocean.tf b/terraform/dns/digital_ocean.tf new file mode 100644 index 0000000..e69de29 diff --git a/terraform/dns/gcp_dns.tf b/terraform/dns/gcp_dns.tf new file mode 100644 index 0000000..e69de29 diff --git a/terraform/dns/godaddy.tf b/terraform/dns/godaddy.tf new file mode 100644 index 0000000..e69de29 diff --git a/terraform/dns/route53.tf b/terraform/dns/route53.tf new file mode 100644 index 0000000..e69de29 diff --git a/terraform/inventory/inventory.ini.j2 b/terraform/inventory/inventory.ini.j2 new file mode 100644 index 0000000..e69de29 diff --git a/terraform/main.tf b/terraform/main.tf index 147587a..4eac52c 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -1,18 +1,36 @@ -provider "digitalocean" { - token = var.do_token +terraform { + required_version = ">= 1.3.0" + + required_providers { + local = { + source = "hashicorp/local" + version = "~> 2.0" + } + } } -resource "digitalocean_droplet" "secure_host" { - name = "cmmc-hardened" - region = "nyc3" - size = "s-2vcpu-4gb" - image = "ubuntu-22-04-x64" - ssh_keys = [var.ssh_fingerprint] - user_data = file("${path.module}/bootstrap.sh") +provider "local" {} - tags = ["cmmc", "secure-host"] +locals { + services = var.vm_definitions } -output "droplet_ip" { - value = digitalocean_droplet.secure_host.ipv4_address +module "vm" { + source = "./modules/vm" + for_each = local.services + + service_name = each.key + provider = var.infrastructure_provider + region = each.value.region + tags = each.value.tags +} + +# Create a DNS A record for each deployed service +module "dns" { + source = "./modules/dns_record" + for_each = local.services + + zone_id = var.cloudflare_zone_id + subdomain = each.key + ip_address = module.vm[each.key].ip_address } diff --git a/terraform/modules/dns_record/main.tf b/terraform/modules/dns_record/main.tf new file mode 100644 index 0000000..1686289 --- /dev/null +++ b/terraform/modules/dns_record/main.tf @@ -0,0 +1,8 @@ +resource "cloudflare_record" "dns" { + zone_id = var.zone_id + name = var.subdomain + type = "A" + value = var.ip_address + ttl = 300 + proxied = false +} diff --git a/terraform/modules/dns_record/outputs.tf b/terraform/modules/dns_record/outputs.tf new file mode 100644 index 0000000..4d1de68 --- /dev/null +++ b/terraform/modules/dns_record/outputs.tf @@ -0,0 +1,3 @@ +output "fqdn" { + value = "${var.subdomain}.${var.root_domain}" +} diff --git a/terraform/modules/dns_record/variables.tf b/terraform/modules/dns_record/variables.tf new file mode 100644 index 0000000..bf6de55 --- /dev/null +++ b/terraform/modules/dns_record/variables.tf @@ -0,0 +1,14 @@ +variable "zone_id" { + description = "Cloudflare Zone ID" + type = string +} + +variable "subdomain" { + description = "Subdomain to create (e.g., mail, auth)" + type = string +} + +variable "ip_address" { + description = "Public IP address to point the A record to" + type = string +} diff --git a/terraform/modules/vm/aws.tf b/terraform/modules/vm/aws.tf new file mode 100644 index 0000000..03769e0 --- /dev/null +++ b/terraform/modules/vm/aws.tf @@ -0,0 +1,10 @@ +resource "aws_instance" "vm" { + count = var.provider == "aws" ? 1 : 0 + ami = var.aws_ami + instance_type = "t3.micro" + key_name = var.ssh_key_name + tags = merge( + var.tags, + { Name = "open-cmmc-${var.service_name}" } + ) +} diff --git a/terraform/modules/vm/azure.tf b/terraform/modules/vm/azure.tf new file mode 100644 index 0000000..7427d64 --- /dev/null +++ b/terraform/modules/vm/azure.tf @@ -0,0 +1,15 @@ +resource "azurerm_linux_virtual_machine" "vm" { + count = var.provider == "azure" ? 1 : 0 + name = "open-cmmc-${var.service_name}" + resource_group_name = var.azure_resource_group + location = var.region + size = "Standard_B1s" + admin_username = "ubuntu" + + admin_ssh_key { + username = "ubuntu" + public_key = var.ssh_public_key + } + + network_interface_ids = [var.azure_nic_id] +} diff --git a/terraform/modules/vm/digitalocean.tf b/terraform/modules/vm/digitalocean.tf new file mode 100644 index 0000000..73e1390 --- /dev/null +++ b/terraform/modules/vm/digitalocean.tf @@ -0,0 +1,9 @@ +resource "digitalocean_droplet" "vm" { + count = var.provider == "digitalocean" ? 1 : 0 + name = "open-cmmc-${var.service_name}" + region = var.region + image = "ubuntu-22-04-x64" + size = "s-1vcpu-1gb" + ssh_keys = var.ssh_keys + tags = [for tag_key, tag_value in var.tags : "${tag_key}:${tag_value}"] +} diff --git a/terraform/modules/vm/gcp.tf b/terraform/modules/vm/gcp.tf new file mode 100644 index 0000000..a5b9c04 --- /dev/null +++ b/terraform/modules/vm/gcp.tf @@ -0,0 +1,23 @@ +resource "google_compute_instance" "vm" { + count = var.provider == "gcp" ? 1 : 0 + name = "open-cmmc-${var.service_name}" + machine_type = "e2-medium" + zone = var.gcp_zone + + boot_disk { + initialize_params { + image = "ubuntu-os-cloud/ubuntu-2204-lts" + } + } + + network_interface { + network = "default" + access_config {} # This allocates a one-to-one NAT IP + } + + metadata = { + ssh-keys = "ubuntu:${var.ssh_public_key}" + } + + tags = [for tag_key, tag_value in var.tags : "${tag_key}:${tag_value}"] +} diff --git a/terraform/modules/vm/main.tf b/terraform/modules/vm/main.tf new file mode 100644 index 0000000..1860b85 --- /dev/null +++ b/terraform/modules/vm/main.tf @@ -0,0 +1,14 @@ +resource "digitalocean_droplet" "vm" { + name = "open-cmmc-${var.service_name}" + region = var.region + size = "s-1vcpu-1gb" + image = "ubuntu-22-04-x64" + + tags = [for tag_key, tag_value in var.tags : "${tag_key}:${tag_value}"] + + ssh_keys = var.ssh_keys + + lifecycle { + ignore_changes = [image] + } +} diff --git a/terraform/modules/vm/outputs.tf b/terraform/modules/vm/outputs.tf new file mode 100644 index 0000000..809ab6f --- /dev/null +++ b/terraform/modules/vm/outputs.tf @@ -0,0 +1,10 @@ +output "ip_address" { + value = ( + var.provider == "digitalocean" && length(digitalocean_droplet.vm) > 0 ? digitalocean_droplet.vm[0].ipv4_address : + var.provider == "aws" && length(aws_instance.vm) > 0 ? aws_instance.vm[0].public_ip : + var.provider == "azure" && length(azurerm_linux_virtual_machine.vm) > 0 ? azurerm_linux_virtual_machine.vm[0].public_ip_address : + var.provider == "gcp" && length(google_compute_instance.vm) > 0 ? google_compute_instance.vm[0].network_interface[0].access_config[0].nat_ip : + var.provider == "proxmox" ? proxmox_vm_qemu.vm[0].ipconfig0 : + null + ) +} \ No newline at end of file diff --git a/terraform/modules/vm/proxmox.tf b/terraform/modules/vm/proxmox.tf new file mode 100644 index 0000000..8059d8b --- /dev/null +++ b/terraform/modules/vm/proxmox.tf @@ -0,0 +1,7 @@ +resource "proxmox_vm_qemu" "vm" { + count = var.provider == "proxmox" ? 1 : 0 + name = "open-cmmc-${var.service_name}" + target_node = var.proxmox_node + clone = var.proxmox_template + ... +} diff --git a/terraform/modules/vm/variables.tf b/terraform/modules/vm/variables.tf new file mode 100644 index 0000000..b4be7ec --- /dev/null +++ b/terraform/modules/vm/variables.tf @@ -0,0 +1,50 @@ +variable "ssh_keys" { + description = "List of DigitalOcean SSH key fingerprints or IDs" + type = list(string) +} + +variable "aws_ami" { + description = "AMI ID to use for AWS EC2 instance" + type = string + default = "ami-0c55b159cbfafe1f0" # Ubuntu 22.04 in us-east-1 (example) +} + +variable "ssh_key_name" { + description = "AWS SSH key pair name" + type = string +} + +variable "azure_resource_group" { + description = "Azure resource group name" + type = string +} + +variable "azure_nic_id" { + description = "Azure network interface ID" + type = string +} + +variable "ssh_public_key" { + description = "Public SSH key for Azure admin login" + type = string +} + +variable "gcp_project_id" { + description = "Google Cloud Project ID" + type = string +} + +variable "gcp_region" { + description = "Google Cloud region" + type = string +} + +variable "gcp_zone" { + description = "Google Cloud zone" + type = string +} + +variable "gcp_credentials_file" { + description = "Path to GCP service account JSON key file" + type = string +} diff --git a/terraform/outputs,tf b/terraform/outputs,tf new file mode 100644 index 0000000..5aa82e6 --- /dev/null +++ b/terraform/outputs,tf @@ -0,0 +1,24 @@ +# Output all service names +output "vm_services" { + value = keys(module.vm) + description = "List of deployed service names" +} + +# Output tags per service +output "vm_tags" { + value = { + for service, mod in module.vm : service => mod.vm_service_tag + } + description = "Tags assigned per service VM" +} + +# Placeholder for service-level info until actual providers are implemented +output "vm_placeholders" { + value = { + for service, mod in module.vm : service => { + service_tag = mod.vm_service_tag + ip_address = "PLACEHOLDER" + } + } + description = "Service-level IPs and tags (will be replaced with real outputs later)" +} diff --git a/terraform/providers/aws.tf b/terraform/providers/aws.tf new file mode 100644 index 0000000..e69de29 diff --git a/terraform/providers/azure.tf b/terraform/providers/azure.tf new file mode 100644 index 0000000..e69de29 diff --git a/terraform/providers/baremetal.tf b/terraform/providers/baremetal.tf new file mode 100644 index 0000000..e69de29 diff --git a/terraform/providers/digitalocean.tf b/terraform/providers/digitalocean.tf new file mode 100644 index 0000000..70b883b --- /dev/null +++ b/terraform/providers/digitalocean.tf @@ -0,0 +1,5 @@ +provider "digitalocean" { + token = var.digitalocean_token +} + +# Optionally configure default region or other settings here diff --git a/terraform/providers/gcp.tf b/terraform/providers/gcp.tf new file mode 100644 index 0000000..3180197 --- /dev/null +++ b/terraform/providers/gcp.tf @@ -0,0 +1,6 @@ +provider "google" { + project = var.gcp_project_id + region = var.gcp_region + zone = var.gcp_zone + credentials = file(var.gcp_credentials_file) +} diff --git a/terraform/providers/proxmox.tf b/terraform/providers/proxmox.tf new file mode 100644 index 0000000..60de227 --- /dev/null +++ b/terraform/providers/proxmox.tf @@ -0,0 +1,6 @@ +provider "proxmox" { + pm_api_url = var.proxmox_api_url + pm_user = var.proxmox_user + pm_password = var.proxmox_password + pm_tls_insecure = true +} diff --git a/terraform/terraform.tfvars.example b/terraform/terraform.tfvars.example index 8ee2f9b..257de2c 100644 --- a/terraform/terraform.tfvars.example +++ b/terraform/terraform.tfvars.example @@ -1,2 +1,62 @@ -do_token = "your_digitalocean_token_here" -ssh_fingerprint = "your_local_ssh_key_fingerprint" +# Base domain for TLS and reverse proxy +fqdn_root = "example.com" + +# Choose your provider: aws, azure, gcp, digitalocean, proxmox +infrastructure_provider = "digitalocean" + +# Define which services should be deployed, and where +vm_definitions = { + mail = { + region = "nyc3" + tags = { + role = "mail" + purpose = "email" + } + } + + files = { + region = "nyc3" + tags = { + role = "file_storage" + purpose = "nextcloud" + } + } + + auth = { + region = "nyc3" + tags = { + role = "identity" + purpose = "keycloak" + } + } +} + +# infrastructure_provider = "aws" +# aws_ami = "ami-0c55b159cbfafe1f0" +# ssh_key_name = "my-aws-keypair" + +# infrastructure_provider = "azure" +# azure_resource_group = "my-cmmc-resources" +# azure_nic_id = "/subscriptions/.../resourceGroups/.../providers/Microsoft.Network/networkInterfaces/..." +# ssh_public_key = "ssh-rsa AAAAB3Nza...your-key-here" + +# infrastructure_provider = "gcp" +# gcp_project_id = "your-gcp-project-id" +# gcp_region = "us-central1" +# gcp_zone = "us-central1-a" +# gcp_credentials_file = "gcp-service-account.json" +# ssh_public_key = "ssh-rsa AAAAB3Nza...your-key-here" + +# ⚙️ Proxmox Example (uncomment to use) +# infrastructure_provider = "proxmox" +# proxmox_api_url = "https://192.168.1.100:8006/api2/json" +# proxmox_user = "root@pam" +# proxmox_password = "yourpassword" +# proxmox_node = "pve" +# proxmox_template = "ubuntu-22.04-template" +# ssh_public_key = "ssh-rsa AAAAB3Nza...your-key-here" + +# dns_provider = "cloudflare" +# fqdn_root = "example.com" +# cloudflare_api_token = "your-cloudflare-api-token" +# cloudflare_zone_id = "your-cloudflare-zone-id" diff --git a/terraform/variables.tf b/terraform/variables.tf index 41de4b2..0f4cd6b 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -1,10 +1,45 @@ -variable "do_token" { - description = "DigitalOcean API token" +variable "fqdn_root" { + description = "Base domain used for subdomain routing (e.g., example.com)" + type = string +} + +variable "infrastructure_provider" { + description = "Target infrastructure provider (e.g., aws, azure, gcp, digitalocean, proxmox)" + type = string +} + +variable "vm_definitions" { + description = "Map of service names to regions and tags" + type = map(object({ + region = string + tags = map(string) + })) +} + +variable "digitalocean_token" { + description = "API token for DigitalOcean" type = string sensitive = true } -variable "ssh_fingerprint" { - description = "Your public SSH key fingerprint registered with DigitalOcean" +variable "proxmox_api_url" { ... } +variable "proxmox_user" { ... } +variable "proxmox_password" { ... } +variable "proxmox_node" { ... } +variable "proxmox_template" { ... } + +variable "fqdn_root" { + description = "Base domain for DNS records (e.g., example.com)" + type = string +} + +variable "cloudflare_api_token" { + description = "API token for Cloudflare with DNS edit permissions" + type = string + sensitive = true +} + +variable "cloudflare_zone_id" { + description = "Zone ID of the domain in Cloudflare" type = string }