#!/usr/bin/env bash # ============================================================================= # Freqtrade NordVPN Addon – Proxmox Host Installer # Erkennt Freqtrade-Container automatisch per Tag, richtet TUN-Device ein # und startet install-vpn.sh im Container. # # Läuft 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}"; } VPN_SCRIPT_URL="https://git.obraasch.de/olli1986/freqtrade-proxmox-deploy/raw/branch/main/scripts/install-vpn.sh" # ── 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-Device ein und startet NordVPN-Setup im LXC${NC}\n" } # ── Proxmox-Check ────────────────────────────────────────────────────────────── check_host() { [[ $EUID -ne 0 ]] && error "Bitte als root ausführen!" command -v pct &>/dev/null || error "pct nicht gefunden – läuft das auf Proxmox?" success "Proxmox Host erkannt" } # ── Container-Auswahl ───────────────────────────────────────────────────────── select_container() { step "Freqtrade Container suchen" # Alle laufenden Container mit Tag "freqtrade" finden mapfile -t TAGGED_IDS < <( pct list 2>/dev/null | awk 'NR>1{print $1}' | while read -r id; do tags=$(pct config "$id" 2>/dev/null | grep '^tags:' | sed 's/tags: *//' || true) if echo "$tags" | grep -qi "freqtrade"; then name=$(pct config "$id" 2>/dev/null | grep '^hostname:' | awk '{print $2}' || echo "?") status=$(pct list 2>/dev/null | awk -v id="$id" '$1==id{print $2}') echo "${id}|${name}|${status}" fi done ) # Auch ungetaggte Container mit Hostname "freqtrade" als Fallback mapfile -t HOSTNAME_IDS < <( pct list 2>/dev/null | awk 'NR>1{print $1}' | while read -r id; do hostname=$(pct config "$id" 2>/dev/null | grep '^hostname:' | awk '{print $2}' || true) tags=$(pct config "$id" 2>/dev/null | grep '^tags:' | sed 's/tags: *//' || true) if echo "$hostname" | grep -qi "freqtrade" && ! echo "$tags" | grep -qi "freqtrade"; then status=$(pct list 2>/dev/null | awk -v id="$id" '$1==id{print $2}') echo "${id}|${hostname}|${status} (kein Tag)" fi done ) ALL_IDS=("${TAGGED_IDS[@]}" "${HOSTNAME_IDS[@]}") if [[ ${#ALL_IDS[@]} -eq 0 ]]; then warn "Keine Freqtrade-Container gefunden (Tag 'freqtrade' oder Hostname 'freqtrade*')" echo echo -e " Alle verfügbaren Container:" pct list 2>/dev/null | awk 'NR==1{printf " %-8s %-12s %s\n",$1,$2,$3} NR>1{printf " %-8s %-12s %s\n",$1,$2,$3}' echo echo -ne "${YELLOW} ➜ ${NC}Container-ID manuell eingeben: " read -r SEL_ID SEL_NAME=$(pct config "$SEL_ID" 2>/dev/null | grep '^hostname:' | awk '{print $2}' || echo "unbekannt") SEL_STATUS=$(pct list 2>/dev/null | awk -v id="$SEL_ID" '$1==id{print $2}') elif [[ ${#ALL_IDS[@]} -eq 1 ]]; then # Genau einer gefunden → direkt nehmen IFS='|' read -r SEL_ID SEL_NAME SEL_STATUS <<< "${ALL_IDS[0]}" echo -e " ${GREEN}Gefunden:${NC} CT ${CYAN}${SEL_ID}${NC} | ${CYAN}${SEL_NAME}${NC} | ${SEL_STATUS}" else # Mehrere → Auswahl anzeigen echo -e " ${CYAN}Gefundene Freqtrade-Container:${NC}\n" echo -e " ${BOLD}Nr ID Hostname Status${NC}" echo -e " ${BLUE}────────────────────────────────────${NC}" local i=1 for entry in "${ALL_IDS[@]}"; do IFS='|' read -r cid cname cstatus <<< "$entry" printf " ${CYAN}%-4s${NC} %-8s %-16s %s\n" "$i" "$cid" "$cname" "$cstatus" ((i++)) done echo echo -ne "${YELLOW} ➜ ${NC}Nummer auswählen [1]: " read -r sel_nr sel_nr="${sel_nr:-1}" IFS='|' read -r SEL_ID SEL_NAME SEL_STATUS <<< "${ALL_IDS[$((sel_nr-1))]}" fi [[ -z "${SEL_ID:-}" ]] && error "Keine Container-ID gewählt!" # Status-Check local cur_status cur_status=$(pct list 2>/dev/null | awk -v id="$SEL_ID" '$1==id{print $2}' || echo "unknown") if [[ "$cur_status" != "running" ]]; then warn "Container $SEL_ID ist nicht running (Status: $cur_status)" echo -ne "${YELLOW} ➜ ${NC}Container starten? [j/N]: " read -r ans if [[ "$ans" =~ ^[jJyY]$ ]]; then pct start "$SEL_ID" sleep 5 success "Container gestartet" else error "Container muss laufen für die Installation!" fi fi success "Gewählt: CT ${SEL_ID} | ${SEL_NAME}" } # ── 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" local tun_ok=true grep -q "lxc.cgroup2.devices.allow: c 10:200 rwm" "$conf" || tun_ok=false grep -q "lxc.mount.entry: /dev/net/tun" "$conf" || tun_ok=false if [[ "$tun_ok" == "true" ]]; then success "TUN-Device bereits konfiguriert" return fi info "TUN-Device Zeilen fehlen – trage ein..." 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 success "TUN-Device in $conf eingetragen" # Container neu starten damit die Config greift info "Container neu starten damit TUN-Device aktiv wird..." pct stop "$SEL_ID" sleep 3 pct start "$SEL_ID" sleep 5 # Warten auf Netzwerk local tries=0 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 läuft wieder mit TUN-Device" } # ── install-vpn.sh im Container ausführen ───────────────────────────────────── run_vpn_install() { step "NordVPN-Setup im Container starten" echo -e "\n ${YELLOW}Das install-vpn.sh Script läuft jetzt interaktiv im Container.${NC}" echo -e " ${YELLOW}Du wirst nach deinen NordVPN Service-Credentials gefragt.${NC}\n" echo -e " Service-Credentials: ${CYAN}https://my.nordaccount.com${NC}" echo -e " ${CYAN}→ NordVPN → 'Set up NordVPN manually'${NC}\n" echo -ne "${YELLOW} ➜ ${NC}Jetzt starten? [j/N]: " read -r ans [[ ! "$ans" =~ ^[jJyY]$ ]] && { info "Abgebrochen. Manuell starten mit:\n pct enter ${SEL_ID} dann: bash -c \"\$(curl -fsSL ${VPN_SCRIPT_URL})\""; exit 0; } # Script in Container laden und interaktiv ausführen info "Lade install-vpn.sh in Container..." pct exec "$SEL_ID" -- bash -c "curl -fsSL '${VPN_SCRIPT_URL}' -o /root/install-vpn.sh && chmod +x /root/install-vpn.sh" success "Script geladen – starte interaktive Session im Container..." echo echo -e "${BOLD}${BLUE}──── Container ${SEL_ID} – NordVPN Setup ────${NC}" # pct enter öffnet eine echte interaktive Shell → Script direkt übergeben pct exec "$SEL_ID" -- bash /root/install-vpn.sh } # ── Hauptprogramm ────────────────────────────────────────────────────────────── main() { print_banner check_host select_container setup_tun run_vpn_install } main "$@"