#!/usr/bin/env bash # ============================================================================= # Freqtrade NordVPN Addon (Gluetun) # Ergänzt eine bestehende Freqtrade-Installation um NordVPN-Routing. # Läuft im FREQTRADE LXC (nicht auf dem Proxmox Host). # # 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:-}" if [[ -n "$default" ]]; then echo -ne "${YELLOW} ➜ ${NC}${prompt} [${CYAN}${default}${NC}]: " else echo -ne "${YELLOW} ➜ ${NC}${prompt}: " fi 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]$ ]] } print_banner() { clear echo -e "${BOLD}${BLUE}" echo " _ _ _ ___ ___ _ _" echo " | \| |___ _ _ __ | \ \ \ / / | \| |" echo " | .\` / _ \ '_/ _\` \ V /\ V /| | .\` |" echo " |_|\_\___/_| \__,_|\_/ \_/ |_|_|\_|" echo " Freqtrade + NordVPN Addon (Gluetun)" echo -e "${NC}" echo -e " ${CYAN}Ergänzt Freqtrade um NordVPN-Routing via Gluetun${NC}" echo -e " ${YELLOW}Läuft im Freqtrade LXC – nicht auf dem Proxmox Host!${NC}\n" } check_requirements() { step "Voraussetzungen prüfen" [[ $EUID -ne 0 ]] && error "Bitte als root ausführen!" command -v docker &>/dev/null || error "Docker nicht gefunden." docker compose version &>/dev/null || error "Docker Compose Plugin nicht gefunden." success "Docker + Compose vorhanden" FT_DIR="" for candidate in /opt/docker-projekte/freqtrade /opt/freqtrade; do if [[ -f "$candidate/docker-compose.yml" ]] && [[ -d "$candidate/user_data" ]]; then FT_DIR="$candidate"; break fi done [[ -z "$FT_DIR" ]] && error "Keine Freqtrade-Installation gefunden in /opt/docker-projekte/freqtrade/" success "Freqtrade gefunden: $FT_DIR" if [[ ! -e /dev/net/tun ]]; then warn "/dev/net/tun fehlt – lege an..." mkdir -p /dev/net mknod /dev/net/tun c 10 200 2>/dev/null || true chmod 600 /dev/net/tun 2>/dev/null || true [[ ! -e /dev/net/tun ]] && error "/dev/net/tun konnte nicht erstellt werden.\nBitte im Proxmox Host prüfen ob in /etc/pve/lxc/.conf diese Zeilen stehen:\n lxc.cgroup2.devices.allow: c 10:200 rwm\n lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file\n\nHinweis: freqtrade-lxc-install.sh richtet das automatisch ein." fi success "/dev/net/tun vorhanden" } collect_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!" ask_secret "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 – spezifischen Server/Dedicated-IP pinnen" ask "Auswahl [1/2]" "1" VPN_MODE="$REPLY" if [[ "$VPN_MODE" == "2" ]]; then ask "Hostname (z.B. de123.nordvpn.com)" VPN_HOSTNAME="$REPLY"; VPN_COUNTRY="" else ask "Serverland" "Germany" VPN_COUNTRY="$REPLY"; VPN_HOSTNAME="" fi CURRENT_PORT=$(grep -oP '"0\.0\.0\.0:\K\d+(?=:\d+")' "$FT_DIR/docker-compose.yml" 2>/dev/null | head -1 || echo "8080") ask "FreqUI Port" "$CURRENT_PORT" FT_PORT="$REPLY" echo -e "\n${BOLD}${BLUE}══════════ Zusammenfassung ══════════${NC}" echo -e " Freqtrade: ${CYAN}$FT_DIR${NC}" echo -e " NordVPN User: ${CYAN}$NORD_USER${NC}" if [[ -n "$VPN_HOSTNAME" ]]; then echo -e " VPN Server: ${CYAN}$VPN_HOSTNAME${NC}" else echo -e " VPN Land: ${CYAN}$VPN_COUNTRY${NC}" fi echo -e " FreqUI Port: ${CYAN}$FT_PORT${NC}" echo -e "${BOLD}${BLUE}=====================================\n${NC}" confirm "Einrichten?" || { info "Abgebrochen."; exit 0; } } backup_and_stop() { step "Backup & Stack stoppen" local BAK="$FT_DIR/docker-compose.yml.bak-$(date +%Y%m%d_%H%M%S)" cp "$FT_DIR/docker-compose.yml" "$BAK" success "Backup: $BAK" cd "$FT_DIR" if docker compose ps --quiet 2>/dev/null | grep -q .; then docker compose down; success "Stack gestoppt" else info "Kein laufender Stack" fi } write_env() { step ".env schreiben" [[ -f "$FT_DIR/.env" ]] && cp "$FT_DIR/.env" "$FT_DIR/.env.bak-$(date +%Y%m%d_%H%M%S)" cat > "$FT_DIR/.env" << ENVEOF # NordVPN Service-Credentials # Quelle: https://my.nordaccount.com → NordVPN → Set up NordVPN manually OPENVPN_USER=${NORD_USER} OPENVPN_PASSWORD=${NORD_PASS} ENVEOF chmod 600 "$FT_DIR/.env" success ".env gespeichert (chmod 600)" } write_compose() { step "docker-compose.yml erstellen" local VPN_SERVER_LINE="" if [[ -n "$VPN_HOSTNAME" ]]; then VPN_SERVER_LINE=" - SERVER_HOSTNAMES=${VPN_HOSTNAME}" else VPN_SERVER_LINE=" - SERVER_COUNTRIES=${VPN_COUNTRY}" fi cat > "$FT_DIR/docker-compose.yml" << COMPOSEEOF # Freqtrade + NordVPN/Gluetun | $(date '+%Y-%m-%d %H:%M:%S') # Netzwerk: [Freqtrade] → [Gluetun] → NordVPN → Internet # WebUI: http://LXC-IP:${FT_PORT} (Port liegt bei Gluetun!) services: # VPN-Gateway – exponiert alle Ports 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} ports: - "0.0.0.0:${FT_PORT}:8080" 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 – KEIN ports: hier, läuft über Gluetun-Netzwerk freqtrade: image: freqtradeorg/freqtrade:stable restart: unless-stopped container_name: freqtrade network_mode: "container:gluetun" depends_on: gluetun: condition: service_healthy 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" } start_and_verify() { step "Stack starten & VPN prüfen" cd "$FT_DIR" docker compose up -d echo info "Warte auf VPN-Verbindung (bis zu 90s)..." local tries=0 vpn_ok=false while [[ $tries -lt 18 ]]; do sleep 5; echo -n "." docker exec gluetun wget -qO- https://ipinfo.io/ip &>/dev/null && { vpn_ok=true; break; } tries=$((tries+1)) done echo if [[ "$vpn_ok" == "true" ]]; then VPN_IP=$(docker exec gluetun wget -qO- https://ipinfo.io/ip 2>/dev/null) success "VPN verbunden! Ausgehende IP: ${BOLD}${VPN_IP}${NC}" else warn "VPN-Verbindung Timeout. Prüfe: docker logs gluetun" VPN_IP="unbekannt – prüfe: docker exec gluetun wget -qO- https://ipinfo.io/ip" fi sleep 5 docker ps | grep -q "freqtrade" && success "Freqtrade läuft" || warn "Freqtrade prüfen: docker logs freqtrade" } show_result() { local LXC_IP; LXC_IP=$(hostname -I | awk '{print $1}') echo echo -e "${BOLD}${GREEN}╔═══════════════════════════════════════╗${NC}" echo -e "${BOLD}${GREEN}║ ✅ NordVPN Addon eingerichtet! ║${NC}" echo -e "${BOLD}${GREEN}╚═══════════════════════════════════════╝${NC}" echo echo -e " ${BOLD}FreqUI:${NC} ${CYAN}http://${LXC_IP}:${FT_PORT}${NC}" echo -e " ${BOLD}NordVPN IP:${NC} ${CYAN}${VPN_IP}${NC}" echo echo -e " ${BOLD}${YELLOW}⚠ Binance API-Key anpassen:${NC}" echo -e " ${YELLOW}In Binance → API Management → IP-Beschränkung${NC}" echo -e " ${YELLOW}Die NordVPN-IP eintragen (nicht mehr die Home-IP)!${NC}" echo echo -e " ${BOLD}Befehle:${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}cd $FT_DIR && docker compose restart${NC} # Neustart" echo } main() { print_banner check_requirements collect_config backup_and_stop write_env write_compose start_and_verify show_result } main "$@"