host_exporters: IPMI tools read a static root-owned FreeIPMI config pinned in sudoers, sudo logs allowed commands
This commit is contained in:
@@ -10,10 +10,16 @@ host_exporters_ipmi_collectors:
|
||||
- bmc
|
||||
- sel
|
||||
- chassis
|
||||
# The tools run as root and parse the FreeIPMI configuration file named by --config-file. The exporter
|
||||
# writes that file itself (a named pipe in its /tmp, empty for in-band local mode), so a compromised
|
||||
# exporter could set options such as sdr-cache-directory and make root write anywhere. The wrappers
|
||||
# therefore replace the path with this static root-owned file, and sudoers admits only that path.
|
||||
host_exporters_ipmi_freeipmi_conf: /etc/ipmi-exporter/freeipmi.conf
|
||||
|
||||
# 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.
|
||||
# --config-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
|
||||
|
||||
@@ -91,13 +91,34 @@
|
||||
mode: "0440"
|
||||
validate: visudo -cf %s
|
||||
content: |
|
||||
Defaults:prometheus !log_allowed, !pam_session
|
||||
Defaults:prometheus !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 }}
|
||||
prometheus ALL = (root) NOPASSWD: /usr/sbin/{{ host_exporters_ipmi_tools[collector].tool }} {{ host_exporters_ipmi_tools[collector].args }} --config-file {{ host_exporters_ipmi_freeipmi_conf }}
|
||||
{% endfor %}
|
||||
register: host_exporters_ipmi_sudo
|
||||
|
||||
# collector_cmd replaces the tool itself, so each collector gets a wrapper that adds sudo.
|
||||
- name: Create the FreeIPMI configuration directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ host_exporters_ipmi_freeipmi_conf | dirname }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
# See the defaults for why the file is static.
|
||||
- name: Write the FreeIPMI configuration
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ host_exporters_ipmi_freeipmi_conf }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: |
|
||||
# In-band access needs no settings. Not even sdr-cache-directory: the tools without an SDR cache
|
||||
# reject it, and the cache of the ipmi tool stays in the home of root.
|
||||
|
||||
# collector_cmd replaces the tool itself, so each collector gets a wrapper that adds sudo. It swaps
|
||||
# the exporter's --config-file for the static one. The exporter fills a named pipe at the path it
|
||||
# passes and waits for a reader; the wrapper drains it so that writer does not hang.
|
||||
- name: Create the sudo wrappers directory
|
||||
ansible.builtin.file:
|
||||
path: /usr/local/libexec/ipmi-exporter
|
||||
@@ -110,7 +131,17 @@
|
||||
mode: "0755"
|
||||
content: |
|
||||
#!/bin/sh
|
||||
exec sudo -n /usr/sbin/{{ item.value.tool }} "$@"
|
||||
n=$#
|
||||
while [ "$n" -gt 0 ]; do
|
||||
a=$1; shift; n=$((n - 1))
|
||||
if [ "$a" = --config-file ] && [ "$n" -gt 0 ]; then
|
||||
timeout 5 cat "$1" > /dev/null 2>&1
|
||||
shift; n=$((n - 1))
|
||||
else
|
||||
set -- "$@" "$a"
|
||||
fi
|
||||
done
|
||||
exec sudo -n /usr/sbin/{{ item.value.tool }} "$@" --config-file {{ host_exporters_ipmi_freeipmi_conf }}
|
||||
loop: "{{ host_exporters_ipmi_tools | dict2items | selectattr('key', 'in', host_exporters_ipmi_collectors) | list }}"
|
||||
loop_control:
|
||||
label: "{{ item.key }}"
|
||||
|
||||
Reference in New Issue
Block a user