208 lines
6.8 KiB
Bash
208 lines
6.8 KiB
Bash
#!/usr/bin/env bash
|
||
# =============================================================================
|
||
# Freqtrade NordVPN Addon – Proxmox Host Installer (Custom Config Edition)
|
||
# Ablauf: System -> Ordner -> Filebrowser -> User-Input -> VPN-Start
|
||
# =============================================================================
|
||
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]$ ]]
|
||
}
|
||
|
||
print_banner() {
|
||
clear
|
||
echo -e "${BOLD}${BLUE} NordVPN Custom Config Installer (Filebrowser-First)${NC}\n"
|
||
}
|
||
|
||
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"
|
||
}
|
||
|
||
select_container() {
|
||
step "Freqtrade Container auswählen"
|
||
local -a display_list=()
|
||
while IFS= read -r line; do
|
||
local ct_id status hostname
|
||
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 "?")
|
||
display_list+=("${ct_id}|${hostname}|${status}")
|
||
done < <(pct list 2>/dev/null | tail -n +2)
|
||
|
||
echo -e " ${BOLD}Nr ID Hostname${NC}"
|
||
local i=1
|
||
for entry in "${display_list[@]}"; do
|
||
IFS='|' read -r cid cname cstatus <<< "$entry"
|
||
printf " ${CYAN}%-4s${NC} %-8s %-18s\n" "$i" "$cid" "$cname"
|
||
((i++))
|
||
done
|
||
echo
|
||
ask "Container Nummer wählen" "1"; local sel_nr="$REPLY"
|
||
IFS='|' read -r SEL_ID SEL_NAME SEL_STATUS <<< "${display_list[$((sel_nr-1))]}"
|
||
|
||
if [[ "$SEL_STATUS" != "running" ]]; then
|
||
info "Starte Container..."
|
||
pct start "$SEL_ID" && sleep 5
|
||
fi
|
||
}
|
||
|
||
setup_base_system() {
|
||
step "Grundsystem & VPN-Ordner vorbereiten"
|
||
|
||
# TUN-Device in LXC Config
|
||
local conf="/etc/pve/lxc/${SEL_ID}.conf"
|
||
if ! grep -q "lxc.mount.entry: /dev/net/tun" "$conf"; then
|
||
info "Konfiguriere TUN-Device..."
|
||
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 2 && pct start "$SEL_ID" && sleep 5
|
||
fi
|
||
|
||
# VPN Ordner erstellen (BEVOR Filebrowser ihn braucht)
|
||
pct exec "$SEL_ID" -- bash -c "
|
||
mkdir -p /opt/docker-projekte/VPN
|
||
touch /opt/docker-projekte/VPN/custom.conf
|
||
chown 1000:1000 -R /opt/docker-projekte/VPN
|
||
"
|
||
success "VPN Ordner ist bereit."
|
||
}
|
||
|
||
update_filebrowser() {
|
||
step "Filebrowser Anbindung (Vorbereitung)"
|
||
local FB_DIR="/opt/docker-projekte/filebrowser"
|
||
local FB_YML="${FB_DIR}/docker-compose.yml"
|
||
|
||
if pct exec "$SEL_ID" -- bash -c "[ -f ${FB_YML} ]"; then
|
||
if ! pct exec "$SEL_ID" -- bash -c "grep -q '/opt/docker-projekte/VPN:/srv/VPN' ${FB_YML}"; then
|
||
info "Binde VPN-Ordner in Filebrowser ein..."
|
||
pct exec "$SEL_ID" -- bash -c "sed -i '/volumes:/a \\ - /opt/docker-projekte/VPN:/srv/VPN' ${FB_YML}"
|
||
pct exec "$SEL_ID" -- bash -c "cd ${FB_DIR} && docker compose down && docker compose up -d"
|
||
success "Filebrowser neu gestartet. VPN-Ordner ist jetzt im Web-UI sichtbar!"
|
||
else
|
||
success "Filebrowser bereits konfiguriert."
|
||
fi
|
||
else
|
||
warn "Filebrowser nicht gefunden. Du musst die custom.conf manuell bearbeiten."
|
||
fi
|
||
}
|
||
|
||
collect_and_wait() {
|
||
step "Konfiguration & User-Input"
|
||
|
||
ask "NordVPN Service-Username"
|
||
NORD_USER="$REPLY"
|
||
ask "NordVPN Service-Passwort"
|
||
NORD_PASS="$REPLY"
|
||
|
||
echo -e "\n${BOLD}${CYAN}=== JETZT CUSTOM.CONF BEFÜLLEN ===${NC}"
|
||
echo -e "Öffne dein Filebrowser Web-Interface (${BOLD}Port 8081${NC})."
|
||
echo -e "Gehe in den Ordner ${BOLD}VPN${NC} und bearbeite die ${BOLD}custom.conf${NC}."
|
||
echo -e "Füge dort den Inhalt deiner .ovpn Datei von NordVPN ein."
|
||
|
||
while true; do
|
||
echo
|
||
if confirm "Ist die custom.conf im Filebrowser gespeichert?"; then
|
||
local size
|
||
size=$(pct exec "$SEL_ID" -- stat -c%s /opt/docker-projekte/VPN/custom.conf 2>/dev/null || echo 0)
|
||
[[ "$size" -gt 10 ]] && { success "Inhalt erkannt!"; break; }
|
||
warn "Die Datei ist noch leer oder zu klein. Bitte nochmal prüfen."
|
||
fi
|
||
done
|
||
}
|
||
|
||
deploy_vpn_stack() {
|
||
step "VPN & Freqtrade Stack starten"
|
||
local FT_DIR="/opt/docker-projekte/freqtrade"
|
||
|
||
# Subnetz ermitteln
|
||
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")
|
||
|
||
info "Schreibe neue docker-compose.yml für Freqtrade..."
|
||
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}
|
||
- 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"
|
||
|
||
pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose down 2>/dev/null || true"
|
||
pct exec "$SEL_ID" -- bash -c "cd ${FT_DIR} && docker compose up -d"
|
||
|
||
success "Freqtrade & VPN Stack gestartet!"
|
||
}
|
||
|
||
main() {
|
||
print_banner
|
||
check_host
|
||
select_container
|
||
setup_base_system
|
||
update_filebrowser
|
||
collect_and_wait
|
||
deploy_vpn_stack
|
||
|
||
# Finaler IP Check
|
||
local IP
|
||
IP=$(pct exec "$SEL_ID" -- bash -c "docker exec gluetun wget -qO- https://ifconfig.me 2>/dev/null" || echo "Check fehlgeschlagen")
|
||
echo -e "\n${GREEN}FERTIG! Externe VPN-IP: ${BOLD}${IP}${NC}"
|
||
}
|
||
|
||
main "$@" |