mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-05-28 03:14:04 +02:00
revert
This commit is contained in:
@@ -10,7 +10,8 @@ if bashio::config.true 'silent'; then
|
|||||||
sed -i 's|/proc/1/fd/1 hassio;|off;|g' /etc/nginx/nginx.conf
|
sed -i 's|/proc/1/fd/1 hassio;|off;|g' /etc/nginx/nginx.conf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Helper for ip
|
# --- Helper Functions ---
|
||||||
|
|
||||||
_fetch_public_ip() {
|
_fetch_public_ip() {
|
||||||
local resp
|
local resp
|
||||||
local url
|
local url
|
||||||
@@ -24,20 +25,102 @@ _fetch_public_ip() {
|
|||||||
)
|
)
|
||||||
local shuffled_urls
|
local shuffled_urls
|
||||||
mapfile -t shuffled_urls < <(printf "%s\n" "${urls[@]}" | shuf)
|
mapfile -t shuffled_urls < <(printf "%s\n" "${urls[@]}" | shuf)
|
||||||
|
# Loop through the now-randomized list
|
||||||
for url in "${shuffled_urls[@]}"; do
|
for url in "${shuffled_urls[@]}"; do
|
||||||
resp=$(curl -fsS --max-time 5 "${url}" 2>/dev/null || true)
|
resp=$(curl -fsS --max-time 5 "${url}" 2>/dev/null || true)
|
||||||
resp="${resp//[[:space:]]/}"
|
resp="${resp//[[:space:]]/}"
|
||||||
|
|
||||||
|
# Validate IPv4 or IPv6
|
||||||
if [[ "${resp}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || [[ "${resp}" =~ ^[0-9a-fA-F:]+$ ]]; then
|
if [[ "${resp}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || [[ "${resp}" =~ ^[0-9a-fA-F:]+$ ]]; then
|
||||||
printf '%s\n' "${resp}"
|
printf '%s\n' "${resp}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_fetch_country_code() {
|
||||||
|
local resp
|
||||||
|
local url
|
||||||
|
local urls=(
|
||||||
|
"https://ipapi.co/country/"
|
||||||
|
"http://ip-api.com/line/?fields=countryCode"
|
||||||
|
"https://ipinfo.io/country"
|
||||||
|
)
|
||||||
|
local shuffled_urls_output
|
||||||
|
shuffled_urls_output=$(printf '%s\n' "${urls[@]}" | shuf)
|
||||||
|
while IFS= read -r url; do
|
||||||
|
# Skip empty lines if any
|
||||||
|
[[ -z "${url}" ]] && continue
|
||||||
|
# Fetch the response with a 5-second timeout
|
||||||
|
resp=$(curl -fsS --max-time 5 "${url}" 2>/dev/null || true)
|
||||||
|
# Clean whitespace/newlines
|
||||||
|
resp="${resp//[[:space:]]/}"
|
||||||
|
# Validation: Ensure the response is exactly 2 letters (ISO 3166-1 alpha-2)
|
||||||
|
if [[ "${resp}" =~ ^[A-Za-z]{2}$ ]]; then
|
||||||
|
# Convert to uppercase and print
|
||||||
|
printf '%s\n' "${resp^^}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done <<< "${shuffled_urls_output}" # Process the shuffled output
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_vpn_monitor_public_ip() {
|
||||||
|
local vpn_label="${1:-VPN}"
|
||||||
|
local current_ip_file="/currentip"
|
||||||
|
local baseline_ip vpn_ip country
|
||||||
|
local interval=${VPN_LEAK_CHECK_INTERVAL:-300}
|
||||||
|
local initial_delay=${VPN_LEAK_INITIAL_DELAY:-30}
|
||||||
|
|
||||||
|
# Pre-flight checks
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
bashio::log.warning "${vpn_label}: curl not found; VPN leak monitoring disabled."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -s "${current_ip_file}" ]]; then
|
||||||
|
bashio::log.warning "${vpn_label}: public ip could not be reached; VPN leak monitoring disabled."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! bashio::fs.file_exists "${current_ip_file}"; then
|
||||||
|
bashio::log.warning "${vpn_label}: baseline IP file ${current_ip_file} not found; VPN leak monitoring disabled."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
baseline_ip="$(tr -d '[:space:]' < "${current_ip_file}")"
|
||||||
|
if [[ -z "${baseline_ip}" ]]; then
|
||||||
|
bashio::log.warning "${vpn_label}: baseline IP in ${current_ip_file} is empty; VPN leak monitoring disabled."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
bashio::log.debug "${vpn_label}: Waiting ${initial_delay}s before first leak check."
|
||||||
|
sleep "${initial_delay}"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
vpn_ip="$(_fetch_public_ip || true)"
|
||||||
|
|
||||||
|
if [[ -z "${vpn_ip}" ]]; then
|
||||||
|
bashio::log.warning "${vpn_label}: Failed to fetch public IP (rate limited or connection down)."
|
||||||
|
else
|
||||||
|
if country="$(_fetch_country_code || true)"; then
|
||||||
|
bashio::log.info "${vpn_label}: Current IP: ${vpn_ip} (${country})"
|
||||||
|
else
|
||||||
|
bashio::log.info "${vpn_label}: Current IP: ${vpn_ip} (Country Unknown)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# LEAK DETECTED
|
||||||
|
if [[ "${vpn_ip}" == "${baseline_ip}" ]]; then
|
||||||
|
bashio::log.fatal "${vpn_label}: VPN LEAK DETECTED! Current IP ${vpn_ip} matches baseline. Stopping container."
|
||||||
|
bashio::addon.stop
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
sleep "${interval}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
# --- WireGuard Specific Logic ---
|
# --- WireGuard Specific Logic ---
|
||||||
|
|
||||||
_setup_wireguard() {
|
_setup_wireguard() {
|
||||||
@@ -139,7 +222,7 @@ _setup_wireguard() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
bashio::log.info "WireGuard interface ${wireguard_interface} is up."
|
bashio::log.info "WireGuard interface ${wireguard_interface} is up."
|
||||||
|
|
||||||
# DNS Refresh
|
# DNS Refresh
|
||||||
if command -v resolvconf >/dev/null 2>&1; then
|
if command -v resolvconf >/dev/null 2>&1; then
|
||||||
resolvconf -u >/dev/null 2>&1 || bashio::log.warning 'resolvconf -u failed.'
|
resolvconf -u >/dev/null 2>&1 || bashio::log.warning 'resolvconf -u failed.'
|
||||||
@@ -147,9 +230,13 @@ _setup_wireguard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# --- Main Execution ---
|
# --- Main Execution ---
|
||||||
|
|
||||||
echo "$(_fetch_public_ip || true)" > /currentip
|
echo "$(_fetch_public_ip || true)" > /currentip
|
||||||
|
|
||||||
if bashio::config.true 'openvpn_enabled'; then
|
if bashio::config.true 'openvpn_enabled'; then
|
||||||
|
# Start Leak Monitor
|
||||||
|
_vpn_monitor_public_ip "OpenVPN" &
|
||||||
|
|
||||||
exec /usr/sbin/openvpn \
|
exec /usr/sbin/openvpn \
|
||||||
--config /config/openvpn/config.ovpn \
|
--config /config/openvpn/config.ovpn \
|
||||||
--script-security 2 \
|
--script-security 2 \
|
||||||
@@ -161,8 +248,15 @@ if bashio::config.true 'openvpn_enabled'; then
|
|||||||
--pull-filter ignore "redirect-gateway ipv6" \
|
--pull-filter ignore "redirect-gateway ipv6" \
|
||||||
--pull-filter ignore "dhcp-option DNS6" \
|
--pull-filter ignore "dhcp-option DNS6" \
|
||||||
&
|
&
|
||||||
elif bashio::config.true 'wireguard_enabled'; then
|
|
||||||
|
elif bashio::config.true 'wireguard_enabled'; then
|
||||||
|
|
||||||
|
# Run modularized WireGuard setup
|
||||||
_setup_wireguard
|
_setup_wireguard
|
||||||
|
|
||||||
|
# Start Leak Monitor
|
||||||
|
_vpn_monitor_public_ip "WireGuard" &
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Launch qBittorrent ---
|
# --- Launch qBittorrent ---
|
||||||
|
|||||||
@@ -1,11 +1,235 @@
|
|||||||
#!/usr/bin/with-contenv bashio
|
#!/usr/bin/with-contenv bashio
|
||||||
# shellcheck shell=bash
|
# shellcheck shell=bash
|
||||||
set -e
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
# Wait for transmission to become available
|
set -euo pipefail
|
||||||
bashio::net.wait_for 8080 localhost 900
|
|
||||||
|
|
||||||
bashio::log.info "Starting NGinx..."
|
export PATH="/usr/local/sbin:/usr/local/bin:${PATH}"
|
||||||
|
|
||||||
exec nginx
|
# File where the real (non-VPN) public IP is stored
|
||||||
|
REAL_IP_FILE="/currentip"
|
||||||
|
|
||||||
|
# How often to re-check VPN IP (seconds)
|
||||||
|
VPN_CHECK_INTERVAL="${VPN_CHECK_INTERVAL:-300}"
|
||||||
|
|
||||||
|
# Service used to get country for a given IP.
|
||||||
|
# If it contains "%s", the IP will be formatted into it.
|
||||||
|
# Otherwise we assume an ipinfo-like base URL and call "${VPN_INFO_URL%/}/${ip}/json".
|
||||||
|
VPN_INFO_URL="${VPN_INFO_URL:-https://ipinfo.io}"
|
||||||
|
|
||||||
|
# -------------------------------
|
||||||
|
# Helpers
|
||||||
|
# -------------------------------
|
||||||
|
|
||||||
|
_fetch_public_ip() {
|
||||||
|
local resp
|
||||||
|
local url
|
||||||
|
local urls=(
|
||||||
|
"https://icanhazip.com"
|
||||||
|
"https://ifconfig.me/ip"
|
||||||
|
"https://api64.ipify.org"
|
||||||
|
"https://checkip.amazonaws.com"
|
||||||
|
"https://domains.google.com/checkip"
|
||||||
|
"https://ipinfo.io/ip"
|
||||||
|
)
|
||||||
|
local shuffled_urls
|
||||||
|
mapfile -t shuffled_urls < <(printf "%s\n" "${urls[@]}" | shuf)
|
||||||
|
|
||||||
|
for url in "${shuffled_urls[@]}"; do
|
||||||
|
resp=$(curl -fsS --max-time 5 "${url}" 2>/dev/null || true)
|
||||||
|
resp="${resp//[[:space:]]/}"
|
||||||
|
|
||||||
|
# Validate IPv4 or IPv6
|
||||||
|
if [[ "${resp}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || [[ "${resp}" =~ ^[0-9a-fA-F:]+$ ]]; then
|
||||||
|
printf '%s\n' "${resp}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
read_real_ip() {
|
||||||
|
# Reads the "real" IP saved before VPN start
|
||||||
|
if [[ -f "${REAL_IP_FILE}" ]]; then
|
||||||
|
local ip
|
||||||
|
ip="$(tr -d '[:space:]' < "${REAL_IP_FILE}")"
|
||||||
|
if [[ -n "${ip}" ]]; then
|
||||||
|
echo "${ip}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# No baseline is not fatal: just return empty
|
||||||
|
echo ""
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
get_ip_info() {
|
||||||
|
# Outputs: "<ip> <country>" on success
|
||||||
|
local ip country json info_url url
|
||||||
|
|
||||||
|
if ! ip="$(_fetch_public_ip)"; then
|
||||||
|
bashio::log.warning "Unable to determine external IP from fallback IP services."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
country="Unknown"
|
||||||
|
info_url="${VPN_INFO_URL:-}"
|
||||||
|
|
||||||
|
if [[ -n "${info_url}" ]]; then
|
||||||
|
# Build URL used to get country for this IP
|
||||||
|
if [[ "${info_url}" == *"%s"* ]]; then
|
||||||
|
# Template style, e.g. "https://ipinfo.io/%s/json"
|
||||||
|
printf -v url "${info_url}" "${ip}"
|
||||||
|
else
|
||||||
|
# ipinfo-style base URL
|
||||||
|
url="${info_url%/}/${ip}/json"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if json="$(curl -fsS --max-time 10 "${url}" 2>/dev/null || true)" && [[ -n "${json}" ]]; then
|
||||||
|
country="$(
|
||||||
|
printf '%s\n' "${json}" \
|
||||||
|
| sed -n 's/.*"country"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
|
||||||
|
| head -n1
|
||||||
|
)"
|
||||||
|
[[ -z "${country}" ]] && country="Unknown"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s %s\n' "${ip}" "${country}"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_vpn_ip() {
|
||||||
|
# Waits until:
|
||||||
|
# - we can read an external IP, AND
|
||||||
|
# - it is different from REAL_IP (if REAL_IP is known)
|
||||||
|
# Outputs "<ip> <country>" when ready.
|
||||||
|
local attempt out ip country
|
||||||
|
|
||||||
|
for attempt in {1..5}; do
|
||||||
|
if out="$(get_ip_info)"; then
|
||||||
|
read -r ip country <<< "${out}"
|
||||||
|
|
||||||
|
if [[ -n "${REAL_IP:-}" ]] && [[ "${ip}" == "${REAL_IP}" ]]; then
|
||||||
|
bashio::log.warning \
|
||||||
|
"External IP still equals real IP (${ip}); VPN not ready yet (attempt ${attempt}/5)."
|
||||||
|
else
|
||||||
|
printf '%s %s\n' "${ip}" "${country}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
bashio::log.warning "Unable to query external IP (attempt ${attempt}/5)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
start_nginx_background() {
|
||||||
|
bashio::log.info "Starting nginx..."
|
||||||
|
nginx &
|
||||||
|
echo $!
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------
|
||||||
|
# Main logic
|
||||||
|
# -------------------------------
|
||||||
|
|
||||||
|
# Detect VPN mode(s)
|
||||||
|
vpn_openvpn=false
|
||||||
|
vpn_wireguard=false
|
||||||
|
|
||||||
|
if bashio::config.true 'openvpn_enabled'; then
|
||||||
|
vpn_openvpn=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if bashio::config.true 'wireguard_enabled'; then
|
||||||
|
vpn_wireguard=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If both are enabled, that's a configuration error
|
||||||
|
if [[ "${vpn_openvpn}" == true && "${vpn_wireguard}" == true ]]; then
|
||||||
|
bashio::log.error "Both OpenVPN and WireGuard are enabled. Only one VPN mode is supported."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
vpn_enabled=false
|
||||||
|
vpn_mode="none"
|
||||||
|
|
||||||
|
if [[ "${vpn_openvpn}" == true ]]; then
|
||||||
|
vpn_enabled=true
|
||||||
|
vpn_mode="OpenVPN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${vpn_wireguard}" == true ]]; then
|
||||||
|
vpn_enabled=true
|
||||||
|
vpn_mode="WireGuard"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${vpn_enabled}" != true ]]; then
|
||||||
|
# No VPN: just boot nginx, no IP leak monitoring
|
||||||
|
bashio::log.info "No VPN enabled (OpenVPN/WireGuard). Starting nginx without IP monitoring."
|
||||||
|
exec nginx
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Read the baseline "real" IP from /currentip
|
||||||
|
REAL_IP="$(read_real_ip)"
|
||||||
|
|
||||||
|
if [[ -n "${REAL_IP}" ]]; then
|
||||||
|
bashio::log.info "Real (non-VPN) IP from ${REAL_IP_FILE}: ${REAL_IP}"
|
||||||
|
else
|
||||||
|
bashio::log.warning "Real IP file ${REAL_IP_FILE} missing or empty; IP leak detection will be less strict."
|
||||||
|
fi
|
||||||
|
|
||||||
|
bashio::log.info "VPN mode detected: ${vpn_mode}. Enabling IP leak protection and periodic monitoring."
|
||||||
|
|
||||||
|
# Wait until VPN is up and external IP != REAL_IP
|
||||||
|
if ! VPN_INFO_OUT="$(wait_for_vpn_ip)"; then
|
||||||
|
bashio::log.error "Unable to obtain a VPN external IP different from real IP. Exiting to avoid leaking traffic."
|
||||||
|
bashio::addon.stop
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r VPN_IP VPN_COUNTRY <<< "${VPN_INFO_OUT}"
|
||||||
|
|
||||||
|
bashio::log.info "VPN external IP: ${VPN_IP} (${VPN_COUNTRY})"
|
||||||
|
|
||||||
|
# Start nginx in background and monitor
|
||||||
|
nginx_pid="$(start_nginx_background)"
|
||||||
|
|
||||||
|
# Forward termination signals
|
||||||
|
trap '
|
||||||
|
bashio::log.info "Signal received, stopping nginx..."
|
||||||
|
kill "'"${nginx_pid}"'" 2>/dev/null || true
|
||||||
|
wait "'"${nginx_pid}"'" 2>/dev/null || true
|
||||||
|
exit 0
|
||||||
|
' SIGTERM SIGINT SIGHUP
|
||||||
|
|
||||||
|
# Monitoring loop
|
||||||
|
while true; do
|
||||||
|
# If nginx died, stop this service and let s6 handle restart policy
|
||||||
|
if ! kill -0 "${nginx_pid}" 2>/dev/null; then
|
||||||
|
bashio::log.error "nginx process exited unexpectedly; leaving service."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep "${VPN_CHECK_INTERVAL}"
|
||||||
|
|
||||||
|
if ! current_out="$(get_ip_info)"; then
|
||||||
|
bashio::log.warning "Failed to refresh external IP; keeping previous assumptions."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r current_ip current_country <<< "${current_out}"
|
||||||
|
|
||||||
|
bashio::log.info "Current external IP: ${current_ip} (${current_country})"
|
||||||
|
|
||||||
|
# If we know the real IP, treat equality as a leak
|
||||||
|
if [[ -n "${REAL_IP}" ]] && [[ "${current_ip}" == "${REAL_IP}" ]]; then
|
||||||
|
bashio::log.error "IP LEAK DETECTED: current external IP ${current_ip} matches real IP ${REAL_IP}."
|
||||||
|
bashio::log.error "Stopping nginx and exiting so the supervisor can restart the add-on."
|
||||||
|
kill "${nginx_pid}" 2>/dev/null || true
|
||||||
|
wait "${nginx_pid}" 2>/dev/null || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/execlineb -S0
|
|
||||||
# ==============================================================================
|
|
||||||
# Take down the S6 supervision tree when Nginx fails
|
|
||||||
# ==============================================================================
|
|
||||||
if { s6-test ${1} -ne 0 }
|
|
||||||
if { s6-test ${1} -ne 256 }
|
|
||||||
|
|
||||||
s6-svscanctl -t /var/run/s6/services
|
|
||||||
@@ -1,212 +0,0 @@
|
|||||||
#!/usr/bin/with-contenv bashio
|
|
||||||
# shellcheck shell=bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
export PATH="/usr/local/sbin:/usr/local/bin:${PATH}"
|
|
||||||
|
|
||||||
REAL_IP_FILE="/currentip"
|
|
||||||
READY_FLAG="/run/nginx.ready"
|
|
||||||
CHECK_INTERVAL="${VPN_CHECK_INTERVAL:-${VPN_LEAK_CHECK_INTERVAL:-300}}"
|
|
||||||
INITIAL_DELAY="${VPN_LEAK_INITIAL_DELAY:-30}"
|
|
||||||
VPN_INFO_URL="${VPN_INFO_URL:-https://ipinfo.io}"
|
|
||||||
|
|
||||||
# -------------------------------
|
|
||||||
# Helpers
|
|
||||||
# -------------------------------
|
|
||||||
|
|
||||||
_fetch_public_ip() {
|
|
||||||
local resp
|
|
||||||
local url
|
|
||||||
local urls=(
|
|
||||||
"https://icanhazip.com"
|
|
||||||
"https://ifconfig.me/ip"
|
|
||||||
"https://api64.ipify.org"
|
|
||||||
"https://checkip.amazonaws.com"
|
|
||||||
"https://domains.google.com/checkip"
|
|
||||||
"https://ipinfo.io/ip"
|
|
||||||
)
|
|
||||||
local shuffled_urls
|
|
||||||
mapfile -t shuffled_urls < <(printf "%s\n" "${urls[@]}" | shuf)
|
|
||||||
|
|
||||||
for url in "${shuffled_urls[@]}"; do
|
|
||||||
resp=$(curl -fsS --max-time 5 "${url}" 2>/dev/null || true)
|
|
||||||
resp="${resp//[[:space:]]/}"
|
|
||||||
|
|
||||||
if [[ "${resp}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || [[ "${resp}" =~ ^[0-9a-fA-F:]+$ ]]; then
|
|
||||||
printf '%s\n' "${resp}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
read_real_ip() {
|
|
||||||
if [[ -f "${REAL_IP_FILE}" ]]; then
|
|
||||||
local ip
|
|
||||||
ip="$(tr -d '[:space:]' < "${REAL_IP_FILE}")"
|
|
||||||
if [[ -n "${ip}" ]]; then
|
|
||||||
echo "${ip}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
get_ip_info() {
|
|
||||||
local ip country json info_url url
|
|
||||||
|
|
||||||
if ! ip="$(_fetch_public_ip)"; then
|
|
||||||
bashio::log.warning "Unable to determine external IP from fallback IP services."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
country="Unknown"
|
|
||||||
info_url="${VPN_INFO_URL:-}"
|
|
||||||
|
|
||||||
if [[ -n "${info_url}" ]]; then
|
|
||||||
if [[ "${info_url}" == *"%s"* ]]; then
|
|
||||||
printf -v url "${info_url}" "${ip}"
|
|
||||||
else
|
|
||||||
url="${info_url%/}/${ip}/json"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if json="$(curl -fsS --max-time 10 "${url}" 2>/dev/null || true)" && [[ -n "${json}" ]]; then
|
|
||||||
country="$(
|
|
||||||
printf '%s\n' "${json}" |
|
|
||||||
sed -n 's/.*"country"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' |
|
|
||||||
head -n1
|
|
||||||
)"
|
|
||||||
[[ -z "${country}" ]] && country="Unknown"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf '%s %s\n' "${ip}" "${country}"
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_vpn_ip() {
|
|
||||||
local attempt out ip country
|
|
||||||
|
|
||||||
for attempt in {1..5}; do
|
|
||||||
if out="$(get_ip_info)"; then
|
|
||||||
read -r ip country <<< "${out}"
|
|
||||||
|
|
||||||
if [[ -n "${REAL_IP:-}" ]] && [[ "${ip}" == "${REAL_IP}" ]]; then
|
|
||||||
bashio::log.warning "External IP still equals real IP (${ip}); VPN not ready yet (attempt ${attempt}/5)."
|
|
||||||
else
|
|
||||||
printf '%s %s\n' "${ip}" "${country}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
bashio::log.warning "Unable to query external IP (attempt ${attempt}/5)."
|
|
||||||
fi
|
|
||||||
|
|
||||||
sleep 5
|
|
||||||
done
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
allow_nginx_start() {
|
|
||||||
mkdir -p "$(dirname "${READY_FLAG}")"
|
|
||||||
touch "${READY_FLAG}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# -------------------------------
|
|
||||||
# Main logic
|
|
||||||
# -------------------------------
|
|
||||||
|
|
||||||
vpn_openvpn=false
|
|
||||||
vpn_wireguard=false
|
|
||||||
|
|
||||||
if bashio::config.true 'openvpn_enabled'; then
|
|
||||||
vpn_openvpn=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if bashio::config.true 'wireguard_enabled'; then
|
|
||||||
vpn_wireguard=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${vpn_openvpn}" == true && "${vpn_wireguard}" == true ]]; then
|
|
||||||
bashio::log.error "Both OpenVPN and WireGuard are enabled. Only one VPN mode is supported."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
vpn_enabled=false
|
|
||||||
vpn_mode="none"
|
|
||||||
|
|
||||||
if [[ "${vpn_openvpn}" == true ]]; then
|
|
||||||
vpn_enabled=true
|
|
||||||
vpn_mode="OpenVPN"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${vpn_wireguard}" == true ]]; then
|
|
||||||
vpn_enabled=true
|
|
||||||
vpn_mode="WireGuard"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${vpn_enabled}" != true ]]; then
|
|
||||||
bashio::log.info "No VPN enabled. Allowing nginx start without leak monitoring."
|
|
||||||
allow_nginx_start
|
|
||||||
exec tail -f /dev/null
|
|
||||||
fi
|
|
||||||
|
|
||||||
REAL_IP="$(read_real_ip)"
|
|
||||||
|
|
||||||
if [[ -z "${REAL_IP}" ]]; then
|
|
||||||
for attempt in {1..5}; do
|
|
||||||
sleep 2
|
|
||||||
REAL_IP="$(read_real_ip)"
|
|
||||||
[[ -n "${REAL_IP}" ]] && break
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n "${REAL_IP}" ]]; then
|
|
||||||
bashio::log.info "Real (non-VPN) IP from ${REAL_IP_FILE}: ${REAL_IP}"
|
|
||||||
else
|
|
||||||
bashio::log.warning "Real IP file ${REAL_IP_FILE} missing or empty; IP leak detection will be less strict."
|
|
||||||
fi
|
|
||||||
|
|
||||||
bashio::log.info "VPN mode detected: ${vpn_mode}. Waiting for VPN to become active."
|
|
||||||
|
|
||||||
if ! VPN_INFO_OUT="$(wait_for_vpn_ip)"; then
|
|
||||||
bashio::log.error "Unable to obtain a VPN external IP different from real IP. Exiting to avoid leaking traffic."
|
|
||||||
bashio::addon.stop
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
read -r VPN_IP VPN_COUNTRY <<< "${VPN_INFO_OUT}"
|
|
||||||
bashio::log.info "VPN external IP: ${VPN_IP} (${VPN_COUNTRY})"
|
|
||||||
|
|
||||||
allow_nginx_start
|
|
||||||
|
|
||||||
if [[ "${INITIAL_DELAY}" -gt 0 ]]; then
|
|
||||||
bashio::log.debug "Initial leak-check delay: ${INITIAL_DELAY}s"
|
|
||||||
sleep "${INITIAL_DELAY}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
bashio::log.info "Starting VPN leak monitoring (interval: ${CHECK_INTERVAL}s)."
|
|
||||||
|
|
||||||
trap 'exit 0' SIGTERM SIGINT SIGHUP
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
sleep "${CHECK_INTERVAL}"
|
|
||||||
|
|
||||||
if ! current_out="$(get_ip_info)"; then
|
|
||||||
bashio::log.warning "Failed to refresh external IP; keeping previous assumptions."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
read -r current_ip current_country <<< "${current_out}"
|
|
||||||
bashio::log.info "Current external IP: ${current_ip} (${current_country})"
|
|
||||||
|
|
||||||
if [[ -n "${REAL_IP}" ]] && [[ "${current_ip}" == "${REAL_IP}" ]]; then
|
|
||||||
bashio::log.fatal "IP LEAK DETECTED: current external IP ${current_ip} matches real IP ${REAL_IP}. Stopping add-on."
|
|
||||||
bashio::addon.stop
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
done
|
|
||||||
Reference in New Issue
Block a user