Files
freqtrade-proxmox-deploy/scripts/proxmox-vpn-install-dev.sh
T

620 lines
25 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
# =============================================================================
# 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 <ID> --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 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; }
}
# ── 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}"
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=""
local VPN_CUSTOM_LINE=""
if [[ -n "$VPN_HOSTNAME" ]]; then
# Dedicated-IP: Custom Config direkt mit IP bypasses Gluetun Hostname-Validierung
VPN_SERVER_LINE=" - VPN_SERVICE_PROVIDER=custom"
VPN_CUSTOM_LINE=" - OPENVPN_CUSTOM_CONFIG=/gluetun/nordvpn-custom.conf"
else
VPN_SERVER_LINE=" - VPN_SERVICE_PROVIDER=nordvpn"
VPN_CUSTOM_LINE=" - SERVER_COUNTRIES=${VPN_COUNTRY}"
fi
# 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: ${GLUETUN_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}
- 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\"
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=""
# 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 per DNS auflösen
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 "")
[[ -z "$server_ip" ]] && error "IP für ${VPN_HOSTNAME} nicht auflösbar!"
info "DNS: ${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 || echo "0")
if [[ "$img_exists" -gt 0 ]]; then
# Prüfen ob der Hostname schon drin ist
local host_in_img
host_in_img=$(pct exec "$SEL_ID" -- bash -c "docker run --rm --entrypoint sh gluetun-custom:latest -c 'cat /gluetun/servers.json 2>/dev/null | grep -c ${VPN_HOSTNAME} || echo 0'" 2>/dev/null || echo "0")
if [[ "$host_in_img" -gt 0 ]]; then
success "gluetun-custom:latest bereits vorhanden und enthält ${VPN_HOSTNAME}"
GLUETUN_IMAGE="gluetun-custom:latest"
return 0 2>/dev/null || true
fi
info "Image vorhanden, aber ${VPN_HOSTNAME} fehlt 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..."
pct exec "$SEL_ID" -- bash -c "python3 << PYEOF
import json, copy, sys
path = '/tmp/gluetun-build/internal/storage/servers.json'
hostname = '${VPN_HOSTNAME}'
ip = '${server_ip}'
with open(path) as f:
db = json.load(f)
servers = db.get('nordvpn', {}).get('servers', [])
if any(s.get('hostname') == hostname for s in servers):
print(f'Bereits vorhanden: {hostname}')
sys.exit(0)
# Deutschen OpenVPN-Eintrag als Template klonen
template = next(
(s for s in servers if s.get('country','').lower() == 'germany' and s.get('vpn') == 'openvpn'),
servers[0]
)
entry = copy.deepcopy(template)
entry['hostname'] = hostname
entry['ips'] = [ip]
servers.append(entry)
db['nordvpn']['servers'] = servers
with open(path, 'w') as f:
json.dump(db, f, indent=2)
print(f'Eingetragen: {hostname} ({ip}) Gesamt NordVPN-Server: {len(servers)}')
PYEOF"
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 -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 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 gluetun 2>&1 | tail -60" 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}
"
# Mehr Log holen die Server-Liste ist sehr lang
local full_log valid_servers
full_log=$(pct exec "$SEL_ID" -- bash -c "docker logs gluetun 2>&1" 2>/dev/null || echo "")
# Nur deutsche Server aus der Liste filtern (de*)
valid_servers=$(echo "$full_log" | grep -oP 'de[0-9]+\.nordvpn\.com' | sort -uV | head -20)
# Falls keine deutschen → alle zeigen
[[ -z "$valid_servers" ]] && valid_servers=$(echo "$full_log" | grep -oP '[a-z]{2}[0-9]+\.nordvpn\.com' | sort -u | head -20)
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 "$@"