From d8aff0f817cb8ab508c19f804a0005481eee4e78 Mon Sep 17 00:00:00 2001 From: Oliver Braasch Date: Mon, 20 Apr 2026 15:26:26 +0200 Subject: [PATCH] Dateien nach "scripts" hochladen --- scripts/install-vpn.sh | 279 +++++++++++++++++++ scripts/proxmox-install.sh | 539 +++++++++++++++++++++++++++++++++++++ 2 files changed, 818 insertions(+) create mode 100644 scripts/install-vpn.sh create mode 100644 scripts/proxmox-install.sh diff --git a/scripts/install-vpn.sh b/scripts/install-vpn.sh new file mode 100644 index 0000000..e39c379 --- /dev/null +++ b/scripts/install-vpn.sh @@ -0,0 +1,279 @@ +#!/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: proxmox-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++)) + 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 "$@" diff --git a/scripts/proxmox-install.sh b/scripts/proxmox-install.sh new file mode 100644 index 0000000..fa90aea --- /dev/null +++ b/scripts/proxmox-install.sh @@ -0,0 +1,539 @@ +#!/usr/bin/env bash +# ============================================================================= +# Freqtrade Proxmox Installer +# Erstellt automatisch einen Docker-LXC und installiert Freqtrade darin. +# Läuft auf dem PROXMOX HOST. +# +# Quelle: https://git.obraasch.de/olli1986/freqtrade-proxmox-deploy +# ============================================================================= +set -euo pipefail + +# ── Farben ──────────────────────────────────────────────────────────────────── +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]$ ]] +} + +# ── Voraussetzungen ─────────────────────────────────────────────────────────── +check_host() { + [[ $EUID -ne 0 ]] && error "Bitte als root ausführen!" + command -v pct &>/dev/null || error "pct nicht gefunden – läuft das auf einem Proxmox Host?" + command -v pvesh &>/dev/null || error "pvesh nicht gefunden – läuft das auf einem Proxmox Host?" + command -v pveam &>/dev/null || error "pveam nicht gefunden." + success "Proxmox Host erkannt" +} + +# ── Banner ──────────────────────────────────────────────────────────────────── +print_banner() { + clear + echo -e "${BOLD}${BLUE}" + cat << 'BANNER' + ___ _ _ + | _ \_ _ _____ _| |_ _ __ _____ __ | |_ ___ ___ + | _/ '_/ _ \ \ / | ' \/ _ \ \ / / | _/ -_) _ \ + |_| |_| \___/_\_\_|_||_\___/_\_\_| \__\___\___/ + + NordVPN + Proxmox LXC Installer +BANNER + echo -e "${NC}" + echo -e " ${CYAN}Freqtrade vollautomatischer Installer für Proxmox${NC}" + echo -e " ${CYAN}git.obraasch.de/olli1986/freqtrade-proxmox-deploy${NC}\n" + echo -e " ${YELLOW}⚠ Nur für Test- und Lernzwecke. Trading auf eigenes Risiko!${NC}\n" +} + +# ── Verfügbare Ressourcen ermitteln ─────────────────────────────────────────── +detect_resources() { + # Nächste freie CT-ID + NEXT_ID=$(pvesh get /cluster/nextid 2>/dev/null | tr -d '[:space:]' || echo "100") + + # Verfügbare Storages (für LXC-Rootfs) + STORAGES=$(pvesm status 2>/dev/null | awk 'NR>1 && $2!="dir" || $2=="dir" {print $1}' | tr '\n' ' ' || echo "local-lvm local") + + # Verfügbare Bridges + BRIDGES=$(ip link show 2>/dev/null | grep -oP '(?<=: )vmbr\d+' | tr '\n' ' ' || echo "vmbr0") + + # Erster Storage als Default + DEFAULT_STORAGE=$(pvesm status 2>/dev/null | awk 'NR==2{print $1}' || echo "local-lvm") + + # Erste Bridge als Default + DEFAULT_BRIDGE=$(ip link show 2>/dev/null | grep -oP '(?<=: )vmbr\d+' | head -1 || echo "vmbr0") +} + +# ── Template holen ──────────────────────────────────────────────────────────── +prepare_template() { + step "Debian 12 Template prüfen" + + # Suche lokal vorhandenes Debian 12 Template + TEMPLATE_FILE=$(pveam list local 2>/dev/null | grep -oP 'debian-12-standard_\S+' | head -1 || true) + + if [[ -z "$TEMPLATE_FILE" ]]; then + info "Template nicht lokal vorhanden – lade herunter..." + pveam update &>/dev/null || warn "pveam update fehlgeschlagen, versuche trotzdem..." + + # Neuestes Debian 12 Template aus der Liste holen + TEMPLATE_FILE=$(pveam available --section system 2>/dev/null \ + | grep -oP 'debian-12-standard_\S+' | sort -V | tail -1 || true) + + [[ -z "$TEMPLATE_FILE" ]] && TEMPLATE_FILE="debian-12-standard_12.7-1_amd64.tar.zst" + + info "Lade Template: $TEMPLATE_FILE" + pveam download local "$TEMPLATE_FILE" || error "Template-Download fehlgeschlagen!" + fi + + TEMPLATE_PATH="local:vztmpl/${TEMPLATE_FILE}" + success "Template bereit: $TEMPLATE_FILE" +} + +# ── Konfiguration abfragen ──────────────────────────────────────────────────── +collect_config() { + step "LXC Konfiguration" + echo -e " ${CYAN}Verfügbare Storages:${NC} $STORAGES" + echo -e " ${CYAN}Verfügbare Bridges:${NC} $BRIDGES\n" + + ask "LXC Container-ID" "$NEXT_ID" + CT_ID="$REPLY" + [[ "$CT_ID" =~ ^[0-9]+$ ]] || error "Container-ID muss eine Zahl sein!" + pct status "$CT_ID" &>/dev/null && error "Container-ID $CT_ID existiert bereits!" + + ask "Hostname" "freqtrade" + CT_HOST="$REPLY" + + ask "CPU Kerne" "2" + CT_CORES="$REPLY" + + ask "RAM in MB" "2048" + CT_RAM="$REPLY" + + ask "Swap in MB" "512" + CT_SWAP="$REPLY" + + ask "Disk-Größe in GB" "10" + CT_DISK="$REPLY" + + ask "Storage Pool" "$DEFAULT_STORAGE" + CT_STORAGE="$REPLY" + + ask "Netzwerk-Bridge" "$DEFAULT_BRIDGE" + CT_BRIDGE="$REPLY" + + ask "Netzwerk (dhcp oder z.B. 192.168.1.100/24)" "dhcp" + CT_IP="$REPLY" + + if [[ "$CT_IP" != "dhcp" ]]; then + ask "Gateway (nur bei statischer IP nötig)" + CT_GW="$REPLY" + else + CT_GW="" + fi + + ask_secret "Root-Passwort für den LXC" + CT_PASS="$REPLY" + [[ ${#CT_PASS} -lt 8 ]] && error "Passwort muss mind. 8 Zeichen haben!" + + step "Freqtrade Konfiguration" + + ask "FreqUI Port" "8080" + FT_PORT="$REPLY" + + ask "Börse" "binance" + FT_EXCHANGE="$REPLY" + + ask "Stake-Währung" "USDC" + FT_STAKE="$REPLY" + + ask "Max. offene Trades" "3" + FT_MAX_TRADES="$REPLY" + + ask "Timeframe" "5m" + FT_TIMEFRAME="$REPLY" + + ask "FreqUI Benutzername" "freqtrader" + FT_API_USER="$REPLY" + + ask_secret "FreqUI Passwort (min. 8 Zeichen)" + FT_API_PASS="$REPLY" + [[ ${#FT_API_PASS} -lt 8 ]] && error "FreqUI Passwort muss mind. 8 Zeichen haben!" + + if confirm "Telegram-Benachrichtigungen aktivieren?"; then + FT_TELEGRAM=true + ask "Telegram Bot-Token" + TG_TOKEN="$REPLY" + ask "Telegram Chat-ID" + TG_CHATID="$REPLY" + else + FT_TELEGRAM=false + TG_TOKEN="" + TG_CHATID="" + fi + + if confirm "Filebrowser (Web-Dateimanager) installieren?"; then + INSTALL_FB=true + ask "Filebrowser Port" "8081" + FB_PORT="$REPLY" + else + INSTALL_FB=false + FB_PORT="8081" + fi + + # ── Zusammenfassung ── + echo -e "\n${BOLD}${BLUE}══════════════ Zusammenfassung ══════════════${NC}" + echo -e " CT-ID: ${CYAN}$CT_ID${NC} Hostname: ${CYAN}$CT_HOST${NC}" + echo -e " CPU/RAM: ${CYAN}$CT_CORES Kerne / ${CT_RAM}MB RAM / ${CT_SWAP}MB Swap${NC}" + echo -e " Disk: ${CYAN}${CT_DISK}GB auf $CT_STORAGE${NC}" + echo -e " Netzwerk: ${CYAN}$CT_IP via $CT_BRIDGE${NC}" + echo -e " FreqUI Port: ${CYAN}$FT_PORT${NC} Börse: ${CYAN}$FT_EXCHANGE${NC}" + echo -e " Filebrowser: $([ "$INSTALL_FB" = true ] && echo "${CYAN}Ja (Port $FB_PORT)${NC}" || echo "${CYAN}Nein${NC}")" + echo -e " Telegram: $([ "$FT_TELEGRAM" = true ] && echo "${CYAN}Ja${NC}" || echo "${CYAN}Nein${NC}")" + echo -e "${BOLD}${BLUE}════════════════════════════════════════════${NC}\n" + + confirm "Installation starten?" || { info "Abgebrochen."; exit 0; } +} + +# ── LXC erstellen ───────────────────────────────────────────────────────────── +create_lxc() { + step "LXC Container $CT_ID erstellen" + + local NET_CONFIG + if [[ "$CT_IP" == "dhcp" ]]; then + NET_CONFIG="name=eth0,bridge=${CT_BRIDGE},ip=dhcp,firewall=1" + else + NET_CONFIG="name=eth0,bridge=${CT_BRIDGE},ip=${CT_IP},gw=${CT_GW},firewall=1" + fi + + pct create "$CT_ID" "$TEMPLATE_PATH" \ + --hostname "$CT_HOST" \ + --password "$CT_PASS" \ + --cores "$CT_CORES" \ + --memory "$CT_RAM" \ + --swap "$CT_SWAP" \ + --rootfs "${CT_STORAGE}:${CT_DISK}" \ + --net0 "$NET_CONFIG" \ + --features "nesting=1,keyctl=1" \ + --unprivileged 1 \ + --onboot 1 \ + --timezone "Europe/Berlin" \ + --start 0 + + # TUN-Device für spätere VPN-Nutzung vorbereiten + cat >> "/etc/pve/lxc/${CT_ID}.conf" << 'TUNCFG' +# TUN-Device für NordVPN/Gluetun (wird von install-vpn.sh genutzt) +lxc.cgroup2.devices.allow: c 10:200 rwm +lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file +TUNCFG + + success "LXC $CT_ID erstellt (mit TUN-Device vorbereitet)" + + # Starten + info "Starte Container..." + pct start "$CT_ID" + + info "Warte auf Netzwerk..." + local tries=0 + until pct exec "$CT_ID" -- bash -c "ip route | grep -q default" 2>/dev/null; do + sleep 3; ((tries++)) + [[ $tries -ge 20 ]] && error "Netzwerk im LXC nicht erreichbar. Prüfe Bridge/IP." + echo -n "." + done + echo + success "Container läuft und hat Netzwerk" +} + +# ── Inneres Setup-Script generieren und ausführen ───────────────────────────── +run_inner_setup() { + step "Freqtrade innerhalb des LXC installieren" + + # Zufällige Secrets für FreqUI generieren + JWT_SECRET=$(openssl rand -hex 32 2>/dev/null || tr -dc 'a-f0-9' < /dev/urandom | head -c 64) + WS_TOKEN=$(openssl rand -hex 16 2>/dev/null || tr -dc 'a-f0-9' < /dev/urandom | head -c 32) + + # Telegram-Block für config.json vorbereiten + if [[ "$FT_TELEGRAM" == "true" ]]; then + TG_JSON='"telegram": {"enabled": true, "token": "'"$TG_TOKEN"'", "chat_id": "'"$TG_CHATID"'"},' + else + TG_JSON='"telegram": {"enabled": false, "token": "", "chat_id": ""},' + fi + + # Inner-Script in eine Temp-Datei schreiben (Variablen werden jetzt expandiert) + cat > /tmp/ft_inner_setup.sh << INNEREOF +#!/usr/bin/env bash +set -euo pipefail + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' +CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' +ok() { echo -e "\${GREEN}[ OK ]\${NC} \$*"; } +inf() { echo -e "\${CYAN}[INF]\${NC} \$*"; } +fail() { echo -e "\${RED}[ERR]\${NC} \$*"; exit 1; } + +INSTALL_DIR="/opt/docker-projekte" +FT_DIR="\$INSTALL_DIR/freqtrade" +FT_PORT="${FT_PORT}" +FT_EXCHANGE="${FT_EXCHANGE}" +FT_STAKE="${FT_STAKE}" +FT_MAX_TRADES="${FT_MAX_TRADES}" +FT_TIMEFRAME="${FT_TIMEFRAME}" +FT_API_USER="${FT_API_USER}" +FT_API_PASS="${FT_API_PASS}" +JWT_SECRET="${JWT_SECRET}" +WS_TOKEN="${WS_TOKEN}" +INSTALL_FB="${INSTALL_FB}" +FB_PORT="${FB_PORT}" + +# ── System-Pakete ───────────────────────────────────────────────────────────── +inf "System aktualisieren..." +export DEBIAN_FRONTEND=noninteractive +apt-get update -qq +apt-get install -y -qq curl ca-certificates gnupg lsb-release net-tools +ok "Systempakete installiert" + +# ── Docker installieren ─────────────────────────────────────────────────────── +inf "Docker installieren..." +curl -fsSL https://get.docker.com | sh > /dev/null 2>&1 +systemctl enable docker --now > /dev/null 2>&1 +ok "Docker installiert: \$(docker --version)" + +# ── Verzeichnisse anlegen ───────────────────────────────────────────────────── +mkdir -p "\$FT_DIR" +mkdir -p "\$INSTALL_DIR/filebrowser/config" +mkdir -p "\$INSTALL_DIR/filebrowser/database" +ok "Verzeichnisse angelegt" + +# ── Filebrowser (optional) ──────────────────────────────────────────────────── +if [[ "\$INSTALL_FB" == "true" ]]; then + inf "Filebrowser installieren..." + cat > "\$INSTALL_DIR/filebrowser/docker-compose.yml" << 'FBEOF' +services: + filebrowser: + image: filebrowser/filebrowser:latest + container_name: filebrowser + user: "0:0" + ports: + - "FBPORT:8080" + volumes: + - /opt/docker-projekte:/srv + - /opt/docker-projekte/filebrowser/database:/database + - /opt/docker-projekte/filebrowser/config:/config + command: ["--address","0.0.0.0","--port","8080", + "--database","/database/filebrowser.db", + "--root","/srv","--noauth=false"] + restart: unless-stopped +FBEOF + # Port einsetzen (war im Heredoc wegen einfacher Anführungszeichen nicht ersetzt) + sed -i "s/FBPORT/\${FB_PORT}/g" "\$INSTALL_DIR/filebrowser/docker-compose.yml" + + cd "\$INSTALL_DIR/filebrowser" + docker compose up -d > /dev/null 2>&1 + sleep 4 + FB_PASS=\$(docker logs filebrowser 2>&1 | grep -oP "(?<=randomly generated password: )\S+" | tail -1 || echo "siehe: docker logs filebrowser") + ok "Filebrowser gestartet – Passwort: \$FB_PASS" + echo "FILEBROWSER_PASS=\$FB_PASS" >> /root/ft_install_info.txt +fi + +# ── Freqtrade docker-compose.yml ────────────────────────────────────────────── +inf "Freqtrade docker-compose.yml herunterladen..." +cd "\$FT_DIR" +curl -fsSL https://raw.githubusercontent.com/freqtrade/freqtrade/stable/docker-compose.yml \ + -o docker-compose.yml +# Nur localhost → 0.0.0.0 für WebUI-Erreichbarkeit +sed -i 's/127\.0\.0\.1/0.0.0.0/g' docker-compose.yml +ok "docker-compose.yml vorbereitet" + +# ── user_data Verzeichnis erstellen ─────────────────────────────────────────── +inf "user_data erstellen..." +docker compose run --rm freqtrade create-userdir --userdir user_data > /dev/null 2>&1 +ok "user_data erstellt" + +# ── config.json generieren ──────────────────────────────────────────────────── +inf "config.json erstellen..." +cat > "\$FT_DIR/user_data/config.json" << CONFIGEOF +{ + "max_open_trades": ${FT_MAX_TRADES}, + "stake_currency": "${FT_STAKE}", + "stake_amount": "unlimited", + "tradable_balance_ratio": 0.99, + "fiat_display_currency": "EUR", + "timeframe": "${FT_TIMEFRAME}", + "dry_run": true, + "dry_run_wallet": 1000, + "cancel_open_orders_on_exit": false, + "trading_mode": "spot", + "margin_mode": "", + "unfilledtimeout": { + "entry": 10, + "exit": 10, + "exit_timeout_count": 0, + "unit": "minutes" + }, + "entry_pricing": { + "price_side": "same", + "use_order_book": true, + "order_book_top": 1, + "price_last_balance": 0.0, + "check_depth_of_market": {"enabled": false, "bids_to_ask_delta": 1} + }, + "exit_pricing": { + "price_side": "same", + "use_order_book": true, + "order_book_top": 1 + }, + "exchange": { + "name": "${FT_EXCHANGE}", + "key": "", + "secret": "", + "ccxt_config": {}, + "ccxt_async_config": {} + }, + "pairlists": [{"method": "StaticPairList"}], + "pair_whitelist": [ + "${FT_STAKE}/USDT", + "BTC/${FT_STAKE}", + "ETH/${FT_STAKE}" + ], + ${TG_JSON} + "api_server": { + "enabled": true, + "listen_ip_address": "0.0.0.0", + "listen_port": 8080, + "verbosity": "error", + "enable_openapi": false, + "jwt_secret_key": "${JWT_SECRET}", + "ws_token": "${WS_TOKEN}", + "CORS_origins": [], + "username": "${FT_API_USER}", + "password": "${FT_API_PASS}" + }, + "bot_name": "freqtrade", + "initial_state": "running", + "force_entry_enable": false, + "internals": {"process_throttle_secs": 5} +} +CONFIGEOF +ok "config.json erstellt" + +# ── Freqtrade starten ───────────────────────────────────────────────────────── +inf "Freqtrade starten..." +cd "\$FT_DIR" +docker compose up -d > /dev/null 2>&1 +sleep 5 +if docker ps | grep -q freqtrade; then + ok "Freqtrade läuft" +else + fail "Freqtrade gestartet nicht – prüfe: docker logs freqtrade" +fi + +# ── Info-Datei für Proxmox-Script ───────────────────────────────────────────── +LXC_IP=\$(hostname -I | awk '{print \$1}') +cat > /root/ft_install_info.txt << INFOEOF +LXC_IP=\$LXC_IP +FT_PORT=${FT_PORT} +FT_API_USER=${FT_API_USER} +FB_PORT=${FB_PORT} +INSTALL_FB=${INSTALL_FB} +INFOEOF + +echo "" +echo "=== Freqtrade Installation abgeschlossen ===" +echo "WebUI: http://\$LXC_IP:${FT_PORT}" +echo "User: ${FT_API_USER}" +INNEREOF + + # Script in den LXC schieben und ausführen + pct push "$CT_ID" /tmp/ft_inner_setup.sh /root/ft_setup.sh + pct exec "$CT_ID" -- chmod +x /root/ft_setup.sh + info "Starte Installation im LXC (kann 2-3 Minuten dauern)..." + pct exec "$CT_ID" -- bash /root/ft_setup.sh + + rm -f /tmp/ft_inner_setup.sh + success "Installation im LXC abgeschlossen" +} + +# ── Ergebnis anzeigen ───────────────────────────────────────────────────────── +show_result() { + # Info-Datei aus dem LXC lesen + local TMP_INFO="/tmp/ft_info_${CT_ID}.txt" + pct pull "$CT_ID" /root/ft_install_info.txt "$TMP_INFO" 2>/dev/null || true + + LXC_IP="" + FB_PASS="" + if [[ -f "$TMP_INFO" ]]; then + # shellcheck disable=SC1090 + source "$TMP_INFO" + rm -f "$TMP_INFO" + fi + + # Falls IP leer, über pct ermitteln + if [[ -z "$LXC_IP" ]]; then + LXC_IP=$(pct exec "$CT_ID" -- hostname -I 2>/dev/null | awk '{print $1}' || echo "unbekannt") + fi + + echo + echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════╗${NC}" + echo -e "${BOLD}${GREEN}║ ✅ Installation erfolgreich! ║${NC}" + echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════╝${NC}" + echo + echo -e " ${BOLD}LXC Container:${NC} ID ${CYAN}$CT_ID${NC} | Host: ${CYAN}$CT_HOST${NC}" + echo -e " ${BOLD}IP-Adresse:${NC} ${CYAN}$LXC_IP${NC}" + echo + echo -e " ${BOLD}${GREEN}FreqUI (Freqtrade WebUI):${NC}" + echo -e " ${CYAN}http://${LXC_IP}:${FT_PORT}${NC}" + echo -e " Benutzer: ${CYAN}$FT_API_USER${NC}" + echo + if [[ "$INSTALL_FB" == "true" ]]; then + echo -e " ${BOLD}${GREEN}Filebrowser (Dateimanager):${NC}" + echo -e " ${CYAN}http://${LXC_IP}:${FB_PORT}${NC}" + echo -e " Benutzer: ${CYAN}admin${NC}" + [[ -n "${FILEBROWSER_PASS:-}" ]] && echo -e " Passwort: ${CYAN}$FILEBROWSER_PASS${NC} ${YELLOW}← sofort ändern!${NC}" + echo + fi + echo -e " ${BOLD}Config-Datei:${NC}" + echo -e " ${CYAN}/opt/docker-projekte/freqtrade/user_data/config.json${NC}" + echo + echo -e " ${BOLD}${YELLOW}Nächste Schritte:${NC}" + echo -e " ${YELLOW}1. Binance API-Keys in config.json eintragen${NC}" + echo -e " ${YELLOW}2. dry_run: false setzen wenn bereit${NC}" + echo -e " ${YELLOW}3. Für NordVPN: install-vpn.sh im LXC ausführen${NC}" + echo + echo -e " ${BOLD}NordVPN Addon (im LXC ausführen):${NC}" + echo -e " ${CYAN}bash -c \"\$(curl -fsSL https://git.obraasch.de/olli1986/freqtrade-proxmox-deploy/raw/branch/main/scripts/install-vpn.sh)\"${NC}" + echo + echo -e " ${BOLD}LXC Konsole öffnen:${NC}" + echo -e " ${CYAN}pct enter $CT_ID${NC}" + echo +} + +# ── Hauptprogramm ───────────────────────────────────────────────────────────── +main() { + print_banner + check_host + detect_resources + prepare_template + collect_config + create_lxc + run_inner_setup + show_result +} + +main "$@"