host_exporters: IPMI exporter with sudo-pinned FreeIPMI tools for hosts with a BMC

This commit is contained in:
2026-10-25 15:04:56 +00:00
parent 7bed387ece
commit f91a9b8fc9
2 changed files with 113 additions and 0 deletions
+25
View File
@@ -2,3 +2,28 @@
# Adds prometheus-smartctl-exporter (trixie-backports) next to the node exporter # Adds prometheus-smartctl-exporter (trixie-backports) next to the node exporter
# on hosts with physical disks. # on hosts with physical disks.
host_exporters_smart: false host_exporters_smart: false
# Adds ipmitool and prometheus-ipmi-exporter (port 9290) on hosts with a BMC reachable through /dev/ipmi0.
host_exporters_ipmi: false
host_exporters_ipmi_collectors:
- ipmi
- bmc
- sel
- chassis
# FreeIPMI tool behind each collector and the fixed arguments the exporter (1.10) passes before
# --config-file <temporary file>. sudo admits exactly these, so a compromised exporter cannot
# run chassis control or clear the SEL. A different exporter version needs matching arguments
# (ipmi_up stays 0 until then). dcmi is not listed: the S1200RP BMC has no power monitoring.
host_exporters_ipmi_tools:
ipmi:
tool: ipmimonitoring
args: --quiet-cache --ignore-unrecognized-events --comma-separated-output --no-header-output --sdr-cache-recreate --output-event-bitmask --output-sensor-state
bmc:
tool: bmc-info
args: ""
sel:
tool: ipmi-sel
args: --info
chassis:
tool: ipmi-chassis
args: --get-chassis-status
+88
View File
@@ -69,3 +69,91 @@
enabled: true enabled: true
state: started state: started
ignore_errors: "{{ ansible_check_mode }}" ignore_errors: "{{ ansible_check_mode }}"
# Reads the BMC through /dev/ipmi0, read-only: no raw commands, no configuration.
# FreeIPMI refuses in-band access for any uid but 0, so the exporter runs as the packaged
# unprivileged user prometheus and starts the FreeIPMI tools it needs through sudo.
- name: Install the IPMI exporter
when: host_exporters_ipmi
block:
- name: Install ipmitool and the IPMI exporter
ansible.builtin.apt:
name:
- ipmitool
- sudo
- prometheus-ipmi-exporter
install_recommends: false
state: present
- name: Allow the exporter user to run the FreeIPMI tools
ansible.builtin.copy:
dest: /etc/sudoers.d/prometheus-ipmi-exporter
mode: "0440"
validate: visudo -cf %s
content: |
Defaults:prometheus !log_allowed, !pam_session
{% for collector in host_exporters_ipmi_collectors %}
prometheus ALL = (root) NOPASSWD: /usr/sbin/{{ host_exporters_ipmi_tools[collector].tool }} {{ host_exporters_ipmi_tools[collector].args }} --config-file /tmp/ipmi_exporter-{{ '[0-9a-f]' * 32 }}
{% endfor %}
register: host_exporters_ipmi_sudo
# collector_cmd replaces the tool itself, so each collector gets a wrapper that adds sudo.
- name: Create the sudo wrappers directory
ansible.builtin.file:
path: /usr/local/libexec/ipmi-exporter
state: directory
mode: "0755"
- name: Write the sudo wrappers
ansible.builtin.copy:
dest: /usr/local/libexec/ipmi-exporter/{{ item.key }}
mode: "0755"
content: |
#!/bin/sh
exec sudo -n /usr/sbin/{{ item.value.tool }} "$@"
loop: "{{ host_exporters_ipmi_tools | dict2items | selectattr('key', 'in', host_exporters_ipmi_collectors) | list }}"
loop_control:
label: "{{ item.key }}"
register: host_exporters_ipmi_wrappers
- name: Write the IPMI exporter configuration
ansible.builtin.copy:
dest: /etc/prometheus/ipmi-local.yml
mode: "0644"
content: |
modules:
default:
collectors:
{% for collector in host_exporters_ipmi_collectors %}
- {{ collector }}
{% endfor %}
collector_cmd:
{% for collector in host_exporters_ipmi_collectors %}
{{ collector }}: /usr/local/libexec/ipmi-exporter/{{ collector }}
{% endfor %}
register: host_exporters_ipmi_config
- name: Create the IPMI exporter unit drop-in directory
ansible.builtin.file:
path: /etc/systemd/system/prometheus-ipmi-exporter.service.d
state: directory
mode: "0755"
# sudo needs setuid, so NoNewPrivileges stays off.
- name: Run the IPMI exporter with that configuration
ansible.builtin.copy:
dest: /etc/systemd/system/prometheus-ipmi-exporter.service.d/local.conf
mode: "0644"
content: |
[Service]
ExecStart=
ExecStart=/usr/bin/prometheus-ipmi-exporter --config.file=/etc/prometheus/ipmi-local.yml
register: host_exporters_ipmi_unit
- name: Enable the IPMI exporter
ansible.builtin.systemd:
name: prometheus-ipmi-exporter
enabled: true
state: "{{ 'restarted' if (host_exporters_ipmi_config.changed or host_exporters_ipmi_unit.changed or host_exporters_ipmi_wrappers.changed) else 'started' }}"
daemon_reload: true
ignore_errors: "{{ ansible_check_mode }}"