Auf einem frisch installierten RHEL liefert dnf ohne Registrierung gar
keine Pakete - weder podman noch git oder dialog. Das ist jetzt in
beiden READMEs als Kapitel 5.1 beschrieben, mit drei Wegen:
1) kostenlose Developer Subscription for Individuals (bis 16 Systeme,
kleine Produktivnutzung eingeschlossen). RHEL 9 nutzt Simple
Content Access, ein "subscription-manager attach" entfaellt.
2) Installations-DVD als lokales Repository - ohne Konto und offline,
aber ohne Sicherheitsupdates, daher nur als Ueberbrueckung.
3) Wechsel auf Rocky Linux oder AlmaLinux.
Dazu eine Tabelle, welche Pakete das Projekt wirklich braucht und aus
welchem Repository sie kommen, sowie der Hinweis, dass EPEL hier nicht
weiterhilft (es setzt die Basis-Repositories voraus) - in dem Fall ist
der Quadlet-Weg ohne podman-compose die Loesung.
install.sh:
- rhel_repo_check prueft vor der Installation, ob ueberhaupt ein
nutzbares Repository vorhanden ist, und nennt sonst die drei Wege,
statt den nackten dnf-Fehler stehen zu lassen.
- "dialog" ist nicht mehr zwingend: es liegt in AppStream, bei
Fehlschlag wird "newt" (whiptail, BaseOS) versucht. Vorher brach die
Installation ab, wenn dialog fehlte - obwohl die Skripte whiptail
ohnehin unterstuetzen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
356 lines
14 KiB
Bash
Executable File
356 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() {
|
|
local compose_bin
|
|
detect_compose
|
|
compose_bin="${COMPOSE_CMD[*]}"
|
|
|
|
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=/bin/sh -c '$compose_bin up -d'
|
|
ExecStartPost=$PROJECT_DIR/scripts/cmk-manage.sh start-all-sites
|
|
ExecStop=/bin/sh -c '$compose_bin down'
|
|
ExecReload=/bin/sh -c '$compose_bin up -d'
|
|
|
|
[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
|
|
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
|