infra/ansible/roles/monitor/tasks/main.yml
Graham McIntire 88bdbbe88f
ansible: add 'monitor' role mirroring live monitor.vntx.net (Icinga2 + Web 2)
Codifies the bare-metal Icinga2 master + Icinga Web 2 stack on
monitor.vntx.net (Ubuntu 22.04, Apache + mod_php, MariaDB 10.6, Icinga
2.16, IDO-MySQL backend). Re-running the role against the live host
should produce a near-identical configuration.

Role includes:
- Icinga apt repo + full package set (icinga2, ido-mysql, icingaweb2,
  monitoring-plugins, nagios-plugins-contrib, php + apache deps).
- MariaDB DB + user provisioning for both icinga2 (IDO daemon) and
  icingaweb2 (UI), plus a separate read-only `icinga` user that the UI
  uses to read the IDO. Schema bootstrapped only on first install.
- Templated icinga2 config (constants/zones/icinga2.conf), features-
  enabled symlinks (api/checker/command/ido-mysql/mainlog), and the
  three secret-bearing conf.d files (api-users, pagerduty x2) wired to
  vault vars.
- 23 dormant conf.d/* host + service definitions copied verbatim under
  files/. The live install has `include_recursive "conf.d"` commented
  out (a node-setup CLI artifact); role mirrors that. Set
  `icinga2_load_confd: true` in host_vars to rehydrate them.
- Icinga Web 2 ini files (config/auth/groups/roles/modules) + templated
  resources.ini for DB credentials. monitoring module enabled via
  symlink. icingaweb2 admin user bootstrapped on first install via a
  one-shot insert into icingaweb_user (gated on schema-not-yet-imported).
- Apache rewrite + ssl modules. Vhost itself is left to certbot --apache
  (Let's Encrypt cert is operator-managed, role doesn't manage it).

Cluster-wide cleanup:
- Gut roles/general/tasks/debian/icinga2.yml down to the uninstall
  safety net (kept the sweep that removes icinga2 from non-monitoring
  hosts). The previous master-side install + cert-distribution tasks
  referenced templates that never existed in the role; the new monitor
  role replaces them properly.

Operator action before first apply:
- Create host_vars/monitor.vntx.net/vault.yml with the seven required
  secrets (see roles/monitor/README.md). Live values can be pulled from
  /etc/icinga2/conf.d/{api-users,pagerduty-*,ido-mysql}.conf and
  /etc/icingaweb2/resources.ini on the live host.
- Generate the icingaweb2 admin password hash via
  `php -r 'echo password_hash("plaintext", PASSWORD_DEFAULT);'`.
- Run --check --diff before applying; the role is meant to be idempotent
  but hasn't been verified against the live install.
2026-05-08 13:00:53 -05:00

267 lines
7.8 KiB
YAML

---
# Builds the Icinga2 master + Icinga Web 2 stack on Ubuntu (matches the
# live install on monitor.vntx.net). Idempotent; safe to re-run.
- name: Assert required vault values
ansible.builtin.assert:
that:
- icinga2_ido_db_password is defined and icinga2_ido_db_password | length > 0
- icingaweb2_db_password is defined and icingaweb2_db_password | length > 0
- icingaweb2_ido_db_password is defined and icingaweb2_ido_db_password | length > 0
- icinga2_api_user_root_password is defined and icinga2_api_user_root_password | length > 0
- icinga2_api_user_icingaweb2_password is defined and icinga2_api_user_icingaweb2_password | length > 0
- icinga2_iftraffic_snmp_community is defined and icinga2_iftraffic_snmp_community | length > 0
fail_msg: >-
Missing one or more required vault variables for the monitor role.
See roles/monitor/README.md for the full list.
# ----------- Repo + packages -----------
- name: Ensure /etc/apt/keyrings exists
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
- name: Add Icinga apt signing key
ansible.builtin.get_url:
url: "{{ icinga2_apt_key_url }}"
dest: "{{ icinga2_apt_keyring }}"
mode: "0644"
force: false
- name: Add Icinga apt repository
ansible.builtin.apt_repository:
repo: "{{ icinga2_apt_repo }}"
filename: icinga
state: present
update_cache: true
- name: Install database stack
ansible.builtin.apt:
name: "{{ icinga2_db_packages }}"
state: present
update_cache: true
- name: Install web stack
ansible.builtin.apt:
name: "{{ icinga2_web_packages }}"
state: present
- name: Install Icinga2 + Icinga Web 2
ansible.builtin.apt:
name: "{{ icinga2_packages }}"
state: present
# ----------- MariaDB -----------
- name: Ensure mariadb is started + enabled
ansible.builtin.systemd:
name: mariadb
state: started
enabled: true
- name: Create IDO database
community.mysql.mysql_db:
name: "{{ icinga2_ido_db_name }}"
state: present
login_unix_socket: /run/mysqld/mysqld.sock
- name: Create IDO daemon DB user (icinga2)
community.mysql.mysql_user:
name: "{{ icinga2_ido_db_user }}"
host: localhost
password: "{{ icinga2_ido_db_password }}"
priv: "{{ icinga2_ido_db_name }}.*:ALL"
state: present
login_unix_socket: /run/mysqld/mysqld.sock
- name: Create read-only IDO user for icingaweb2 (icinga)
community.mysql.mysql_user:
name: "{{ icingaweb2_ido_db_user }}"
host: localhost
password: "{{ icingaweb2_ido_db_password }}"
priv: "{{ icingaweb2_ido_db_name }}.*:SELECT,SHOW VIEW"
state: present
login_unix_socket: /run/mysqld/mysqld.sock
- name: Create icingaweb2 database
community.mysql.mysql_db:
name: "{{ icingaweb2_db_name }}"
state: present
login_unix_socket: /run/mysqld/mysqld.sock
- name: Create icingaweb2 DB user
community.mysql.mysql_user:
name: "{{ icingaweb2_db_user }}"
host: localhost
password: "{{ icingaweb2_db_password }}"
priv: "{{ icingaweb2_db_name }}.*:ALL"
state: present
login_unix_socket: /run/mysqld/mysqld.sock
- name: Check whether IDO schema has been imported
community.mysql.mysql_query:
login_db: "{{ icinga2_ido_db_name }}"
query: "SHOW TABLES LIKE 'icinga_dbversion'"
login_unix_socket: /run/mysqld/mysqld.sock
register: ido_schema_check
changed_when: false
- name: Import IDO schema (first install only)
community.mysql.mysql_db:
name: "{{ icinga2_ido_db_name }}"
state: import
target: "{{ icinga2_ido_schema_path }}"
login_unix_socket: /run/mysqld/mysqld.sock
when: ido_schema_check.rowcount[0] == 0
notify: Restart icinga2
- name: Check whether icingaweb2 schema has been imported
community.mysql.mysql_query:
login_db: "{{ icingaweb2_db_name }}"
query: "SHOW TABLES LIKE 'icingaweb_user'"
login_unix_socket: /run/mysqld/mysqld.sock
register: iweb2_schema_check
changed_when: false
- name: Import icingaweb2 schema (first install only)
community.mysql.mysql_db:
name: "{{ icingaweb2_db_name }}"
state: import
target: /usr/share/icingaweb2/schema/mysql.schema.sql
login_unix_socket: /run/mysqld/mysqld.sock
when: iweb2_schema_check.rowcount[0] == 0
- name: Bootstrap icingaweb2 admin account (first install only)
community.mysql.mysql_query:
login_db: "{{ icingaweb2_db_name }}"
query: >-
INSERT INTO icingaweb_user (name, active, password_hash) VALUES
(%(user)s, 1, %(hash)s)
ON DUPLICATE KEY UPDATE password_hash = VALUES(password_hash)
named_args:
user: "{{ icingaweb2_admin_user }}"
hash: "{{ icingaweb2_admin_password_hash }}"
login_unix_socket: /run/mysqld/mysqld.sock
when:
- iweb2_schema_check.rowcount[0] == 0
- icingaweb2_admin_password_hash is defined
# ----------- Icinga2 daemon config -----------
- name: Render icinga2.conf
ansible.builtin.template:
src: icinga2/icinga2.conf.j2
dest: /etc/icinga2/icinga2.conf
owner: nagios
group: nagios
mode: "0640"
notify: Restart icinga2
- name: Render constants.conf
ansible.builtin.template:
src: icinga2/constants.conf.j2
dest: /etc/icinga2/constants.conf
owner: nagios
group: nagios
mode: "0640"
notify: Restart icinga2
- name: Render zones.conf
ansible.builtin.template:
src: icinga2/zones.conf.j2
dest: /etc/icinga2/zones.conf
owner: nagios
group: nagios
mode: "0640"
notify: Restart icinga2
- name: Render features-available/ido-mysql.conf
ansible.builtin.template:
src: icinga2/features-available/ido-mysql.conf.j2
dest: /etc/icinga2/features-available/ido-mysql.conf
owner: nagios
group: nagios
mode: "0640"
notify: Restart icinga2
- name: Enable Icinga2 features
ansible.builtin.file:
src: "/etc/icinga2/features-available/{{ item }}.conf"
dest: "/etc/icinga2/features-enabled/{{ item }}.conf"
state: link
force: true
loop: "{{ icinga2_enabled_features }}"
notify: Restart icinga2
# ----------- conf.d/ host + service definitions -----------
- name: Sync static conf.d files
ansible.builtin.copy:
src: icinga2/conf.d/
dest: /etc/icinga2/conf.d/
owner: nagios
group: nagios
mode: "0640"
notify: Restart icinga2
- name: Render templated conf.d files (api-users + pagerduty)
ansible.builtin.template:
src: "icinga2/conf.d/{{ item }}.j2"
dest: "/etc/icinga2/conf.d/{{ item }}"
owner: nagios
group: nagios
mode: "0640"
loop:
- api-users.conf
- pagerduty-icinga2.conf
- pagerduty-servers-icinga2.conf
notify: Restart icinga2
# ----------- Icinga Web 2 config -----------
- name: Sync icingaweb2 ini files (non-secret)
ansible.builtin.copy:
src: icingaweb2/
dest: /etc/icingaweb2/
owner: www-data
group: icingaweb2
mode: "0660"
directory_mode: "02770"
notify: Reload apache2
- name: Render icingaweb2 resources.ini (DB credentials)
ansible.builtin.template:
src: icingaweb2/resources.ini.j2
dest: /etc/icingaweb2/resources.ini
owner: www-data
group: icingaweb2
mode: "0660"
notify: Reload apache2
- name: Enable monitoring module
ansible.builtin.file:
src: /usr/share/icingaweb2/modules/monitoring
dest: /etc/icingaweb2/enabledModules/monitoring
state: link
force: true
notify: Reload apache2
# ----------- Apache -----------
- name: Enable apache modules required by icingaweb2
community.general.apache2_module:
name: "{{ item }}"
state: present
loop:
- rewrite
- ssl
notify: Restart apache2
- name: Ensure apache is started + enabled
ansible.builtin.systemd:
name: apache2
state: started
enabled: true
# ----------- Service start -----------
- name: Ensure icinga2 is started + enabled
ansible.builtin.systemd:
name: icinga2
state: started
enabled: true