Wer die HTTPS-Terminierung bereits mit einem NGINX auf einem anderen
System loest, braucht den Container nicht. NPM_ENABLED=off sorgt dafuer,
dass er gar nicht erst angelegt wird: compose bekommt dann nur noch den
Checkmk-Dienst genannt.
NPM_ENABLED=off -> compose up -d checkmk
NPM_ENABLED=on -> compose up -d checkmk nginx-proxy-manager
Das gilt auch fuer "pull" - abgeschaltet wird nicht einmal das Image
geladen. Beim Abschalten wird ein vorhandener Container gestoppt UND
entfernt, damit ihn weder eine Restart-Regel noch ein Reboot wieder
hochzieht.
Die Daten bleiben in jedem Fall erhalten: Konfiguration, Datenbank und
Zertifikate liegen in den Bind-Mounts unter ./data/npm, nicht im
Container. Vor dem Abschalten werden beide Verzeichnisse mit Groesse
angezeigt; beim Einschalten wird die vorhandene Konfiguration samt
Zertifikaten unveraendert wieder aufgenommen.
Bedienung:
- Menuepunkt "Proxy Manager betreiben" unter Einstellungen -> npm
- cmk-manage.sh npm-enable [on|off] (ohne Argument: Stand anzeigen)
- config set NPM_ENABLED off
"npm-enable off" gibt gleich die noetige Firewall-Regel und eine
Beispielkonfiguration fuer den externen NGINX aus - Port 5000 muss dann
fuer dessen IP erreichbar sein. Der Agent-Receiver auf Port 8000 ist
davon nicht betroffen, den sprechen die Agents ohnehin direkt an.
Damit der Schalter auch nach einem Reboot gilt, ruft die von install.sh
erzeugte systemd-Unit jetzt cmk-manage.sh auf statt compose direkt.
Bei Quadlet wird die Unit nginx-proxy-manager.container uebersprungen
und entfernt, solange der Schalter auf "off" steht.
Verifiziert mit einem compose-Stub, dass in beiden Schalterstellungen
genau die erwarteten Dienste uebergeben werden, sowie ueber das Menue.
Dokumentiert als Abschnitt 12.4 in beiden Sprachfassungen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
362 lines
14 KiB
Bash
Executable File
362 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() {
|
|
if command -v firewall-cmd >/dev/null 2>&1 && firewall-cmd --state >/dev/null 2>&1; then
|
|
cat <<'EOF'
|
|
|
|
Firewall (firewalld) - empfohlene Regeln:
|
|
firewall-cmd --permanent --add-service=http
|
|
firewall-cmd --permanent --add-service=https
|
|
firewall-cmd --permanent --add-port=8000/tcp # Agent-Receiver
|
|
# Admin-Oberflaeche des Proxy Managers nur aus dem Admin-Netz:
|
|
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=10.0.0.0/24 port port=81 protocol=tcp accept'
|
|
# Checkmk-Port 5000 NICHT nach aussen oeffnen (nur ueber den Proxy)
|
|
firewall-cmd --reload
|
|
EOF
|
|
elif command -v ufw >/dev/null 2>&1; then
|
|
cat <<'EOF'
|
|
|
|
Firewall (ufw) - empfohlene Regeln:
|
|
ufw allow 80/tcp
|
|
ufw allow 443/tcp
|
|
ufw allow 8000/tcp # Agent-Receiver
|
|
ufw allow from 10.0.0.0/24 to any port 81 proto tcp # Proxy-Manager-Admin
|
|
# Port 5000 (Checkmk direkt) bleibt zu - Zugriff nur ueber den Proxy
|
|
EOF
|
|
fi
|
|
}
|
|
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
|