Der NGINX Proxy Manager laesst sich jetzt auch dann einrichten, wenn
seine Oberflaeche auf Port 81 aus dem Netz nicht erreichbar ist.
browser
Terminal-Browser in einem eigenen Container, gestartet mit
"run --rm -it --network host" - damit erreicht er 127.0.0.1 genau wie
der Proxy Manager selbst. Engines: browsh und carbonyl (rendern einen
echten Browser und koennen JavaScript, das die Adminoberflaeche
benoetigt) sowie links2/w3m/lynx und eine Shell mit curl/jq/openssl
fuer Diagnose. Werkzeug-Image unter containers/browser/.
Bewusst kein Compose-Dienst: ein Browser ist interaktiv, und
podman-compose behandelt Profile unzuverlaessig.
npm-port
Port der Adminoberflaeche frei waehlbar. Das Image bindet fest auf 81
("listen 81 default;"), eine Umgebungsvariable dafuer gibt es in 2.15.1
noch nicht. Ein abweichender Port wird deshalb ueber den nginx-
Erweiterungspunkt /data/nginx/custom/http.conf bereitgestellt. Nur der
markierte Block wird verwaltet, eigene Eintraege bleiben erhalten.
Port 81 lauscht weiterhin lokal und ist per Firewall zu sperren.
config
Alle Werte der .env sind menuegefuehrt bzw. ueber config get/set
aenderbar - der Editor bleibt als Menuepunkt erhalten. Geprueft wird
beim Setzen: Portbereich und Kollisionen, Site-Namen, vollqualifizierte
Image-Namen, installierte Runtime, Zahlen- und URL-Felder.
Fehlerbehebungen
- ui_*-Funktionen initialisieren sich selbst; eine von aussen gesetzte
UI_BIN fuehrte unter "set -u" zum Abbruch.
- Eine nicht installierte CMK_RUNTIME machte das gesamte Werkzeug
unbenutzbar, auch den Aufruf zum Korrigieren. Jetzt wird gewarnt und
selbst gesucht.
- env_set setzt Werte mit Leerzeichen in Anfuehrungszeichen und laesst
optional leere Werte zu.
README auf 22 Kapitel erweitert: Kapitel 11 (Terminal-Browser inkl.
NPM-API als Fallback fuer den Zertifikats-Upload), Kapitel 12 (Admin-Port)
und Abschnitt 14.3 (Einstellungsmenue).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
315 lines
12 KiB
Bash
Executable File
315 lines
12 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)"
|
|
}
|
|
|
|
install_rhel() {
|
|
log "Installiere Podman ..."
|
|
local PM=dnf
|
|
command -v dnf >/dev/null 2>&1 || PM=yum
|
|
$PM install -y podman tar dialog openssl || die "Podman-Installation fehlgeschlagen."
|
|
|
|
# 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
|