Files
freqtrade-proxmox-deploy/scripts/proxmox-vpn-install.sh
T

468 lines
18 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# =============================================================================
# Freqtrade NordVPN Addon Proxmox Host Installer
# 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
# =============================================================================
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:-}"
[[ -n "$default" ]] \
&& echo -ne "${YELLOW}${NC}${prompt} [${CYAN}${default}${NC}]: " \
|| echo -ne "${YELLOW}${NC}${prompt}: "
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]$ ]]
}
# ── 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 + Gluetun/NordVPN vollautomatisch ein${NC}"
echo -e " ${CYAN}Quelle: git.obraasch.de/olli1986/freqtrade-proxmox-deploy${NC}\n"
}
# ── Proxmox-Check ──────────────────────────────────────────────────────────────
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=()
while IFS= read -r line; do
local ct_id status
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=" "
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
# 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 " ${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"
((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))]}"
[[ -z "${SEL_ID:-}" ]] && error "Ungültige Auswahl!"
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"
fi
}
# ── 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"
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)
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"
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"
}
# ── 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"
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 Dedicated-IP oder spez. Server pinnen"
ask "Auswahl [1/2]" "1"
VPN_MODE="$REPLY"
if [[ "$VPN_MODE" == "2" ]]; then
echo -e "\n ${YELLOW}Gültige Hostnamen haben das Format: de507.nordvpn.com, de654.nordvpn.com etc.${NC}"
echo -e " ${YELLOW}Beispiele für Deutschland: de507, de654, de675 ... de1118${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 config ermitteln
local current_port
current_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")
ask "FreqUI Port" "$current_port"
FT_PORT="$REPLY"
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; }
}
# ── Konfiguration in Container schreiben ──────────────────────────────────────
write_vpn_config() {
step "VPN-Konfiguration in Container schreiben"
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)
local VPN_SERVER_LINE=""
[[ -n "$VPN_HOSTNAME" ]] \
&& VPN_SERVER_LINE=" - SERVER_HOSTNAMES=${VPN_HOSTNAME}" \
|| VPN_SERVER_LINE=" - SERVER_COUNTRIES=${VPN_COUNTRY}"
# 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
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}
services:
# ── VPN-Proxy ──────────────────────────────────────────────────────────────
# Gluetun läuft als HTTP-Proxy auf Port 8888.
# Freqtrade schickt nur den Exchange-Traffic darüber.
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}
# 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\"
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 ───────────────────────────────────────────────────────────────
# 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_healthy
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
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"
}
# ── Stack starten mit Fehlerbehandlung ─────────────────────────────────────────
start_and_verify() {
step "VPN-Stack starten"
local FT_DIR="/opt/docker-projekte/freqtrade"
local MAX_ATTEMPTS=3
local attempt=1
while [[ $attempt -le $MAX_ATTEMPTS ]]; do
echo -e "\n ${CYAN}Versuch ${attempt}/${MAX_ATTEMPTS}...${NC}"
# Stack starten
pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose up -d 2>&1" || true
# Auf Gluetun warten (bis zu 60s)
info "Warte auf VPN-Verbindung (bis zu 60s)..."
local tries=0
local vpn_ok=false
while [[ $tries -lt 12 ]]; do
sleep 5; echo -n "."; tries=$((tries+1))
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
if [[ "$vpn_ok" == "true" ]]; then
VPN_IP=$(pct exec "$SEL_ID" -- bash -c \
"docker exec gluetun wget -qO- https://ipinfo.io/ip 2>/dev/null" || echo "unbekannt")
success "VPN verbunden! Ausgehende IP: ${BOLD}${GREEN}${VPN_IP}${NC}"
break
fi
# Fehleranalyse
echo
warn "VPN-Verbindung fehlgeschlagen. Gluetun-Log:"
echo -e "${YELLOW}─────────────────────────────────────────${NC}"
pct exec "$SEL_ID" -- bash -c \
"docker logs gluetun 2>&1 | grep -E 'ERROR|WARN|hostname|credentials|password|Invalid' | tail -20" \
2>/dev/null || true
echo -e "${YELLOW}─────────────────────────────────────────${NC}"
# Häufige Fehler erkennen und erklären
local gluetun_errors
gluetun_errors=$(pct exec "$SEL_ID" -- bash -c \
"docker logs gluetun 2>&1 | tail -30" 2>/dev/null || echo "")
if echo "$gluetun_errors" | grep -qi "hostname.*not valid\|not one of the possible choices"; then
echo -e "\n ${RED}FEHLER: Ungültiger Hostname!${NC}"
echo -e " ${YELLOW}Gültige deutsche Server haben das Format: de507.nordvpn.com${NC}"
echo -e " ${YELLOW}Sieh die Fehlermeldung für eine Liste gültiger Hostnamen.${NC}"
elif echo "$gluetun_errors" | grep -qi "auth\|credential\|password\|unauthorized\|401"; then
echo -e "\n ${RED}FEHLER: Ungültige Credentials!${NC}"
echo -e " ${YELLOW}Bitte Service-Credentials prüfen unter:${NC}"
echo -e " ${CYAN}https://my.nordaccount.com → NordVPN → Set up NordVPN manually${NC}"
fi
pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose down 2>/dev/null || true"
if [[ $attempt -lt $MAX_ATTEMPTS ]]; then
echo
if confirm "Andere Konfiguration versuchen?"; then
collect_vpn_config
write_vpn_config
else
error "Abgebrochen. Stack wurde gestoppt."
fi
fi
attempt=$((attempt+1))
done
[[ "$vpn_ok" != "true" ]] && error "VPN konnte nach ${MAX_ATTEMPTS} Versuchen nicht verbunden werden."
# 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")
echo
echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════╗${NC}"
echo -e "${BOLD}${GREEN}║ ✅ NordVPN erfolgreich eingerichtet! ║${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
}
# ── Hauptprogramm ──────────────────────────────────────────────────────────────
main() {
print_banner
check_host
select_container
setup_tun
collect_vpn_config
write_vpn_config
start_and_verify
show_result
}
main "$@"