Files
MajorWiki/02-selfhosting/monitoring/netdata-selinux-avc-chart.md

4.7 KiB

title, domain, category, tags, status, created, updated
title domain category tags status created updated
Netdata SELinux AVC Denial Monitoring selfhosting monitoring
netdata
selinux
fedora
monitoring
ausearch
charts.d
published 2026-03-27 2026-03-27

Netdata SELinux AVC Denial Monitoring

A custom charts.d plugin that tracks SELinux AVC denials over time via Netdata. Deployed on all Fedora boxes in the fleet where SELinux is Enforcing.

What It Does

The plugin runs ausearch -m avc every 60 seconds and reports the count of AVC denial events from the last 10 minutes. This gives a real-time chart in Netdata Cloud showing SELinux denial spikes — useful for catching misconfigurations after service changes or package updates.

Where It's Deployed

Host OS SELinux Chart Installed
majorhome Fedora 43 Enforcing Yes
majorlab Fedora 43 Enforcing Yes
majormail Fedora 43 Enforcing Yes
majordiscord Fedora 43 Enforcing Yes

Ubuntu hosts (dca, teelia, tttpod, majortoot, majorlinux) do not run SELinux and do not have this chart.

Installation

1. Create the Chart Plugin

Create /etc/netdata/charts.d/selinux.chart.sh:

cat > /etc/netdata/charts.d/selinux.chart.sh << 'EOF'
# SELinux AVC denial counter for Netdata charts.d
selinux_update_every=60
selinux_priority=90000

selinux_check() {
    which ausearch >/dev/null 2>&1 || return 1
    return 0
}

selinux_create() {
    cat <<CHART
CHART selinux.avc_denials '' 'SELinux AVC Denials (last 10 min)' 'denials' selinux '' line 90000 $selinux_update_every ''
DIMENSION denials '' absolute 1 1
CHART
    return 0
}

selinux_update() {
    local count
    count=$(sudo /usr/bin/ausearch -m avc -if /var/log/audit/audit.log -ts recent 2>/dev/null | grep -c "type=AVC")
    echo "BEGIN selinux.avc_denials $1"
    echo "SET denials = ${count}"
    echo "END"
    return 0
}
EOF

2. Grant Netdata Sudo Access to ausearch

ausearch requires root to read the audit log. Add a sudoers entry for the netdata user:

echo 'netdata ALL=(root) NOPASSWD: /usr/bin/ausearch -m avc -if /var/log/audit/audit.log -ts recent' > /etc/sudoers.d/netdata-selinux
chmod 440 /etc/sudoers.d/netdata-selinux
visudo -c

The visudo -c validates syntax. If it reports errors, fix the file before proceeding — a broken sudoers file can lock out sudo entirely.

3. Restart Netdata

systemctl restart netdata

4. Verify

Check that the chart is collecting data:

curl -s 'http://localhost:19999/api/v1/chart?chart=selinux.avc_denials' | python3 -c "
import sys, json
d = json.load(sys.stdin)
print(f'Chart: {d[\"id\"]}')
print(f'Update every: {d[\"update_every\"]}s')
print(f'Type: {d[\"chart_type\"]}')
"

If the chart doesn't appear, check that charts.d is enabled in /etc/netdata/netdata.conf and that the plugin file is readable by the netdata user.

Known Side Effect: pam_systemd Log Noise

Because the netdata user calls sudo ausearch every 60 seconds, pam_systemd logs a warning each time:

pam_systemd(sudo:session): Failed to check if /run/user/0/bus exists, ignoring: Permission denied

This is cosmetic. The sudo command succeeds — pam_systemd just can't find a D-Bus user session for the netdata service account, which is expected. The message volume scales with the collection interval (1,440/day at 60-second intervals).

To suppress it, the system-auth PAM config on Fedora already marks pam_systemd.so as -session optional (the - prefix means "don't fail if the module errors"). The messages are informational log noise, not actual failures. No PAM changes are needed.

If the log volume is a concern for log analysis or monitoring, filter it at the journald level:

# /etc/rsyslog.d/suppress-pam-systemd.conf
:msg, contains, "pam_systemd(sudo:session): Failed to check" stop

Or in Netdata's log alert config, exclude the pattern from any log-based alerts.

Fleet Audit

To verify the chart is deployed and functioning on all Fedora hosts:

for host in majorhome majorlab majormail majordiscord; do
  echo -n "=== $host: "
  ssh root@$host "curl -s 'http://localhost:19999/api/v1/chart?chart=selinux.avc_denials' 2>/dev/null | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d[\"id\"], \"every\", str(d[\"update_every\"])+\"s\")' 2>/dev/null || echo 'NOT FOUND'"
done