diff --git a/.gitignore b/.gitignore index abcb75a..3375724 100644 --- a/.gitignore +++ b/.gitignore @@ -5,17 +5,21 @@ ansible/playbooks/*.retry *.retry # === Terraform === -terraform/.terraform/ -terraform/.terraform.lock.* +.terraform/ +.terraform.lock.hcl +*.terraform.lock.* +crash.log +*.crash.log # === State Files === -terraform/terraform.tfstate -terraform/terraform.tfstate.backup -terraform/terraform.tfstate.d/ +*.tfstate +*.tfstate.* +terraform.tfstate +terraform.tfstate.* # === Variables === -terraform/*.tfvars -terraform/*.tfvars.json +*.tfvars +*.tfvars.json # === OS & IDE === .DS_Store diff --git a/ansible/my_inventory/group_vars/all.yml b/ansible/my_inventory/group_vars/all.yml index 035a346..c022c1f 100644 --- a/ansible/my_inventory/group_vars/all.yml +++ b/ansible/my_inventory/group_vars/all.yml @@ -7,6 +7,7 @@ lan_subnet: "172.30.1.0/24" host_ip: "172.30.1.200" gateway_ip: "172.30.1.254" +utility_ip: "172.30.1.80" freeipa_ip: "172.30.1.85" portfolio_ip: "172.30.1.93" minecraft_ip: "172.30.1.91" @@ -20,3 +21,9 @@ admin_user: "sho" ipa_admin_password: "ChangeMeIPAAdmin123!" ipa_directory_manager_password: "ChangeMeIPAAdmin123!" + +minio_root_user: "admin" +minio_root_password: "ChangeThisPasswordSecurely" +minio_port: "9000" +minio_console_port: "9001" +minio_data_dir: "/home/{{ admin_user }}/containers/minio/data" diff --git a/ansible/my_inventory/hosts b/ansible/my_inventory/hosts index 97d1c0c..7302cb4 100644 --- a/ansible/my_inventory/hosts +++ b/ansible/my_inventory/hosts @@ -2,6 +2,7 @@ hypervisor.lab.local ansible_host=172.30.1.200 ansible_user=sho [vms] +utility.lab.local ansible_host=172.30.1.80 ansible_user=sho freeipa.lab.local ansible_host=172.30.1.85 ansible_user=sho portfolio.lab.local ansible_host=172.30.1.93 ansible_user=sho minecraft.lab.local ansible_host=172.30.1.91 ansible_user=sho diff --git a/ansible/playbooks/08_utility_services.yml b/ansible/playbooks/08_utility_services.yml new file mode 100644 index 0000000..3f87a82 --- /dev/null +++ b/ansible/playbooks/08_utility_services.yml @@ -0,0 +1,65 @@ +--- +- name: Deploy MinIO Infrastructure on Utility VM + hosts: utility.lab.local + gather_facts: true + become: false + + tasks: + - name: Install Podman and rootless container dependencies + become: true + ansible.builtin.package: + name: + - podman + - uidmap + - dbus-user-session + state: present + + - name: Create MinIO directory structures + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: "{{ admin_user }}" + group: "{{ admin_user }}" + mode: "0755" + loop: + - "/home/{{ admin_user }}/containers/minio" + - "{{ minio_data_dir }}" + + - name: Deploy MinIO Systemd Service + become: true + ansible.builtin.template: + src: "./templates/minio.service.j2" + dest: "/etc/systemd/system/minio.service" + mode: "0644" + + - name: Start and enable MinIO service + become: true + ansible.builtin.systemd_service: + name: minio.service + state: restarted + enabled: true + daemon_reload: true + + - name: Wait for MinIO API to be ready + ansible.builtin.wait_for: + port: 9000 + delay: 2 + timeout: 30 + + - name: Install MinIO client (mc) + become: true + ansible.builtin.get_url: + url: "https://dl.min.io/client/mc/release/linux-amd64/mc" + dest: "/usr/local/bin/mc" + mode: "0755" + + - name: Configure mc local alias + ansible.builtin.command: + cmd: "/usr/local/bin/mc alias set localhttp http://localhost:9000 {{ minio_root_user }} {{ minio_root_password }}" + changed_when: false + + - name: Create 'terraform-state' bucket if it doesn't exist + ansible.builtin.command: + cmd: "/usr/local/bin/mc mb --ignore-existing localhttp/terraform-state" + register: mb_result + changed_when: "'Bucket created successfully' in mb_result.stdout" diff --git a/ansible/playbooks/templates/minio.service.j2 b/ansible/playbooks/templates/minio.service.j2 new file mode 100644 index 0000000..f38f992 --- /dev/null +++ b/ansible/playbooks/templates/minio.service.j2 @@ -0,0 +1,21 @@ +[Unit] +Description=MinIO Object Storage Container +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +Restart=always +ExecStartPre=-/usr/bin/podman rm -f minio +ExecStart=/usr/bin/podman run --name minio \ + -p 9000:9000 \ + -p 9001:9001 \ + -v {{ minio_data_dir }}:/data:z,U \ + -e "MINIO_ROOT_USER={{ minio_root_user }}" \ + -e "MINIO_ROOT_PASSWORD={{ minio_root_password }}" \ + quay.io/minio/minio:latest server /data --console-address ":9001" +ExecStop=/usr/bin/podman stop -t 10 minio +ExecStopPost=-/usr/bin/podman rm -f minio + +[Install] +WantedBy=multi-user.target diff --git a/terraform/bootstrap/main.tf b/terraform/bootstrap/main.tf new file mode 100644 index 0000000..59a51dd --- /dev/null +++ b/terraform/bootstrap/main.tf @@ -0,0 +1,67 @@ +terraform { + required_version = ">=1.5.0" + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + version = "0.7.6" + } + } +} + +provider "libvirt" { + uri = "qemu+ssh://sho@172.30.1.200/system" +} + +resource "libvirt_volume" "debian12_base" { + name = "debian12-base-bootstrap.qcow2" + pool = "vm_pool" + source = "https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2" + format = "qcow2" +} + +resource "libvirt_volume" "utility_disk" { + name = "utility-disk.qcow2" + pool = "vm_pool" + base_volume_id = libvirt_volume.debian12_base.id + size = 21474836480 + format = "qcow2" +} + +data "template_file" "user_data" { + template = file("${path.module}/templates/cloud_init.cfg") + vars = { + admin_user = "sho" + ssh_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILM/84tpkx+yYsA8Zr5or1xuELOGMl0JEP576SyUc9eC sho@bazzite" + } +} + +resource "libvirt_cloudinit_disk" "utility_init" { + name = "utility-init.iso" + pool = "vm_pool" + user_data = data.template_file.user_data.rendered + network_config = templatefile("${path.module}/templates/network_config.cfg.tpl", { + interface_name = "ens3" + ip_address = "172.30.1.80" + gateway_ip = "172.30.1.254" + dns_ip = "172.30.1.85" + }) +} + +resource "libvirt_domain" "utility_vm" { + name = "utility" + memory = "2048" + vcpu = 2 + cpu { mode = "host-passthrough" } + cloudinit = libvirt_cloudinit_disk.utility_init.id + network_interface { + bridge = "br0" + mac = "52:54:00:ee:ef:60" + } + disk { volume_id = libvirt_volume.utility_disk.id } + + console { + type = "pty" + target_port = "0" + target_type = "serial" + } +} diff --git a/terraform/bootstrap/templates/cloud_init.cfg b/terraform/bootstrap/templates/cloud_init.cfg new file mode 100644 index 0000000..10f6d2d --- /dev/null +++ b/terraform/bootstrap/templates/cloud_init.cfg @@ -0,0 +1,15 @@ +#cloud-config +package_update: true +package_upgrade: false + +users: + - name: ${admin_user} + groups: wheel, systemd-journal + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + shell: /bin/bash + ssh_authorized_keys: + - ${ssh_key} + +runcmd: + - sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication no/g' /etc/ssh/sshd_config + - systemctl restart sshd diff --git a/terraform/bootstrap/templates/network_config.cfg.tpl b/terraform/bootstrap/templates/network_config.cfg.tpl new file mode 100644 index 0000000..3e8cd11 --- /dev/null +++ b/terraform/bootstrap/templates/network_config.cfg.tpl @@ -0,0 +1,14 @@ +#cloud-config +version: 2 +ethernets: + ${interface_name}: + dhcp4: no + addresses: + - ${ip_address}/24 + routes: + - to: default + via: ${gateway_ip} + nameservers: + addresses: + - ${dns_ip} + - 1.1.1.1 diff --git a/terraform/workloads/main.tf b/terraform/workloads/main.tf new file mode 100644 index 0000000..6980e85 --- /dev/null +++ b/terraform/workloads/main.tf @@ -0,0 +1,220 @@ +terraform { + required_version = ">=1.5.0" + + backend "s3" { + bucket = "terraform-state" + key = "workloads/terraform.tfstate" + region = "main" + endpoints = { s3 = "http://172.30.1.80:9000" } + skip_credentials_validation = true + skip_metadata_api_check = true + skip_region_validation = true + skip_requesting_account_id = true + use_path_style = true + } + + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + version = "0.7.6" + } + } +} + +provider "libvirt" { + uri = "qemu+ssh://sho@172.30.1.200/system" +} + +resource "libvirt_volume" "almalinux10_image" { + name = "almalinux10-base.qcow2" + pool = "vm_pool" + source = "https://repo.almalinux.org/almalinux/10/cloud/x86_64/images/AlmaLinux-10-GenericCloud-latest.x86_64.qcow2" + format = "qcow2" +} + +resource "libvirt_volume" "debian12_image" { + name = "debian12-base.qcow2" + pool = "vm_pool" + source = "https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2" + format = "qcow2" +} + +resource "libvirt_volume" "freeipa_disk" { + name = "freeipa-disk.qcow2" + pool = "vm_pool" + base_volume_id = libvirt_volume.almalinux10_image.id + size = 42949672960 + format = "qcow2" +} + +resource "libvirt_volume" "portfolio_disk" { + name = "portfolio-disk.qcow2" + pool = "vm_pool" + base_volume_id = libvirt_volume.debian12_image.id + size = 10737418240 + format = "qcow2" +} + +resource "libvirt_volume" "minecraft_disk" { + name = "minecraft-disk.qcow2" + pool = "vm_pool" + base_volume_id = libvirt_volume.debian12_image.id + size = 21474836480 + format = "qcow2" +} + +resource "libvirt_volume" "navidrome_disk" { + name = "navidrome-disk.qcow2" + pool = "vm_pool" + base_volume_id = libvirt_volume.debian12_image.id + size = 16106127360 + format = "qcow2" +} + +data "template_file" "user_data" { + template = file("${path.module}/templates/cloud_init.cfg") + vars = { + admin_user = "sho" + ssh_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILM/84tpkx+yYsA8Zr5or1xuELOGMl0JEP576SyUc9eC sho@bazzite" + } +} + +resource "libvirt_cloudinit_disk" "freeipa_init" { + name = "freeipa-init.iso" + pool = "vm_pool" + user_data = data.template_file.user_data.rendered + network_config = templatefile("${path.module}/templates/network_config.cfg.tpl", { + interface_name = "etho0" + ip_address = "172.30.1.85" + gateway_ip = "172.30.1.254" + dns_ip = "172.30.1.85" + }) +} + +resource "libvirt_cloudinit_disk" "portfolio_init" { + name = "portfolio-init.iso" + pool = "vm_pool" + user_data = data.template_file.user_data.rendered + network_config = templatefile("${path.module}/templates/network_config.cfg.tpl", { + interface_name = "ens3" + ip_address = "172.30.1.93" + gateway_ip = "172.30.1.254" + dns_ip = "172.30.1.85" + }) +} + +resource "libvirt_cloudinit_disk" "minecraft_init" { + name = "minecraft-init.iso" + pool = "vm_pool" + user_data = data.template_file.user_data.rendered + network_config = templatefile("${path.module}/templates/network_config.cfg.tpl", { + interface_name = "ens3" + ip_address = "172.30.1.91" + gateway_ip = "172.30.1.254" + dns_ip = "172.30.1.85" + }) +} + +resource "libvirt_cloudinit_disk" "navidrome_init" { + name = "navidrome-init.iso" + pool = "vm_pool" + user_data = data.template_file.user_data.rendered + network_config = templatefile("${path.module}/templates/network_config.cfg.tpl", { + interface_name = "ens3" + ip_address = "172.30.1.92" + gateway_ip = "172.30.1.254" + dns_ip = "172.30.1.85" + }) +} + +resource "libvirt_domain" "freeipa_vm" { + name = "freeipa" + memory = "3072" + vcpu = 2 + cpu { mode = "host-passthrough" } + cloudinit = libvirt_cloudinit_disk.freeipa_init.id + network_interface { + bridge = "br0" + mac = "52:54:00:ee:ef:61" + } + console { + type = "pty" + target_port = "0" + target_type = "serial" + } + disk { volume_id = libvirt_volume.freeipa_disk.id } + graphics { + type = "spice" + listen_type = "address" + autoport = true + } +} + +resource "libvirt_domain" "portfolio_vm" { + name = "portfolio" + memory = "1024" + vcpu = 1 + cpu { mode = "host-passthrough" } + cloudinit = libvirt_cloudinit_disk.portfolio_init.id + network_interface { + bridge = "br0" + mac = "52:54:00:ee:ef:62" + } + console { + type = "pty" + target_port = "0" + target_type = "serial" + } + disk { volume_id = libvirt_volume.portfolio_disk.id } + graphics { + type = "spice" + listen_type = "address" + autoport = true + } +} + +resource "libvirt_domain" "minecraft_vm" { + name = "minecraft" + memory = "6144" + vcpu = 2 + cpu { mode = "host-passthrough" } + cloudinit = libvirt_cloudinit_disk.minecraft_init.id + network_interface { + bridge = "br0" + mac = "52:54:00:ee:ef:63" + } + console { + type = "pty" + target_port = "0" + target_type = "serial" + } + disk { volume_id = libvirt_volume.minecraft_disk.id } + graphics { + type = "spice" + listen_type = "address" + autoport = true + } +} + +resource "libvirt_domain" "navidrome_vm" { + name = "navidrome" + memory = "1024" + vcpu = 1 + cpu { mode = "host-passthrough" } + cloudinit = libvirt_cloudinit_disk.navidrome_init.id + network_interface { + bridge = "br0" + mac = "52:54:00:ee:ef:65" + } + console { + type = "pty" + target_port = "0" + target_type = "serial" + } + disk { volume_id = libvirt_volume.navidrome_disk.id } + graphics { + type = "spice" + listen_type = "address" + autoport = true + } +} diff --git a/terraform/workloads/templates/cloud_init.cfg b/terraform/workloads/templates/cloud_init.cfg new file mode 100644 index 0000000..10f6d2d --- /dev/null +++ b/terraform/workloads/templates/cloud_init.cfg @@ -0,0 +1,15 @@ +#cloud-config +package_update: true +package_upgrade: false + +users: + - name: ${admin_user} + groups: wheel, systemd-journal + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + shell: /bin/bash + ssh_authorized_keys: + - ${ssh_key} + +runcmd: + - sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication no/g' /etc/ssh/sshd_config + - systemctl restart sshd diff --git a/terraform/workloads/templates/network_config.cfg.tpl b/terraform/workloads/templates/network_config.cfg.tpl new file mode 100644 index 0000000..3e8cd11 --- /dev/null +++ b/terraform/workloads/templates/network_config.cfg.tpl @@ -0,0 +1,14 @@ +#cloud-config +version: 2 +ethernets: + ${interface_name}: + dhcp4: no + addresses: + - ${ip_address}/24 + routes: + - to: default + via: ${gateway_ip} + nameservers: + addresses: + - ${dns_ip} + - 1.1.1.1