Dateien nach "scripts" hochladen

This commit is contained in:
2026-04-22 12:39:23 +02:00
parent c18922c3b4
commit 1a5c7ebe2a
+121 -488
View File
@@ -1,14 +1,12 @@
#!/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
# =============================================================================
set -euo pipefail
@@ -29,11 +27,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 +37,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,548 +52,191 @@ 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 <ID> --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
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"
# ── 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}=== WICHTIGER SCHRITT ===${NC}"
echo -e "Bitte öffne nun auf deinem Server (oder via SFTP) die Datei im Container:"
echo -e " ${BOLD}/opt/docker-projekte/VPN/custom.conf${NC}"
echo -e "Füge dort den kompletten Text deiner NordVPN-Konfigurationsdatei (.ovpn) ein und speichere."
echo -e "Tipp: Du kannst auch in einer zweiten Konsole ausführen:"
echo -e " pct enter ${SEL_ID} und dann 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")
info "Ermitteltes lokales Subnetz: ${SUBNET}"
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
# 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}
version: '3.8'
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 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")
# Stack starten
info "Starte Stack neu..."
pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose up -d"
sleep 5
success "Deployment abgeschlossen!"
# VPN IP Check
local VPN_IP
VPN_IP=$(pct exec "$SEL_ID" -- bash -c "docker exec gluetun wget -qO- https://ifconfig.me 2>/dev/null" || echo "FEHLER")
echo
echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════╗${NC}"
echo -e "${BOLD}${GREEN}║ ✅ NordVPN erfolgreich eingerichtet!${NC}"
echo -e "${BOLD}${GREEN}║ ✅ Setup erfolgreich abgeschlossen! ${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
echo -e " Neue externe VPN-IP des Bots: ${CYAN}${VPN_IP}${NC}"
echo -e " Dein lokales Subnetz (${SUBNET}) wurde in der Firewall freigegeben."
echo -e " Das Freqtrade-Webinterface bleibt über Port 8080 erreichbar."
}
# ── Hauptprogramm ──────────────────────────────────────────────────────────────
@@ -609,11 +244,9 @@ main() {
print_banner
check_host
select_container
setup_tun
collect_vpn_config
write_vpn_config
start_and_verify
show_result
setup_system
collect_and_wait
deploy_stack
}
main "$@"
main "$@"