diff --git a/Docker/Containerfile b/Docker/Containerfile index 481d621..e88a5bb 100644 --- a/Docker/Containerfile +++ b/Docker/Containerfile @@ -18,7 +18,7 @@ COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh # Health check: can we reach the internet through the VPN? -HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ - CMD ping -c 1 -W 5 1.1.1.1 || exit 1 +HEALTHCHECK --interval=30s --timeout=10s --retries=5 \ + CMD curl -sf --max-time 5 http://1.1.1.1 || exit 1 ENTRYPOINT ["/entrypoint.sh"] diff --git a/Docker/entrypoint.sh b/Docker/entrypoint.sh index 1764dde..f3fa411 100644 --- a/Docker/entrypoint.sh +++ b/Docker/entrypoint.sh @@ -101,7 +101,9 @@ setup_killswitch() { # ────────────────────────────────────────────── enable_forwarding() { echo "[init] Enabling IP forwarding..." - if echo 1 > /proc/sys/net/ipv4/ip_forward 2>/dev/null; then + if cat /proc/sys/net/ipv4/ip_forward 2>/dev/null | grep -q 1; then + echo "[init] IP forwarding already enabled." + elif echo 1 > /proc/sys/net/ipv4/ip_forward 2>/dev/null; then echo "[init] IP forwarding enabled via /proc." else echo "[init] /proc read-only — relying on --sysctl net.ipv4.ip_forward=1" @@ -139,6 +141,20 @@ start_vpn() { ip route add 0.0.0.0/1 dev "$INTERFACE" ip route add 128.0.0.0/1 dev "$INTERFACE" + # ── Policy routing: ensure responses to incoming LAN traffic go back via eth0 ── + if [ -n "$DEFAULT_GW" ] && [ -n "$DEFAULT_IF" ]; then + # Get the container's eth0 IP address (BusyBox-compatible, no grep -P) + ETH0_IP=$(ip -4 addr show "$DEFAULT_IF" | awk '/inet / {split($2, a, "/"); print a[1]}' | head -1) + ETH0_SUBNET=$(ip -4 route show dev "$DEFAULT_IF" | grep -v default | head -1 | awk '{print $1}') + if [ -n "$ETH0_IP" ] && [ -n "$ETH0_SUBNET" ]; then + echo "[vpn] Setting up policy routing for incoming traffic (${ETH0_IP} on ${DEFAULT_IF})" + ip route add default via "$DEFAULT_GW" dev "$DEFAULT_IF" table 100 2>/dev/null || true + ip route add "$ETH0_SUBNET" dev "$DEFAULT_IF" table 100 2>/dev/null || true + ip rule add from "$ETH0_IP" table 100 priority 100 2>/dev/null || true + echo "[vpn] Policy routing active — incoming connections will be routed back via ${DEFAULT_IF}" + fi + fi + # Set up DNS VPN_DNS=$(grep -i '^DNS' "$CONFIG_FILE" | head -1 | sed 's/.*= *//;s/ //g') if [ -n "$VPN_DNS" ]; then @@ -169,7 +185,7 @@ health_loop() { while true; do sleep "$CHECK_INTERVAL" - if ping -c 1 -W 5 "$CHECK_HOST" > /dev/null 2>&1; then + if curl -sf --max-time 5 "http://$CHECK_HOST" > /dev/null 2>&1; then if [ "$failures" -gt 0 ]; then echo "[health] VPN recovered." failures=0 diff --git a/Docker/podman-compose.prod.yml b/Docker/podman-compose.prod.yml new file mode 100644 index 0000000..2b572e4 --- /dev/null +++ b/Docker/podman-compose.prod.yml @@ -0,0 +1,54 @@ +# Production compose — pulls pre-built images from Gitea registry. +# +# Usage: +# podman login git.lpl-mind.de +# podman-compose -f podman-compose.prod.yml pull +# podman-compose -f podman-compose.prod.yml up -d +# +# Required files: +# - wg0.conf (WireGuard configuration in the same directory) + +services: + vpn: + image: git.lpl-mind.de/lukas.pupkalipinski/aniworld/vpn:latest + container_name: vpn-wireguard + cap_add: + - NET_ADMIN + - SYS_MODULE + sysctls: + - net.ipv4.ip_forward=1 + - net.ipv4.conf.all.src_valid_mark=1 + volumes: + - /server/server_aniworld/wg0.conf:/etc/wireguard/wg0.conf:ro + - /lib/modules:/lib/modules:ro + ports: + - "2000:8000" + environment: + - HEALTH_CHECK_INTERVAL=10 + - HEALTH_CHECK_HOST=1.1.1.1 + - LOCAL_PORTS=8000 + - PUID=1013 + - PGID=1001 + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-sf", "--max-time", "5", "http://1.1.1.1"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 60s + + app: + image: git.lpl-mind.de/lukas.pupkalipinski/aniworld/app:latest + container_name: aniworld-app + network_mode: "service:vpn" + depends_on: + vpn: + condition: service_healthy + environment: + - PYTHONUNBUFFERED=1 + - PUID=1013 + - PGID=1001 + volumes: + - /server/server_aniworld/data:/app/data + - /server/server_aniworld/logs:/app/logs + restart: unless-stopped diff --git a/Docker/push.sh b/Docker/push.sh new file mode 100644 index 0000000..72d5f15 --- /dev/null +++ b/Docker/push.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# filepath: /home/lukas/Volume/repo/Aniworld/Docker/push.sh +# +# Build and push Aniworld container images to the Gitea registry. +# +# Usage: +# ./push.sh # builds & pushes with tag "latest" +# ./push.sh v1.2.3 # builds & pushes with tag "v1.2.3" +# ./push.sh v1.2.3 --no-build # pushes existing images only +# +# Prerequisites: +# podman login git.lpl-mind.de + +set -euo pipefail + +# --------------------------------------------------------------------------- +# Configuration +# --------------------------------------------------------------------------- +REGISTRY="git.lpl-mind.de" +NAMESPACE="lukas.pupkalipinski" +PROJECT="aniworld" + +APP_IMAGE="${REGISTRY}/${NAMESPACE}/${PROJECT}/app" +VPN_IMAGE="${REGISTRY}/${NAMESPACE}/${PROJECT}/vpn" + +TAG="${1:-latest}" +SKIP_BUILD=false +if [[ "${2:-}" == "--no-build" ]]; then + SKIP_BUILD=true +fi + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- +log() { echo -e "\n>>> $*"; } +err() { echo -e "\n❌ ERROR: $*" >&2; exit 1; } + +# --------------------------------------------------------------------------- +# Pre-flight checks +# --------------------------------------------------------------------------- +echo "============================================" +echo " Aniworld — Build & Push" +echo " Registry : ${REGISTRY}" +echo " Tag : ${TAG}" +echo "============================================" + +command -v podman &>/dev/null || err "podman is not installed." + +if ! podman login --get-login "${REGISTRY}" &>/dev/null; then + err "Not logged in. Run:\n podman login ${REGISTRY}" +fi + +# --------------------------------------------------------------------------- +# Build +# --------------------------------------------------------------------------- +if [[ "${SKIP_BUILD}" == false ]]; then + log "Building app image → ${APP_IMAGE}:${TAG}" + podman build \ + -t "${APP_IMAGE}:${TAG}" \ + -f "${SCRIPT_DIR}/Dockerfile.app" \ + "${PROJECT_ROOT}" + + log "Building VPN image → ${VPN_IMAGE}:${TAG}" + podman build \ + -t "${VPN_IMAGE}:${TAG}" \ + -f "${SCRIPT_DIR}/Containerfile" \ + "${SCRIPT_DIR}" +fi + +# --------------------------------------------------------------------------- +# Push +# --------------------------------------------------------------------------- +log "Pushing ${APP_IMAGE}:${TAG}" +podman push "${APP_IMAGE}:${TAG}" + +log "Pushing ${VPN_IMAGE}:${TAG}" +podman push "${VPN_IMAGE}:${TAG}" + +# --------------------------------------------------------------------------- +# Summary +# --------------------------------------------------------------------------- +echo "" +echo "============================================" +echo " ✅ Push complete!" +echo "" +echo " Images:" +echo " ${APP_IMAGE}:${TAG}" +echo " ${VPN_IMAGE}:${TAG}" +echo "" +echo " Deploy on server:" +echo " podman login ${REGISTRY}" +echo " podman-compose -f podman-compose.prod.yml pull" +echo " podman-compose -f podman-compose.prod.yml up -d" +echo "============================================" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 322817a..6d7cc24 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,4 +18,10 @@ aiosqlite>=0.19.0 aiohttp>=3.9.0 lxml>=5.0.0 pillow>=10.0.0 -APScheduler>=3.10.4 \ No newline at end of file +APScheduler>=3.10.4 +Events>=0.5 +requests>=2.31.0 +beautifulsoup4>=4.12.0 +fake-useragent>=1.4.0 +yt-dlp>=2024.1.0 +urllib3>=2.0.0 \ No newline at end of file