From d6cf15165af4d54e41ebbd327860230c4c90071a Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Sun, 21 Jun 2026 19:06:33 -0500 Subject: [PATCH] restore UISP server + caddy frontend, fix SSH host key regeneration, add tofu proxmox provider --- ...nixos-resolver.yml => nixos-resolver1.yml} | 0 ansible/host_vars/uisp.vntx.net.yml | 1 + ansible/hosts | 3 +- ansible/playbook.yml | 126 +++++++++++++++++- ansible/roles/base/tasks/system.yml | 1 + .../templates/Caddyfile-uisp.vntx.net.j2 | 15 +++ ansible/roles/uisp/tasks/main.yml | 8 +- tofu/.terraform.lock.hcl | 34 +++++ tofu/main.tf | 11 ++ tofu/proxmox.tf | 65 +++++++++ tofu/variables.tf | 5 + 11 files changed, 264 insertions(+), 5 deletions(-) rename ansible/host_vars/{nixos-resolver.yml => nixos-resolver1.yml} (100%) create mode 100644 ansible/roles/caddy/templates/Caddyfile-uisp.vntx.net.j2 create mode 100644 tofu/proxmox.tf create mode 100644 tofu/variables.tf diff --git a/ansible/host_vars/nixos-resolver.yml b/ansible/host_vars/nixos-resolver1.yml similarity index 100% rename from ansible/host_vars/nixos-resolver.yml rename to ansible/host_vars/nixos-resolver1.yml diff --git a/ansible/host_vars/uisp.vntx.net.yml b/ansible/host_vars/uisp.vntx.net.yml index 952a0bb..b0bd4ce 100644 --- a/ansible/host_vars/uisp.vntx.net.yml +++ b/ansible/host_vars/uisp.vntx.net.yml @@ -1,5 +1,6 @@ --- ansible_host: 204.110.191.224 +ansible_user: debian ansible_python_interpreter: /usr/bin/python3 ansible_ssh_common_args: '-o StrictHostKeyChecking=accept-new' diff --git a/ansible/hosts b/ansible/hosts index a9c483e..084f2e8 100755 --- a/ansible/hosts +++ b/ansible/hosts @@ -40,6 +40,7 @@ aprs.w5isp.com [caddy_servers] skippy.w5isp.com netbox.vntx.net +uisp.vntx.net [netbox_servers] netbox.vntx.net @@ -65,5 +66,5 @@ us.manero.org ansible_host=manero-us ca.manero.org ansible_host=manero-ca [nixos_resolvers] -nixos-resolver ansible_host=nixos-resolver +nixos-resolver1 ansible_host=nixos-resolver1 diff --git a/ansible/playbook.yml b/ansible/playbook.yml index 61309d0..a156e61 100644 --- a/ansible/playbook.yml +++ b/ansible/playbook.yml @@ -117,6 +117,7 @@ api_user: "{{ proxmox_api_user }}" api_password: "{{ proxmox_api_password }}" api_host: "{{ proxmox_api_host }}" + validate_certs: false storage: "{{ proxmox_storage }}" timeout: 90 full: true @@ -129,6 +130,7 @@ api_user: "{{ proxmox_api_user }}" api_password: "{{ proxmox_api_password }}" api_host: "{{ proxmox_api_host }}" + validate_certs: false cores: "{{ vm_cores }}" memory: "{{ vm_memory }}" update: true @@ -139,6 +141,7 @@ api_user: "{{ proxmox_api_user }}" api_password: "{{ proxmox_api_password }}" api_host: "{{ proxmox_api_host }}" + validate_certs: false disk: scsi0 size: "{{ vm_disk_size }}G" state: resized @@ -151,6 +154,7 @@ api_user: "{{ proxmox_api_user }}" api_password: "{{ proxmox_api_password }}" api_host: "{{ proxmox_api_host }}" + validate_certs: false ipconfig: ipconfig0: "ip={{ vm_ip }}/26,gw={{ vm_gateway }}" nameservers: @@ -168,6 +172,7 @@ api_user: "{{ proxmox_api_user }}" api_password: "{{ proxmox_api_password }}" api_host: "{{ proxmox_api_host }}" + validate_certs: false state: started - name: Wait for VM to be accessible via SSH @@ -466,6 +471,122 @@ roles: - aprsc +- name: Provision UISP VM on Proxmox + hosts: localhost + gather_facts: false + tags: + - uisp + - provision + - never + vars: + # Proxmox connection settings + proxmox_node: "{{ lookup('env', 'PROXMOX_NODE') | default('vm2-380', true) }}" + proxmox_api_host: 10.0.0.2 + proxmox_api_user: "{{ lookup('env', 'PROXMOX_API_USER') | default('root@pam', true) }}" + proxmox_api_password: "{{ lookup('env', 'PROXMOX_API_PASSWORD') }}" + proxmox_storage: local-lvm + proxmox_template_name: debian-cloud-init + proxmox_template_vmid: 9000 + + # UISP VM configuration + vm_name: uisp.vntx.net + vm_vmid: 224 + vm_ip: 204.110.191.224 + vm_gateway: 204.110.191.254 + vm_cores: 20 + vm_memory: 16384 + vm_disk_size: 64 + vm_vlan: 9 + ansible_ssh_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFghGMnDdkfNC6JPEhMxrKhYDmNcXjHXp/y/mf3uLtzb" + + tasks: + - name: Clone cloud-init template for UISP server + community.proxmox.proxmox_kvm: + node: "{{ proxmox_node }}" + vmid: "{{ proxmox_template_vmid }}" + clone: "{{ proxmox_template_name }}" + newid: "{{ vm_vmid }}" + name: "{{ vm_name }}" + api_user: "{{ proxmox_api_user }}" + api_password: "{{ proxmox_api_password }}" + api_host: "{{ proxmox_api_host }}" + validate_certs: false + storage: "{{ proxmox_storage }}" + timeout: 90 + full: true + + - name: Configure VM resources + community.proxmox.proxmox_kvm: + node: "{{ proxmox_node }}" + vmid: "{{ vm_vmid }}" + name: "{{ vm_name }}" + api_user: "{{ proxmox_api_user }}" + api_password: "{{ proxmox_api_password }}" + api_host: "{{ proxmox_api_host }}" + validate_certs: false + cores: "{{ vm_cores }}" + memory: "{{ vm_memory }}" + net: + net0: "virtio,bridge=vmbr0,tag={{ vm_vlan }}" + update: true + + - name: Resize disk + community.proxmox.proxmox_disk: + vmid: "{{ vm_vmid }}" + api_user: "{{ proxmox_api_user }}" + api_password: "{{ proxmox_api_password }}" + api_host: "{{ proxmox_api_host }}" + validate_certs: false + disk: scsi0 + size: "{{ vm_disk_size }}G" + state: resized + + - name: Configure cloud-init network + community.proxmox.proxmox_kvm: + node: "{{ proxmox_node }}" + vmid: "{{ vm_vmid }}" + name: "{{ vm_name }}" + api_user: "{{ proxmox_api_user }}" + api_password: "{{ proxmox_api_password }}" + api_host: "{{ proxmox_api_host }}" + validate_certs: false + ipconfig: + ipconfig0: "ip={{ vm_ip }}/26,gw={{ vm_gateway }}" + nameservers: + - 1.1.1.1 + - 8.8.8.8 + ciuser: debian + cipassword: "sxcbctolxALGnj2R8GStXrfW8GVd3s/ju91fNon7T1U=" + searchdomains: vntx.net + sshkeys: "{{ ansible_ssh_key }}" + update: true + + - name: Start VM + community.proxmox.proxmox_kvm: + node: "{{ proxmox_node }}" + vmid: "{{ vm_vmid }}" + name: "{{ vm_name }}" + api_user: "{{ proxmox_api_user }}" + api_password: "{{ proxmox_api_password }}" + api_host: "{{ proxmox_api_host }}" + validate_certs: false + state: started + + - name: Wait for VM to be accessible via SSH + ansible.builtin.wait_for: + host: "{{ vm_ip }}" + port: 22 + delay: 30 + timeout: 300 + state: started + + - name: Add VM to in-memory inventory + ansible.builtin.add_host: + name: "{{ vm_name }}" + ansible_host: "{{ vm_ip }}" + ansible_user: debian + groups: uisp_servers + - name: Configure UISP servers hosts: uisp_servers become: true @@ -473,7 +594,10 @@ tags: - uisp roles: - - uisp + - role: base + tags: base + - role: uisp + tags: uisp - name: Configure Dokku servers hosts: dokku_servers diff --git a/ansible/roles/base/tasks/system.yml b/ansible/roles/base/tasks/system.yml index 85a6544..a0be8dc 100644 --- a/ansible/roles/base/tasks/system.yml +++ b/ansible/roles/base/tasks/system.yml @@ -27,6 +27,7 @@ - name: Regenerate SSH host keys ansible.builtin.command: + cmd: ssh-keygen -A args: creates: /etc/ssh/ssh_host_ed25519_key when: ansible_facts['os_family'] == "Debian" diff --git a/ansible/roles/caddy/templates/Caddyfile-uisp.vntx.net.j2 b/ansible/roles/caddy/templates/Caddyfile-uisp.vntx.net.j2 new file mode 100644 index 0000000..2ec84f9 --- /dev/null +++ b/ansible/roles/caddy/templates/Caddyfile-uisp.vntx.net.j2 @@ -0,0 +1,15 @@ +uisp.vntx.net { + header { + Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" + } + + reverse_proxy https://localhost:8443 { + transport http { + tls_insecure_skip_verify + } + header_up Host {host} + header_up X-Forwarded-Host {host} + header_up X-Real-IP {remote_host} + header_up X-Forwarded-Proto {scheme} + } +} diff --git a/ansible/roles/uisp/tasks/main.yml b/ansible/roles/uisp/tasks/main.yml index 2c5730e..f8d5fd1 100644 --- a/ansible/roles/uisp/tasks/main.yml +++ b/ansible/roles/uisp/tasks/main.yml @@ -105,7 +105,7 @@ - name: Download UISP installer script ansible.builtin.get_url: - url: https://getuisp.com/install.sh + url: https://uisp.ui.com/install dest: "{{ uisp_app_dir }}/update.sh" owner: "{{ uisp_user }}" group: root @@ -119,9 +119,11 @@ failed_when: false - name: Run UISP installer (first install only — skips existing) - ansible.builtin.command: "{{ uisp_app_dir }}/update.sh" + ansible.builtin.command: "{{ uisp_app_dir }}/update.sh --update" become: true become_user: "{{ uisp_user }}" + environment: + PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" when: - not uisp_postgres_container.exists | default(false) register: uisp_install @@ -131,7 +133,7 @@ ansible.builtin.copy: dest: /etc/cron.d/unms-update content: | - * * * * * {{ uisp_user }} {{ uisp_app_dir }}/update.sh --cron > /dev/null 2>&1 || true + * * * * * {{ uisp_user }} {{ uisp_app_dir }}/update.sh --update > /dev/null 2>&1 || true owner: root group: root mode: '0644' diff --git a/tofu/.terraform.lock.hcl b/tofu/.terraform.lock.hcl index 5b6446e..f7240e7 100644 --- a/tofu/.terraform.lock.hcl +++ b/tofu/.terraform.lock.hcl @@ -1,6 +1,40 @@ # This file is maintained automatically by "tofu init". # Manual edits may be lost in future updates. +provider "registry.opentofu.org/bpg/proxmox" { + version = "0.110.0" + constraints = "~> 0.73" + hashes = [ + "h1:CmD2DUi0wm0QrmOo0+y9AWo0IHVnbBusJyXZUfJygog=", + "h1:GyRZ1NI0nZD0MRsL2fwzkM4js4wZMPrSS54culEoVL8=", + "h1:Lt4c3HAwfiLkZR6d4VGs39DbFJUPmAYuUsZWlrtGc7E=", + "h1:NTgKnVpERB79Njen2hi8qs4lqo/jAh65ossyHZ8vLsc=", + "h1:ONmlUxolb7Wsvw+uXJHat9qqoO2k4oB1pSX3jHdtc8c=", + "h1:Por3tFR3Xlup/nvDNI/NCc7bCgRJotcYOgBYOCbWtos=", + "h1:Vt35/l/0+/HBaFrZGKt3dlhstxaUJz77yZFHPHdjnjg=", + "h1:WWArX57beGP4K7BA+H8WRrxZTMNixQI2ad++K8P1JCo=", + "h1:biwuaVoTqlXMFxtjSJ7Pz3WgYXRRp/KsloLvK3SB2+c=", + "h1:eCQKW6guUjlUU06MKgGFgIuJRBlSmZJvsQEe8BpNAtg=", + "h1:qqL9hKHlFnfwxJJ0gi10gwkUCAnMdfiv3qt2fx+DKag=", + "h1:wz47cguCgZsPR+S7BC3F2FPu3NhMJ6vp/K0mCE7bswE=", + "h1:yWBV4ZnuKV4TwR32cqKj6+dkNf5cIbB06fxKOopM6ZI=", + "zh:208b983701f3dc1d1028d6bd6830453c5b778226899de97207317b140e56083e", + "zh:21d22ee1082745e309be59f690ca8fc63374b59d396c314905fba5ed13f7159c", + "zh:28523eee5fc04df414f67175bfe14260946d86be5a6fd1e1b124be18639b7545", + "zh:2b863781982476ed24d05cec19c23c8dc3894714c3e2f6571f489c297e3ed12a", + "zh:3bbeb3cbb705245d2084769c0612099ea267bc45d0f61d5d21604a1e8d341eae", + "zh:94706cacd457a15120ce7345cab3a7274714f12dc80150d468e80abe0bac867d", + "zh:9976da036955ad9b3cbb0c2a710d799d7b91a2d192f14050716b90c6493a35be", + "zh:ac30d314e03cebe281c5da1f7963c8ded3d470fb4ef5a0b9086c08d574f35b91", + "zh:c2f27ffbd83614d2507a948c56f0f5b4b1ecdc5654d65f68f2929ab4dbdf5d7b", + "zh:d9126e674744127976aa365b3ce2f8073f206dea945fc028a89d293bcbd94dab", + "zh:ecf37aca9a909ac1b9e9bd2e3cd3330dc8f5e666fde5dc436e520c19bf5eee55", + "zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597", + "zh:f3e30cce3165227f3417ab4497af873bae2a459be1adbb6196595ee1ca8ad6ae", + "zh:f4b2edd80b4c34917942401f9e4e7fb1cc5b803e7a12dc1dba7bbf834a5c7eb0", + ] +} + provider "registry.opentofu.org/cloudflare/cloudflare" { version = "5.19.1" constraints = "~> 5.0" diff --git a/tofu/main.tf b/tofu/main.tf index eece44c..36299c2 100644 --- a/tofu/main.tf +++ b/tofu/main.tf @@ -8,6 +8,10 @@ terraform { source = "cloudflare/cloudflare" version = "~> 5.0" } + proxmox = { + source = "bpg/proxmox" + version = "~> 0.73" + } } } @@ -18,3 +22,10 @@ provider "porkbun" { provider "cloudflare" { # Credentials via CLOUDFLARE_API_TOKEN env var } + +provider "proxmox" { + endpoint = "https://10.0.0.2:8006" + username = "root@pam" + password = var.proxmox_password + insecure = true +} diff --git a/tofu/proxmox.tf b/tofu/proxmox.tf new file mode 100644 index 0000000..5dfb941 --- /dev/null +++ b/tofu/proxmox.tf @@ -0,0 +1,65 @@ +# NixOS resolver VM — Unbound DNS resolver on Proxmox +# Template (VMID 9010) must exist first; built via: ansible-playbook playbook.yml --tags template + +locals { + nixos_ssh_keys = trimspace(<<-EOT + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHLyVSEEAEbGZZqwPwEup0XrlTpu3x3iF9ExvvQPA4xN + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOzrDMNn3nINGdIogzRxY05fkn05LSYi98BGW1Bz5yXD + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIER/8suIKcrT3a/NZHjvOpRosPC5uX4kcznIJHt/98qj + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEEKtgXwoW8HZGqC+KJok9iNpI/lK4glvoryL4Ng/AL+ + EOT + ) +} + +resource "proxmox_virtual_environment_vm" "nixos_resolver" { + node_name = "vm2-380" + name = "nixos-resolver1" + vm_id = 110 + + clone { + vm_id = 9010 + full = true + } + + cpu { + cores = 2 + } + + memory { + dedicated = 4096 + } + + disk { + datastore_id = "local-lvm" + interface = "scsi0" + size = 64 + } + + network_device { + bridge = "vmbr0" + } + + initialization { + datastore_id = "local-lvm" + + ip_config { + ipv4 { + address = "10.0.0.30/24" + gateway = "10.0.0.254" + } + } + + dns { + servers = ["9.9.9.9"] + } + + user_account { + username = "root" + keys = [local.nixos_ssh_keys] + } + } + + agent { + enabled = true + } +} diff --git a/tofu/variables.tf b/tofu/variables.tf new file mode 100644 index 0000000..160a640 --- /dev/null +++ b/tofu/variables.tf @@ -0,0 +1,5 @@ +variable "proxmox_password" { + description = "Proxmox API password (set via TF_VAR_proxmox_password env var)" + type = string + sensitive = true +}