infra/ansible/findings.md
Graham McIntire 118aa70f51
cleanup: remove broken makefile targets, fix general.yml references
- Remove all docker/build/push targets (no Dockerfile exists)
- Remove broken qc-web target (group doesn't exist)
- Remove clean, dirs, base-*, local, latest targets (docker-only)
- Fix bootstrap-network, run-all to use playbook.yml not general.yml
- Fix install-collections (deleted files referenced)
- Update findings.md
2026-06-04 14:37:55 -05:00

9.9 KiB

Ansible Repo Audit — Findings

Generated 2026-06-04 after auditing ~70 role files, 7 playbooks/configs, 21 host_vars, and 50+ templates.

Completed Fixes

  • Removed all Forgejo references — deleted roles/forgejo/, removed forgejo play from playbook.yml, removed forgejo_servers group from hosts, removed forgejo vars from host_vars/git.mcintire.me.yml, removed docker-mirror CNAME from DNS zone.
  • Fixed hardcoded SSH private key paths — replaced all 20 occurrences of /Users/graham/.ssh/id_ed25519 and /Users/graham/.ssh/ansible with ansible_ssh_private_key_file: "{{ lookup('env', 'ANSIBLE_SSH_PRIVATE_KEY_FILE') | default('~/.ssh/ansible', true) }}" in group_vars/all/all.yml. Set ANSIBLE_SSH_PRIVATE_KEY_FILE env var to override.
  • Fixed community.mysql collection — added to requirements.yml and installed (fixes monitor role crash).
  • Removed broken setup-user Makefile target — referenced non-existent setup-user.sh.
  • Fixed tracking issuesgit rm --cached for 5 .DS_Store files and .vscode/settings.json; added .ansible/ to .gitignore.
  • Fixed stale documentation — updated CLAUDE.md and README.md to reflect actual playbook structure, roles, inventory groups, and collections.
  • Fixed non-FQCN module references — converted ~290 short module names (apt:, service:, template:, etc.) to FQCNs (ansible.builtin.apt:, etc.) across all roles and playbooks.
  • Fixed become:/gather_facts: boolean formsbecome: yesbecome: true, gather_facts: nogather_facts: false in all plays.
  • Removed deprecated inject_facts_as_vars from ansible.cfg.
  • Added missing task namesimport_tasks/include_role calls in base/tasks/main.yml and general/tasks/main.yml now have descriptive names.
  • Fixed partial-become bug — added become: yes to librenms clone task.
  • Added pipefail to risky shell command in udp-gro-fix.yml.
  • Quoted octal mode valuesmode: 0755mode: '0755', etc.
  • Hardened .ansible-lint config — set profile: production, added exclude paths, removed all skip_list entries.
  • Added defaults for 4 undefined template variablesntpserver, template_run_date, deb_mirror, sshd.* all have safe fallbacks now.
  • Removed Alpine role referenceinclude_role: name=alpine in roles/base/tasks/main.yml was pointing to a non-existent role.
  • Fixed SSH -t flag placement — Moved before hostname in both bootstrap-reboot and reboot Makefile targets.
  • Fixed general.yml referencesbootstrap-network, run-all, qc-web Makefile targets now use playbook.yml; removed qc-web target (qc_web group doesn't exist).
  • Removed all Docker targetsbuild-*, run-*, push-*, base-*, latest, local, clean, dirs — no Dockerfile at repo root ever existed.
  • Fixed install-collections — removed references to deleted roles/requirements.yml and scripts/fix-deprecated-imports.sh.
  • Deleted unreferenced roles — removed icinga2-container/, debug/, mailcow/, syncthing/ (no plays applied them).
  • Removed unused Galaxy dependencies — deleted geerlingguy.docker, geerlingguy.kubernetes from requirements.yml; deleted entire roles/requirements.yml (graylog2.graylog, bertvv.bind, artis3n.tailscale); merged netbox.netbox into root file.
  • Cleaned fact_cache — removed 56 stale files.
  • Removed unused inventory groupshome_cluster and dns_servers (defined but never targeted).

🔴 Security (fix immediately)

# Issue Location
1 Plaintext secrets in .envrc — Tailscale auth key, PowerDNS API key, PostgreSQL password, Proxmox root password in plaintext on disk. Use ansible-vault or a secrets manager. .envrc:3,6,9,14
2 Hardcoded cloud-init passwordcipassword in plaintext for BIND9 VM. Anyone with repo access can SSH in as debian. playbook.yml:159
3 Hardcoded SSH public key — Same ed25519 key in playbook, setup-host.sh, and base/files/ansible.pub. Not vaulted. Rotate + move to vault. playbook.yml:106, setup-host.sh:17, base/files/ansible.pub

🔴 Broken Infrastructure (will fail at runtime)

# Issue Location
5 general.yml does not exist — Referenced by 3 Makefile targets plus docs. Content was merged into playbook.yml but Makefile was never updated. Makefile:40,56,68
6 No Dockerfile at repo root — All build-*, run-*, push-* Makefile targets fail. base-latest/base-local reference non-existent sub-targets. Makefile:3-30
8 geerlingguy.postgresql not in requirements.yml — Role referenced in playbook.yml:73 for postgresql_servers but not installable via ansible-galaxy. playbook.yml:73, requirements.yml
9 alpine role referenced but doesn't existinclude_role: name=alpine will crash on any Alpine host. roles/base/tasks/main.yml:46-48
10 db.aprs.me has host_vars but no inventory entry — Orphaned host_vars, Ansible never loads them. host_vars/db.aprs.me.yml, hosts

🔴 Runtime Crashes (undefined template variables)

All template variable defaults have been added. See Completed Fixes for details.


🟡 Logic Bugs

# Issue Location
18 Firewall role runs twice on every non-Proxmox host — once via base role's include_role and once via explicit firewall play. playbook.yml:18-25, roles/base/tasks/main.yml:50-55
19 SSH -t flag in wrong position — Placed after hostname where it's treated as a remote command, not a local SSH option. Makefile:43,65
20 ansible_user in [ssh_connection] section — Silently ignored by Ansible. Should be remote_user under [defaults]. ansible.cfg:32
21 run.sh masks playbook failurestee pipeline without set -o pipefail always exits 0. run.sh:18
22 playbook_handlers.yml is orphaned — Defines Restart firewalld handler but is never imported by any playbook. playbook_handlers.yml
23 changed_when: true on idempotent commandsremoves prevents re-execution but changed_when overrides natural detection. roles/debian/tasks/main.yml:5, roles/general/tasks/udp-gro-fix.yml:27, roles/mailcow/tasks/main.yml:156
24 run-local mounts wrong path${PWD}/run/fact_cache doesn't exist. dirs creates tmp/fact_cache which is never used. Makefile:10,34
25 TAILSCALE_AUTHKEY vs TAILSCALE_KEY mismatch.envrc exports TAILSCALE_AUTHKEY but docs say role expects TAILSCALE_KEY. .envrc:3 vs CLAUDE.md
26 bootstrap.yml second play unconditionally uses become_method: sudo — Will hang if graham needs sudo password and -K not provided. bootstrap.yml:71-81
27 mail.mcintire.me and git.mcintire.me use bare ansible_host=mail/ansible_host=git — DNS resolution depends on control node's search domain. hosts:24,51
28 Variable naming inconsistency — Some templates use flat names (nameservers), some use nested (network.nameservers). resolv.conf.j2 vs group_vars/all/all.yml

🟡 Best Practice / Lint Violations

All items in this section have been fixed.


🟡 Dead Code & Stale Files

All items in this section have been fixed.


🟢 Previously Fixed

# Issue Fix
4 Hardcoded Forgejo runner token in host_vars Deleted with forgejo cleanup
7 setup-user.sh doesn't exist Removed broken Makefile target
11 community.mysql.mysql_db module not found Added community.mysql to requirements.yml and installed
45 5 .DS_Store files tracked by git git rm --cached
46 .vscode/settings.json tracked despite gitignore git rm --cached
47 .ansible/ directory not in .gitignore Added to .gitignore
48 Stale CLAUDE.md (references general.yml, missing roles, wrong commands) Rewritten with accurate playbooks, roles, collections, commands
49 Stale README.md (wrong directory tree, incomplete groups) Rewritten with accurate structure and inventory groups
29 Non-FQCN module references (~290 instances) Converted all to FQCNs across all roles and playbooks
30 Unquoted octal mode values (9 instances) All quoted with single quotes
31 become: yes / gather_facts: no Changed to become: true / gather_facts: false
32 inject_facts_as_vars deprecated Removed from ansible.cfg
33 Missing task names on import_tasks/include_role Added descriptive names
34 partial-become bug in librenms Added become: yes
35 risky-shell-pipe without pipefail Added set -o pipefail
36 Minimal .ansible-lint config Set profile: production, added exclude paths, empty skip_list
37 Unreferenced icinga2-container/ and debug/ roles Deleted
38 Unreferenced mailcow/ and syncthing/ roles Deleted
39 geerlingguy.docker and geerlingguy.kubernetes never used Removed from requirements.yml
40 graylog2.graylog and bertvv.bind never used Deleted roles/requirements.yml
41 collections/requirements.yml redundant Merged into root file, deleted
42 Stale fact_cache files (56 files) Deleted all
43 home_cluster and dns_servers groups never targeted Removed from hosts
12-15 Undefined template variables (ntpserver, template_run_date, deb_mirror, sshd.*) Added `
-- Alpine role reference to non-existent role Removed from roles/base/tasks/main.yml
-- SSH -t flag after hostname in Makefile Moved before hostname

  1. Rotate all 4 plaintext secrets in .envrc and move to ansible-vault
  2. Add geerlingguy.postgresql to requirements.yml — will crash otherwise