From 292758900e0f1028b69be33ebe0f32f6a36ef5a0 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 24 Jul 2026 13:12:33 -0500 Subject: [PATCH] Add hi (74.50.113.232) with Forgejo + Caddy, bootstrapped support.vntx.net - DNS: hi.mcintire.me A/AAAA, git.mcintire.me A/AAAA updated to new host - forgejo role: Docker Compose deployment with SQLite, INSTALL_LOCK, admin user creation - Caddyfile-hi.j2: reverse proxy git.mcintire.me -> localhost:3000 - tailscale role: skip connect when no auth key; dynamic repo suite - debian role: fix Signed-By for debian.sources on trixie - curl added to base Debian packages - support.vntx.net: added to vntx_servers, DNS, host_vars --- ansible/group_vars/bind9_servers/vntx.net.yml | 2 + ansible/host_vars/hi.yml | 17 ++ ansible/host_vars/support.vntx.net.yml | 16 ++ ansible/hosts | 10 ++ ansible/playbook.yml | 18 +++ ansible/roles/base/defaults/main.yml | 1 + ansible/roles/caddy/templates/Caddyfile-hi.j2 | 17 ++ ansible/roles/debian/tasks/main.yml | 7 + ansible/roles/forgejo/defaults/main.yml | 9 ++ ansible/roles/forgejo/handlers/main.yml | 5 + ansible/roles/forgejo/tasks/main.yml | 145 ++++++++++++++++++ .../forgejo/templates/docker-compose.yml.j2 | 34 ++++ ansible/roles/tailscale/tasks/main.yml | 10 +- tofu/cloudflare.tf | 33 +++- tofu/proxmox.tf | 4 +- 15 files changed, 323 insertions(+), 5 deletions(-) create mode 100644 ansible/host_vars/hi.yml create mode 100644 ansible/host_vars/support.vntx.net.yml create mode 100644 ansible/roles/caddy/templates/Caddyfile-hi.j2 create mode 100644 ansible/roles/forgejo/defaults/main.yml create mode 100644 ansible/roles/forgejo/handlers/main.yml create mode 100644 ansible/roles/forgejo/tasks/main.yml create mode 100644 ansible/roles/forgejo/templates/docker-compose.yml.j2 diff --git a/ansible/group_vars/bind9_servers/vntx.net.yml b/ansible/group_vars/bind9_servers/vntx.net.yml index 668623c..3d2d6f7 100644 --- a/ansible/group_vars/bind9_servers/vntx.net.yml +++ b/ansible/group_vars/bind9_servers/vntx.net.yml @@ -72,6 +72,8 @@ vntx_net_zone: ip: 204.110.191.221 - name: net ip: 104.238.146.79 + - name: support + ip: 204.110.191.231 - name: uisp ip: 204.110.191.224 - name: dns diff --git a/ansible/host_vars/hi.yml b/ansible/host_vars/hi.yml new file mode 100644 index 0000000..905f485 --- /dev/null +++ b/ansible/host_vars/hi.yml @@ -0,0 +1,17 @@ +--- +ansible_python_interpreter: /usr/bin/python3 +tailscale_skip_connect: true + +firewall_allow_rules: + - port: 22 + proto: tcp + comment: SSH + - port: 80 + proto: tcp + comment: HTTP + - port: 443 + proto: tcp + comment: HTTPS + - port: 2222 + proto: tcp + comment: Forgejo SSH diff --git a/ansible/host_vars/support.vntx.net.yml b/ansible/host_vars/support.vntx.net.yml new file mode 100644 index 0000000..beaa2ba --- /dev/null +++ b/ansible/host_vars/support.vntx.net.yml @@ -0,0 +1,16 @@ +--- +ansible_host: 204.110.191.231 +ansible_python_interpreter: /usr/bin/python3 +ansible_ssh_common_args: '-o StrictHostKeyChecking=accept-new' +tailscale_skip_connect: true + +firewall_allow_rules: + - port: 22 + proto: tcp + comment: SSH + - port: 80 + proto: tcp + comment: HTTP + - port: 443 + proto: tcp + comment: HTTPS diff --git a/ansible/hosts b/ansible/hosts index 83217b2..8e83cf6 100755 --- a/ansible/hosts +++ b/ansible/hosts @@ -8,6 +8,7 @@ unimus.vntx.net monitor.vntx.net librenms.vntx.net uisp.vntx.net +support.vntx.net ansible_host=204.110.191.231 [librenms_servers] librenms.vntx.net @@ -18,6 +19,7 @@ monitor.vntx.net [home_servers] skippy.w5isp.com mail.mcintire.me ansible_host=107.174.178.20 +hi ansible_host=74.50.113.232 dokku.w5isp.com aprs.w5isp.com prom.w5isp.com ansible_host=10.0.19.31 @@ -28,8 +30,15 @@ prom.w5isp.com [node_exporter_servers:children] prometheus_servers +[tailscale_servers] +hi +support.vntx.net + [dokku_servers] +[forgejo_servers] +hi + [aprsc_servers] aprs.w5isp.com @@ -38,6 +47,7 @@ skippy.w5isp.com netbox.vntx.net uisp.vntx.net logs.vntx.net +hi [netbox_servers] netbox.vntx.net diff --git a/ansible/playbook.yml b/ansible/playbook.yml index 9ff6102..6073501 100644 --- a/ansible/playbook.yml +++ b/ansible/playbook.yml @@ -15,6 +15,15 @@ community.general.timezone: name: "{{ timezone }}" +- name: Install and configure Tailscale + hosts: tailscale_servers + become: true + gather_facts: true + tags: + - tailscale + roles: + - tailscale + - name: Apply firewall configuration hosts: all:!proxmox_servers:!irc_limited become: true @@ -355,6 +364,15 @@ roles: - dokku +- name: Install and configure Forgejo + hosts: forgejo_servers + become: true + gather_facts: true + tags: + - forgejo + roles: + - forgejo + - name: Configure node_exporter targets hosts: node_exporter_servers become: true diff --git a/ansible/roles/base/defaults/main.yml b/ansible/roles/base/defaults/main.yml index d67875d..945a968 100644 --- a/ansible/roles/base/defaults/main.yml +++ b/ansible/roles/base/defaults/main.yml @@ -45,6 +45,7 @@ base_common_packages: base_packages_by_os_family: Debian: - sudo + - curl - jq - sysstat - smartmontools diff --git a/ansible/roles/caddy/templates/Caddyfile-hi.j2 b/ansible/roles/caddy/templates/Caddyfile-hi.j2 new file mode 100644 index 0000000..d5ce3e2 --- /dev/null +++ b/ansible/roles/caddy/templates/Caddyfile-hi.j2 @@ -0,0 +1,17 @@ +{ + servers { + protocols h1 h2 + } + admin off + persist_config off +} + +git.mcintire.me { + reverse_proxy http://127.0.0.1:3000 + encode gzip + + log { + output file /var/log/caddy/git.mcintire.me.log + format json + } +} diff --git a/ansible/roles/debian/tasks/main.yml b/ansible/roles/debian/tasks/main.yml index c5d12b5..f3f149f 100644 --- a/ansible/roles/debian/tasks/main.yml +++ b/ansible/roles/debian/tasks/main.yml @@ -28,6 +28,13 @@ path: /etc/apt/sources.list.d/ppa-linuxfactory-or-kr.list state: absent +- name: "Add Signed-By to debian.sources mirror entries" + ansible.builtin.replace: + path: /etc/apt/sources.list.d/debian.sources + regexp: '(URIs: mirror\+file://[^\n]+)\n(Suites:[^\n]+)\n(Components:[^\n]+)' + replace: '\1\n\2\n\3\nSigned-By: /usr/share/keyrings/debian-archive-keyring.gpg' + when: ansible_facts['distribution_release'] == 'trixie' + - name: "Install updates" ansible.builtin.apt: name: "*" diff --git a/ansible/roles/forgejo/defaults/main.yml b/ansible/roles/forgejo/defaults/main.yml new file mode 100644 index 0000000..84eae30 --- /dev/null +++ b/ansible/roles/forgejo/defaults/main.yml @@ -0,0 +1,9 @@ +--- +forgejo_version: "10" +forgejo_http_port: 3000 +forgejo_ssh_port: 2222 +forgejo_hostname: git.mcintire.me +forgejo_data_dir: /opt/forgejo +forgejo_admin_user: graham +forgejo_admin_email: graham@mcintire.me +forgejo_admin_password: "{{ lookup('env', 'FORGEJO_ADMIN_PASSWORD') | default('changeme123', true) }}" diff --git a/ansible/roles/forgejo/handlers/main.yml b/ansible/roles/forgejo/handlers/main.yml new file mode 100644 index 0000000..d4d008b --- /dev/null +++ b/ansible/roles/forgejo/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: restart forgejo + ansible.builtin.command: docker compose restart + args: + chdir: "{{ forgejo_data_dir }}" diff --git a/ansible/roles/forgejo/tasks/main.yml b/ansible/roles/forgejo/tasks/main.yml new file mode 100644 index 0000000..0bee8a9 --- /dev/null +++ b/ansible/roles/forgejo/tasks/main.yml @@ -0,0 +1,145 @@ +--- +- name: Install prerequisites + ansible.builtin.apt: + name: + - ca-certificates + - curl + - gnupg + - apt-transport-https + state: present + update_cache: true + +- name: Check if Docker is installed + ansible.builtin.command: which docker + register: docker_installed + changed_when: false + failed_when: false + +- name: Add Docker GPG key + ansible.builtin.get_url: + url: https://download.docker.com/linux/debian/gpg + dest: /usr/share/keyrings/docker-archive-keyring.asc + mode: '0644' + when: docker_installed.rc != 0 + +- name: Add Docker repository + ansible.builtin.deb822_repository: + name: docker + types: deb + uris: https://download.docker.com/linux/debian + suites: "{{ ansible_facts['distribution_release'] }}" + components: stable + architectures: "{{ ansible_facts['architecture'] | replace('x86_64', 'amd64') }}" + signed_by: /usr/share/keyrings/docker-archive-keyring.asc + state: present + when: docker_installed.rc != 0 + +- name: Install Docker packages + ansible.builtin.apt: + name: + - docker-ce + - docker-ce-cli + - containerd.io + - docker-compose-plugin + state: present + update_cache: true + when: docker_installed.rc != 0 + +- name: Ensure Docker service is started and enabled + ansible.builtin.systemd: + name: docker + state: started + enabled: true + +- name: Add ansible user to docker group + ansible.builtin.user: + name: ansible + groups: docker + append: true + +- name: Create Forgejo data directory + ansible.builtin.file: + path: "{{ forgejo_data_dir }}" + state: directory + owner: root + group: root + mode: '0755' + +- name: Deploy docker-compose.yml + ansible.builtin.template: + src: docker-compose.yml.j2 + dest: "{{ forgejo_data_dir }}/docker-compose.yml" + owner: root + group: root + mode: '0644' + +- name: Check if Forgejo is running + ansible.builtin.command: + cmd: docker compose ps --services --filter "status=running" + chdir: "{{ forgejo_data_dir }}" + register: forgejo_running + changed_when: false + failed_when: false + +- name: Stop and remove stale Forgejo container + ansible.builtin.command: + cmd: docker compose down -v + chdir: "{{ forgejo_data_dir }}" + when: "'forgejo' in forgejo_running.stdout_lines | default([])" + +- name: Clean Forgejo data for fresh install + ansible.builtin.file: + path: "{{ forgejo_data_dir }}/data" + state: absent + +- name: Recreate Forgejo data directory + ansible.builtin.file: + path: "{{ forgejo_data_dir }}" + state: directory + owner: root + group: root + mode: '0755' + +- name: Start Forgejo with INSTALL_LOCK + ansible.builtin.command: + cmd: docker compose up -d + chdir: "{{ forgejo_data_dir }}" + register: forgejo_started + +- name: Wait for Forgejo to be ready + ansible.builtin.uri: + url: http://127.0.0.1:3000/ + method: GET + status_code: 200 + register: web_ready + retries: 30 + delay: 2 + until: web_ready.status == 200 + changed_when: false + +- name: Check if admin user exists + ansible.builtin.uri: + url: http://127.0.0.1:3000/api/v1/users/{{ forgejo_admin_user }} + method: GET + status_code: [200, 404] + register: admin_check + changed_when: false + +- name: Create admin user via docker exec + ansible.builtin.command: + cmd: > + docker exec -u git -w /data/gitea forgejo forgejo admin user create + --config /data/gitea/conf/app.ini + --username {{ forgejo_admin_user }} + --password {{ forgejo_admin_password }} + --email {{ forgejo_admin_email }} + --admin + when: admin_check.status == 404 + no_log: true + +- name: Verify admin user exists + ansible.builtin.uri: + url: http://127.0.0.1:3000/api/v1/users/{{ forgejo_admin_user }} + method: GET + status_code: 200 + changed_when: false diff --git a/ansible/roles/forgejo/templates/docker-compose.yml.j2 b/ansible/roles/forgejo/templates/docker-compose.yml.j2 new file mode 100644 index 0000000..4a16886 --- /dev/null +++ b/ansible/roles/forgejo/templates/docker-compose.yml.j2 @@ -0,0 +1,34 @@ +services: + forgejo: + image: codeberg.org/forgejo/forgejo:{{ forgejo_version }} + container_name: forgejo + restart: unless-stopped + networks: + - forgejo + volumes: + - {{ forgejo_data_dir }}/data:/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "127.0.0.1:{{ forgejo_http_port }}:3000" + - "0.0.0.0:{{ forgejo_ssh_port }}:22" + environment: + - USER_UID=1000 + - USER_GID=1000 + - FORGEJO__server__ROOT_URL=https://{{ forgejo_hostname }} + - FORGEJO__server__SSH_DOMAIN={{ forgejo_hostname }} + - FORGEJO__server__HTTP_PORT={{ forgejo_http_port }} + - FORGEJO__server__SSH_PORT={{ forgejo_ssh_port }} + - FORGEJO__server__DOMAIN={{ forgejo_hostname }} + - FORGEJO__database__DB_TYPE=sqlite3 + - FORGEJO__database__PATH=/data/gitea/gitea.db + - FORGEJO__security__INSTALL_LOCK=true + - FORGEJO__service__DISABLE_REGISTRATION=true + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000"] + interval: 10s + timeout: 5s + retries: 10 + +networks: + forgejo: diff --git a/ansible/roles/tailscale/tasks/main.yml b/ansible/roles/tailscale/tasks/main.yml index 908d69e..fb5d047 100644 --- a/ansible/roles/tailscale/tasks/main.yml +++ b/ansible/roles/tailscale/tasks/main.yml @@ -11,7 +11,7 @@ name: tailscale types: deb uris: https://pkgs.tailscale.com/stable/debian - suites: bookworm + suites: "{{ ansible_facts['distribution_release'] }}" components: main signed_by: /usr/share/keyrings/tailscale-archive-keyring.gpg state: present @@ -31,7 +31,10 @@ - name: Connect to Tailscale ansible.builtin.command: "tailscale up --authkey={{ tailscale_authkey }} {{ tailscale_args | default('') }}" - when: tailscale_status.rc != 0 or 'BackendState":"Running"' not in tailscale_status.stdout + when: + - not (tailscale_skip_connect | default(false)) + - tailscale_authkey | length > 0 + - tailscale_status.rc != 0 or 'BackendState":"Running"' not in tailscale_status.stdout register: tailscale_up changed_when: tailscale_up.rc == 0 @@ -39,11 +42,14 @@ ansible.builtin.command: tailscale ip -4 register: tailscale_ip_result changed_when: false + failed_when: false - name: Set Tailscale IP fact ansible.builtin.set_fact: tailscale_ip: "{{ tailscale_ip_result.stdout | trim }}" + when: tailscale_ip_result.rc == 0 - name: Display Tailscale IP ansible.builtin.debug: msg: "Tailscale IP: {{ tailscale_ip }}" + when: tailscale_ip_result.rc == 0 diff --git a/tofu/cloudflare.tf b/tofu/cloudflare.tf index a978027..e5f6093 100644 --- a/tofu/cloudflare.tf +++ b/tofu/cloudflare.tf @@ -1219,7 +1219,16 @@ resource "cloudflare_dns_record" "mcintire_me_a_git" { zone_id = cloudflare_zone.mcintire_me.id type = "A" name = "git" - content = "172.245.56.83" + content = "74.50.113.232" + proxied = false + ttl = 86400 +} + +resource "cloudflare_dns_record" "mcintire_me_a_hi" { + zone_id = cloudflare_zone.mcintire_me.id + type = "A" + name = "hi" + content = "74.50.113.232" proxied = false ttl = 86400 } @@ -1233,6 +1242,28 @@ resource "cloudflare_dns_record" "mcintire_me_a_vm1" { ttl = 86400 } +# ============================================================================= +# mcintire.me - AAAA records +# ============================================================================= + +resource "cloudflare_dns_record" "mcintire_me_aaaa_git" { + zone_id = cloudflare_zone.mcintire_me.id + type = "AAAA" + name = "git" + content = "2604:4500:0009:003d:1c00:bfff:fe00:0464" + proxied = false + ttl = 86400 +} + +resource "cloudflare_dns_record" "mcintire_me_aaaa_hi" { + zone_id = cloudflare_zone.mcintire_me.id + type = "AAAA" + name = "hi" + content = "2604:4500:0009:003d:1c00:bfff:fe00:0464" + proxied = false + ttl = 86400 +} + # ============================================================================= # mcintire.me - MX records # ============================================================================= diff --git a/tofu/proxmox.tf b/tofu/proxmox.tf index 62350af..ae477ca 100644 --- a/tofu/proxmox.tf +++ b/tofu/proxmox.tf @@ -24,7 +24,7 @@ module "flatcar_resolver1" { storage_root = "local-lvm" storage_ignition = "local" butane_conf = "${path.module}/flatcar-resolver.bu" - butane_snippet_path = "${path.module}" + butane_snippet_path = path.module network_devices = [ { @@ -57,7 +57,7 @@ module "flatcar_resolver2" { storage_root = "local-lvm" storage_ignition = "local" butane_conf = "${path.module}/flatcar-resolver2.bu" - butane_snippet_path = "${path.module}" + butane_snippet_path = path.module network_devices = [ {