Files
docker-podman-installation-…/install.sh
T
duffyduckandClaude Opus 5 4bbd1de4f6 Schalter: NGINX Proxy Manager abschaltbar (externer NGINX)
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>
2026-09-20 15:11:55 +02:00

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