abd939d760
Hinzugefügt wurden anklickbare Links in der Übersicht von Proxmox bei den Hinweisen.
485 lines
24 KiB
Bash
485 lines
24 KiB
Bash
#!/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>🌐 IP-Adresse:</strong> ${NOTES_IP}</div>"
|
||
|
||
if $INSTALL_PORTAINER; then
|
||
NOTE_CONTENT+="<div><strong>🐳 Portainer:</strong> <a href=\"${PORTAINER_URL}\" target=\"_blank\">${PORTAINER_URL}</a></div>"
|
||
fi
|
||
|
||
if $ENABLE_ROOT_SSH; then
|
||
NOTE_CONTENT+="<div><strong>🔑 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+=" | <a href=\"https://www.debian.org/releases/\" target=\"_blank\">Debian Docs</a>"
|
||
NOTE_CONTENT+=" | <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}" |