diff --git a/scripts/proxmox-install.sh b/scripts/proxmox-install.sh deleted file mode 100644 index 2119487..0000000 --- a/scripts/proxmox-install.sh +++ /dev/null @@ -1,543 +0,0 @@ -#!/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 – NUR solche die rootdir (LXC-Rootfs) unterstützen - # "local" unterstützt nur ISO/Templates, nicht Container-Rootfs! - STORAGES=$(pvesm status --content rootdir 2>/dev/null | awk 'NR>1{print $1}' | tr '\n' ' ' || echo "local-lvm") - [[ -z "${STORAGES// }" ]] && STORAGES="local-lvm" - - # Verfügbare Bridges - BRIDGES=$(ip link show 2>/dev/null | grep -oP '(?<=: )vmbr\d+' | tr '\n' ' ' || echo "vmbr0") - - # Erster rootdir-fähiger Storage als Default - DEFAULT_STORAGE=$(pvesm status --content rootdir 2>/dev/null | awk 'NR==2{print $1}' || echo "local-lvm") - [[ -z "$DEFAULT_STORAGE" ]] && DEFAULT_STORAGE="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}Storages (rootdir-fähig):${NC} ${BOLD}$STORAGES${NC}" - echo -e " ${YELLOW} Nur diese unterstützen LXC-Container (local reicht nicht!)${NC}" - 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 "$@"