277 lines
11 KiB
Bash
277 lines
11 KiB
Bash
#!/usr/bin/env bash
|
||
# =============================================================================
|
||
# Freqtrade NordVPN Addon – Proxmox Host Installer (Custom Config Edition)
|
||
# Läuft komplett auf dem PROXMOX HOST.
|
||
# - Richtet TUN-Device ein
|
||
# - Erstellt VPN-Ordner & Berechtigungen
|
||
# - Fragt NordVPN-Credentials ab & pausiert für custom.conf
|
||
# - Ermittelt automatisch das lokale Subnetz
|
||
# - Schreibt docker-compose.yml & startet den Stack
|
||
# - NEU: Integriert den VPN-Ordner automatisch in den Filebrowser
|
||
# =============================================================================
|
||
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}"
|
||
}
|
||
|
||
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 Custom Config Installer
|
||
BANNER
|
||
echo -e "${NC}"
|
||
echo -e " ${CYAN}Automatisiertes Setup für feste IPs via custom.conf${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?"
|
||
success "Proxmox Host erkannt"
|
||
}
|
||
|
||
# ── Container-Auswahl ─────────────────────────────────────────────────────────
|
||
select_container() {
|
||
step "Freqtrade Container auswählen"
|
||
local -a display_list=()
|
||
|
||
while IFS= read -r line; do
|
||
local ct_id status hostname tags
|
||
ct_id=$(echo "$line" | awk '{print $1}')
|
||
status=$(echo "$line" | awk '{print $2}')
|
||
[[ "$ct_id" =~ ^[0-9]+$ ]] || continue
|
||
|
||
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 "")
|
||
|
||
if echo "$tags" | grep -qi "freqtrade"; then
|
||
display_list+=("${ct_id}|${hostname}|${status}")
|
||
fi
|
||
done < <(pct list 2>/dev/null | tail -n +2)
|
||
|
||
[[ ${#display_list[@]} -eq 0 ]] && error "Keine Container mit Tag 'freqtrade' gefunden!"
|
||
|
||
echo -e " ${BOLD}Nr ID Hostname Status${NC}"
|
||
echo -e " ${BLUE}─────────────────────────────────────────${NC}"
|
||
local i=1
|
||
for entry in "${display_list[@]}"; do
|
||
IFS='|' read -r cid cname cstatus <<< "$entry"
|
||
printf " ${CYAN}%-4s${NC} %-8s %-18s %-10s\n" "$i" "$cid" "$cname" "$cstatus"
|
||
((i++))
|
||
done
|
||
echo
|
||
|
||
local sel_nr=1
|
||
[[ ${#display_list[@]} -gt 1 ]] && { ask "Container Nummer wählen" "1"; sel_nr="$REPLY"; }
|
||
|
||
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}"
|
||
|
||
if [[ "$SEL_STATUS" != "running" ]]; then
|
||
info "Starte Container..."
|
||
pct start "$SEL_ID" && sleep 5
|
||
fi
|
||
}
|
||
|
||
# ── TUN-Device & Ordner einrichten ─────────────────────────────────────────────
|
||
setup_system() {
|
||
step "System & VPN-Ordner vorbereiten"
|
||
|
||
# 1. TUN-Device in LXC Config schreiben
|
||
local conf="/etc/pve/lxc/${SEL_ID}.conf"
|
||
if ! grep -q "lxc.mount.entry: /dev/net/tun" "$conf"; then
|
||
info "Trage TUN-Device in Config ein..."
|
||
cat >> "$conf" << 'TUNCFG'
|
||
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"
|
||
sleep 5
|
||
success "TUN-Device eingerichtet & Container neu gestartet"
|
||
else
|
||
success "TUN-Device war bereits konfiguriert"
|
||
fi
|
||
|
||
# 2. Ordner und Datei erstellen & Rechte setzen
|
||
pct exec "$SEL_ID" -- bash -c "
|
||
mkdir -p /opt/docker-projekte/VPN
|
||
touch /opt/docker-projekte/VPN/custom.conf
|
||
chmod -R 775 /opt/docker-projekte/VPN
|
||
chown 1000:1000 -R /opt/docker-projekte/VPN
|
||
"
|
||
success "VPN-Ordner und custom.conf erstellt (/opt/docker-projekte/VPN)"
|
||
}
|
||
|
||
# ── Filebrowser anpassen ───────────────────────────────────────────────────────
|
||
update_filebrowser() {
|
||
step "Filebrowser aktualisieren"
|
||
local FB_DIR="/opt/docker-projekte/filebrowser"
|
||
local FB_YML="${FB_DIR}/docker-compose.yml"
|
||
|
||
# Prüfen, ob der Filebrowser überhaupt da ist
|
||
if pct exec "$SEL_ID" -- bash -c "[ -f ${FB_YML} ]"; then
|
||
info "Filebrowser gefunden. Prüfe Konfiguration..."
|
||
|
||
# Prüfen, ob der Mount schon existiert, um Doppel-Einträge zu vermeiden
|
||
if pct exec "$SEL_ID" -- bash -c "grep -q '/opt/docker-projekte/VPN:/srv/VPN' ${FB_YML}"; then
|
||
success "VPN-Ordner ist bereits im Filebrowser gemountet."
|
||
else
|
||
info "Füge VPN-Mountpunkt zur docker-compose.yml hinzu..."
|
||
# Fügt die Zeile exakt nach 'volumes:' ein
|
||
pct exec "$SEL_ID" -- bash -c "sed -i '/volumes:/a \\ - /opt/docker-projekte/VPN:/srv/VPN' ${FB_YML}"
|
||
success "Konfiguration ergänzt."
|
||
|
||
info "Starte Filebrowser neu..."
|
||
pct exec "$SEL_ID" -- bash -c "cd ${FB_DIR} && docker compose down && docker compose up -d"
|
||
success "Filebrowser ist neugestartet und hat jetzt Zugriff auf den VPN-Ordner!"
|
||
fi
|
||
else
|
||
info "Kein Filebrowser unter ${FB_DIR} gefunden. Überspringe diesen Schritt."
|
||
fi
|
||
}
|
||
# ── Konfiguration abfragen & Datei befüllen lassen ─────────────────────────────
|
||
collect_and_wait() {
|
||
step "NordVPN Credentials & Config"
|
||
|
||
echo -e " ${YELLOW}Hinweis: Bitte nutze die Service-Credentials von der NordVPN-Website!${NC}"
|
||
ask "NordVPN Service-Username"
|
||
NORD_USER="$REPLY"
|
||
ask "NordVPN Service-Passwort"
|
||
NORD_PASS="$REPLY"
|
||
|
||
echo -e "\n${BOLD}${CYAN}=== 📝 KONFIGURATION ERFORDERLICH ===${NC}"
|
||
echo -e "Bitte öffne jetzt dein ${BOLD}Filebrowser Web-Interface${NC} (Port 8081)."
|
||
echo -e "1. Navigiere in den Ordner: ${BOLD}VPN${NC}"
|
||
echo -e "2. Öffne die Datei: ${BOLD}custom.conf${NC}"
|
||
echo -e "3. Kopiere den ${BOLD}gesamten Inhalt${NC} deiner NordVPN .ovpn Datei hinein."
|
||
echo -e "4. ${BOLD}Speichern${NC} nicht vergessen!"
|
||
echo -e "\n${CYAN}Tipp:${NC} Falls du lieber im Terminal arbeitest:"
|
||
echo -e "nano /opt/docker-projekte/VPN/custom.conf"
|
||
|
||
while true; do
|
||
echo
|
||
if confirm "Hast du die custom.conf befüllt und gespeichert?"; then
|
||
# Kurzer Check, ob die Datei nicht mehr leer ist
|
||
local size
|
||
size=$(pct exec "$SEL_ID" -- stat -c%s /opt/docker-projekte/VPN/custom.conf 2>/dev/null || echo 0)
|
||
if [[ "$size" -gt 10 ]]; then
|
||
success "Datei ist befüllt!"
|
||
break
|
||
else
|
||
warn "Die Datei scheint noch leer zu sein. Bitte überprüfe das."
|
||
fi
|
||
fi
|
||
done
|
||
}
|
||
|
||
# ── Docker Compose schreiben & anwenden ────────────────────────────────────────
|
||
deploy_stack() {
|
||
step "Docker Stack anwenden"
|
||
local FT_DIR="/opt/docker-projekte/freqtrade"
|
||
|
||
# Lokales Subnetz automatisch ermitteln (z.B. 192.168.178.0/24)
|
||
local SUBNET
|
||
#SUBNET=$(pct exec "$SEL_ID" -- bash -c "ip -o -f inet addr show eth0 | awk '{print \$4}' | sed 's/\.[0-9]\{1,3\}\//.0\//'" 2>/dev/null || echo "192.168.0.0/24")
|
||
SUBNET=$(pct exec "$SEL_ID" -- bash -c "python3 -c 'import ipaddress; import subprocess; ip = subprocess.check_output([\"ip\", \"-o\", \"-f\", \"inet\", \"addr\", \"show\", \"eth0\"]).decode().split()[3]; print(ipaddress.ip_interface(ip).network)'" 2>/dev/null || echo "192.168.0.0/23")
|
||
info "Ermitteltes lokales Subnetz: ${SUBNET}"
|
||
|
||
# Alten Stack stoppen
|
||
info "Stoppe aktuellen Stack..."
|
||
pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose down 2>/dev/null || true"
|
||
|
||
# Neue docker-compose.yml generieren
|
||
info "Schreibe neue docker-compose.yml..."
|
||
pct exec "$SEL_ID" -- bash -c "cat > ${FT_DIR}/docker-compose.yml << 'COMPOSEEOF'
|
||
version: '3.8'
|
||
|
||
services:
|
||
gluetun:
|
||
image: qmcgaw/gluetun:latest
|
||
container_name: gluetun
|
||
cap_add:
|
||
- NET_ADMIN
|
||
devices:
|
||
- /dev/net/tun:/dev/net/tun
|
||
environment:
|
||
- TZ=Europe/Berlin
|
||
- VPN_SERVICE_PROVIDER=custom
|
||
- VPN_TYPE=openvpn
|
||
- OPENVPN_CUSTOM_CONFIG=/gluetun/custom.conf
|
||
- OPENVPN_USER=${NORD_USER}
|
||
- OPENVPN_PASSWORD=${NORD_PASS}
|
||
# Automatisch ermitteltes lokales Netz für Erreichbarkeit
|
||
- FIREWALL_OUTBOUND_SUBNETS=${SUBNET}
|
||
volumes:
|
||
- /opt/docker-projekte/VPN:/gluetun
|
||
ports:
|
||
- \"8080:8080\"
|
||
restart: unless-stopped
|
||
|
||
freqtrade:
|
||
image: freqtradeorg/freqtrade:stable
|
||
container_name: freqtrade
|
||
restart: unless-stopped
|
||
network_mode: \"container:gluetun\"
|
||
volumes:
|
||
- \"./user_data:/freqtrade/user_data\"
|
||
depends_on:
|
||
- gluetun
|
||
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
|
||
COMPOSEEOF"
|
||
|
||
# Stack starten
|
||
info "Starte Stack neu..."
|
||
pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose up -d"
|
||
|
||
sleep 5
|
||
success "Deployment abgeschlossen!"
|
||
}
|
||
|
||
main() {
|
||
print_banner
|
||
check_host
|
||
select_container
|
||
setup_system
|
||
update_filebrowser
|
||
collect_and_wait
|
||
deploy_stack
|
||
|
||
# Finaler IP Check
|
||
local IP
|
||
IP=$(pct exec "$SEL_ID" -- bash -c "docker exec gluetun wget -qO- --user-agent=curl https://ifconfig.me 2>/dev/null" || echo "Check fehlgeschlagen")
|
||
echo -e "\n${GREEN}FERTIG! Externe VPN-IP: ${BOLD}${IP}${NC}"
|
||
}
|
||
|
||
main "$@" |