Files
Ubuntu-24.04-LXC/ubuntu-lxc.sh
T
2026-05-24 10:26:20 +02:00

544 lines
26 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: Ubuntu Server LTS (minimal, sauber, produktionsbereit)
# Aufruf auf dem PVE-Host:
# bash <(curl -fsSL https://your-host/ubuntu-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 Ubuntu LTS Installer ║${NC}"
echo -e "${BOLD}╚══════════════════════════════════════════════════╝${NC}\n"
# ── Standardwerte ──────────────────────────────────────────────────────────────
DEFAULT_CTID=$(pvesh get /cluster/nextid)
DEFAULT_HOSTNAME="ubuntu"
DEFAULT_PASSWORD="ChangeMe123!"
DEFAULT_DISK=4 # GB
DEFAULT_RAM=1024 # MB (Ubuntu LTS empfohlen)
DEFAULT_SWAP=512 # MB
DEFAULT_CORES=1
DEFAULT_BRIDGE="vmbr0"
DEFAULT_IP="dhcp"
DEFAULT_GW=""
# ── Hilfsfunktionen ────────────────────────────────────────────────────────────
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:-j}"
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"
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
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"
local sfree=0
if [[ "$stotal" =~ ^[0-9]+$ ]] && [[ "$sused" =~ ^[0-9]+$ ]]; then
sfree=$(( stotal - sused ))
fi
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
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 ""
}
# ══════════════════════════════════════════════════════════════════════════════
# KONFIGURATION
# ══════════════════════════════════════════════════════════════════════════════
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.50/24${NC}'"
ask "IP-Adresse" "$DEFAULT_IP" IP_ADDR
if [[ "$IP_ADDR" != "dhcp" ]]; then
ask "Gateway" "$DEFAULT_GW" GATEWAY
fi
echo ""
echo -e "${CYAN}── Container-Typ ─────────────────────────────────────────────${NC}"
if ask_yn "Unprivilegierten Container erstellen? (empfohlen)" "j"; then
UNPRIVILEGED=1
else
UNPRIVILEGED=0
fi
echo ""
echo -e "${CYAN}── Optionale Pakete ──────────────────────────────────────────${NC}"
echo -e " Wähle zusätzliche Pakete die installiert werden sollen:\n"
if ask_yn "curl, wget, git (Basis-Tools)" "j"; then
INSTALL_BASETOOLS=true
else
INSTALL_BASETOOLS=false
fi
if ask_yn "htop, ncdu, tree, net-tools (System-Monitoring)" "j"; then
INSTALL_MONITORING=true
else
INSTALL_MONITORING=false
fi
if ask_yn "vim + nano (Editoren)" "j"; then
INSTALL_EDITORS=true
else
INSTALL_EDITORS=false
fi
if ask_yn "ufw (Firewall, deaktiviert)" "n"; then
INSTALL_UFW=true
else
INSTALL_UFW=false
fi
if ask_yn "cron + logrotate (Automatisierung)" "j"; then
INSTALL_CRON=true
else
INSTALL_CRON=false
fi
if ask_yn "fail2ban (Schutz gegen Brute-Force)" "n"; then
INSTALL_FAIL2BAN=true
else
INSTALL_FAIL2BAN=false
fi
echo ""
echo -e "${CYAN}── Zeitzone ──────────────────────────────────────────────────${NC}"
ask "Zeitzone" "Europe/Berlin" TIMEZONE
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 (Inhalt von ${YELLOW}~/.ssh/id_rsa.pub${NC} o.ä.):"
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
# ── Zusammenfassung ────────────────────────────────────────────────────────────
echo ""
echo -e "${BOLD}╔══════════════════ Zusammenfassung ════════════════════╗${NC}"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "Container-ID:" "$CTID"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "Hostname:" "$HOSTNAME"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "Storage:" "$STORAGE"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "Disk / RAM / Swap:" "${DISK} GB / ${RAM} MB / ${SWAP} MB"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "CPU-Kerne:" "$CORES"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "Netzwerk:" "$IP_ADDR"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "Zeitzone:" "$TIMEZONE"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "Unprivilegiert:" "$( [[ $UNPRIVILEGED -eq 1 ]] && echo "Ja" || echo "Nein" )"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "Basis-Tools:" "$( $INSTALL_BASETOOLS && echo "Ja" || echo "Nein" )"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "Monitoring-Tools:" "$( $INSTALL_MONITORING && echo "Ja" || echo "Nein" )"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "Editoren:" "$( $INSTALL_EDITORS && echo "Ja" || echo "Nein" )"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "UFW Firewall:" "$( $INSTALL_UFW && echo "Ja" || echo "Nein" )"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "Cron/Logrotate:" "$( $INSTALL_CRON && echo "Ja" || echo "Nein" )"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "fail2ban:" "$( $INSTALL_FAIL2BAN && echo "Ja" || echo "Nein" )"
printf "${BOLD}${NC} %-22s %-26s ${BOLD}${NC}\n" "Root-SSH:" "$( $ENABLE_ROOT_SSH && echo "Ja (Port ${SSH_PORT}, ${SSH_AUTH})" || echo "Nein" )"
echo -e "${BOLD}╚═══════════════════════════════════════════════════════╝${NC}"
echo ""
ask_yn "Jetzt installieren?" "j" || { echo "Abgebrochen."; exit 0; }
# ══════════════════════════════════════════════════════════════════════════════
# INSTALLATION
# ══════════════════════════════════════════════════════════════════════════════
# ── Ubuntu-Template herunterladen ──────────────────────────────────────────────
info "Suche aktuelles Ubuntu-LXC-Template …"
TEMPLATE_STORAGE="local"
TEMPLATE=$(pveam available --section system | awk '/ubuntu-24\.04/ {print $2}' | sort -V | tail -1)
[[ -z "$TEMPLATE" ]] && error "Kein Ubuntu-LTS-Template gefunden. Bitte '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 ──────────────────────────────────────────────────────────────
lxc_exec() { pct exec "$CTID" -- bash -c "$*"; }
# ── System aktualisieren ───────────────────────────────────────────────────────
info "System aktualisieren …"
lxc_exec "apt-get update -qq && apt-get upgrade -y -qq"
lxc_exec "apt-get install -y -qq ca-certificates locales"
success "System aktuell."
# ── Zeitzone setzen ────────────────────────────────────────────────────────────
info "Zeitzone setzen: ${TIMEZONE}"
lxc_exec "
ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime
echo '${TIMEZONE}' > /etc/timezone
dpkg-reconfigure -f noninteractive tzdata 2>/dev/null || true
"
success "Zeitzone gesetzt."
# ── Locale konfigurieren ───────────────────────────────────────────────────────
info "Locale konfigurieren (de_DE.UTF-8 + en_US.UTF-8) …"
lxc_exec "
sed -i 's/^# *de_DE.UTF-8/de_DE.UTF-8/' /etc/locale.gen
sed -i 's/^# *en_US.UTF-8/en_US.UTF-8/' /etc/locale.gen
locale-gen 2>/dev/null
update-locale LANG=de_DE.UTF-8 LC_MESSAGES=en_US.UTF-8
" 2>/dev/null || warn "Locale-Konfiguration übersprungen."
success "Locale konfiguriert."
# ── Optionale Pakete installieren ──────────────────────────────────────────────
PKGS=()
if $INSTALL_BASETOOLS; then
PKGS+=(curl wget git)
fi
if $INSTALL_MONITORING; then
PKGS+=(htop ncdu tree net-tools iputils-ping)
fi
if $INSTALL_EDITORS; then
PKGS+=(vim nano)
fi
if $INSTALL_CRON; then
PKGS+=(cron logrotate)
fi
if $INSTALL_FAIL2BAN; then
PKGS+=(fail2ban)
fi
if $INSTALL_UFW; then
PKGS+=(ufw)
fi
if [[ ${#PKGS[@]} -gt 0 ]]; then
info "Installiere optionale Pakete: ${PKGS[*]}"
lxc_exec "apt-get install -y -qq ${PKGS[*]}"
success "Pakete installiert."
fi
# ── fail2ban konfigurieren (falls installiert) ─────────────────────────────────
if $INSTALL_FAIL2BAN; then
info "fail2ban konfigurieren …"
lxc_exec "
cat > /etc/fail2ban/jail.local << 'F2B'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd
[sshd]
enabled = true
F2B
systemctl enable fail2ban --now
"
success "fail2ban aktiv (SSH-Schutz: 5 Versuche / 10 Min → 1h Ban)."
fi
# ── UFW konfigurieren (falls installiert) ──────────────────────────────────────
if $INSTALL_UFW; then
info "UFW vorbereiten (deaktiviert, SSH freigegeben) …"
lxc_exec "
ufw default deny incoming
ufw default allow outgoing
ufw allow ${SSH_PORT}/tcp comment 'SSH'
# ufw enable ← bewusst nicht aktiviert; bitte manuell nach Prüfung aktivieren
"
success "UFW konfiguriert (noch deaktiviert mit 'ufw enable' aktivieren)."
fi
# ── SSH vollständig konfigurieren ─────────────────────────────────────────────
if $ENABLE_ROOT_SSH; then
info "Installiere und konfiguriere SSH-Server …"
lxc_exec "apt-get install -y -qq openssh-server"
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
"
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
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
lxc_exec "systemctl enable ssh --now && systemctl restart ssh"
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
# ── Proxmox Hinweise-Panel setzen (Markdown) ──────────────────────────────────
info "Setze Proxmox-Hinweise (Notes) für Container $CTID"
sleep 2
NOTES_IP=$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "")
[[ -z "$NOTES_IP" ]] && NOTES_IP="n/a (DHCP bitte in Konsole prüfen)"
# Ubuntu-Version ermitteln
UBUNTU_VER=$(pct exec "$CTID" -- bash -c ". /etc/os-release && echo \$VERSION" 2>/dev/null || echo "Ubuntu")
# SSH-Befehl
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
# Badges + Info als Markdown
NOTE="[![Ubuntu](https://img.shields.io/badge/${UBUNTU_VER// /%20}-A81D33?style=for-the-badge&logo=ubuntu&logoColor=white)](https://ubuntu.com)"
NOTE+=$'\n\n'
NOTE+="---"$'\n\n'
NOTE+="**🌐 IP-Adresse:** \`${NOTES_IP}\`"$'\n\n'
if $ENABLE_ROOT_SSH; then
NOTE+="**🔑 SSH:** \`${SSH_LINE}\`"$'\n\n'
fi
# Installierte optionale Pakete auflisten
INSTALLED_LIST=""
$INSTALL_BASETOOLS && INSTALLED_LIST+="curl wget git "
$INSTALL_MONITORING && INSTALLED_LIST+="htop ncdu net-tools "
$INSTALL_EDITORS && INSTALLED_LIST+="vim nano "
$INSTALL_CRON && INSTALLED_LIST+="cron logrotate "
$INSTALL_FAIL2BAN && INSTALLED_LIST+="fail2ban "
$INSTALL_UFW && INSTALLED_LIST+="ufw "
if [[ -n "$INSTALLED_LIST" ]]; then
NOTE+="**📦 Pakete:** \`${INSTALLED_LIST% }\`"$'\n\n'
fi
NOTE+="**🕐 Zeitzone:** ${TIMEZONE}"$'\n\n'
NOTE+="---"$'\n\n'
NOTE+="Installiert mit [gitea.vourx.com](https://gitea.vourx.com)"
NOTE+=" | [Ubuntu Docs](https://ubuntu.com/server/docs)"
NOTE+=" | [Ubuntu Packages](https://packages.ubuntu.com)"
pvesh set /nodes/$(hostname)/lxc/${CTID}/config --description "${NOTE}" 2>/dev/null \
&& success "Proxmox-Hinweise gesetzt." \
|| warn "Hinweise konnten nicht gesetzt werden bitte manuell eintragen."
# ── System aufräumen ───────────────────────────────────────────────────────────
info "System aufräumen …"
lxc_exec "apt-get autoremove -y -qq && apt-get autoclean -qq"
success "Fertig aufgeräumt."
# ── 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} %-20s ${BOLD}%s${NC}\n" "Container-ID:" "$CTID"
printf "${GREEN}${NC} %-20s ${BOLD}%s${NC}\n" "Hostname:" "$HOSTNAME"
printf "${GREEN}${NC} %-20s ${BOLD}%s${NC}\n" "IP-Adresse:" "$CONTAINER_IP"
printf "${GREEN}${NC} %-20s ${BOLD}%s${NC}\n" "Zeitzone:" "$TIMEZONE"
echo -e "${GREEN}${NC}"
if $ENABLE_ROOT_SSH; then
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} %-20s ${BOLD}%s${NC}\n" "Auth-Methode:" "$SSH_AUTH"
echo -e "${GREEN}${NC}"
fi
if $INSTALL_UFW; then
echo -e "${GREEN}${NC} ${YELLOW}🔒 UFW installiert aber noch nicht aktiv.${NC}"
echo -e "${GREEN}${NC} ${YELLOW} Im Container mit 'ufw enable' aktivieren.${NC}"
echo -e "${GREEN}${NC}"
fi
echo -e "${GREEN}${NC} ${YELLOW}⚠ Root-Passwort nach erstem Login bitte ändern!${NC}"
echo -e "${GREEN}${NC}"
echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════════════════╝${NC}"
echo ""