Dotfiles/setup/modules/FreeipaAnsible/image/ansipa-checkmk-setup.sh

187 lines
8.3 KiB
Bash

#!/bin/bash
# ansipa-checkmk-setup.sh — CheckMK CE integration for the IPA container.
#
# Phase 1 (one-time, guarded by /data/ansipa-checkmk.done):
# Creates /ansipa folder, IPA monitoring groups, stores CMK credentials.
#
# Phase 2 (runs every invocation after phase 1):
# Writes push-mode requests for all hosts in dev_mon_base to /cmk-creds/.
# The CheckMK container polls /cmk-creds/push-mode-*.req and updates hosts.mk.
# This lets clients in NAT environments push agent data without pull reachability.
#
# Credential format in IPA dev_mon_base description:
# cmk://<host>:<port>/<site>:automation:<secret>
set -euo pipefail
LOG_TAG="ansipa-checkmk-setup"
DONE_FLAG="/data/ansipa-checkmk.done"
CMK_CREDS_FILE="/cmk-creds/automation.secret"
log() { echo "[$LOG_TAG] $*"; }
warn() { echo "[$LOG_TAG][WARN] $*" >&2; }
# ── Phase 2: write push-mode requests for enrolled monitoring hosts ───────────
# Runs on EVERY invocation (not guarded by done flag). The CheckMK container
# polls /cmk-creds/push-mode-*.req and adds explicit_host_conf push-agent entries.
_write_push_requests() {
[[ -d /cmk-creds ]] || return 0
local _hosts _h
# `ipa hostgroup-show` prints members on the SAME line as the label
# (" Member hosts: a.example, b.example") and wraps long lists onto
# indented continuation lines. Capture the label line's own value AND any
# continuation lines, stopping at the next " Key:" field. (The previous
# awk did `next` on the label line, discarding the very line that holds the
# hostnames, so no host was ever extracted and no push request written.)
_hosts=$(ipa hostgroup-show dev_mon_base --all 2>/dev/null \
| awk '
/^ [A-Za-z][A-Za-z0-9 _-]*:/ {
if ($0 ~ /Member hosts:/) { sub(/.*Member hosts:[[:space:]]*/, ""); p=1; print; next }
else { p=0 }
}
p { print }
' \
| grep -oE '[a-zA-Z0-9._-]+\.[a-zA-Z]{2,}' | sort -u || true)
[[ -z "$_hosts" ]] && return 0
while IFS= read -r _h; do
[[ -z "$_h" ]] && continue
_req="/cmk-creds/push-mode-${_h}.req"
[[ -f "$_req" ]] && continue
echo "$_h" > "$_req" && log "Push-mode request created for ${_h}"
done <<< "$_hosts"
}
# ── Resolve configuration ──────────────────────────────────────────────────────
# Must run BEFORE the DONE_FLAG early-exit: that path calls _write_push_requests,
# which needs IPA_REALM + IPA_ADMIN_PASS to kinit. Previously these were resolved
# only after the early exit, so every rerun kinit'd with an empty password, failed
# silently, and never wrote push-mode requests — leaving NAT'd clients stuck in
# pull mode (unreachable) forever.
# docker-env.service writes container env to /etc/container.env; source it.
[[ -f /etc/container.env ]] && source /etc/container.env || true
CMK_URL="${CMK_URL:-http://172.30.0.12:5000}"
CMK_SITE="${CMK_SITE_ID:-cmk}"
CMK_API="${CMK_URL}/${CMK_SITE}/check_mk/api/1.0"
IPA_REALM="${IPA_REALM:-}"
IPA_ADMIN_PASS="${IPA_ADMIN_PASSWORD:-}"
# Skip re-initialization but still run push-mode requests if already set up
if [[ -f "$DONE_FLAG" ]]; then
if [[ -f /etc/ipa/default.conf && -n "$IPA_ADMIN_PASS" && -n "$IPA_REALM" ]]; then
echo "$IPA_ADMIN_PASS" | kinit "admin@${IPA_REALM}" &>/dev/null \
&& _write_push_requests 2>/dev/null
kdestroy &>/dev/null || true
fi
log "Already configured (delete $DONE_FLAG to force re-initialization)"
exit 0
fi
if [[ -z "$IPA_REALM" || -z "$IPA_ADMIN_PASS" ]]; then
warn "IPA_REALM or IPA_ADMIN_PASSWORD not set — cannot configure IPA side"
exit 1
fi
# ── Wait for IPA to be fully configured ──────────────────────────────────────
if [[ ! -f /etc/ipa/default.conf ]]; then
warn "IPA not yet configured — waiting for ipa-first-boot to complete"
exit 1
fi
# ── Wait for CheckMK automation secret (written by checkmk container) ─────────
if [[ ! -f "$CMK_CREDS_FILE" ]]; then
warn "CheckMK automation secret not yet available at $CMK_CREDS_FILE"
warn " (CheckMK container writes it after site init — will retry)"
exit 1
fi
CMK_SECRET=$(tr -d '[:space:]' < "$CMK_CREDS_FILE")
if [[ -z "$CMK_SECRET" ]]; then
warn "CheckMK automation secret is empty — CheckMK not ready yet"
exit 1
fi
# ── Wait for CheckMK web API (use automation secret — no admin password needed) ──
log "Waiting for CheckMK API at ${CMK_API}..."
_wait=0
until curl -sf \
-H "Authorization: Bearer automation ${CMK_SECRET}" \
"${CMK_API}/version" >/dev/null 2>&1; do
_wait=$(( _wait + 15 ))
if [[ $_wait -ge 300 ]]; then
warn "CheckMK API not reachable after 5 minutes — will retry on next start"
exit 1
fi
sleep 15
done
log "CheckMK API is up"
# ── Create /ansipa folder in CheckMK ─────────────────────────────────────────
_folder_result=$(curl -sf -o /dev/null -w '%{http_code}' \
-X POST "${CMK_API}/domain-types/folder_config/collections/all" \
-H "Authorization: Bearer automation ${CMK_SECRET}" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"name":"ansipa","title":"Ansipa Managed Hosts","parent":"/"}' \
2>/dev/null || echo "000")
if [[ "$_folder_result" =~ ^2 ]]; then
log "Created /ansipa folder in CheckMK"
elif [[ "$_folder_result" == "422" ]]; then
log "/ansipa folder already exists"
else
warn "Unexpected response creating /ansipa folder: HTTP $_folder_result"
fi
# ── Activate changes in CheckMK ───────────────────────────────────────────────
curl -sf \
-X POST "${CMK_API}/domain-types/activation_run/actions/activate-changes/invoke" \
-H "Authorization: Bearer automation ${CMK_SECRET}" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"redirect":false,"sites":[],"force_foreign_changes":false}' \
>/dev/null 2>&1 && log "Changes activated" || warn "Activation failed (non-fatal)"
# ── Store CheckMK credentials in IPA host-group descriptions ─────────────────
# Format: cmk://<host>:<port>/<site>:automation:<secret>
# CMK_ADVERTISED_URL is what CLIENTS use to reach CheckMK. It must be a
# host-routable address (e.g. http://<docker-host>:8090), NOT the container's
# internal 172.30.0.x address — clients outside the docker network cannot
# reach that, and every dev_mon_* policy would silently fail to register.
# Falls back to CMK_URL for docker-network-internal test setups.
CMK_CLIENT_URL="${CMK_ADVERTISED_URL:-$CMK_URL}"
CMK_HOST=$(echo "$CMK_CLIENT_URL" | sed 's|http://||')
CMK_DESC="cmk://${CMK_HOST}/${CMK_SITE}:automation:${CMK_SECRET}"
echo "$IPA_ADMIN_PASS" | kinit "admin@${IPA_REALM}" &>/dev/null || {
warn "kinit admin failed"
exit 1
}
# dev_mon_base stores the credentials; clients parse them to register.
if ipa hostgroup-show dev_mon_base &>/dev/null 2>&1; then
ipa hostgroup-mod dev_mon_base --desc="$CMK_DESC" &>/dev/null \
&& log "Updated dev_mon_base description with CMK credentials"
else
ipa hostgroup-add dev_mon_base --desc="$CMK_DESC" &>/dev/null \
&& log "Created IPA hostgroup dev_mon_base with CMK credentials"
fi
# Create supplementary monitoring host-groups (no credentials needed in description).
for _hgrp in dev_mon_malware dev_mon_timeshift dev_mon_power; do
ipa hostgroup-show "$_hgrp" &>/dev/null 2>&1 || \
ipa hostgroup-add "$_hgrp" &>/dev/null || true
done
# usr_mon_logins is a USER group (monitoring follows the user, not the device).
ipa group-show usr_mon_logins &>/dev/null 2>&1 || \
ipa group-add usr_mon_logins \
--desc="Members: SSH login monitoring enabled on enrolled devices" &>/dev/null || true
kdestroy &>/dev/null || true
touch "$DONE_FLAG"
log "CheckMK setup complete."
log " Phase 2 (push-mode requests) will run on next invocation."
log " Web UI: ${CMK_URL}/${CMK_SITE}/"
log " Login: cmkadmin / (CMK_ADMIN_PASSWORD from .env)"
log " Clients: add to dev_mon_base hostgroup — agent self-registers via ansipa"