fix(ansipa): docker stack volume/network naming, healthchecks, client-routable CMK URL
Found while bringing up the FreeIPA+CheckMK+Keycloak stack in a VM: - Pin volume and network names (name:) so compose and the cgroup-v2 run.sh wrapper share the same objects. Compose otherwise prefixes the project name (image_cmk-creds, image_ipa-net), so the run.sh-started freeipa container mounted a different cmk-creds volume than the compose-started checkmk wrote to — the automation secret never reached IPA and the dev_mon_* integration retried forever. run.sh also now labels the network it creates so compose adopts it instead of erroring "exists but not created by compose". - run.sh: mount cmk-creds into freeipa and include checkmk in the "all" target; both were missing, so the CheckMK path was never wired up via run.sh. - Fix both healthchecks: Keycloak has no curl/wget and serves /health on the management port 9000 only when KC_HEALTH_ENABLED=true, so the old curl probe left it permanently unhealthy — use a bash /dev/tcp probe against 9000. CheckMK's /api/1.0/version needs auth (401), so probe the login page instead. - Advertise a client-routable CheckMK URL (CMK_ADVERTISED_URL) in the dev_mon_base description instead of the container-internal 172.30.0.12, which enrolled clients cannot reach; falls back to the internal URL when unset. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUhrcFU8J1Hnf7vNqNxZNifeat/astal-menu
parent
3ee375b1c1
commit
0f767fb051
|
|
@ -24,9 +24,16 @@ LUKS_KEY_UPLOAD_PASSWORD=ChangeMe_LuksUpload!
|
||||||
# CMK_ADMIN_PASSWORD — web UI password for cmkadmin.
|
# CMK_ADMIN_PASSWORD — web UI password for cmkadmin.
|
||||||
# CMK_SITE_ID — OMD site name (default: cmk). Used in all CheckMK URLs and
|
# CMK_SITE_ID — OMD site name (default: cmk). Used in all CheckMK URLs and
|
||||||
# agent registration. Must match whatever was used on first start.
|
# agent registration. Must match whatever was used on first start.
|
||||||
|
# CMK_ADVERTISED_URL — URL enrolled CLIENTS use to reach CheckMK; stored in the
|
||||||
|
# dev_mon_base hostgroup description. Must be routable from
|
||||||
|
# the clients' network (the docker HOST address + published
|
||||||
|
# port, e.g. http://mon.corp.example.com:8090) — NOT the
|
||||||
|
# container-internal 172.30.0.12:5000. Leave empty only if
|
||||||
|
# all monitored clients run inside the same docker network.
|
||||||
# Web UI: http://localhost:8090/cmk/ after `docker compose up -d`
|
# Web UI: http://localhost:8090/cmk/ after `docker compose up -d`
|
||||||
CMK_ADMIN_PASSWORD=ChangeMe_CMK!
|
CMK_ADMIN_PASSWORD=ChangeMe_CMK!
|
||||||
CMK_SITE_ID=cmk
|
CMK_SITE_ID=cmk
|
||||||
|
CMK_ADVERTISED_URL=
|
||||||
|
|
||||||
# ── Keycloak ──────────────────────────────────────────────────────────────────
|
# ── Keycloak ──────────────────────────────────────────────────────────────────
|
||||||
KC_HOSTNAME=keycloak.corp.example.com
|
KC_HOSTNAME=keycloak.corp.example.com
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,13 @@ curl -sf \
|
||||||
|
|
||||||
# ── Store CheckMK credentials in IPA host-group descriptions ─────────────────
|
# ── Store CheckMK credentials in IPA host-group descriptions ─────────────────
|
||||||
# Format: cmk://<host>:<port>/<site>:automation:<secret>
|
# Format: cmk://<host>:<port>/<site>:automation:<secret>
|
||||||
CMK_HOST=$(echo "$CMK_URL" | sed 's|http://||')
|
# 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}"
|
CMK_DESC="cmk://${CMK_HOST}/${CMK_SITE}:automation:${CMK_SECRET}"
|
||||||
|
|
||||||
echo "$IPA_ADMIN_PASS" | kinit "admin@${IPA_REALM}" &>/dev/null || {
|
echo "$IPA_ADMIN_PASS" | kinit "admin@${IPA_REALM}" &>/dev/null || {
|
||||||
|
|
|
||||||
|
|
@ -25,14 +25,29 @@
|
||||||
#
|
#
|
||||||
# On plain cgroup v1 hosts `docker compose up -d` works without run.sh.
|
# On plain cgroup v1 hosts `docker compose up -d` works without run.sh.
|
||||||
|
|
||||||
|
# Volume names are pinned (name:) so they match what run.sh creates with plain
|
||||||
|
# `docker volume create` on cgroup-v2 hosts. Without this, compose prefixes the
|
||||||
|
# project name (image_cmk-creds, ...) and the freeipa container started by
|
||||||
|
# run.sh mounts a DIFFERENT cmk-creds volume than the compose-managed checkmk
|
||||||
|
# container writes to — the automation secret then never reaches IPA and the
|
||||||
|
# CheckMK integration retries forever.
|
||||||
volumes:
|
volumes:
|
||||||
freeipa-data:
|
freeipa-data:
|
||||||
|
name: freeipa-data
|
||||||
keycloak-db:
|
keycloak-db:
|
||||||
|
name: keycloak-db
|
||||||
cmk-data: # CheckMK site data (persistent across restarts)
|
cmk-data: # CheckMK site data (persistent across restarts)
|
||||||
|
name: cmk-data
|
||||||
cmk-creds: # shared: CheckMK writes automation.secret here; FreeIPA reads it
|
cmk-creds: # shared: CheckMK writes automation.secret here; FreeIPA reads it
|
||||||
|
name: cmk-creds
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
ipa-net:
|
ipa-net:
|
||||||
|
# Pin the real network name: run.sh (cgroup-v2 hosts) creates/uses a bare
|
||||||
|
# "ipa-net", while compose would otherwise create a project-prefixed
|
||||||
|
# "image_ipa-net" on the same subnet — the second one to run then fails
|
||||||
|
# with "Pool overlaps with other one on this address space".
|
||||||
|
name: ipa-net
|
||||||
ipam:
|
ipam:
|
||||||
config:
|
config:
|
||||||
- subnet: 172.30.0.0/24
|
- subnet: 172.30.0.0/24
|
||||||
|
|
@ -65,6 +80,9 @@ services:
|
||||||
IPA_SETUP_KRA: ${IPA_SETUP_KRA:-false}
|
IPA_SETUP_KRA: ${IPA_SETUP_KRA:-false}
|
||||||
SMB_SCAN_PASSWORD: ${SMB_SCAN_PASSWORD:?set SMB_SCAN_PASSWORD in .env}
|
SMB_SCAN_PASSWORD: ${SMB_SCAN_PASSWORD:?set SMB_SCAN_PASSWORD in .env}
|
||||||
CMK_URL: http://172.30.0.12:5000
|
CMK_URL: http://172.30.0.12:5000
|
||||||
|
# Client-facing CheckMK URL stored in the dev_mon_base description —
|
||||||
|
# must be routable from enrolled clients (e.g. http://<docker-host>:8090).
|
||||||
|
CMK_ADVERTISED_URL: ${CMK_ADVERTISED_URL:-}
|
||||||
CMK_SITE_ID: ${CMK_SITE_ID:-cmk}
|
CMK_SITE_ID: ${CMK_SITE_ID:-cmk}
|
||||||
ports:
|
ports:
|
||||||
- "389:389"
|
- "389:389"
|
||||||
|
|
@ -165,7 +183,11 @@ services:
|
||||||
ipa-net:
|
ipa-net:
|
||||||
ipv4_address: 172.30.0.12
|
ipv4_address: 172.30.0.12
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "curl -sf http://localhost:5000/${CMK_SITE_ID:-cmk}/check_mk/api/1.0/version >/dev/null 2>&1"]
|
# Probe the login page, not /api/1.0/version: the REST API version
|
||||||
|
# endpoint requires authentication in CheckMK 2.3, so an unauthenticated
|
||||||
|
# curl -f gets 401 and the container stays "unhealthy" forever even
|
||||||
|
# though the site is fully up.
|
||||||
|
test: ["CMD-SHELL", "curl -sf http://localhost:5000/${CMK_SITE_ID:-cmk}/check_mk/login.py -o /dev/null"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 15s
|
timeout: 15s
|
||||||
retries: 20
|
retries: 20
|
||||||
|
|
@ -188,6 +210,7 @@ services:
|
||||||
KC_HTTP_PORT: 8080
|
KC_HTTP_PORT: 8080
|
||||||
KC_HTTPS_PORT: 8443
|
KC_HTTPS_PORT: 8443
|
||||||
KC_HTTP_ENABLED: "true"
|
KC_HTTP_ENABLED: "true"
|
||||||
|
KC_HEALTH_ENABLED: "true" # exposes /health/* on management port 9000 (KC 25+)
|
||||||
KC_FEATURES: preview
|
KC_FEATURES: preview
|
||||||
KEYCLOAK_ADMIN: ${KC_ADMIN:-admin}
|
KEYCLOAK_ADMIN: ${KC_ADMIN:-admin}
|
||||||
KEYCLOAK_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD:?set KC_ADMIN_PASSWORD in .env}
|
KEYCLOAK_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD:?set KC_ADMIN_PASSWORD in .env}
|
||||||
|
|
@ -202,7 +225,11 @@ services:
|
||||||
networks:
|
networks:
|
||||||
ipa-net:
|
ipa-net:
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "curl -fs http://localhost:8080/health/ready || exit 1"]
|
# The official Keycloak image ships neither curl nor wget, and since
|
||||||
|
# KC 25 the health endpoints live on the management port (9000) and are
|
||||||
|
# only served when KC_HEALTH_ENABLED=true. Use bash's /dev/tcp instead
|
||||||
|
# of curl so the check works inside the stock image.
|
||||||
|
test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/9000 && echo -e 'GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && grep -q '\"status\": \"UP\"' <&3"]
|
||||||
interval: 20s
|
interval: 20s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 20
|
retries: 20
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,23 @@ start_freeipa() {
|
||||||
docker compose up --no-start freeipa 2>/dev/null || true
|
docker compose up --no-start freeipa 2>/dev/null || true
|
||||||
NETWORK=$(docker network ls --filter name=ipa-net --format '{{.Name}}' | grep ipa-net | head -1)
|
NETWORK=$(docker network ls --filter name=ipa-net --format '{{.Name}}' | grep ipa-net | head -1)
|
||||||
if [[ -z "$NETWORK" ]]; then
|
if [[ -z "$NETWORK" ]]; then
|
||||||
docker network create --subnet=172.30.0.0/24 ipa-net
|
# Label the network exactly as compose would so a later
|
||||||
|
# `docker compose up` (checkmk/postgres/keycloak) adopts it instead of
|
||||||
|
# failing with "network exists but was not created by compose".
|
||||||
|
docker network create --subnet=172.30.0.0/24 \
|
||||||
|
--label com.docker.compose.network=ipa-net \
|
||||||
|
--label com.docker.compose.project=image \
|
||||||
|
--label com.docker.compose.version=2 \
|
||||||
|
ipa-net
|
||||||
NETWORK=ipa-net
|
NETWORK=ipa-net
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure the data volume exists
|
# Ensure the data volumes exist (cmk-creds is shared with the checkmk
|
||||||
|
# container: CheckMK writes automation.secret there, the IPA container's
|
||||||
|
# ansipa-checkmk-setup.sh reads it — without this mount the whole
|
||||||
|
# dev_mon_* CheckMK integration silently never activates)
|
||||||
docker volume create freeipa-data 2>/dev/null || true
|
docker volume create freeipa-data 2>/dev/null || true
|
||||||
|
docker volume create cmk-creds 2>/dev/null || true
|
||||||
|
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name freeipa \
|
--name freeipa \
|
||||||
|
|
@ -51,6 +62,7 @@ start_freeipa() {
|
||||||
--tmpfs /tmp \
|
--tmpfs /tmp \
|
||||||
-v /sys/fs/cgroup:/sys/fs/cgroup:rw \
|
-v /sys/fs/cgroup:/sys/fs/cgroup:rw \
|
||||||
-v freeipa-data:/data \
|
-v freeipa-data:/data \
|
||||||
|
-v cmk-creds:/cmk-creds \
|
||||||
--network "$NETWORK" \
|
--network "$NETWORK" \
|
||||||
--ip 172.30.0.10 \
|
--ip 172.30.0.10 \
|
||||||
-e IPA_DOMAIN="${IPA_DOMAIN:?}" \
|
-e IPA_DOMAIN="${IPA_DOMAIN:?}" \
|
||||||
|
|
@ -62,6 +74,9 @@ start_freeipa() {
|
||||||
-e IPA_SETUP_KRA="${IPA_SETUP_KRA:-false}" \
|
-e IPA_SETUP_KRA="${IPA_SETUP_KRA:-false}" \
|
||||||
-e SMB_SCAN_PASSWORD="${SMB_SCAN_PASSWORD:?}" \
|
-e SMB_SCAN_PASSWORD="${SMB_SCAN_PASSWORD:?}" \
|
||||||
-e LUKS_KEY_UPLOAD_PASSWORD="${LUKS_KEY_UPLOAD_PASSWORD:?}" \
|
-e LUKS_KEY_UPLOAD_PASSWORD="${LUKS_KEY_UPLOAD_PASSWORD:?}" \
|
||||||
|
-e CMK_URL="http://172.30.0.12:5000" \
|
||||||
|
-e CMK_ADVERTISED_URL="${CMK_ADVERTISED_URL:-}" \
|
||||||
|
-e CMK_SITE_ID="${CMK_SITE_ID:-cmk}" \
|
||||||
-p 389:389 \
|
-p 389:389 \
|
||||||
-p 636:636 \
|
-p 636:636 \
|
||||||
-p 88:88 \
|
-p 88:88 \
|
||||||
|
|
@ -88,8 +103,8 @@ case "$MODE" in
|
||||||
;;
|
;;
|
||||||
all)
|
all)
|
||||||
start_freeipa
|
start_freeipa
|
||||||
docker compose up -d postgres keycloak
|
docker compose up -d postgres keycloak checkmk
|
||||||
echo "Full stack started (freeipa + postgres + keycloak)."
|
echo "Full stack started (freeipa + postgres + keycloak + checkmk)."
|
||||||
;;
|
;;
|
||||||
"")
|
"")
|
||||||
start_freeipa
|
start_freeipa
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue