Files
Docker-LXC/docker-lxc.sh
T
xosna abd939d760 docker-lxc.sh aktualisiert
Hinzugefügt wurden anklickbare Links in der Übersicht von Proxmox bei den Hinweisen.
2026-05-24 09:14:45 +02:00

485 lines
24 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# ==============================================================================
# Proxmox LXC: Debian + Docker + Portainer (optional)
# Aufruf auf dem PVE-Host:
# bash <(curl -fsSL https://your-host/debian-docker-portainer-lxc.sh)
# ==============================================================================
set -euo pipefail
# ── Farben ─────────────────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
success() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }
# ── Voraussetzungen prüfen ─────────────────────────────────────────────────────
[[ $EUID -ne 0 ]] && error "Dieses Skript muss als root auf dem Proxmox-Host ausgeführt werden."
command -v pct &>/dev/null || error "'pct' nicht gefunden läuft das Skript auf einem PVE-Host?"
command -v pvesh &>/dev/null || error "'pvesh' nicht gefunden läuft das Skript auf einem PVE-Host?"
# ══════════════════════════════════════════════════════════════════════════════
echo -e "\n${BOLD}╔══════════════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}║ Proxmox LXC Debian + Docker + Portainer Installer ║${NC}"
echo -e "${BOLD}╚══════════════════════════════════════════════════════════╝${NC}\n"
# ── Standardwerte ──────────────────────────────────────────────────────────────
DEFAULT_CTID=$(pvesh get /cluster/nextid)
DEFAULT_HOSTNAME="debian-docker"
DEFAULT_PASSWORD="ChangeMe123!"
DEFAULT_DISK=8 # GB
DEFAULT_RAM=2048 # MB
DEFAULT_SWAP=512 # MB
DEFAULT_CORES=2
DEFAULT_BRIDGE="vmbr0"
DEFAULT_IP="dhcp" # oder z.B. 192.168.1.100/24
DEFAULT_GW="" # Gateway (nur bei statischer IP)
# ── Interaktive Eingabe ────────────────────────────────────────────────────────
ask() {
local prompt="$1" default="$2" var_name="$3"
echo -ne "${BOLD}${prompt}${NC} [${YELLOW}${default}${NC}]: "
read -r input
printf -v "$var_name" '%s' "${input:-$default}"
}
ask_yn() {
local prompt="$1" default="${2:-y}"
echo -ne "${BOLD}${prompt}${NC} [${YELLOW}${default}${NC}]: "
read -r yn
[[ "${yn:-$default}" =~ ^[Jj1Yy] ]]
}
# ── Storage-Auswahl (dynamisch) ───────────────────────────────────────────────
select_storage() {
echo -e "${CYAN}── Storage-Auswahl ─────────────────────────────────────────${NC}"
echo -e " Verfügbare Storages auf diesem Host:\n"
# pvesm status liefert: Name, Type, Status, Total, Used, Available, %
# Wir filtern nur aktive Storages, die LXC-Container unterstützen
mapfile -t STORAGE_LINES < <(
pvesm status --content rootdir 2>/dev/null \
| awk 'NR>1 && $3=="active" {print $1, $2, $5, $4}' \
| sort
)
if [[ ${#STORAGE_LINES[@]} -eq 0 ]]; then
# Fallback: alle aktiven Storages anzeigen (ohne Content-Filter)
mapfile -t STORAGE_LINES < <(
pvesm status 2>/dev/null \
| awk 'NR>1 && $3=="active" {print $1, $2, $5, $4}' \
| sort
)
fi
if [[ ${#STORAGE_LINES[@]} -eq 0 ]]; then
warn "Keine aktiven Storages gefunden bitte manuell eingeben."
ask "Storage-Name" "local-lvm" STORAGE
return
fi
local i=1
local -a STORAGE_NAMES=()
printf " ${BOLD}%-4s %-20s %-14s %-12s %-12s${NC}\n" "Nr." "Name" "Typ" "Frei" "Gesamt"
echo -e " ──────────────────────────────────────────────────────────"
for line in "${STORAGE_LINES[@]}"; do
read -r sname stype sused stotal <<< "$line"
# Freien Speicher berechnen (Werte in KB von pvesm)
local sfree=0
if [[ "$stotal" =~ ^[0-9]+$ ]] && [[ "$sused" =~ ^[0-9]+$ ]]; then
sfree=$(( stotal - sused ))
fi
# Lesbare Größen
human_size() {
local kb=$1
if (( kb >= 1073741824 )); then printf "%.1f TiB" "$(echo "scale=1; $kb/1073741824" | bc)"
elif (( kb >= 1048576 )); then printf "%.1f GiB" "$(echo "scale=1; $kb/1048576" | bc)"
elif (( kb >= 1024 )); then printf "%.1f MiB" "$(echo "scale=1; $kb/1024" | bc)"
else printf "%d KiB" "$kb"; fi
}
local free_str total_str
if [[ "$stotal" =~ ^[0-9]+$ ]] && (( stotal > 0 )); then
free_str=$(human_size "$sfree")
total_str=$(human_size "$stotal")
else
free_str="n/a"
total_str="n/a"
fi
# Farbe je nach freiem Platz
local color="$GREEN"
if [[ "$stotal" =~ ^[0-9]+$ ]] && (( stotal > 0 )); then
local pct=$(( sfree * 100 / stotal ))
(( pct < 15 )) && color="$RED"
(( pct >= 15 && pct < 30 )) && color="$YELLOW"
fi
printf " ${BOLD}%-4s${NC} ${color}%-20s${NC} %-14s %-12s %-12s\n" \
"[$i]" "$sname" "$stype" "$free_str" "$total_str"
STORAGE_NAMES+=("$sname")
(( i++ ))
done
echo ""
local DEFAULT_NUM=1
while true; do
echo -ne "${BOLD}Auswahl (Nummer eingeben)${NC} [${YELLOW}${DEFAULT_NUM}${NC}]: "
read -r sel
sel="${sel:-$DEFAULT_NUM}"
if [[ "$sel" =~ ^[0-9]+$ ]] && (( sel >= 1 && sel <= ${#STORAGE_NAMES[@]} )); then
STORAGE="${STORAGE_NAMES[$((sel-1))]}"
success "Storage gewählt: ${BOLD}${STORAGE}${NC}"
break
else
warn "Ungültige Eingabe. Bitte eine Zahl zwischen 1 und ${#STORAGE_NAMES[@]} eingeben."
fi
done
echo ""
}
echo -e "${CYAN}── Container-Grundkonfiguration ────────────────────────────${NC}"
ask "Container-ID" "$DEFAULT_CTID" CTID
ask "Hostname" "$DEFAULT_HOSTNAME" HOSTNAME
ask "Root-Passwort" "$DEFAULT_PASSWORD" PASSWORD
echo ""
select_storage
ask "Disk-Größe (GB)" "$DEFAULT_DISK" DISK
ask "RAM (MB)" "$DEFAULT_RAM" RAM
ask "Swap (MB)" "$DEFAULT_SWAP" SWAP
ask "CPU-Kerne" "$DEFAULT_CORES" CORES
ask "Netzwerk-Bridge" "$DEFAULT_BRIDGE" BRIDGE
echo ""
echo -e "${CYAN}── Netzwerk ────────────────────────────────────────────────${NC}"
echo -e " Für DHCP einfach '${YELLOW}dhcp${NC}' eingeben."
echo -e " Für statische IP z.B. '${YELLOW}192.168.1.100/24${NC}'"
ask "IP-Adresse" "$DEFAULT_IP" IP_ADDR
if [[ "$IP_ADDR" != "dhcp" ]]; then
ask "Gateway" "$DEFAULT_GW" GATEWAY
fi
echo ""
echo -e "${CYAN}── Optionale Komponenten ───────────────────────────────────${NC}"
if ask_yn "Portainer CE installieren? (Web-UI für Docker)" "j"; then
INSTALL_PORTAINER=true
ask "Portainer HTTPS-Port" "9443" PORTAINER_PORT
else
INSTALL_PORTAINER=false
fi
echo ""
echo -e "${CYAN}── SSH-Zugriff ─────────────────────────────────────────────${NC}"
if ask_yn "SSH-Root-Login von externen Terminals aktivieren?" "j"; then
ENABLE_ROOT_SSH=true
ask "SSH-Port" "22" SSH_PORT
echo ""
echo -e " ${BOLD}Authentifizierungsmethode:${NC}"
echo -e " ${YELLOW}[1]${NC} Nur Passwort"
echo -e " ${YELLOW}[2]${NC} Nur SSH-Key (du gibst deinen Public Key ein)"
echo -e " ${YELLOW}[3]${NC} Passwort ${BOLD}und${NC} SSH-Key (beide erlaubt)"
echo -ne "${BOLD}Auswahl${NC} [${YELLOW}1${NC}]: "
read -r ssh_auth_sel
case "${ssh_auth_sel:-1}" in
2) SSH_AUTH="key" ;;
3) SSH_AUTH="both" ;;
*) SSH_AUTH="password" ;;
esac
SSH_PUBKEY=""
if [[ "$SSH_AUTH" == "key" || "$SSH_AUTH" == "both" ]]; then
echo ""
echo -e " Gib deinen SSH-Public-Key ein (z.B. Inhalt von ${YELLOW}~/.ssh/id_rsa.pub${NC}):"
echo -ne " ${BOLD}Public Key:${NC} "
read -r SSH_PUBKEY
if [[ -z "$SSH_PUBKEY" ]]; then
warn "Kein Public Key eingegeben falle auf Passwort-Auth zurück."
SSH_AUTH="password"
fi
fi
else
ENABLE_ROOT_SSH=false
SSH_PORT="22"
SSH_AUTH="password"
fi
if ask_yn "Unprivilegierten Container erstellen? (empfohlen)" "j"; then
UNPRIVILEGED=1
else
UNPRIVILEGED=0
fi
# ── Zusammenfassung ────────────────────────────────────────────────────────────
echo ""
echo -e "${BOLD}╔══════════════ Zusammenfassung ════════════════╗${NC}"
printf "${BOLD}${NC} %-22s %-20s ${BOLD}${NC}\n" "Container-ID:" "$CTID"
printf "${BOLD}${NC} %-22s %-20s ${BOLD}${NC}\n" "Hostname:" "$HOSTNAME"
printf "${BOLD}${NC} %-22s %-20s ${BOLD}${NC}\n" "Storage:" "$STORAGE"
printf "${BOLD}${NC} %-22s %-20s ${BOLD}${NC}\n" "Disk:" "${DISK} GB"
printf "${BOLD}${NC} %-22s %-20s ${BOLD}${NC}\n" "RAM / Swap:" "${RAM} MB / ${SWAP} MB"
printf "${BOLD}${NC} %-22s %-20s ${BOLD}${NC}\n" "CPU-Kerne:" "$CORES"
printf "${BOLD}${NC} %-22s %-20s ${BOLD}${NC}\n" "Netzwerk:" "$IP_ADDR"
printf "${BOLD}${NC} %-22s %-20s ${BOLD}${NC}\n" "Portainer:" "$( $INSTALL_PORTAINER && echo "Ja (:${PORTAINER_PORT})" || echo "Nein" )"
printf "${BOLD}${NC} %-22s %-20s ${BOLD}${NC}\n" "Root-SSH:" "$( $ENABLE_ROOT_SSH && echo "Ja (Port ${SSH_PORT}, Auth: ${SSH_AUTH})" || echo "Nein" )"
printf "${BOLD}${NC} %-22s %-20s ${BOLD}${NC}\n" "Unprivilegiert:" "$( [[ $UNPRIVILEGED -eq 1 ]] && echo "Ja" || echo "Nein" )"
echo -e "${BOLD}╚══════════════════════════════════════════════╝${NC}"
echo ""
ask_yn "Jetzt installieren?" "j" || { echo "Abgebrochen."; exit 0; }
# ── Debian-Template herunterladen ──────────────────────────────────────────────
info "Suche aktuelles Debian-LXC-Template …"
TEMPLATE_STORAGE="local"
TEMPLATE=$(pveam available --section system | awk '/debian-1[2-9]/ {print $2}' | sort -V | tail -1)
[[ -z "$TEMPLATE" ]] && error "Kein Debian-Template in der PVE-Datenbank gefunden. 'pveam update' ausführen!"
DOWNLOADED=$(pveam list "$TEMPLATE_STORAGE" 2>/dev/null | awk '{print $1}' | grep -F "$TEMPLATE" || true)
if [[ -z "$DOWNLOADED" ]]; then
info "Lade Template herunter: $TEMPLATE"
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE"
success "Template geladen."
else
success "Template bereits vorhanden: $TEMPLATE"
fi
TEMPLATE_PATH="${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}"
# ── LXC-Container erstellen ────────────────────────────────────────────────────
info "Erstelle LXC-Container $CTID"
NET_CONFIG="name=eth0,bridge=${BRIDGE}"
if [[ "$IP_ADDR" == "dhcp" ]]; then
NET_CONFIG+=",ip=dhcp"
else
NET_CONFIG+=",ip=${IP_ADDR}"
[[ -n "${GATEWAY:-}" ]] && NET_CONFIG+=",gw=${GATEWAY}"
fi
pct create "$CTID" "$TEMPLATE_PATH" \
--hostname "$HOSTNAME" \
--password "$PASSWORD" \
--storage "$STORAGE" \
--rootfs "${STORAGE}:${DISK}" \
--memory "$RAM" \
--swap "$SWAP" \
--cores "$CORES" \
--net0 "$NET_CONFIG" \
--unprivileged "$UNPRIVILEGED" \
--features "keyctl=1,nesting=1" \
--onboot 1 \
--start 0
success "Container $CTID erstellt."
# ── Container starten ──────────────────────────────────────────────────────────
info "Starte Container …"
pct start "$CTID"
sleep 5
# ── Hilfsfunktion: Befehl im Container ausführen ──────────────────────────────
lxc_exec() { pct exec "$CTID" -- bash -c "$*"; }
# ── System aktualisieren ───────────────────────────────────────────────────────
info "System aktualisieren (apt update & upgrade) …"
lxc_exec "apt-get update -qq && apt-get upgrade -y -qq"
lxc_exec "apt-get install -y -qq ca-certificates curl gnupg lsb-release wget"
success "System aktuell."
# ── Docker installieren (offizieller Weg) ─────────────────────────────────────
info "Docker Engine installieren …"
lxc_exec "
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg \
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg --yes
chmod a+r /etc/apt/keyrings/docker.gpg
echo \"deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian \
\$(. /etc/os-release && echo \"\$VERSION_CODENAME\") stable\" \
> /etc/apt/sources.list.d/docker.list
apt-get update -qq
apt-get install -y -qq \
docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
systemctl enable docker --now
"
success "Docker installiert."
# ── Docker testen ──────────────────────────────────────────────────────────────
info "Docker-Funktionstest …"
if lxc_exec "docker run --rm hello-world" &>/dev/null; then
success "Docker läuft korrekt."
else
warn "Docker-Test fehlgeschlagen bitte manuell prüfen."
fi
# ── Portainer installieren ─────────────────────────────────────────────────────
if $INSTALL_PORTAINER; then
info "Portainer CE installieren (Port ${PORTAINER_PORT}) …"
lxc_exec "
docker volume create portainer_data
docker run -d \
--name portainer \
--restart always \
-p 8000:8000 \
-p ${PORTAINER_PORT}:9443 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:latest
"
success "Portainer CE gestartet."
fi
# ── Proxmox Hinweise-Panel setzen ─────────────────────────────────────────────
info "Setze Proxmox-Hinweise (Notes) für Container $CTID"
# IP ermitteln (kurz nach Start warten)
sleep 2
NOTES_IP=$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "")
[[ -z "$NOTES_IP" ]] && NOTES_IP="(IP via DHCP siehe Konsole)"
# Debian-Logo SVG (inline Base64 als data-URI vermeiden → direkte SVG-Strings)
DEBIAN_SVG='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="52" height="52"><circle cx="50" cy="50" r="48" fill="#d70751"/><path d="M61.7 32.2c-1-.1-2 0-2.9.2 3.6 1.5 5.6 5.4 5.1 9.3-.4 3.3-2.6 6-5.4 7.4-1.6.8-3.4 1.1-5.2.9-3.6-.4-6.8-3-7.9-6.5-.4-1.3-.5-2.7-.2-4 .5-2.4 2.1-4.5 4.3-5.6 1.8-.9 3.9-1 5.8-.3-2.3-1.8-5.4-2.4-8.2-1.5-3.5 1.1-6 4.4-6.2 8.1-.1 1.9.4 3.8 1.4 5.4 2.2 3.6 6.5 5.5 10.7 5 2-.2 3.9-1 5.5-2.2 3.2-2.4 4.9-6.3 4.5-10.2-.1-2.2-.9-4.4-1.3-6z" fill="#fff"/><path d="M53.3 37.7c-1.6-.2-3.3.5-4.3 1.8-1.1 1.4-1.3 3.4-.5 5 .8 1.5 2.4 2.5 4.1 2.6 1.6.1 3.2-.6 4.2-1.9 1-1.3 1.3-3.1.7-4.7-.6-1.6-2.3-2.7-4.2-2.8z" fill="#fff"/></svg>'
DOCKER_SVG='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="52" height="52"><rect width="100" height="100" rx="14" fill="#2496ed"/><g fill="#fff"><rect x="13" y="46" width="12" height="11" rx="1"/><rect x="27" y="46" width="12" height="11" rx="1"/><rect x="41" y="46" width="12" height="11" rx="1"/><rect x="55" y="46" width="12" height="11" rx="1"/><rect x="27" y="33" width="12" height="11" rx="1"/><rect x="41" y="33" width="12" height="11" rx="1"/><rect x="41" y="20" width="12" height="11" rx="1"/><path d="M78 49c-1-4-5-6-9-5-1-4-5-7-9-7v1c3 0 6 2 7 5l1 2 2-1c3-1 6 1 7 4l0 1-1 0c-4 1-25 0-28 0l0 1c1 7 7 12 14 12 8 0 15-5 17-13l0-1z"/></g></svg>'
# Portainer-URL
if $INSTALL_PORTAINER; then
PORTAINER_URL="https://${NOTES_IP}:${PORTAINER_PORT}"
else
PORTAINER_URL=""
fi
# SSH-Zeile
SSH_LINE=""
if $ENABLE_ROOT_SSH; then
if [[ "$SSH_PORT" == "22" ]]; then
SSH_LINE="ssh root@${NOTES_IP}"
else
SSH_LINE="ssh root@${NOTES_IP} -p ${SSH_PORT}"
fi
fi
# HTML für den Proxmox Notes-Bereich zusammenbauen
NOTE_CONTENT="<div style=\"font-family:sans-serif;line-height:1.6;padding:4px\">"
NOTE_CONTENT+="<div style=\"display:flex;align-items:center;gap:14px;margin-bottom:10px\">"
NOTE_CONTENT+="<a href=\"https://www.debian.org\" target=\"_blank\" title=\"Debian\">${DEBIAN_SVG}</a>"
NOTE_CONTENT+="<a href=\"https://www.docker.com\" target=\"_blank\" title=\"Docker\">${DOCKER_SVG}</a>"
NOTE_CONTENT+="</div>"
NOTE_CONTENT+="<div><strong>&#127760; IP-Adresse:</strong> ${NOTES_IP}</div>"
if $INSTALL_PORTAINER; then
NOTE_CONTENT+="<div><strong>&#128051; Portainer:</strong> <a href=\"${PORTAINER_URL}\" target=\"_blank\">${PORTAINER_URL}</a></div>"
fi
if $ENABLE_ROOT_SSH; then
NOTE_CONTENT+="<div><strong>&#128273; SSH:</strong> <code>${SSH_LINE}</code></div>"
fi
NOTE_CONTENT+="<div style=\"margin-top:10px;padding-top:8px;border-top:1px solid #ddd;font-size:0.85em;color:#555\">"
NOTE_CONTENT+="Installiert mit <a href=\"https://gitea.vourx.com\" target=\"_blank\">gitea.vourx.com</a>"
NOTE_CONTENT+=" &nbsp;|&nbsp; <a href=\"https://www.debian.org/releases/\" target=\"_blank\">Debian Docs</a>"
NOTE_CONTENT+=" &nbsp;|&nbsp; <a href=\"https://docs.docker.com\" target=\"_blank\">Docker Docs</a>"
NOTE_CONTENT+="</div></div>"
# In Proxmox als Container-Description (Hinweise) setzen
pvesh set /nodes/$(hostname)/lxc/${CTID}/config --description "${NOTE_CONTENT}" 2>/dev/null \
&& success "Proxmox-Hinweise gesetzt (sichtbar unter Container → Hinweise)." \
|| warn "Hinweise konnten nicht automatisch gesetzt werden."
# ── SSH vollständig konfigurieren ─────────────────────────────────────────────
if $ENABLE_ROOT_SSH; then
info "Installiere und konfiguriere SSH-Server …"
# openssh-server sicherstellen (Debian Minimal hat es evtl. nicht)
lxc_exec "apt-get install -y -qq openssh-server"
# sshd_config: Root-Login und Port setzen
lxc_exec "
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
grep -q '^PermitRootLogin' /etc/ssh/sshd_config || echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
sed -i 's/^#*Port .*/Port ${SSH_PORT}/' /etc/ssh/sshd_config
grep -q '^Port ' /etc/ssh/sshd_config || echo 'Port ${SSH_PORT}' >> /etc/ssh/sshd_config
"
# Authentifizierungsmethode konfigurieren
if [[ \"\$SSH_AUTH\" == \"password\" ]]; then
lxc_exec "
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
grep -q '^PasswordAuthentication' /etc/ssh/sshd_config || echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config
sed -i 's/^#*PubkeyAuthentication.*/PubkeyAuthentication no/' /etc/ssh/sshd_config
"
elif [[ \"\$SSH_AUTH\" == \"key\" ]]; then
lxc_exec "
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/^#*PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
grep -q '^PubkeyAuthentication' /etc/ssh/sshd_config || echo 'PubkeyAuthentication yes' >> /etc/ssh/sshd_config
mkdir -p /root/.ssh && chmod 700 /root/.ssh
echo '${SSH_PUBKEY}' >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
"
else
# both
lxc_exec "
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
grep -q '^PasswordAuthentication' /etc/ssh/sshd_config || echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config
sed -i 's/^#*PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
grep -q '^PubkeyAuthentication' /etc/ssh/sshd_config || echo 'PubkeyAuthentication yes' >> /etc/ssh/sshd_config
mkdir -p /root/.ssh && chmod 700 /root/.ssh
echo '${SSH_PUBKEY}' >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
"
fi
# SSH-Dienst aktivieren & (neu-)starten
lxc_exec "systemctl enable ssh --now && systemctl restart ssh"
# Verbindungstest vom PVE-Host (nc prüft ob Port offen ist)
info "Prüfe SSH-Erreichbarkeit auf Port ${SSH_PORT}"
sleep 3
CT_TEST_IP=$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}' || true)
if [[ -n "$CT_TEST_IP" ]] && nc -z -w5 "$CT_TEST_IP" "$SSH_PORT" 2>/dev/null; then
success "SSH erreichbar → ssh root@${CT_TEST_IP} -p ${SSH_PORT}"
else
warn "SSH-Port noch nicht erreichbar evtl. kurz warten und erneut testen."
fi
fi
# ── Abschluss ──────────────────────────────────────────────────────────────────
CONTAINER_IP=$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "unbekannt")
echo ""
echo -e "${BOLD}${GREEN}╔═══════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}${NC} ${BOLD}Installation abgeschlossen!${NC}"
echo -e "${BOLD}${GREEN}╠═══════════════════════════════════════════════════════╣${NC}"
echo -e "${GREEN}${NC}"
printf "${GREEN}${NC} %-18s ${BOLD}%s${NC}\n" "Container-ID:" "$CTID"
printf "${GREEN}${NC} %-18s ${BOLD}%s${NC}\n" "Hostname:" "$HOSTNAME"
printf "${GREEN}${NC} %-18s ${BOLD}%s${NC}\n" "IP-Adresse:" "$CONTAINER_IP"
echo -e "${GREEN}${NC}"
printf "${GREEN}${NC} %-18s ${BOLD}%s${NC}\n" "Docker:" "installiert & aktiv"
if $INSTALL_PORTAINER; then
printf "${GREEN}${NC} %-18s ${BOLD}https://%s:%s${NC}\n" "Portainer UI:" "$CONTAINER_IP" "$PORTAINER_PORT"
fi
if $ENABLE_ROOT_SSH; then
echo -e "${GREEN}${NC}"
echo -e "${GREEN}${NC} ${CYAN}── SSH-Verbindung ─────────────────────────────────${NC}"
if [[ "$SSH_PORT" == "22" ]]; then
printf "${GREEN}${NC} ${BOLD}ssh root@%s${NC}\n" "$CONTAINER_IP"
else
printf "${GREEN}${NC} ${BOLD}ssh root@%s -p %s${NC}\n" "$CONTAINER_IP" "$SSH_PORT"
fi
printf "${GREEN}${NC} %-18s ${BOLD}%s${NC}\n" "Auth-Methode:" "$SSH_AUTH"
fi
echo -e "${GREEN}${NC}"
echo -e "${GREEN}${NC} ${YELLOW}⚠ Root-Passwort nach erstem Login bitte ändern!${NC}"
echo -e "${GREEN}${NC}"
echo -e "${BOLD}${GREEN}╚═══════════════════════════════════════════════════════╝${NC}"