From b3b3e740d37df03c708d361546de57ab836ba736 Mon Sep 17 00:00:00 2001 From: karim Date: Mon, 25 May 2026 23:14:34 +0200 Subject: [PATCH] Initial commit: Hugo-Host LXC Setup Script --- README.md | 124 ++++++++++++++++++++ setup-hugo-host.sh | 276 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 400 insertions(+) create mode 100644 README.md create mode 100755 setup-hugo-host.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..3b338a9 --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +# lxc-hugo-host + +Setup-Script für minimalistische Hugo-Hosting-Container unter Proxmox. + +Richtet einen frischen Debian-12-LXC-Container in einem Schritt ein: + +- Nginx mit Security Headers (HTTP only, TLS via vorgelagertem Reverse-Proxy) +- SSH-Daemon als reiner SFTP-Server (kein Shell-Login von außen) +- SFTP-User mit Chroot auf `/var/www` +- UFW, fail2ban, unattended-upgrades, sysctl-Hardening +- Public Key direkt im Script hinterlegt + +## Konfiguration + +Vor dem ersten Lauf einmalig den eigenen Public Key oben im Script eintragen: + +```bash +SSH_PUBKEYS="ssh-ed25519 AAAA... user@host" +``` + +Mehrere Keys: eine Zeile pro Key innerhalb der Variable. + +## Pro neuem Server + +### 1. Container in Proxmox erstellen + +- Template: `debian-12-standard` +- Unprivileged: ja +- 512 MB RAM, 1 Core reicht + +### 2. In den Container + +Auf dem Proxmox-Host: + +```bash +pct enter +``` + +### 3. Script ausführen + +```bash +curl -fsSL https://git.kgva.ch/karim/lxc-hugo-host/raw/branch/main/setup-hugo-host.sh \ + | bash -s -- meine-domain.ch +``` + +Das Script: +- installiert und konfiguriert alles (inkl. `apt update` + `upgrade`) +- legt den SFTP-User `webedit-meine-domain-ch` an +- gibt am Ende die fertige Deploy-Zeile aus + +### 4. Reverse-Proxy für TLS + +Im Nginx Proxy Manager (oder Caddy) ein Proxy-Host anlegen: + +- **Domain:** `meine-domain.ch` +- **Forward zu:** `http://:80` +- **SSL:** Let's Encrypt + +### 5. Deployen + +Vom Arbeitsrechner: + +```bash +hugo --minify +lftp -u webedit-meine-domain-ch, sftp:// \ + -e "mirror -R --delete public/ /meine-domain.ch/; quit" +``` + +Wegen Chroot ist der Zielpfad `/meine-domain.ch/`, nicht +`/var/www/meine-domain.ch/`. + +Installation von `lftp` falls noch nicht da: + +```bash +brew install lftp # macOS +apt install lftp # Debian/Ubuntu +``` + +## Mehrere Sites pro Container + +Script einfach nochmal laufen lassen — neue nginx-Site + neuer SFTP-User +werden zusätzlich angelegt, das bestehende Setup bleibt unangetastet. + +```bash +./setup-hugo-host.sh weitere-domain.ch +``` + +## SSH-/SFTP-Config auf dem Client (optional) + +Damit du dir die langen User-/Hostnamen nicht merken musst, in +`~/.ssh/config` ergänzen: + +``` +Host meine-domain + HostName 192.168.1.46 + User webedit-meine-domain-ch + IdentityFile ~/.ssh/id_ed25519 +``` + +Dann reicht: + +```bash +lftp -e "mirror -R --delete public/ /meine-domain.ch/; quit" sftp://meine-domain +``` + +## Sicherheits-Modell + +| Angriffsfläche | Status | +|----------------------|------------------------------------------------| +| Port 22 (SSH-Shell) | Geschlossen — kein Shell-Login möglich | +| Port 22 (SFTP) | Nur Key-Auth, User chrooted auf `/var/www` | +| Port 80 (nginx) | Statische Files, Security Headers | +| Port 443 | Nicht im Container — Reverse-Proxy davor | +| Administration | Nur via Proxmox-Host (`pct enter`) | +| Auto-Updates | Security-Patches täglich, Reboot 03:00 | +| Key-Management | Public Key im Script (Git-versioniert) | + +## Key rotieren / neue Maschine hinzufügen + +1. Neuen Public Key oben im Script in `SSH_PUBKEYS` ergänzen, committen +2. Auf jedem bestehenden Server Script erneut laufen lassen + (überschreibt `/etc/ssh/sftp-keys//authorized_keys`) + +Alternativ einzeln per Hand die `authorized_keys` updaten. diff --git a/setup-hugo-host.sh b/setup-hugo-host.sh new file mode 100755 index 0000000..b99b7f5 --- /dev/null +++ b/setup-hugo-host.sh @@ -0,0 +1,276 @@ +#!/bin/bash +# +# setup-hugo-host.sh +# +# Richtet einen frischen Debian-12-LXC-Container als Hugo-Hosting-Server ein: +# - nginx auf Port 80 (TLS via vorgelagertem Reverse-Proxy, z.B. NPM) +# - SFTP-only-User für Deployment (chroot auf /var/www) +# - ufw, fail2ban, unattended-upgrades, sysctl-hardening +# +# Verwendung: +# ./setup-hugo-host.sh +# +# Beispiel: +# ./setup-hugo-host.sh example.com +# +# Wichtig: +# Nach dem Lauf ist KEIN interaktiver SSH-Login mehr möglich (nur SFTP). +# Administration erfolgt über die Proxmox-Konsole: pct enter +# + +set -euo pipefail + +# ── KONFIGURATION ───────────────────────────────────────────────────────────── +# Public Keys, die SFTP-Zugriff erhalten. Mehrere Keys: eine Zeile pro Key. + +SSH_PUBKEYS="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDMeSZTfX3tLpgujHXzP+VrbbxO1/cificO8beYvaTyb karim@mac" + +# ── Argumente ───────────────────────────────────────────────────────────────── + +if [ $# -ne 1 ]; then + echo "Usage: $0 " + echo "" + echo "Example:" + echo " $0 example.com" + exit 1 +fi + +DOMAIN="$1" +SFTP_USER="webedit-$(echo "$DOMAIN" | tr '.' '-')" + +if [ "$(id -u)" -ne 0 ]; then + echo "Bitte als root ausführen." + exit 1 +fi + +echo "===============================================" +echo "Hugo-Host Setup" +echo " Domain: $DOMAIN" +echo " SFTP-User: $SFTP_USER" +echo "===============================================" +echo "" + +# ── 1. Pakete ───────────────────────────────────────────────────────────────── + +echo "[1/9] Pakete installieren..." +export DEBIAN_FRONTEND=noninteractive +apt-get update -qq +apt-get upgrade -y -qq +apt-get install -y -qq nginx ufw fail2ban unattended-upgrades curl ca-certificates + +# ── 2. SSH-Daemon als reiner SFTP-Server ────────────────────────────────────── + +echo "[2/9] SSH-Daemon auf SFTP-only konfigurieren..." + +getent group sftp-users >/dev/null || groupadd sftp-users + +cat > /etc/ssh/sshd_config <<'EOF' +PermitRootLogin no +PasswordAuthentication no +PubkeyAuthentication yes +KbdInteractiveAuthentication no + +X11Forwarding no +AllowAgentForwarding no +AllowTcpForwarding no +PermitTunnel no +MaxAuthTries 3 +ClientAliveInterval 300 +ClientAliveCountMax 2 + +AllowGroups sftp-users + +Subsystem sftp internal-sftp + +Match Group sftp-users + ChrootDirectory /var/www + ForceCommand internal-sftp -u 0002 + AuthorizedKeysFile /etc/ssh/sftp-keys/%u/authorized_keys +EOF + +sshd -t +systemctl restart ssh + +# ── 3. Firewall ─────────────────────────────────────────────────────────────── + +echo "[3/9] UFW konfigurieren..." + +ufw --force reset >/dev/null +ufw default deny incoming +ufw default allow outgoing +ufw allow 22/tcp +ufw allow 80/tcp +ufw allow 443/tcp +ufw --force enable + +# ── 4. fail2ban ─────────────────────────────────────────────────────────────── + +echo "[4/9] fail2ban konfigurieren..." + +cat > /etc/fail2ban/jail.local <<'EOF' +[DEFAULT] +bantime = 1h +findtime = 10m +maxretry = 4 + +[sshd] +enabled = true + +[nginx-http-auth] +enabled = true + +[nginx-botsearch] +enabled = true +EOF + +systemctl enable --now fail2ban + +# ── 5. Automatische Security-Updates ────────────────────────────────────────── + +echo "[5/9] Automatische Updates aktivieren..." + +cat > /etc/apt/apt.conf.d/20auto-upgrades <<'EOF' +APT::Periodic::Update-Package-Lists "1"; +APT::Periodic::Unattended-Upgrade "1"; +APT::Periodic::AutocleanInterval "7"; +EOF + +sed -i 's|^//Unattended-Upgrade::Automatic-Reboot "false";|Unattended-Upgrade::Automatic-Reboot "true";|' \ + /etc/apt/apt.conf.d/50unattended-upgrades +sed -i 's|^//Unattended-Upgrade::Automatic-Reboot-Time "02:00";|Unattended-Upgrade::Automatic-Reboot-Time "03:00";|' \ + /etc/apt/apt.conf.d/50unattended-upgrades + +# ── 6. Sysctl-Hardening ─────────────────────────────────────────────────────── + +echo "[6/9] Sysctl-Hardening..." + +cat > /etc/sysctl.d/99-hardening.conf <<'EOF' +net.ipv4.conf.all.rp_filter = 1 +net.ipv4.conf.all.accept_redirects = 0 +net.ipv4.conf.all.send_redirects = 0 +net.ipv4.tcp_syncookies = 1 +net.ipv6.conf.all.accept_redirects = 0 +kernel.dmesg_restrict = 1 +EOF + +sysctl --system >/dev/null + +# ── 7. Nginx-Site einrichten ────────────────────────────────────────────────── + +echo "[7/9] Nginx-Site für $DOMAIN einrichten..." + +rm -f /etc/nginx/sites-enabled/default + +chown root:root /var/www +chmod 755 /var/www +mkdir -p "/var/www/$DOMAIN" + +cat > "/etc/nginx/sites-available/$DOMAIN" <$DOMAIN — OK" > "/var/www/$DOMAIN/index.html" + +nginx -t +systemctl reload nginx + +# ── 8. SFTP-User anlegen ────────────────────────────────────────────────────── + +echo "[8/9] SFTP-User $SFTP_USER anlegen..." + +mkdir -p /etc/ssh/sftp-keys +chown root:root /etc/ssh/sftp-keys +chmod 755 /etc/ssh/sftp-keys + +if id "$SFTP_USER" >/dev/null 2>&1; then + echo " User $SFTP_USER existiert bereits" +else + adduser --disabled-password --gecos "" --shell /usr/sbin/nologin "$SFTP_USER" + # adduser --disabled-password setzt das Shadow-Feld auf "!" → sshd sieht + # "Account locked" auch für Key-Auth. passwd -d entfernt das Passwort + # komplett → Status "NP" → Key-Auth funktioniert. + passwd -d "$SFTP_USER" +fi + +usermod -aG sftp-users "$SFTP_USER" + +chown "$SFTP_USER:sftp-users" "/var/www/$DOMAIN" +chmod 775 "/var/www/$DOMAIN" + +mkdir -p "/etc/ssh/sftp-keys/$SFTP_USER" +chown -R root:root "/etc/ssh/sftp-keys/$SFTP_USER" +chmod 755 "/etc/ssh/sftp-keys/$SFTP_USER" + +# ── 9. Public Keys eintragen ────────────────────────────────────────────────── + +echo "[9/9] Public Keys eintragen..." + +AUTH_KEYS="/etc/ssh/sftp-keys/$SFTP_USER/authorized_keys" + +if ! grep -qE '^(ssh-(rsa|ed25519|dss)|ecdsa-sha2-)' <<<"$SSH_PUBKEYS"; then + echo " ⚠ SSH_PUBKEYS enthält keinen gültigen Public Key — bitte oben im Script setzen." + exit 1 +fi + +printf '%s\n' "$SSH_PUBKEYS" > "$AUTH_KEYS" +chown root:root "$AUTH_KEYS" +chmod 644 "$AUTH_KEYS" + +KEY_COUNT=$(grep -cE '^(ssh-(rsa|ed25519|dss)|ecdsa-sha2-)' "$AUTH_KEYS") +echo " ✓ $KEY_COUNT Public Key(s) eingetragen" + +# ── Fertig ──────────────────────────────────────────────────────────────────── + +IP=$(hostname -I | awk '{print $1}') + +echo "" +echo "===============================================" +echo " FERTIG" +echo "===============================================" +echo "" +echo "Domain: $DOMAIN" +echo "Webroot: /var/www/$DOMAIN" +echo "Container-IP: $IP" +echo "" +echo "Deployment (vom lokalen Hugo-Projekt aus):" +echo "" +echo " hugo --minify" +echo " lftp -u $SFTP_USER, sftp://$IP -e \"mirror -R --delete public/ /$DOMAIN/; quit\"" +echo "" +echo "Alternative mit sftp (ohne --delete):" +echo " sftp $SFTP_USER@$IP <<< \"put -r public/* /$DOMAIN/\"" +echo "" +echo "Tipp: für TLS einen Reverse-Proxy (z.B. Nginx Proxy Manager)" +echo " auf $DOMAIN → http://$IP:80 zeigen lassen." +echo "" +echo "Hinweis: SSH-Shell-Login ist deaktiviert (SFTP-only)." +echo " Administration via Proxmox-Konsole: pct enter " +echo ""