Dotfiles/setup/modules/FreeipaAnsible/image/ansipa-opnsense-checkmk-ins...

166 lines
7.5 KiB
Bash
Executable File

#!/bin/sh
# ansipa-opnsense-checkmk-install.sh — wire an OPNsense firewall into CheckMK.
#
# Run as root ON THE FIREWALL. POSIX sh only: OPNsense is FreeBSD and has no
# bash in the base system.
#
# Usage:
# ansipa-opnsense-checkmk-install.sh <CMK_URL> <CMK_SITE> <CMK_SECRET> [HOST_FQDN] [CMK_USER]
#
# Copy this script plus ansipa-opnsense-monitor.sh to the firewall first, e.g.
# scp ansipa-opnsense-*.sh root@fw:/root/
#
# ── Transport: why SSH and not the usual agent port ──────────────────────────
# The normal CheckMK agent listens on TCP 6556 via inetd. On OPNsense that is a
# poor fit: /etc/inetd.conf, /etc/services and /etc/hosts.allow are GENERATED
# from config.xml, so the firewall silently reverts those edits on the next
# reboot or config apply, and monitoring dies quietly weeks later.
#
# So this installs the agent as a plain script and leaves the transport to
# CheckMK's "individual program call" datasource — CheckMK SSHes in and runs
# the agent on demand. Nothing listens, nothing to punch through the firewall
# rules, and nothing for OPNsense to overwrite. The script prints the exact
# rule to create at the end.
#
# It also registers the host in CheckMK via the REST API, in folder
# /ansipa/infra alongside the PVE/PBS hosts.
set -eu
LOG_TAG="ansipa-opnsense-checkmk"
log() { echo "[$LOG_TAG] $*"; }
warn() { echo "[$LOG_TAG][WARN] $*" >&2; }
die() { echo "[$LOG_TAG][ERROR] $*" >&2; exit 1; }
[ "$(id -u)" -eq 0 ] || die "Run as root."
[ $# -ge 3 ] || die "Usage: $0 <CMK_URL> <CMK_SITE> <CMK_SECRET> [HOST_FQDN] [CMK_USER]"
CMK_URL=$(echo "$1" | sed 's|/$||')
CMK_SITE="$2"
CMK_SECRET="$3"
HOST_FQDN="${4:-$(hostname)}"
CMK_USER="${5:-automation}"
CMK_API="${CMK_URL}/${CMK_SITE}/check_mk/api/1.0"
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
AGENT_BIN=/usr/local/bin/check_mk_agent
LOCAL_DIR=/usr/local/lib/check_mk_agent/local
command -v opnsense-version >/dev/null 2>&1 \
|| die "opnsense-version not found — this does not look like an OPNsense host."
log "OPNsense detected: $(opnsense-version 2>/dev/null | head -1)"
# ── Install the FreeBSD agent ────────────────────────────────────────────────
# Pulled from the monitoring server so agent and server versions always match.
if [ ! -x "$AGENT_BIN" ]; then
log "Downloading the FreeBSD agent from ${CMK_URL}..."
fetch -q -o "$AGENT_BIN" \
--user-agent="ansipa" \
"${CMK_URL}/${CMK_SITE}/check_mk/agents/check_mk_agent.freebsd" 2>/dev/null \
|| curl -sf -u "${CMK_USER}:${CMK_SECRET}" \
"${CMK_URL}/${CMK_SITE}/check_mk/agents/check_mk_agent.freebsd" \
-o "$AGENT_BIN" 2>/dev/null \
|| die "Could not download check_mk_agent.freebsd — check CMK_URL/site/secret."
chmod 755 "$AGENT_BIN"
log "Agent installed at $AGENT_BIN"
else
log "Agent already present at $AGENT_BIN"
fi
# ── Install the ansipa local check ───────────────────────────────────────────
mkdir -p "$LOCAL_DIR"
if [ -f "$SCRIPT_DIR/ansipa-opnsense-monitor.sh" ]; then
install -m 755 "$SCRIPT_DIR/ansipa-opnsense-monitor.sh" "$LOCAL_DIR/ansipa_opnsense"
log "Installed local check: $LOCAL_DIR/ansipa_opnsense"
else
warn "ansipa-opnsense-monitor.sh not found next to this script — local check NOT installed."
fi
# ── Keep the firmware status cache warm ──────────────────────────────────────
# The local check reads a cached update count rather than hitting the mirrors on
# every poll. /usr/local/etc/rc.syshook.d survives reboots and firmware updates,
# unlike /etc/crontab which OPNsense regenerates.
SYSHOOK_DIR=/usr/local/etc/rc.syshook.d/start
mkdir -p "$SYSHOOK_DIR"
cat > "$SYSHOOK_DIR/99-ansipa-cmk" <<'HOOK'
#!/bin/sh
# ansipa: refresh the firmware status cache once a day so the CheckMK local
# check can report pending updates without going to the network itself.
(
while true; do
/usr/local/sbin/configctl firmware check >/dev/null 2>&1 || true
sleep 86400
done
) &
HOOK
chmod 755 "$SYSHOOK_DIR/99-ansipa-cmk"
log "Installed firmware-cache refresher: $SYSHOOK_DIR/99-ansipa-cmk"
"$SYSHOOK_DIR/99-ansipa-cmk" 2>/dev/null || true
# ── Register the host in CheckMK ─────────────────────────────────────────────
# Same folder the PVE/PBS appliances land in. 422 means it already exists.
_folder_http=$(curl -sf -o /dev/null -w '%{http_code}' \
-X POST "${CMK_API}/domain-types/folder_config/collections/all" \
-H "Authorization: Bearer ${CMK_USER} ${CMK_SECRET}" \
-H "Content-Type: application/json" -H "Accept: application/json" \
-d '{"name":"infra","title":"Ansipa Infra (PVE/PBS)","parent":"/ansipa"}' \
2>/dev/null || echo "000")
case "$_folder_http" in
2*|422) ;;
*) warn "Unexpected response creating /ansipa/infra: HTTP $_folder_http" ;;
esac
_HOST_IP=$(ifconfig 2>/dev/null | awk '/inet /{if($2!="127.0.0.1"){print $2; exit}}')
if curl -sf -o /dev/null \
-H "Authorization: Bearer ${CMK_USER} ${CMK_SECRET}" \
"${CMK_API}/objects/host_config/${HOST_FQDN}" 2>/dev/null; then
log "${HOST_FQDN} already registered in CheckMK"
else
_body="{\"host_name\":\"${HOST_FQDN}\",\"folder\":\"/ansipa/infra\""
[ -n "$_HOST_IP" ] && _body="${_body},\"attributes\":{\"ipaddress\":\"${_HOST_IP}\",\"tag_role\":\"opnsense\"}"
_body="${_body}}"
_http=$(curl -sf -o /dev/null -w '%{http_code}' \
-X POST "${CMK_API}/domain-types/host_config/collections/all" \
-H "Authorization: Bearer ${CMK_USER} ${CMK_SECRET}" \
-H "Content-Type: application/json" -H "Accept: application/json" \
-d "$_body" 2>/dev/null || echo "000")
case "$_http" in
2*) log "Registered ${HOST_FQDN} in CheckMK (/ansipa/infra)" ;;
*) warn "Host registration failed (HTTP ${_http}) — add it in the UI." ;;
esac
fi
cat <<EOF
[$LOG_TAG] Firewall side is done. Two manual steps remain on the CheckMK server,
because both involve credentials that should not travel in this script:
1. Let the CheckMK site SSH to this firewall as a monitoring user.
On the CheckMK server, as the site user:
ssh-keygen -t ed25519 -f ~/.ssh/id_ansipa_opnsense -N ''
ssh-copy-id -i ~/.ssh/id_ansipa_opnsense root@${HOST_FQDN}
Restrict it on this firewall by prefixing the key in
/root/.ssh/authorized_keys with:
command="${AGENT_BIN}",no-port-forwarding,no-pty
2. Tell CheckMK to call the agent over SSH instead of TCP 6556:
Setup → Hosts → ${HOST_FQDN} → "Individual program call instead of agent access"
(rule: "Datasource programs → Individual program call"), command:
ssh -i \$HOME/.ssh/id_ansipa_opnsense -o StrictHostKeyChecking=accept-new root@${HOST_FQDN}
Then run a service discovery on ${HOST_FQDN} and activate changes.
Services this adds beyond the stock FreeBSD checks:
Ansipa_OPN_Gateways gateway up/down
Ansipa_OPN_States pf state-table usage vs the hard limit
Ansipa_OPN_Firmware pending updates
Ansipa_OPN_CARP HA health (silent on standalone firewalls)
Prefer no SSH access at all? Enable SNMP in OPNsense
(Services → SNMP) and set this host to "SNMP" in CheckMK instead — you get
interfaces, CPU, memory and uptime, but none of the Ansipa_OPN_* checks
above, since those need local command execution.
EOF