166 lines
7.5 KiB
Bash
Executable File
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
|