From 2a63e72862038a274661a3f65066e328592d9ae5 Mon Sep 17 00:00:00 2001 From: Oliver Braasch Date: Wed, 22 Apr 2026 13:49:08 +0200 Subject: [PATCH] Dateien nach "scripts" hochladen --- scripts/proxmox-vpn-install.sh | 602 ++++++++------------------------- 1 file changed, 148 insertions(+), 454 deletions(-) diff --git a/scripts/proxmox-vpn-install.sh b/scripts/proxmox-vpn-install.sh index b8aed5c..09c6b8b 100644 --- a/scripts/proxmox-vpn-install.sh +++ b/scripts/proxmox-vpn-install.sh @@ -1,14 +1,13 @@ #!/usr/bin/env bash # ============================================================================= -# Freqtrade NordVPN Addon – Proxmox Host Installer +# Freqtrade NordVPN Addon – Proxmox Host Installer (Custom Config Edition) # Läuft komplett auf dem PROXMOX HOST. -# - Zeigt Freqtrade-Container zur Auswahl (gefiltert nach Tag/Hostname) -# - Richtet TUN-Device ein (Container-Neustart) -# - Fragt NordVPN-Credentials ab -# - Schreibt .env + docker-compose.yml mit Gluetun im Container -# - Startet Stack und prüft VPN-IP -# -# Quelle: https://git.obraasch.de/olli1986/freqtrade-proxmox-deploy +# - Richtet TUN-Device ein +# - Erstellt VPN-Ordner & Berechtigungen +# - Fragt NordVPN-Credentials ab & pausiert für custom.conf +# - Ermittelt automatisch das lokale Subnetz +# - Schreibt docker-compose.yml & startet den Stack +# - NEU: Integriert den VPN-Ordner automatisch in den Filebrowser # ============================================================================= set -euo pipefail @@ -29,11 +28,6 @@ ask() { read -r REPLY; REPLY="${REPLY:-$default}" } -ask_secret() { - echo -ne "${YELLOW} ➜ ${NC}${1}: " - read -rs REPLY; echo -} - confirm() { echo -ne "${YELLOW} ➜ ${NC}${1} [${CYAN}j${NC}/N]: " read -r ans; [[ "$ans" =~ ^[jJyY]$ ]] @@ -44,15 +38,14 @@ print_banner() { clear echo -e "${BOLD}${BLUE}" cat << 'BANNER' - _ _ _ _ ___ _ _ + _ _ _ _ ___ _ _ | \| |___ _ _ __| \ \ / / | \| | | .` / _ \ '_/ _` \ V /| | .` | |_|\_\___/_| \__,_|\_/ |_|_|\_| - NordVPN Addon – Proxmox Host Installer + NordVPN Custom Config Installer BANNER echo -e "${NC}" - echo -e " ${CYAN}Richtet TUN + Gluetun/NordVPN vollautomatisch ein${NC}" - echo -e " ${CYAN}Quelle: git.obraasch.de/olli1986/freqtrade-proxmox-deploy${NC}\n" + echo -e " ${CYAN}Automatisiertes Setup für feste IPs via custom.conf${NC}\n" } # ── Proxmox-Check ────────────────────────────────────────────────────────────── @@ -60,522 +53,223 @@ check_host() { step "Proxmox Host prüfen" [[ $EUID -ne 0 ]] && error "Bitte als root ausführen!" command -v pct &>/dev/null || error "pct nicht gefunden – läuft das auf Proxmox?" - command -v pvesh &>/dev/null || error "pvesh nicht gefunden." success "Proxmox Host erkannt" } # ── Container-Auswahl ───────────────────────────────────────────────────────── select_container() { step "Freqtrade Container auswählen" - - # Alle Container auflisten mit Tag-Info - local -a ct_list=() + local -a display_list=() + while IFS= read -r line; do - local ct_id status + local ct_id status hostname tags ct_id=$(echo "$line" | awk '{print $1}') status=$(echo "$line" | awk '{print $2}') [[ "$ct_id" =~ ^[0-9]+$ ]] || continue - local hostname tags marker="" hostname=$(pct config "$ct_id" 2>/dev/null | awk '/^hostname:/{print $2}' || echo "?") tags=$(pct config "$ct_id" 2>/dev/null | awk '/^tags:/{print $2}' || echo "") - # Freqtrade-Container markieren if echo "$tags" | grep -qi "freqtrade"; then - marker="${GREEN}★${NC}" - else - marker=" " + display_list+=("${ct_id}|${hostname}|${status}") fi - ct_list+=("${ct_id}|${hostname}|${status}|${marker}") done < <(pct list 2>/dev/null | tail -n +2) - if [[ ${#ct_list[@]} -eq 0 ]]; then - error "Keine Container gefunden!" - fi + [[ ${#display_list[@]} -eq 0 ]] && error "Keine Container mit Tag 'freqtrade' gefunden!" - # Nur Container mit Tag "freqtrade" anzeigen - local -a display_list=() - for entry in "${ct_list[@]}"; do - if echo "$entry" | grep -q "★"; then - display_list+=("$entry") - fi - done - - if [[ ${#display_list[@]} -eq 0 ]]; then - error "Keine Container mit Tag 'freqtrade' gefunden!\n Bitte zuerst freqtrade-lxc-install.sh ausführen oder manuell setzen:\n pct set --tags freqtrade" - fi - - echo - echo -e " ${BOLD}Nr ID Hostname Status ${GREEN}★${NC}${BOLD} = Freqtrade${NC}" + echo -e " ${BOLD}Nr ID Hostname Status${NC}" echo -e " ${BLUE}─────────────────────────────────────────${NC}" local i=1 for entry in "${display_list[@]}"; do - IFS='|' read -r cid cname cstatus cmark <<< "$entry" - printf " ${CYAN}%-4s${NC} %-8s %-18s %-10s %b\n" "$i" "$cid" "$cname" "$cstatus" "$cmark" + IFS='|' read -r cid cname cstatus <<< "$entry" + printf " ${CYAN}%-4s${NC} %-8s %-18s %-10s\n" "$i" "$cid" "$cname" "$cstatus" ((i++)) done echo local sel_nr=1 - if [[ ${#display_list[@]} -gt 1 ]]; then - ask "Container Nummer wählen" "1" - sel_nr="$REPLY" - else - info "Nur ein Container vorhanden – wird automatisch gewählt." - fi - - IFS='|' read -r SEL_ID SEL_NAME SEL_STATUS _ <<< "${display_list[$((sel_nr-1))]}" + [[ ${#display_list[@]} -gt 1 ]] && { ask "Container Nummer wählen" "1"; sel_nr="$REPLY"; } + + IFS='|' read -r SEL_ID SEL_NAME SEL_STATUS <<< "${display_list[$((sel_nr-1))]}" [[ -z "${SEL_ID:-}" ]] && error "Ungültige Auswahl!" + success "Gewählt: CT ${SEL_ID}" - success "Gewählt: CT ${SEL_ID} | ${SEL_NAME} | ${SEL_STATUS}" - - # Starten falls nötig if [[ "$SEL_STATUS" != "running" ]]; then - warn "Container ist nicht running. Starte..." - pct start "$SEL_ID" - sleep 5 - success "Container gestartet" + info "Starte Container..." + pct start "$SEL_ID" && sleep 5 fi } -# ── TUN-Device einrichten ────────────────────────────────────────────────────── -setup_tun() { - step "TUN-Device für CT ${SEL_ID} prüfen" +# ── TUN-Device & Ordner einrichten ───────────────────────────────────────────── +setup_system() { + step "System & VPN-Ordner vorbereiten" + + # 1. TUN-Device in LXC Config schreiben local conf="/etc/pve/lxc/${SEL_ID}.conf" - [[ ! -f "$conf" ]] && error "LXC-Config nicht gefunden: $conf" - - if grep -q "lxc.cgroup2.devices.allow: c 10:200 rwm" "$conf" && \ - grep -q "lxc.mount.entry: /dev/net/tun" "$conf"; then - success "TUN-Device bereits konfiguriert" - return - fi - - info "Trage TUN-Device in Config ein und starte Container neu..." - cat >> "$conf" << 'TUNCFG' -# TUN-Device fuer NordVPN/Gluetun (eingetragen von proxmox-vpn-install.sh) + if ! grep -q "lxc.mount.entry: /dev/net/tun" "$conf"; then + info "Trage TUN-Device in Config ein..." + cat >> "$conf" << 'TUNCFG' lxc.cgroup2.devices.allow: c 10:200 rwm lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file TUNCFG + pct stop "$SEL_ID" && sleep 3 && pct start "$SEL_ID" + sleep 5 + success "TUN-Device eingerichtet & Container neu gestartet" + else + success "TUN-Device war bereits konfiguriert" + fi - pct stop "$SEL_ID" - sleep 3 - pct start "$SEL_ID" - - local tries=0 - info "Warte auf Netzwerk..." - until pct exec "$SEL_ID" -- bash -c "ip route | grep -q default" 2>/dev/null; do - sleep 3; tries=$((tries+1)); echo -n "." - [[ $tries -ge 15 ]] && error "Netzwerk nach Neustart nicht erreichbar!" - done - echo - success "Container neu gestartet mit TUN-Device" + # 2. Ordner und Datei erstellen & Rechte setzen + pct exec "$SEL_ID" -- bash -c " + mkdir -p /opt/docker-projekte/VPN + touch /opt/docker-projekte/VPN/custom.conf + chmod -R 775 /opt/docker-projekte/VPN + chown 1000:1000 -R /opt/docker-projekte/VPN + " + success "VPN-Ordner und custom.conf erstellt (/opt/docker-projekte/VPN)" } -# ── NordVPN Konfiguration abfragen ───────────────────────────────────────────── -collect_vpn_config() { - step "NordVPN Konfiguration" - echo -e "\n ${BOLD}Service-Credentials${NC} findest du unter:" - echo -e " ${CYAN}https://my.nordaccount.com → NordVPN → 'Set up NordVPN manually'${NC}" - echo -e " ${RED} Nicht deine E-Mail/Passwort – die Service-Credentials!${NC}\n" +# ── Filebrowser anpassen ─────────────────────────────────────────────────────── +update_filebrowser() { + step "Filebrowser aktualisieren" + local FB_DIR="/opt/docker-projekte/filebrowser" + local FB_YML="${FB_DIR}/docker-compose.yml" + # Prüfen, ob der Filebrowser überhaupt da ist + if pct exec "$SEL_ID" -- bash -c "[ -f ${FB_YML} ]"; then + info "Filebrowser gefunden. Prüfe Konfiguration..." + + # Prüfen, ob der Mount schon existiert, um Doppel-Einträge zu vermeiden + if pct exec "$SEL_ID" -- bash -c "grep -q '/opt/docker-projekte/VPN:/srv/VPN' ${FB_YML}"; then + success "VPN-Ordner ist bereits im Filebrowser gemountet." + else + info "Füge VPN-Mountpunkt zur docker-compose.yml hinzu..." + # Fügt die Zeile exakt nach 'volumes:' ein + pct exec "$SEL_ID" -- bash -c "sed -i '/volumes:/a \\ - /opt/docker-projekte/VPN:/srv/VPN' ${FB_YML}" + success "Konfiguration ergänzt." + + info "Starte Filebrowser neu..." + pct exec "$SEL_ID" -- bash -c "cd ${FB_DIR} && docker compose down && docker compose up -d" + success "Filebrowser ist neugestartet und hat jetzt Zugriff auf den VPN-Ordner!" + fi + else + info "Kein Filebrowser unter ${FB_DIR} gefunden. Überspringe diesen Schritt." + fi +} +# ── Konfiguration abfragen & Datei befüllen lassen ───────────────────────────── +collect_and_wait() { + step "NordVPN Credentials & Config" + + echo -e " ${YELLOW}Hinweis: Bitte nutze die Service-Credentials von der NordVPN-Website!${NC}" ask "NordVPN Service-Username" NORD_USER="$REPLY" - [[ -z "$NORD_USER" ]] && error "Username darf nicht leer sein!" - - stty echo 2>/dev/null || true ask "NordVPN Service-Passwort" NORD_PASS="$REPLY" - [[ -z "$NORD_PASS" ]] && error "Passwort darf nicht leer sein!" - - echo - echo -e " ${BOLD}VPN-Server Modus:${NC}" - echo -e " ${CYAN}1)${NC} Serverland – normaler Server (IP kann wechseln)" - echo -e " ${CYAN}2)${NC} Hostname – Dedicated-IP oder spez. Server pinnen" - ask "Auswahl [1/2]" "1" - VPN_MODE="$REPLY" - - VPN_PROTO="udp" # Start mit UDP, automatischer Fallback auf TCP bei TLS-Fehler - - if [[ "$VPN_MODE" == "2" ]]; then - echo -e "\n ${YELLOW}Hostnamen Format: de507.nordvpn.com, de654.nordvpn.com etc.${NC}" - echo -e " ${YELLOW}Wichtig: NordVPN zeigt oft neue Server die Gluetun noch nicht kennt.${NC}" - echo -e " ${YELLOW}Falls 'Ungültiger Hostname' erscheint → die angezeigten gültigen Server nutzen.${NC}" - echo -e " ${CYAN}Tipp: Modus 1 (Serverland) ist einfacher – Gluetun wählt selbst.${NC}" - ask "Hostname (z.B. de507.nordvpn.com)" - VPN_HOSTNAME="$REPLY"; VPN_COUNTRY="" - else - ask "Serverland" "Germany" - VPN_COUNTRY="$REPLY"; VPN_HOSTNAME="" - fi - - # FreqUI Port aus bestehender docker-compose.yml lesen (ändert sich nicht) - FT_PORT=$(pct exec "$SEL_ID" -- bash -c "grep -oP '0\.0\.0\.0:\K[0-9]+(?=:[0-9]+)' /opt/docker-projekte/freqtrade/docker-compose.yml 2>/dev/null | head -1 || echo 8080" 2>/dev/null || echo "8080") - info "FreqUI Port: ${FT_PORT} (aus bestehender Konfiguration)" - - echo -e "\n${BOLD}${BLUE}══════════ Zusammenfassung ══════════${NC}" - echo -e " Container: ${CYAN}CT ${SEL_ID} | ${SEL_NAME}${NC}" - echo -e " NordVPN User: ${CYAN}${NORD_USER}${NC}" - [[ -n "$VPN_HOSTNAME" ]] \ - && echo -e " VPN Server: ${CYAN}${VPN_HOSTNAME}${NC}" \ - || echo -e " VPN Land: ${CYAN}${VPN_COUNTRY}${NC}" - echo -e " FreqUI Port: ${CYAN}${FT_PORT}${NC}" - echo -e "${BOLD}${BLUE}=====================================\n${NC}" - confirm "Einrichten?" || { info "Abgebrochen."; exit 0; } + + echo -e "\n${BOLD}${CYAN}=== 📝 KONFIGURATION ERFORDERLICH ===${NC}" + echo -e "Bitte öffne jetzt dein ${BOLD}Filebrowser Web-Interface${NC} (Port 8081)." + echo -e "1. Navigiere in den Ordner: ${BOLD}VPN${NC}" + echo -e "2. Öffne die Datei: ${BOLD}custom.conf${NC}" + echo -e "3. Kopiere den ${BOLD}gesamten Inhalt${NC} deiner NordVPN .ovpn Datei hinein." + echo -e "4. ${BOLD}Speichern${NC} nicht vergessen!" + echo -e "\n${CYAN}Tipp:${NC} Falls du lieber im Terminal arbeitest:" + echo -e "nano /opt/docker-projekte/VPN/custom.conf" + + while true; do + echo + if confirm "Hast du die custom.conf befüllt und gespeichert?"; then + # Kurzer Check, ob die Datei nicht mehr leer ist + local size + size=$(pct exec "$SEL_ID" -- stat -c%s /opt/docker-projekte/VPN/custom.conf 2>/dev/null || echo 0) + if [[ "$size" -gt 10 ]]; then + success "Datei ist befüllt!" + break + else + warn "Die Datei scheint noch leer zu sein. Bitte überprüfe das." + fi + fi + done } -# ── Konfiguration in Container schreiben ────────────────────────────────────── -write_vpn_config() { - step "VPN-Konfiguration in Container schreiben" - # Image: custom build bei Dedicated-IP, sonst offizielles Image - GLUETUN_IMAGE="${GLUETUN_IMAGE:-qmcgaw/gluetun:latest}" - +# ── Docker Compose schreiben & anwenden ──────────────────────────────────────── +deploy_stack() { + step "Docker Stack anwenden" local FT_DIR="/opt/docker-projekte/freqtrade" - local NORD_USER_B64 NORD_PASS_B64 - NORD_USER_B64=$(printf '%s' "$NORD_USER" | base64 -w0) - NORD_PASS_B64=$(printf '%s' "$NORD_PASS" | base64 -w0) + + # Lokales Subnetz automatisch ermitteln (z.B. 192.168.178.0/24) + local SUBNET + #SUBNET=$(pct exec "$SEL_ID" -- bash -c "ip -o -f inet addr show eth0 | awk '{print \$4}' | sed 's/\.[0-9]\{1,3\}\//.0\//'" 2>/dev/null || echo "192.168.0.0/24") + SUBNET=$(pct exec "$SEL_ID" -- bash -c "python3 -c 'import ipaddress; import subprocess; ip = subprocess.check_output([\"ip\", \"-o\", \"-f\", \"inet\", \"addr\", \"show\", \"eth0\"]).decode().split()[3]; print(ipaddress.ip_interface(ip).network)'" 2>/dev/null || echo "192.168.0.0/23") + info "Ermitteltes lokales Subnetz: ${SUBNET}" - local VPN_SERVER_LINE="" - local VPN_CUSTOM_LINE="" - if [[ -n "$VPN_HOSTNAME" ]]; then - # Dedicated-IP: Custom Build hat den Hostname in der Binary → normaler nordvpn Provider - VPN_SERVER_LINE=" - VPN_SERVICE_PROVIDER=nordvpn" - VPN_CUSTOM_LINE=" - SERVER_HOSTNAMES=${VPN_HOSTNAME}" - else - VPN_SERVER_LINE=" - VPN_SERVICE_PROVIDER=nordvpn" - VPN_CUSTOM_LINE=" - SERVER_COUNTRIES=${VPN_COUNTRY}" - fi + # Alten Stack stoppen + info "Stoppe aktuellen Stack..." + pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose down 2>/dev/null || true" - # Backup - pct exec "$SEL_ID" -- bash -c \ - "cp ${FT_DIR}/docker-compose.yml ${FT_DIR}/docker-compose.yml.bak-\$(date +%Y%m%d_%H%M%S) 2>/dev/null || true" - success "Backup erstellt" - - # Stack stoppen - pct exec "$SEL_ID" -- bash -c \ - "cd ${FT_DIR} && docker compose down 2>/dev/null || true" - success "Stack gestoppt" - - # .env schreiben - pct exec "$SEL_ID" -- bash -c " - echo \"OPENVPN_USER=\$(echo ${NORD_USER_B64} | base64 -d)\" > ${FT_DIR}/.env - echo \"OPENVPN_PASSWORD=\$(echo ${NORD_PASS_B64} | base64 -d)\" >> ${FT_DIR}/.env - chmod 600 ${FT_DIR}/.env - " - success ".env geschrieben (chmod 600)" - - # docker-compose.yml schreiben - # Architektur: Gluetun als HTTP-Proxy (Port 8888) - # → Freqtrade behält eigenes Netzwerk (WebUI bleibt auf LXC-IP erreichbar) - # → Nur Binance-Traffic läuft via HTTPS_PROXY durch den VPN-Tunnel + # Neue docker-compose.yml generieren + info "Schreibe neue docker-compose.yml..." pct exec "$SEL_ID" -- bash -c "cat > ${FT_DIR}/docker-compose.yml << 'COMPOSEEOF' -# Freqtrade + NordVPN/Gluetun | generiert von proxmox-vpn-install.sh -# -# Architektur: -# Freqtrade (eigenes Netz, WebUI :${FT_PORT} lokal erreichbar) -# |-- Binance API --> HTTP_PROXY --> Gluetun --> NordVPN --> Internet -# -# WebUI bleibt erreichbar unter: http://LXC-IP:${FT_PORT} - services: - - # ── VPN-Proxy ────────────────────────────────────────────────────────────── - # Gluetun läuft als HTTP-Proxy auf Port 8888. - # Freqtrade schickt nur den Exchange-Traffic darüber. gluetun: - image: ${GLUETUN_IMAGE:-qmcgaw/gluetun:latest} + image: qmcgaw/gluetun:latest container_name: gluetun - restart: unless-stopped cap_add: - NET_ADMIN devices: - /dev/net/tun:/dev/net/tun environment: -${VPN_SERVER_LINE} + - TZ=Europe/Berlin + - VPN_SERVICE_PROVIDER=custom - VPN_TYPE=openvpn - - OPENVPN_USER=\${OPENVPN_USER} - - OPENVPN_PASSWORD=\${OPENVPN_PASSWORD} -${VPN_CUSTOM_LINE} - # HTTP-Proxy aktivieren – darüber leitet Freqtrade den Binance-Traffic - - HTTPPROXY=on - - HTTPPROXY_LOG=off - - HTTPPROXY_STEALTH=on - ports: - # Kein WebUI-Port hier – Freqtrade hat sein eigenes Netzwerk - - \"127.0.0.1:8888:8888\" + - OPENVPN_CUSTOM_CONFIG=/gluetun/custom.conf + - OPENVPN_USER=${NORD_USER} + - OPENVPN_PASSWORD=${NORD_PASS} + # Automatisch ermitteltes lokales Netz für Erreichbarkeit + - FIREWALL_OUTBOUND_SUBNETS=${SUBNET} volumes: - - gluetun_data:/gluetun - healthcheck: - test: [\"CMD-SHELL\", \"wget -qO- https://ipinfo.io/ip || exit 1\"] - interval: 30s - timeout: 15s - retries: 5 - start_period: 45s + - /opt/docker-projekte/VPN:/gluetun + ports: + - \"8080:8080\" + restart: unless-stopped - # ── Freqtrade ─────────────────────────────────────────────────────────────── - # Eigenes Netzwerk → WebUI bleibt auf LXC-IP:${FT_PORT} erreichbar. - # Exchange-Traffic geht via HTTP_PROXY durch Gluetun. freqtrade: image: freqtradeorg/freqtrade:stable - restart: unless-stopped container_name: freqtrade - depends_on: - gluetun: - condition: service_started - ports: - - \"0.0.0.0:${FT_PORT}:8080\" - environment: - # Nur HTTPS-Traffic (Binance API) läuft durch den VPN-Proxy - - HTTPS_PROXY=http://gluetun:8888 - - HTTP_PROXY=http://gluetun:8888 - # Lokale Verbindungen (WebUI, DB) NICHT durch Proxy - - NO_PROXY=localhost,127.0.0.1 + restart: unless-stopped + network_mode: \"container:gluetun\" volumes: - \"./user_data:/freqtrade/user_data\" + depends_on: + - gluetun command: > trade --logfile /freqtrade/user_data/logs/freqtrade.log --db-url sqlite:////freqtrade/user_data/tradesv3.sqlite --config /freqtrade/user_data/config.json --strategy SampleStrategy - -volumes: - gluetun_data: COMPOSEEOF" - success "docker-compose.yml geschrieben" + + # Stack starten + info "Starte Stack neu..." + pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose up -d" + + sleep 5 + success "Deployment abgeschlossen!" } -# ── Stack starten mit automatischem UDP→TCP Fallback ────────────────────────── -start_and_verify() { - step "VPN-Stack starten" - - local FT_DIR="/opt/docker-projekte/freqtrade" - local vpn_ok=false - VPN_IP="" - - # Bei Hostname-Modus: Gluetun aus GitHub klonen, Server eintragen, neu kompilieren - # → Löst das Problem dauerhaft: de1370 wird in die Binary eingebaut - if [[ -n "${VPN_HOSTNAME:-}" ]]; then - step "Gluetun Custom Build für ${VPN_HOSTNAME}" - - # IP ermitteln: zuerst per DNS, dann User bestätigen lassen - local server_ip="" - server_ip=$(pct exec "$SEL_ID" -- bash -c "getent hosts ${VPN_HOSTNAME} 2>/dev/null | awk '{print \$1}' | head -1" 2>/dev/null || echo "") - [[ -z "$server_ip" ]] && server_ip=$(pct exec "$SEL_ID" -- bash -c "nslookup ${VPN_HOSTNAME} 2>/dev/null | awk '/Address:/ && !/#/{print \$2}' | head -1" 2>/dev/null || echo "") - - # IP anzeigen und Korrektur ermöglichen - # Bei Dedicated-IP weicht DNS oft von der echten NordVPN-IP ab - if [[ -n "$server_ip" ]]; then - echo -e " ${YELLOW}DNS hat aufgelöst: ${CYAN}${VPN_HOSTNAME} → ${server_ip}${NC}" - else - echo -e " ${YELLOW}DNS-Auflösung fehlgeschlagen.${NC}" - fi - echo -e " ${YELLOW}Die korrekte IP findest du in deinem NordVPN-Account unter Dedicated IP.${NC}" - ask "IP-Adresse für ${VPN_HOSTNAME}" "${server_ip}" - server_ip="$REPLY" - [[ -z "$server_ip" ]] && error "Keine IP angegeben!" - info "Verwende IP: ${VPN_HOSTNAME} → ${server_ip}" - - # Prüfen ob Image bereits existiert (kein Rebuild nötig) - local img_exists - img_exists=$(pct exec "$SEL_ID" -- bash -c "docker image inspect gluetun-custom:latest 2>/dev/null | grep -c 'Id' || echo 0" 2>/dev/null | tr -d '\n\r ' || echo "0") - - if [[ "${img_exists:-0}" -gt 0 ]] 2>/dev/null; then - # Prüfen ob Image für diesen Hostname gebaut wurde - local built_for - built_for=$(pct exec "$SEL_ID" -- bash -c \ - "docker inspect gluetun-custom:latest --format=\"{{json .Config.Labels}}\" 2>/dev/null" \ - 2>/dev/null | grep -o '["]built_for["]:["]' | cut -d'"' -f4 2>/dev/null | tr -d '\n' || echo "") - if [[ "$built_for" == "${VPN_HOSTNAME}" ]]; then - success "gluetun-custom:latest bereits für ${VPN_HOSTNAME} gebaut" - GLUETUN_IMAGE="gluetun-custom:latest" - pct exec "$SEL_ID" -- bash -c \ - "sed -i 's|image: .*gluetun.*|image: gluetun-custom:latest|' ${FT_DIR}/docker-compose.yml" - return 0 2>/dev/null || true - fi - info "Image vorhanden, aber nicht für ${VPN_HOSTNAME} – baue neu..." - fi - - # git installieren falls nötig - pct exec "$SEL_ID" -- bash -c "command -v git &>/dev/null || apt-get install -y -qq git" 2>/dev/null - - info "Klone Gluetun von GitHub (einmalig, ~3-5 Min zum Kompilieren)..." - pct exec "$SEL_ID" -- bash -c "rm -rf /tmp/gluetun-build && git clone --depth 1 https://github.com/qdm12/gluetun.git /tmp/gluetun-build 2>&1 | tail -3" - - info "Füge ${VPN_HOSTNAME} (${server_ip}) in servers.json ein..." - # Python-Script lokal erstellen und in Container pushen - cat > /tmp/add_nordvpn_server.py << 'PYEOF' -import json, copy, sys -path, hostname, ip = sys.argv[1], sys.argv[2], sys.argv[3] -db = json.load(open(path)) -servers = db.get("nordvpn", {}).get("servers", []) -if any(s.get("hostname") == hostname for s in servers): - sys.exit(0) -tmpl = next((s for s in servers if s.get("country","").lower()=="germany" and s.get("vpn")=="openvpn"), servers[0]) -e = copy.deepcopy(tmpl) -e["hostname"] = hostname -e["ips"] = [ip] -servers.append(e) -db["nordvpn"]["servers"] = servers -json.dump(db, open(path,"w"), indent=2) -print("Eingetragen: " + hostname + " (" + ip + ") - Server: " + str(len(servers))) -PYEOF - pct push "$SEL_ID" /tmp/add_nordvpn_server.py /tmp/add_nordvpn_server.py - pct exec "$SEL_ID" -- python3 /tmp/add_nordvpn_server.py \ - /tmp/gluetun-build/internal/storage/servers.json \ - "${VPN_HOSTNAME}" "${server_ip}" - - info "Kompiliere Gluetun Docker Image (dauert ~3-5 Minuten)..." - echo -e " ${YELLOW}Bitte warten – Go Multi-Stage Build...${NC}" - pct exec "$SEL_ID" -- bash -c "cd /tmp/gluetun-build && docker build --label built_for=${VPN_HOSTNAME} -t gluetun-custom:latest . 2>&1 | grep -E '^Step|Successfully|error|Error|=>|RUN|COPY' | tail -30" && success "gluetun-custom:latest erfolgreich gebaut!" || error "Docker Build fehlgeschlagen. Prüfe: pct exec ${SEL_ID} -- docker build /tmp/gluetun-build" - - GLUETUN_IMAGE="gluetun-custom:latest" - info "Aufräumen..." - pct exec "$SEL_ID" -- bash -c "rm -rf /tmp/gluetun-build" 2>/dev/null || true - else - GLUETUN_IMAGE="qmcgaw/gluetun:latest" - fi - - # docker-compose.yml mit korrektem Image aktualisieren - info "Setze Image in docker-compose.yml: ${GLUETUN_IMAGE}" - pct exec "$SEL_ID" -- bash -c "sed -i 's|image: .*gluetun.*|image: ${GLUETUN_IMAGE}|' ${FT_DIR}/docker-compose.yml" - - - # Protokoll-Reihenfolge: UDP zuerst, dann automatisch TCP - for proto in udp tcp; do - [[ "$proto" == "tcp" ]] && info "UDP fehlgeschlagen – versuche TCP automatisch..." - - # Protokoll in .env setzen (Gluetun liest OPENVPN_PROTOCOL) - pct exec "$SEL_ID" -- bash -c "sed -i '/^OPENVPN_PROTOCOL=/d' ${FT_DIR}/.env; echo 'OPENVPN_PROTOCOL=${proto}' >> ${FT_DIR}/.env" - - # Stack starten (ggf. neu) - pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose down 2>/dev/null || true" - pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose up -d 2>&1" || true - - info "Warte auf VPN-Verbindung via ${proto^^}..." - local tries=0 - local max_wait=75 # UDP: 75s reicht (TLS-Timeout ist 60s + retry) - [[ "$proto" == "tcp" ]] && max_wait=90 - - while [[ $((tries * 5)) -lt $max_wait ]]; do - sleep 5; tries=$((tries+1)) - local elapsed=$(( tries * 5 )) - echo -ne "\r ${CYAN}[INFO]${NC} ${proto^^} – ${elapsed}s... " - - # Verbindungstest (schnell, kein docker logs) - if pct exec "$SEL_ID" -- bash -c "docker exec gluetun wget -qO- --timeout=3 https://ipinfo.io/ip 2>/dev/null" &>/dev/null; then - vpn_ok=true; break - fi - done - echo - - # Nach Timeout: Log prüfen für Fehlerdiagnose - if [[ "$vpn_ok" != "true" ]]; then - local log_out - log_out=$(timeout 10 pct exec "$SEL_ID" -- bash -c "docker logs gluetun 2>&1 | tail -30" 2>/dev/null || echo "") - - if echo "$log_out" | grep -qiE "TLS Error|TLS key negotiation|TLS handshake"; then - echo; warn "TLS-Fehler nach ${proto^^}-Timeout – UDP geblockt, wechsle auf TCP..." - tls_error=true - elif echo "$log_out" | grep -qiE "AUTH_FAILED|auth.*fail|wrong.*password|authentication failed"; then - echo - echo -e " ${RED}✗ Authentifizierung fehlgeschlagen!${NC}" - echo -e " ${CYAN}→ https://my.nordaccount.com → NordVPN → Set up NordVPN manually${NC}" - pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose down 2>/dev/null || true" - if confirm "Neue Credentials eingeben?"; then - collect_vpn_config; write_vpn_config; start_and_verify; return - else - error "Abgebrochen." - fi - elif echo "$log_out" | grep -qiE "hostname.*not valid|not one of the possible choices"; then - echo -e " ${RED}✗ Ungültiger Hostname: ${VPN_HOSTNAME}${NC}" - pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose down 2>/dev/null || true" - error "Hostname nicht in Gluetun-DB. Custom Build prüfen." - else - echo -e " ${YELLOW}Letztes Gluetun-Log:${NC}" - echo "$log_out" | grep -E "INFO|WARN|ERROR|openvpn" | tail -8 | while IFS= read -r l; do - echo -e " ${YELLOW}│${NC} $l" - done - fi - fi - echo - echo - - [[ "$vpn_ok" == "true" ]] && break - - # Bei TCP auch TLS-Fehler → endgültig fehlgeschlagen - if [[ "$proto" == "tcp" ]]; then - echo -e " ${RED}✗ VPN konnte weder via UDP noch TCP verbunden werden.${NC}" - echo -e " ${YELLOW}Mögliche Ursachen:${NC}" - echo -e " ${YELLOW}· Falsche NordVPN Service-Credentials${NC}" - echo -e " ${YELLOW}· Server temporär nicht erreichbar – anderen Server wählen${NC}" - echo -e " ${YELLOW}· Netzwerkproblem im LXC${NC}" - pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose down 2>/dev/null || true" - - if confirm "Neue Konfiguration eingeben?"; then - collect_vpn_config - write_vpn_config - start_and_verify - return - else - error "Abgebrochen. Stack gestoppt." - fi - fi - done - - [[ "$vpn_ok" != "true" ]] && error "VPN-Verbindung fehlgeschlagen." - - # Freqtrade prüfen - sleep 6 - if pct exec "$SEL_ID" -- bash -c \ - "docker ps --filter name=freqtrade --filter status=running | grep -q freqtrade" 2>/dev/null; then - success "Freqtrade läuft" - else - warn "Freqtrade prüfen: pct exec ${SEL_ID} -- docker logs freqtrade" - fi -} - -# ── Ergebnis anzeigen ────────────────────────────────────────────────────────── -show_result() { - local LXC_IP - LXC_IP=$(pct exec "$SEL_ID" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "unbekannt") - - echo - echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════╗${NC}" - echo -e "${BOLD}${GREEN}║ ✅ NordVPN erfolgreich eingerichtet! ║${NC}" - echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════╝${NC}" - echo - echo -e " ${BOLD}FreqUI (lokal erreichbar):${NC}" - echo -e " ${CYAN}http://${LXC_IP}:${FT_PORT}${NC}" - echo - echo -e " ${BOLD}Binance-Traffic läuft über NordVPN:${NC}" - echo -e " Ausgehende IP: ${BOLD}${GREEN}${VPN_IP}${NC}" - echo - echo -e " ${BOLD}${YELLOW}⚠ Binance API-Key anpassen:${NC}" - echo -e " ${YELLOW}Binance → API Management → IP-Beschränkung${NC}" - echo -e " ${YELLOW}Diese NordVPN-IP eintragen: ${BOLD}${VPN_IP}${NC}" - echo - echo -e " ${BOLD}Architektur:${NC}" - echo -e " ${CYAN}WebUI${NC} → direkt über LXC-IP (kein VPN)" - echo -e " ${CYAN}Binance${NC} → HTTP_PROXY → Gluetun → NordVPN" - echo - echo -e " ${BOLD}Nützliche Befehle (im LXC):${NC}" - echo -e " ${CYAN}docker exec gluetun wget -qO- https://ipinfo.io/ip${NC} # VPN-IP prüfen" - echo -e " ${CYAN}docker logs gluetun${NC} # VPN-Logs" - echo -e " ${CYAN}docker logs freqtrade${NC} # Freqtrade-Logs" - echo -e " ${CYAN}cd /opt/docker-projekte/freqtrade && docker compose restart${NC}" - echo -} - -# ── Hauptprogramm ────────────────────────────────────────────────────────────── main() { print_banner check_host select_container - setup_tun - collect_vpn_config - write_vpn_config - start_and_verify - show_result + setup_system + update_filebrowser + collect_and_wait + deploy_stack + + # Finaler IP Check + local IP + IP=$(pct exec "$SEL_ID" -- bash -c "docker exec gluetun wget -qO- --user-agent=curl https://ifconfig.me 2>/dev/null" || echo "Check fehlgeschlagen") + echo -e "\n${GREEN}FERTIG! Externe VPN-IP: ${BOLD}${IP}${NC}" } -main "$@" +main "$@" \ No newline at end of file