125 lines
6.9 KiB
Bash
Executable File
125 lines
6.9 KiB
Bash
Executable File
#!/bin/sh
|
|
# ansipa_opnsense — CheckMK local check for an OPNsense firewall.
|
|
#
|
|
# Install: copy to /usr/local/lib/check_mk_agent/local/ansipa_opnsense, chmod 755
|
|
# (ansipa-opnsense-checkmk-install.sh does this for you).
|
|
#
|
|
# ── Why this is /bin/sh and not bash ────────────────────────────────────────
|
|
# OPNsense is FreeBSD. There is no bash in the base system, no GNU grep (so no
|
|
# `grep -P`), and no `mapfile`. Everything here is POSIX sh + awk/sed, unlike
|
|
# the sibling ansipa-pve/pbs-monitor.sh scripts which target Debian and may use
|
|
# bashisms. Do not "modernise" this file with [[ ]] or grep -o -P; it will
|
|
# silently stop producing output on the firewall.
|
|
#
|
|
# Emits (CheckMK local-check format: "<state> <service> <perfdata> <text>"):
|
|
# Ansipa_OPN_Gateways per-gateway up/down from the gateway status API
|
|
# Ansipa_OPN_States pf state-table usage against the configured hard limit
|
|
# Ansipa_OPN_Firmware pending firmware/package updates
|
|
# Ansipa_OPN_CARP CARP interface health (skipped cleanly if unused)
|
|
#
|
|
# CPU / RAM / disk / interface counters are already covered by the stock
|
|
# FreeBSD agent checks — deliberately not duplicated here.
|
|
|
|
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
|
|
export PATH
|
|
|
|
# ── Gateways ────────────────────────────────────────────────────────────────
|
|
# `configctl interface gateways status` returns a JSON blob. There is no jq in
|
|
# the OPNsense base system, so the objects are split onto separate lines and
|
|
# read with sed. We count anything not explicitly Online/none as degraded.
|
|
if command -v configctl >/dev/null 2>&1; then
|
|
_gw=$(configctl interface gateways status 2>/dev/null)
|
|
if [ -z "$_gw" ]; then
|
|
echo "3 Ansipa_OPN_Gateways - could not query gateway status"
|
|
else
|
|
# One JSON object per line, then pull name + status_translated per line.
|
|
_parsed=$(printf '%s' "$_gw" | sed 's/},[[:space:]]*{/}\
|
|
{/g')
|
|
_total=$(echo "$_parsed" | grep -c '"name"' 2>/dev/null || echo 0)
|
|
_downlist=$(echo "$_parsed" \
|
|
| sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*"status_translated"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1=\2/p' \
|
|
| grep -v '=Online$' | tr '\n' ' ')
|
|
_down=$(printf '%s' "$_downlist" | wc -w | tr -d ' ')
|
|
|
|
if [ "${_total:-0}" -eq 0 ]; then
|
|
echo "3 Ansipa_OPN_Gateways - no gateways found in status output"
|
|
elif [ "${_down:-0}" -gt 0 ]; then
|
|
echo "2 Ansipa_OPN_Gateways gateways_total=${_total};; gateways_down=${_down};1;1 ${_down}/${_total} gateway(s) not online: ${_downlist}"
|
|
else
|
|
echo "0 Ansipa_OPN_Gateways gateways_total=${_total};; gateways_down=0;1;1 all ${_total} gateway(s) online"
|
|
fi
|
|
fi
|
|
else
|
|
echo "3 Ansipa_OPN_Gateways - configctl not found (not an OPNsense host?)"
|
|
fi
|
|
|
|
# ── pf state table ──────────────────────────────────────────────────────────
|
|
# A firewall that hits its state limit drops new connections while looking
|
|
# perfectly healthy on CPU/RAM, so this is the check that actually catches it.
|
|
if command -v pfctl >/dev/null 2>&1; then
|
|
_cur=$(pfctl -si 2>/dev/null | awk '/current entries/ {print $3; exit}')
|
|
_lim=$(pfctl -sm 2>/dev/null | awk '/^states/ {print $4; exit}')
|
|
if [ -z "$_cur" ] || [ -z "$_lim" ] || [ "${_lim:-0}" -eq 0 ] 2>/dev/null; then
|
|
echo "3 Ansipa_OPN_States - could not read pf state table counters"
|
|
else
|
|
_pct=$(( _cur * 100 / _lim ))
|
|
_perf="states=${_cur};$(( _lim * 80 / 100 ));$(( _lim * 90 / 100 ));0;${_lim} states_pct=${_pct}%;80;90"
|
|
if [ "$_pct" -ge 90 ]; then
|
|
echo "2 Ansipa_OPN_States ${_perf} pf states ${_cur}/${_lim} (${_pct}%) — near the hard limit"
|
|
elif [ "$_pct" -ge 80 ]; then
|
|
echo "1 Ansipa_OPN_States ${_perf} pf states ${_cur}/${_lim} (${_pct}%)"
|
|
else
|
|
echo "0 Ansipa_OPN_States ${_perf} pf states ${_cur}/${_lim} (${_pct}%)"
|
|
fi
|
|
fi
|
|
else
|
|
echo "3 Ansipa_OPN_States - pfctl not found"
|
|
fi
|
|
|
|
# ── Firmware / package updates ──────────────────────────────────────────────
|
|
# Read the cached firmware status rather than triggering a check: this runs
|
|
# every agent poll, and hitting the mirrors that often is antisocial and slow.
|
|
# `configctl firmware check` refreshes the cache; a cron job on the firewall
|
|
# should do that once a day (the installer sets one up).
|
|
_fw_status=/tmp/pkg_upgrade.progress
|
|
if command -v opnsense-version >/dev/null 2>&1; then
|
|
_pending=""
|
|
if [ -r /var/cache/opnsense-update/changelog/firmware.json ]; then
|
|
_pending=$(sed -n 's/.*"new_packages_count"[[:space:]]*:[[:space:]]*"\{0,1\}\([0-9]*\).*/\1/p' \
|
|
/var/cache/opnsense-update/changelog/firmware.json 2>/dev/null | head -1)
|
|
fi
|
|
if [ -z "$_pending" ] && command -v pkg >/dev/null 2>&1; then
|
|
# Offline count from the local package DB — no network access.
|
|
_pending=$(pkg version -vIL= 2>/dev/null | wc -l | tr -d ' ')
|
|
fi
|
|
_ver=$(opnsense-version 2>/dev/null | head -1)
|
|
if [ -z "$_pending" ]; then
|
|
echo "3 Ansipa_OPN_Firmware - could not determine pending update count (${_ver})"
|
|
elif [ "$_pending" -gt 0 ] 2>/dev/null; then
|
|
echo "1 Ansipa_OPN_Firmware pending_updates=${_pending};1;;0; ${_pending} update(s) pending — ${_ver}"
|
|
else
|
|
echo "0 Ansipa_OPN_Firmware pending_updates=0;1;;0; up to date — ${_ver}"
|
|
fi
|
|
else
|
|
echo "3 Ansipa_OPN_Firmware - opnsense-version not found"
|
|
fi
|
|
unset _fw_status
|
|
|
|
# ── CARP (high availability) ────────────────────────────────────────────────
|
|
# Only meaningful on an HA pair. On a standalone firewall there are no carp
|
|
# interfaces and we stay silent rather than emitting a permanent UNKNOWN.
|
|
if command -v ifconfig >/dev/null 2>&1; then
|
|
_carp=$(ifconfig 2>/dev/null | grep -c ' carp:' 2>/dev/null || echo 0)
|
|
if [ "${_carp:-0}" -gt 0 ]; then
|
|
_demote=$(sysctl -n net.inet.carp.demotion 2>/dev/null || echo 0)
|
|
_init=$(ifconfig 2>/dev/null | grep ' carp:' | grep -c 'INIT' || echo 0)
|
|
if [ "${_init:-0}" -gt 0 ]; then
|
|
echo "2 Ansipa_OPN_CARP carp_ifaces=${_carp};; carp_demotion=${_demote};; ${_init} CARP interface(s) in INIT state"
|
|
elif [ "${_demote:-0}" -ne 0 ]; then
|
|
echo "1 Ansipa_OPN_CARP carp_ifaces=${_carp};; carp_demotion=${_demote};; CARP demotion factor is ${_demote} (not 0)"
|
|
else
|
|
echo "0 Ansipa_OPN_CARP carp_ifaces=${_carp};; carp_demotion=0;; ${_carp} CARP interface(s) healthy"
|
|
fi
|
|
fi
|
|
fi
|