Auf RHEL ist firewalld ab Werk aktiv und gibt in aller Regel nur SSH
frei. Der Stack laeuft dann sauber, ist aber von aussen nicht zu
erreichen: die Agents kommen nicht durch den Receiver auf 8000, und im
Browser passiert nichts. Das war bisher nur eine Handvoll statischer
Beispielzeilen in der README und in install.sh.
Neu ist "cmk-manage.sh firewall". Erkannt werden firewalld, ufw,
nftables und iptables; welche Ports noetig sind, ergibt sich aus der
Konfiguration statt aus einer festen Liste:
- ohne Proxy Manager wird 5000 zum Pflichtport, mit ihm soll es zu
bleiben - offenes 5000 wird deshalb ausdruecklich angemerkt
- der Admin-Port kommt aus NPM_ADMIN_PORT, nicht fest aus 81
- FW_ROLE unterscheidet single/central/remote: eine Remote-Site
braucht 6557 eingehend, die Zentrale baut die Verbindungen selbst
auf und braucht dafuer nichts. Steht FW_ROLE=remote bei
CMK_LIVESTATUS_TCP=off, ist der Port zwar offen, es lauscht aber
niemand - auch darauf weist der Bericht hin.
- Event Console ueber FW_EC_SYSLOG / FW_EC_SNMPTRAP / FW_EC_TCP,
Agent dieses Hosts ueber FW_AGENT, alles Weitere (Grafana, NSCA)
ueber FW_EXTRA_PORTS
Syslog und SNMP-Traps laufen ueber UDP, eine TCP-Freigabe nuetzt ihnen
nichts. Ports werden deshalb durchweg als nummer/protokoll gefuehrt -
beim Pruefen wie beim Erzeugen der Regeln. Bestehende Regeln werden
entsprechend gelesen: Portbereiche (6556-6558 bzw. 6556:6558) werden
aufgeloest, firewalld-Dienstnamen und Rich Rules zaehlen mit, bei
firewalld alle aktiven Zonen statt nur der Standardzone, und eine
ufw-Regel ohne Protokoll zaehlt fuer TCP und UDP - so wie ufw sie
anlegt.
"firewall open" zeigt die passenden Kommandos, --apply setzt sie nach
Rueckfrage, --source begrenzt sie auf ein Quellnetz (fuer Port 80 mit
Warnung, weil Lets Encrypt von aussen prueft). Ohne Befund ist der
Rueckgabewert 1, damit sich das auswerten laesst. In der Oberflaeche
steckt derselbe Bericht unter "Firewall pruefen".
Nebenbei: env_set hat Werte mit Leerzeichen samt Anfuehrungszeichen in
die laufende Umgebung exportiert. In der Datei gehoeren sie hin, im
Wert nicht - das fiel erst mit den Beschreibungen in FW_EXTRA_PORTS auf.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
360 lines
14 KiB
Bash
Executable File
360 lines
14 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# =====================================================================
|
|
# install.sh - Installation des Checkmk-/NGINX-Proxy-Manager-Stacks
|
|
#
|
|
# Debian/Ubuntu : Docker CE + Compose-Plugin
|
|
# RHEL/Rocky/Alma/CentOS/Fedora : Podman + podman-compose
|
|
#
|
|
# Zusaetzlich: dialog (ncurses-Oberflaeche), openssl, tar, curl
|
|
# =====================================================================
|
|
set -euo pipefail
|
|
|
|
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
. "$PROJECT_DIR/scripts/lib/common.sh"
|
|
|
|
WITH_SYSTEMD=""
|
|
WITH_QUADLET=""
|
|
SKIP_RUNTIME=""
|
|
ROOTLESS=""
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
install.sh - Installation des Checkmk-Container-Stacks
|
|
|
|
OPTIONEN
|
|
--with-systemd systemd-Unit einrichten, damit der Stack nach einem
|
|
Neustart des Hosts automatisch wieder startet
|
|
(Vorgabe bei Podman)
|
|
--quadlet statt der Compose-Unit Podman-Quadlet-Dateien
|
|
(/etc/containers/systemd/) einrichten
|
|
--rootless Podman rootless vorbereiten (linger + Ports ab 80)
|
|
--skip-runtime Docker/Podman nicht installieren (nur Projekt einrichten)
|
|
-h, --help Hilfe
|
|
EOF
|
|
}
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--with-systemd) WITH_SYSTEMD=1; shift ;;
|
|
--quadlet) WITH_QUADLET=1; WITH_SYSTEMD=1; shift ;;
|
|
--rootless) ROOTLESS=1; shift ;;
|
|
--skip-runtime) SKIP_RUNTIME=1; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) die "Unbekannte Option: $1" ;;
|
|
esac
|
|
done
|
|
|
|
[ "$(id -u)" -eq 0 ] || die "Bitte als root ausfuehren: sudo ./install.sh"
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Distribution erkennen
|
|
# ---------------------------------------------------------------------
|
|
[ -r /etc/os-release ] || die "/etc/os-release nicht lesbar - Distribution unbekannt."
|
|
# shellcheck disable=SC1091
|
|
. /etc/os-release
|
|
OS_ID="${ID:-unknown}"
|
|
OS_LIKE="${ID_LIKE:-}"
|
|
OS_NAME="${PRETTY_NAME:-$OS_ID}"
|
|
|
|
FAMILY=""
|
|
case "$OS_ID" in
|
|
debian|ubuntu|linuxmint|raspbian|pop) FAMILY=debian ;;
|
|
rhel|centos|rocky|almalinux|fedora|ol|oracle|scientific) FAMILY=rhel ;;
|
|
*)
|
|
case " $OS_LIKE " in
|
|
*debian*) FAMILY=debian ;;
|
|
*rhel*|*fedora*|*centos*) FAMILY=rhel ;;
|
|
esac
|
|
;;
|
|
esac
|
|
[ -n "$FAMILY" ] || die "Nicht unterstuetzte Distribution: $OS_NAME"
|
|
|
|
printf '\n%s=== Checkmk Container-Stack - Installation ===%s\n\n' "$C_BLD" "$C_RST"
|
|
log "System : $OS_NAME"
|
|
log "Familie : $FAMILY"
|
|
log "Projektpfad : $PROJECT_DIR"
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Runtime installieren
|
|
# ---------------------------------------------------------------------
|
|
install_debian() {
|
|
log "Installiere Docker CE aus dem offiziellen Docker-Repository ..."
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
apt-get update -qq
|
|
apt-get install -y ca-certificates curl gnupg tar dialog openssl
|
|
|
|
if [ "$OS_ID" = debian ] || [ "$OS_ID" = ubuntu ]; then
|
|
install -m 0755 -d /etc/apt/keyrings
|
|
if [ ! -f /etc/apt/keyrings/docker.asc ]; then
|
|
curl -fsSL "https://download.docker.com/linux/$OS_ID/gpg" -o /etc/apt/keyrings/docker.asc
|
|
chmod a+r /etc/apt/keyrings/docker.asc
|
|
fi
|
|
local codename="${VERSION_CODENAME:-}"
|
|
[ -n "$codename" ] || codename="$(. /etc/os-release && echo "${UBUNTU_CODENAME:-stable}")"
|
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$OS_ID $codename stable" \
|
|
>/etc/apt/sources.list.d/docker.list
|
|
apt-get update -qq
|
|
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
|
else
|
|
warn "Abgeleitete Distribution ($OS_NAME) - verwende die Distributionspakete."
|
|
apt-get install -y docker.io docker-compose
|
|
fi
|
|
|
|
systemctl enable --now docker
|
|
ok "Docker installiert: $(docker --version)"
|
|
}
|
|
|
|
# Prueft, ob ueberhaupt ein nutzbares Repository vorhanden ist. Auf einem
|
|
# nicht registrierten RHEL ist das nicht der Fall - dnf meldet dann nur
|
|
# "This system is not registered with an entitlement server".
|
|
rhel_repo_check() {
|
|
local PM="$1"
|
|
$PM -q list --available podman >/dev/null 2>&1 && return 0
|
|
err "Es ist kein nutzbares Paket-Repository vorhanden."
|
|
cat >&2 <<'EOF'
|
|
|
|
Auf Red Hat Enterprise Linux liefert dnf ohne Subskription keine Pakete.
|
|
Drei Wege fuehren weiter (ausfuehrlich in der README, Kapitel 5.1):
|
|
|
|
1) Kostenlose Developer-Subskription (empfohlen, bis 16 Systeme):
|
|
Konto auf https://developers.redhat.com anlegen, dann
|
|
subscription-manager register --username <benutzer>
|
|
subscription-manager status
|
|
|
|
2) Installations-DVD als lokales Repository (ohne Konto, offline):
|
|
mount -o loop,ro /pfad/rhel-9.x-x86_64-dvd.iso /mnt/rhel
|
|
(Repo-Datei anlegen - siehe README, Kapitel 5.1)
|
|
|
|
3) Auf Rocky Linux oder AlmaLinux wechseln (binaerkompatibel, frei):
|
|
dieses Projekt laeuft dort unveraendert.
|
|
|
|
Pruefen, was aktiv ist: subscription-manager status ; dnf repolist
|
|
EOF
|
|
return 1
|
|
}
|
|
|
|
install_rhel() {
|
|
log "Installiere Podman ..."
|
|
local PM=dnf
|
|
command -v dnf >/dev/null 2>&1 || PM=yum
|
|
|
|
rhel_repo_check "$PM" || die "Abbruch - bitte zuerst den Paketzugang einrichten."
|
|
|
|
$PM install -y podman tar openssl || die "Podman-Installation fehlgeschlagen."
|
|
|
|
# dialog ist optional: liegt in AppStream. Fehlt es, genuegt whiptail
|
|
# aus dem Paket "newt" (BaseOS) - die Skripte koennen beides.
|
|
if ! $PM install -y dialog 2>/dev/null; then
|
|
warn "'dialog' nicht verfuegbar - versuche 'newt' (whiptail)."
|
|
$PM install -y newt 2>/dev/null \
|
|
|| warn "Weder dialog noch newt installierbar - die ncurses-Oberflaeche
|
|
steht dann nicht zur Verfuegung, die Kommandozeile funktioniert weiterhin."
|
|
fi
|
|
|
|
# podman-compose besorgen (Reihenfolge: Distro-Repo -> EPEL -> pip)
|
|
if ! command -v podman-compose >/dev/null 2>&1; then
|
|
log "Suche podman-compose ..."
|
|
if ! $PM install -y podman-compose 2>/dev/null; then
|
|
if [ "$OS_ID" != fedora ]; then
|
|
local rel; rel="${VERSION_ID%%.*}"
|
|
log "Aktiviere EPEL ${rel} ..."
|
|
$PM install -y "https://dl.fedoraproject.org/pub/epel/epel-release-latest-${rel}.noarch.rpm" 2>/dev/null || true
|
|
$PM install -y podman-compose 2>/dev/null || true
|
|
fi
|
|
fi
|
|
fi
|
|
if ! command -v podman-compose >/dev/null 2>&1; then
|
|
log "Versuche podman-compose per pip ..."
|
|
$PM install -y python3-pip 2>/dev/null || true
|
|
pip3 install --quiet podman-compose 2>/dev/null || true
|
|
fi
|
|
|
|
if command -v podman-compose >/dev/null 2>&1; then
|
|
ok "podman-compose vorhanden: $(podman-compose --version 2>&1 | head -n1)"
|
|
else
|
|
warn "podman-compose konnte nicht installiert werden.
|
|
Alternative: Installation ueber Quadlet ( ./install.sh --quadlet ),
|
|
dafuer wird kein Compose benoetigt."
|
|
fi
|
|
ok "Podman installiert: $(podman --version)"
|
|
|
|
# SELinux: Bind-Mounts brauchen den passenden Kontext (:Z ist im Compose gesetzt)
|
|
if command -v getenforce >/dev/null 2>&1 && [ "$(getenforce)" = "Enforcing" ]; then
|
|
log "SELinux ist aktiv - die Bind-Mounts sind im Compose mit ':Z' versehen."
|
|
fi
|
|
}
|
|
|
|
if [ -z "$SKIP_RUNTIME" ]; then
|
|
case "$FAMILY" in
|
|
debian) install_debian ;;
|
|
rhel) install_rhel ;;
|
|
esac
|
|
else
|
|
log "Runtime-Installation uebersprungen (--skip-runtime)."
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Projekt vorbereiten
|
|
# ---------------------------------------------------------------------
|
|
log "Bereite Projektverzeichnis vor ..."
|
|
mkdir -p "$PROJECT_DIR/data/checkmk/sites" \
|
|
"$PROJECT_DIR/data/npm/data" \
|
|
"$PROJECT_DIR/data/npm/letsencrypt" \
|
|
"$PROJECT_DIR/data/npm/data/nginx/custom" \
|
|
"$PROJECT_DIR/data/npm/custom-certs" \
|
|
"$PROJECT_DIR/backups" \
|
|
"$PROJECT_DIR/images"
|
|
chmod +x "$PROJECT_DIR"/scripts/*.sh "$PROJECT_DIR/install.sh" 2>/dev/null || true
|
|
|
|
if [ ! -f "$PROJECT_DIR/.env" ]; then
|
|
cp "$PROJECT_DIR/.env.example" "$PROJECT_DIR/.env"
|
|
chmod 600 "$PROJECT_DIR/.env"
|
|
ok ".env aus .env.example erzeugt - bitte Passwort und Site-Namen anpassen!"
|
|
else
|
|
log ".env ist bereits vorhanden - bleibt unveraendert."
|
|
fi
|
|
|
|
load_env "$PROJECT_DIR/.env"
|
|
detect_runtime
|
|
ok "Verwendete Runtime: $RUNTIME"
|
|
|
|
# Podman: Autostart nach Reboot ohne systemd-Unit funktioniert nicht ->
|
|
# Unit standardmaessig einrichten.
|
|
if [ "$RUNTIME" = podman ] && [ -z "$WITH_SYSTEMD" ]; then
|
|
warn "Podman hat keinen Daemon: ohne systemd-Unit starten die Container
|
|
nach einem Reboot NICHT automatisch. Die Unit wird jetzt eingerichtet."
|
|
WITH_SYSTEMD=1
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Rootless-Vorbereitung (optional)
|
|
# ---------------------------------------------------------------------
|
|
if [ -n "$ROOTLESS" ]; then
|
|
log "Bereite rootless-Betrieb vor ..."
|
|
echo 'net.ipv4.ip_unprivileged_port_start=80' >/etc/sysctl.d/99-checkmk-rootless.conf
|
|
sysctl -q --system || true
|
|
ok "Unprivilegierte Ports ab 80 erlaubt."
|
|
warn "Zusaetzlich noetig: loginctl enable-linger <benutzer>
|
|
und die Unit als '--user'-Unit einrichten (siehe README)."
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------
|
|
# systemd
|
|
# ---------------------------------------------------------------------
|
|
install_compose_unit() {
|
|
detect_compose
|
|
log "Verwendetes Compose-Kommando: ${COMPOSE_CMD[*]}"
|
|
# Die Unit ruft bewusst cmk-manage.sh auf statt compose direkt: nur so
|
|
# werden NPM_ENABLED und der Start aller Sites beruecksichtigt.
|
|
log "Richte systemd-Unit 'checkmk-stack.service' ein ..."
|
|
cat >/etc/systemd/system/checkmk-stack.service <<EOF
|
|
[Unit]
|
|
Description=Checkmk + NGINX Proxy Manager (Container-Stack)
|
|
Documentation=file://$PROJECT_DIR/README.md
|
|
Wants=network-online.target
|
|
After=network-online.target
|
|
$([ "$RUNTIME" = docker ] && echo "Requires=docker.service" || true)
|
|
$([ "$RUNTIME" = docker ] && echo "After=docker.service" || true)
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
RemainAfterExit=yes
|
|
WorkingDirectory=$PROJECT_DIR
|
|
EnvironmentFile=$PROJECT_DIR/.env
|
|
TimeoutStartSec=0
|
|
ExecStart=$PROJECT_DIR/scripts/cmk-manage.sh -y up
|
|
ExecStartPost=$PROJECT_DIR/scripts/cmk-manage.sh start-all-sites
|
|
ExecStop=$PROJECT_DIR/scripts/cmk-manage.sh -y down
|
|
ExecReload=$PROJECT_DIR/scripts/cmk-manage.sh -y up
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
systemctl daemon-reload
|
|
systemctl enable checkmk-stack.service
|
|
ok "Unit aktiviert. Steuerung: systemctl start|stop|status checkmk-stack"
|
|
}
|
|
|
|
install_quadlet_units() {
|
|
log "Richte Podman-Quadlet-Units ein (/etc/containers/systemd/) ..."
|
|
[ -d /etc/containers/systemd ] || mkdir -p /etc/containers/systemd
|
|
local f
|
|
for f in "$PROJECT_DIR"/systemd/quadlet/*.container; do
|
|
# Proxy Manager abgeschaltet? Dann auch keine Unit dafuer.
|
|
if [ "$(basename "$f")" = "nginx-proxy-manager.container" ] && ! npm_enabled; then
|
|
log "NPM_ENABLED=off - ueberspringe nginx-proxy-manager.container"
|
|
rm -f /etc/containers/systemd/nginx-proxy-manager.container
|
|
continue
|
|
fi
|
|
sed -e "s|__PROJECT_DIR__|$PROJECT_DIR|g" \
|
|
-e "s|__CMK_IMAGE__|${CMK_IMAGE:-docker.io/checkmk/check-mk-raw:2.3.0-latest}|g" \
|
|
-e "s|__NPM_IMAGE__|${NPM_IMAGE:-docker.io/jc21/nginx-proxy-manager:2}|g" \
|
|
-e "s|__CMK_SITE_ID__|${CMK_SITE_ID:-cmk}|g" \
|
|
-e "s|__CMK_CONTAINER_NAME__|${CMK_CONTAINER_NAME:-checkmk}|g" \
|
|
-e "s|__NPM_CONTAINER_NAME__|${NPM_CONTAINER_NAME:-nginx-proxy-manager}|g" \
|
|
"$f" >"/etc/containers/systemd/$(basename "$f")"
|
|
done
|
|
systemctl daemon-reload
|
|
ok "Quadlet-Units geschrieben. Start: systemctl start checkmk nginx-proxy-manager"
|
|
log "Die Units starten durch 'WantedBy=multi-user.target' automatisch nach einem Reboot."
|
|
}
|
|
|
|
if [ -n "$WITH_SYSTEMD" ]; then
|
|
if [ -n "$WITH_QUADLET" ]; then
|
|
[ "$RUNTIME" = podman ] || die "--quadlet ist nur mit Podman moeglich."
|
|
install_quadlet_units
|
|
else
|
|
install_compose_unit
|
|
fi
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Firewall-Hinweise
|
|
# ---------------------------------------------------------------------
|
|
print_firewall_hint() {
|
|
local be=""
|
|
if command -v firewall-cmd >/dev/null 2>&1 && firewall-cmd --state >/dev/null 2>&1; then
|
|
be=firewalld
|
|
elif command -v ufw >/dev/null 2>&1 \
|
|
&& ( set +o pipefail; ufw status 2>/dev/null | grep -qi '^Status: active' ); then
|
|
be=ufw
|
|
elif command -v nft >/dev/null 2>&1 && [ -n "$(nft list ruleset 2>/dev/null)" ]; then
|
|
be=nftables
|
|
fi
|
|
[ -n "$be" ] || return 0
|
|
cat <<EOF
|
|
|
|
Firewall: ${be} ist aktiv. Auf RHEL ist ab Werk nur SSH freigegeben - dann
|
|
kommen weder Browser noch Agents durch. Welche Ports dieser Stack braucht und
|
|
ob sie offen sind, sagt:
|
|
|
|
$PROJECT_DIR/scripts/cmk-manage.sh firewall
|
|
sudo $PROJECT_DIR/scripts/cmk-manage.sh firewall open --apply
|
|
|
|
Port 5000 (Checkmk direkt) bleibt dabei zu, solange der Proxy Manager laeuft.
|
|
EOF
|
|
}
|
|
print_firewall_hint
|
|
|
|
cat <<EOF
|
|
|
|
${C_BLD}Installation abgeschlossen.${C_RST}
|
|
|
|
Naechste Schritte:
|
|
1) Konfiguration anpassen:
|
|
\$EDITOR $PROJECT_DIR/.env (CMK_SITE_ID, CMK_PASSWORD, TZ ...)
|
|
2) Stack starten:
|
|
$PROJECT_DIR/scripts/cmk-manage.sh up
|
|
3) NGINX Proxy Manager oeffnen:
|
|
http://$(primary_ip):81 Erstlogin: admin@example.com / changeme
|
|
4) Ist Port 81 im Netz gesperrt, laesst sich die Adminoberflaeche auf einen
|
|
anderen Port legen:
|
|
$PROJECT_DIR/scripts/cmk-manage.sh npm-port 10443
|
|
Oder ganz ohne Netzzugriff per Terminal-Browser auf dieser Maschine:
|
|
$PROJECT_DIR/scripts/cmk-manage.sh browser
|
|
5) Verwaltung / Backup / Migration - ncurses-Oberflaeche:
|
|
$PROJECT_DIR/scripts/cmk-manage.sh gui
|
|
|
|
Alles Weitere steht in der README.md.
|
|
EOF
|