From 5f7243c335367beb1042637681290ff387012766 Mon Sep 17 00:00:00 2001 From: Oliver Braasch Date: Mon, 20 Apr 2026 22:23:06 +0200 Subject: [PATCH] Dateien nach "scripts" hochladen --- scripts/proxmox-vpn-install-dev.sh | 526 +++++++++++++++++++++++++++++ 1 file changed, 526 insertions(+) create mode 100644 scripts/proxmox-vpn-install-dev.sh diff --git a/scripts/proxmox-vpn-install-dev.sh b/scripts/proxmox-vpn-install-dev.sh new file mode 100644 index 0000000..a61553c --- /dev/null +++ b/scripts/proxmox-vpn-install-dev.sh @@ -0,0 +1,526 @@ +#!/usr/bin/env bash +# ============================================================================= +# Freqtrade NordVPN Addon – Proxmox Host Installer +# 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 +# ============================================================================= +set -euo pipefail + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' +BLUE='\033[0;34m'; 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}[FAIL]${NC} $*"; exit 1; } +step() { echo -e "\n${BOLD}${BLUE}▶ $*${NC}"; } + +ask() { + local prompt="$1" default="${2:-}" + [[ -n "$default" ]] \ + && echo -ne "${YELLOW} ➜ ${NC}${prompt} [${CYAN}${default}${NC}]: " \ + || echo -ne "${YELLOW} ➜ ${NC}${prompt}: " + 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]$ ]] +} + +# ── Banner ───────────────────────────────────────────────────────────────────── +print_banner() { + clear + echo -e "${BOLD}${BLUE}" + cat << 'BANNER' + _ _ _ _ ___ _ _ + | \| |___ _ _ __| \ \ / / | \| | + | .` / _ \ '_/ _` \ V /| | .` | + |_|\_\___/_| \__,_|\_/ |_|_|\_| + NordVPN Addon – Proxmox Host 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" +} + +# ── Proxmox-Check ────────────────────────────────────────────────────────────── +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=() + while IFS= read -r line; do + local ct_id status + 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=" " + 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 + + # 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 " ${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" + ((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))]}" + [[ -z "${SEL_ID:-}" ]] && error "Ungültige Auswahl!" + + 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" + fi +} + +# ── TUN-Device einrichten ────────────────────────────────────────────────────── +setup_tun() { + step "TUN-Device für CT ${SEL_ID} prüfen" + 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) +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" + + 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" +} + +# ── 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" + + 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 config ermitteln + local current_port + current_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") + ask "FreqUI Port" "$current_port" + FT_PORT="$REPLY" + + 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; } +} + +# ── Konfiguration in Container schreiben ────────────────────────────────────── +write_vpn_config() { + step "VPN-Konfiguration in Container schreiben" + + 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) + + local VPN_SERVER_LINE="" + [[ -n "$VPN_HOSTNAME" ]] \ + && VPN_SERVER_LINE=" - SERVER_HOSTNAMES=${VPN_HOSTNAME}" \ + || VPN_SERVER_LINE=" - SERVER_COUNTRIES=${VPN_COUNTRY}" + + # 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 + 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: qmcgaw/gluetun:latest + container_name: gluetun + restart: unless-stopped + cap_add: + - NET_ADMIN + devices: + - /dev/net/tun:/dev/net/tun + environment: + - VPN_SERVICE_PROVIDER=nordvpn + - VPN_TYPE=openvpn + - OPENVPN_USER=\${OPENVPN_USER} + - OPENVPN_PASSWORD=\${OPENVPN_PASSWORD} +${VPN_SERVER_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\" + 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 + + # ── 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 + volumes: + - \"./user_data:/freqtrade/user_data\" + 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 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="" + + # 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 tls_error=false + + while [[ $tries -lt 18 ]]; do + sleep 5; tries=$((tries+1)) + local elapsed=$(( tries * 5 )) + echo -ne " + ${CYAN}[INFO]${NC} ${proto^^} – ${elapsed}s..." + + local log_snippet + log_snippet=$(pct exec "$SEL_ID" -- bash -c "docker logs --since 30s gluetun 2>&1" 2>/dev/null || echo "") + + # Auth-Fehler → sofort abbrechen, kein Retry + if echo "$log_snippet" | grep -qi "AUTH_FAILED\|auth.*fail\|wrong.*password\|incorrect.*password"; then + echo + echo -e " + ${RED}✗ Authentifizierung fehlgeschlagen!${NC}" + echo -e " ${YELLOW}Service-Credentials prüfen:${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 + fi + + # Hostname ungültig → gültige Server aus Log parsen und zur Auswahl anbieten + if echo "$log_snippet" | grep -qi "hostname.*not valid\|not one of the possible choices"; then + echo + echo -e " ${RED}✗ Ungültiger Hostname: ${VPN_HOSTNAME}${NC}" + echo -e " ${YELLOW}Nicht in Gluetuns Server-Datenbank. Bitte einen der folgenden wählen:${NC} +" + + local valid_servers + valid_servers=$(echo "$log_snippet" | grep -oP '[a-z]{2}[0-9]+\.nordvpn\.com' | sort -u | head -15) + + if [[ -n "$valid_servers" ]]; then + local i=1 + while IFS= read -r srv; do + printf " ${CYAN}%2s)${NC} %s +" "$i" "$srv" + i=$((i+1)) + done <<< "$valid_servers" + echo + echo -ne "${YELLOW} ➜ ${NC}Nummer wählen oder Hostname direkt eingeben: " + read -r sel + if [[ "$sel" =~ ^[0-9]+$ ]]; then + VPN_HOSTNAME=$(echo "$valid_servers" | sed -n "${sel}p") + elif [[ -n "$sel" ]]; then + VPN_HOSTNAME="$sel" + fi + if [[ -n "$VPN_HOSTNAME" ]]; then + info "Neuer Hostname: $VPN_HOSTNAME – schreibe Config neu..." + pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose down 2>/dev/null || true" + write_vpn_config + tries=0; tls_error=false + pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose up -d 2>&1" || true + continue + fi + else + echo -e " ${YELLOW}Keine Server im Log gefunden. Modus 1 (Serverland) empfohlen.${NC}" + fi + pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose down 2>/dev/null || true" + error "Abgebrochen." + fi + + # TLS-Fehler → nächstes Protokoll versuchen + if echo "$log_snippet" | grep -q "TLS Error\|TLS key negotiation"; then + tls_error=true + break + fi + + # Verbunden? + if pct exec "$SEL_ID" -- bash -c "docker exec gluetun wget -qO- https://ipinfo.io/ip 2>/dev/null" &>/dev/null; then + vpn_ok=true + break + fi + done + 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 +} + +main "$@"