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
|
- bmc
|
||||||
- sel
|
- sel
|
||||||
- chassis
|
- 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
|
# 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
|
# --config-file. sudo admits exactly these, so a compromised exporter cannot run chassis control or
|
||||||
# run chassis control or clear the SEL. A different exporter version needs matching arguments
|
# clear the SEL. A different exporter version needs matching arguments (ipmi_up stays 0 until then).
|
||||||
# (ipmi_up stays 0 until then). dcmi is not listed: the S1200RP BMC has no power monitoring.
|
# dcmi is not listed: the S1200RP BMC has no power monitoring.
|
||||||
host_exporters_ipmi_tools:
|
host_exporters_ipmi_tools:
|
||||||
ipmi:
|
ipmi:
|
||||||
tool: ipmimonitoring
|
tool: ipmimonitoring
|
||||||
|
|||||||
@@ -91,13 +91,34 @@
|
|||||||
mode: "0440"
|
mode: "0440"
|
||||||
validate: visudo -cf %s
|
validate: visudo -cf %s
|
||||||
content: |
|
content: |
|
||||||
Defaults:prometheus !log_allowed, !pam_session
|
Defaults:prometheus !pam_session
|
||||||
{% for collector in host_exporters_ipmi_collectors %}
|
{% 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 %}
|
{% endfor %}
|
||||||
register: host_exporters_ipmi_sudo
|
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
|
- name: Create the sudo wrappers directory
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: /usr/local/libexec/ipmi-exporter
|
path: /usr/local/libexec/ipmi-exporter
|
||||||
@@ -110,7 +131,17 @@
|
|||||||
mode: "0755"
|
mode: "0755"
|
||||||
content: |
|
content: |
|
||||||
#!/bin/sh
|
#!/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: "{{ host_exporters_ipmi_tools | dict2items | selectattr('key', 'in', host_exporters_ipmi_collectors) | list }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.key }}"
|
label: "{{ item.key }}"
|
||||||
|
|||||||
Reference in New Issue
Block a user