#!/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://:/:automation: 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://:/:automation: # CMK_ADVERTISED_URL is what CLIENTS use to reach CheckMK. It must be a # host-routable address (e.g. http://: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"