22 Commits

Author SHA1 Message Date
Alexandre
86e10379cd Update run 2025-11-24 09:47:56 +01:00
github-actions
c610500d15 GitHub bot: changelog 2025-11-24 08:30:10 +00:00
Alexandre
374b3b8c4c Remove version 5.1.2-18 entry from CHANGELOG
Removed outdated entry for version 5.1.2-18 from CHANGELOG.
2025-11-24 09:23:49 +01:00
Alexandre
f94b1fd18f Update config.yaml 2025-11-24 09:23:38 +01:00
Alexandre
a230b28ffd Update run 2025-11-24 09:22:28 +01:00
Alexandre
81cee949ab Modify VPN monitor condition for public IP check
Change condition to check if /currentip is not empty before starting the VPN monitor.
2025-11-24 09:09:56 +01:00
Alexandre
72d1071fa5 Change condition for VPN leakage service start 2025-11-24 09:09:21 +01:00
github-actions
5fd7b5ed32 GitHub bot: changelog 2025-11-24 08:08:06 +00:00
Alexandre
6f28b28d3d Cleanup changelog by removing old entries
Removed outdated changelog entries for versions 5.1.2-17 and 5.1.2-16.
2025-11-24 09:02:57 +01:00
Alexandre
f74569d99a Update qbittorrent version to 5.1.2-18 2025-11-24 09:02:47 +01:00
Alexandre
b3be37640e Improve validation and error handling in svc-qbittorrent
Refactor validation and error handling in WireGuard setup.
2025-11-24 09:02:35 +01:00
Alexandre
7ee0c3785e Clean up country code URL sources in run script
Removed unused URL sources for country code lookup.
2025-11-24 08:59:09 +01:00
github-actions
7308f78a39 GitHub bot: changelog 2025-11-24 07:56:47 +00:00
Alexandre
3f17818ce9 Update qBittorrent version to 5.1.2-17 2025-11-24 08:51:17 +01:00
Alexandre
0264ef9d97 Implement public IP check for VPN leak monitoring
Added public IP fetching and logging for VPN services.
2025-11-24 08:50:17 +01:00
Alexandre
822c03dac9 Refactor VPN and WireGuard handling in run script
Refactor VPN leak monitoring and WireGuard setup logic, improving clarity and functionality. Adjust log messages for better understanding.
2025-11-24 08:46:48 +01:00
Alexandre
bda1ffedde Enhance VPN leak monitoring and IP fetching methods
Refactor public IP fetching and VPN leak monitoring logic. Introduce new helper functions for fetching public IP and country code with improved error handling and randomization of URL sources. Update WireGuard and OpenVPN integration to include leak monitoring.
2025-11-24 08:45:37 +01:00
Alexandre
6e7308f4e6 Remove IP address retrieval from WireGuard script
Removed the code that retrieves the current IP address.
2025-11-24 08:18:38 +01:00
Alexandre
79ed3e5808 clean 2025-11-24 07:18:00 +00:00
Alexandre
e4b963a267 Update 93-openvpn.sh 2025-11-24 08:17:16 +01:00
Alexandre
f59c21e72e Refactor public_ip.sh for better portability and error handling
Updated shebang to use env for portability and improved error handling in public IP retrieval.
2025-11-24 08:13:46 +01:00
Alexandre
5a41d1c265 Add script to fetch public IP from multiple providers
This script attempts to retrieve the public IP address by querying multiple services in a randomized order. If successful, it writes the IP to /currentip; otherwise, it outputs an error message.
2025-11-24 08:11:13 +01:00
5 changed files with 251 additions and 158 deletions

View File

@@ -1,4 +1,4 @@
## 5.1.2-16 (23-11-2025)
## 5.1.2-19 (24-11-2025)
- Cycle through ip providers to avoid rate limiting api
## 5.1.2-13 (19-11-2025)

View File

@@ -145,4 +145,4 @@ schema:
slug: qbittorrent
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: 5.1.2-16
version: 5.1.2-19

View File

@@ -14,11 +14,6 @@ if bashio::config.true 'openvpn_enabled'; then
bashio::log.info "Openvpn enabled, configuring"
bashio::log.info "----------------------------"
# Get current ip
( curl -fsS --max-time 10 https://ifconfig.co/ip \
|| curl -fsS --max-time 10 https://api64.ipify.org \
|| curl -fsS --max-time 10 https://ipecho.net/plain ) > /currentip
# Function to check for files path
function check_path() {

View File

@@ -76,9 +76,4 @@ else
bashio::log.warning "qBittorrent config file not found. Bind the client manually to interface ${interface_name}."
fi
# Get current ip
( curl -fsS --max-time 10 https://ifconfig.co/ip \
|| curl -fsS --max-time 10 https://api64.ipify.org \
|| curl -fsS --max-time 10 https://ipecho.net/plain ) > /currentip
bashio::log.info "WireGuard prepared with interface ${interface_name} using configuration ${wireguard_config##*/}."

View File

@@ -2,26 +2,241 @@
# shellcheck shell=bash
WEBUI_PORT=${WEBUI_PORT:-8080}
export PATH="/usr/local/sbin:/usr/local/bin:${PATH}"
# --- New helper: get public IP with rate-limiting fallbacks ---
_get_public_ip() {
local ip
ip=$(
curl -fsS --max-time 10 https://ifconfig.co/ip \
|| curl -fsS --max-time 10 https://api64.ipify.org \
|| curl -fsS --max-time 10 https://ipecho.net/plain
) || return 1
printf '%s\n' "${ip}"
}
# --- Configuration & Pre-checks ---
if bashio::config.true 'silent'; then
sed -i 's|/proc/1/fd/1 hassio;|off;|g' /etc/nginx/nginx.conf
fi
# --- Helper Functions ---
_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)
# Loop through the now-randomized list
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
}
_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:-60}
# 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."
s6-svscanctl -t /var/run/s6/services 2>/dev/null || true
exit 1
fi
fi
sleep "${interval}"
done
}
# --- WireGuard Specific Logic ---
_setup_wireguard() {
local WIREGUARD_STATE_DIR="/var/run/wireguard"
local output=""
local status=0
if ! bashio::fs.file_exists "${WIREGUARD_STATE_DIR}/config"; then
bashio::exit.nok 'WireGuard runtime configuration not prepared. Please restart the add-on.'
fi
local wireguard_config
wireguard_config="$(cat "${WIREGUARD_STATE_DIR}/config")"
local wireguard_interface
wireguard_interface="$(cat "${WIREGUARD_STATE_DIR}/interface" 2>/dev/null || echo 'wg0')"
if ip link show "${wireguard_interface}" >/dev/null 2>&1; then
bashio::log.warning "WireGuard interface ${wireguard_interface} already exists. Resetting."
wg-quick down "${wireguard_config}" >/dev/null 2>&1 || true
fi
bashio::log.info "Starting WireGuard interface ${wireguard_interface}..."
# Internal helper: fallback for iptables-legacy
_wg_prepare_legacy() {
local legacy_bin_dir="${WIREGUARD_STATE_DIR}/iptables-legacy-bin"
mkdir -p "${legacy_bin_dir}"
local cmd
for cmd in iptables iptables-save iptables-restore ip6tables ip6tables-save ip6tables-restore; do
if command -v "${cmd}-legacy" >/dev/null 2>&1; then
ln -sf "$(command -v "${cmd}-legacy")" "${legacy_bin_dir}/${cmd}"
fi
done
chmod 700 "${legacy_bin_dir}" 2>/dev/null || true
export PATH="${legacy_bin_dir}:${PATH}"
bashio::log.warning 'Retrying WireGuard using iptables-legacy wrappers.'
}
# Internal helper: Attempt connection
_wg_up_attempt() {
local config_path="$1"
output="$(wg-quick up "${config_path}" 2>&1)" || status=$?
if [ "${status}" -eq 0 ]; then return 0; fi
# Check for iptables errors and try legacy fallback
if echo "${output}" | grep -qiE 'iptables-restore|ip6tables-restore|xtables'; then
if command -v iptables-legacy >/dev/null 2>&1; then
wg-quick down "${config_path}" >/dev/null 2>&1 || true
_wg_prepare_legacy
output="$(wg-quick up "${config_path}" 2>&1)" || status=$?
else
bashio::log.warning 'iptables errors detected but iptables-legacy missing.'
status=1
fi
fi
return "${status}"
}
# 1. First Attempt
if ! _wg_up_attempt "${wireguard_config}"; then
bashio::log.warning 'Initial WireGuard connection failed. Trying IPv4-only endpoints.'
bashio::log.debug "Output: ${output}"
# 2. IPv4 Fallback Preparation
local ipv4_config="${WIREGUARD_STATE_DIR}/${wireguard_interface}-ipv4.conf"
: > "${ipv4_config}"
chmod 600 "${ipv4_config}" 2>/dev/null || true
local line endpoint endpoint_host endpoint_port
while IFS= read -r line || [ -n "$line" ]; do
if [[ "${line}" =~ ^Endpoint ]]; then
endpoint="${line#Endpoint = }"
endpoint_host="${endpoint%:*}"
endpoint_port="${endpoint##*:}"
# Resolve hostname to IPv4
mapfile -t ipv4_candidates < <(getent ahostsv4 "${endpoint_host}" | awk '{print $1}' | uniq)
if [ ${#ipv4_candidates[@]} -gt 0 ]; then
bashio::log.debug "Resolved ${endpoint_host} to ${ipv4_candidates[0]}"
echo "Endpoint = ${ipv4_candidates[0]}:${endpoint_port}" >> "${ipv4_config}"
else
echo "${line}" >> "${ipv4_config}"
fi
else
echo "${line}" >> "${ipv4_config}"
fi
done < "${wireguard_config}"
wg-quick down "${wireguard_config}" >/dev/null 2>&1 || true
# 3. Second Attempt (IPv4 only)
if ! _wg_up_attempt "${ipv4_config}"; then
bashio::log.error 'WireGuard failed to establish connection.'
bashio::log.error "${output}"
bashio::exit.nok 'WireGuard start failed.'
fi
fi
bashio::log.info "WireGuard interface ${wireguard_interface} is up."
# DNS Refresh
if command -v resolvconf >/dev/null 2>&1; then
resolvconf -u >/dev/null 2>&1 || bashio::log.warning 'resolvconf -u failed.'
fi
}
# --- Main Execution ---
echo "$(_fetch_public_ip || true)" > /currentip
if bashio::config.true 'openvpn_enabled'; then
# Start Leak Monitor
_vpn_monitor_public_ip "OpenVPN" &
exec /usr/sbin/openvpn \
--config /config/openvpn/config.ovpn \
--script-security 2 \
@@ -31,141 +246,29 @@ if bashio::config.true 'openvpn_enabled'; then
--pull-filter ignore "ifconfig-ipv6" \
--pull-filter ignore "tun-ipv6" \
--pull-filter ignore "redirect-gateway ipv6" \
--pull-filter ignore "dhcp-option DNS6"
else
if bashio::config.true 'wireguard_enabled'; then
WIREGUARD_STATE_DIR="/var/run/wireguard"
--pull-filter ignore "dhcp-option DNS6" \
&
if ! bashio::fs.file_exists "${WIREGUARD_STATE_DIR}/config"; then
bashio::exit.nok 'WireGuard runtime configuration not prepared. Please restart the add-on.'
fi
elif bashio::config.true 'wireguard_enabled'; then
# Run modularized WireGuard setup
_setup_wireguard
wireguard_config="$(cat "${WIREGUARD_STATE_DIR}/config")"
wireguard_interface="$(cat "${WIREGUARD_STATE_DIR}/interface" 2>/dev/null || echo 'wg0')"
# Start Leak Monitor
_vpn_monitor_public_ip "WireGuard" &
if ip link show "${wireguard_interface}" &> /dev/null; then
bashio::log.warning "WireGuard interface ${wireguard_interface} already exists. Attempting to reset it."
wg-quick down "${wireguard_config}" >/dev/null 2>&1 || true
fi
bashio::log.info "Starting WireGuard interface ${wireguard_interface} using ${wireguard_config##*/}."
# Prefer host-provided iptables-legacy binaries if the default backend fails.
_wireguard_prepare_iptables_legacy() {
local legacy_bin_dir="${WIREGUARD_STATE_DIR}/iptables-legacy-bin"
mkdir -p "${legacy_bin_dir}"
for cmd in iptables iptables-save iptables-restore ip6tables ip6tables-save ip6tables-restore; do
if command -v "${cmd}-legacy" >/dev/null 2>&1; then
ln -sf "$(command -v "${cmd}-legacy")" "${legacy_bin_dir}/${cmd}"
fi
done
chmod 700 "${legacy_bin_dir}" 2>/dev/null || true
export PATH="${legacy_bin_dir}:${PATH}"
bashio::log.warning 'Retrying WireGuard bring-up using iptables-legacy wrappers.'
}
_wireguard_up_with_iptables_fallback() {
local config_path="$1"
local status
output=""
output=$(wg-quick up "${config_path}" 2>&1)
status=$?
if [ "$status" -eq 0 ]; then
return 0
fi
if echo "${output}" | grep -qiE 'iptables-restore|ip6tables-restore|xtables'; then
if command -v iptables-legacy >/dev/null 2>&1 || command -v ip6tables-legacy >/dev/null 2>&1; then
wg-quick down "${config_path}" >/dev/null 2>&1 || true
_wireguard_prepare_iptables_legacy
output=$(wg-quick up "${config_path}" 2>&1)
status=$?
else
bashio::log.warning 'iptables errors detected but iptables-legacy binaries are unavailable in the image.'
status=1
fi
fi
return "${status}"
}
if ! _wireguard_up_with_iptables_fallback "${wireguard_config}"; then
bashio::log.warning 'Initial WireGuard connection attempt failed. Trying again with IPv4-only endpoints.'
bashio::log.warning "First attempt output:${bashio::constants.LF}${output}"
ipv4_config="${WIREGUARD_STATE_DIR}/${wireguard_interface}-ipv4.conf"
echo -n > "${ipv4_config}"
chmod 600 "${ipv4_config}" 2>/dev/null || true
while IFS= read -r line; do
if [[ "${line}" =~ ^Endpoint ]]; then
endpoint="${line#Endpoint = }"
endpoint_host="${endpoint%:*}"
endpoint_port="${endpoint##*:}"
mapfile -t ipv4_candidates < <(getent ahostsv4 "${endpoint_host}" | awk '{print $1}' | uniq)
if [ ${#ipv4_candidates[@]} -gt 0 ]; then
bashio::log.debug "Resolved ${endpoint_host} to IPv4 address ${ipv4_candidates[0]} for WireGuard fallback."
echo "Endpoint = ${ipv4_candidates[0]}:${endpoint_port}" >> "${ipv4_config}"
else
bashio::log.warning "No IPv4 address found for ${endpoint_host}. Keeping original endpoint for fallback."
echo "${line}" >> "${ipv4_config}"
fi
else
echo "${line}" >> "${ipv4_config}"
fi
done < "${wireguard_config}"
wg-quick down "${wireguard_config}" >/dev/null 2>&1 || true
if ! _wireguard_up_with_iptables_fallback "${ipv4_config}"; then
bashio::log.error 'WireGuard failed to establish a connection after IPv4-only retry.'
bashio::log.error "wg-quick output:"
bashio::log.error "${output}"
bashio::log.error 'Troubleshooting steps:'
bashio::log.error " 1. Confirm that the WireGuard configuration file '${wireguard_config}' exists inside the container and contains valid private/public keys, endpoint and AllowedIPs."
bashio::log.error ' 2. Ensure UDP port 51820 (or the port defined in your config) is forwarded on your router to this host and not blocked by your firewall or ISP.'
bashio::log.error ' 3. Verify that the configured endpoint (IP/hostname and port) is reachable from this container (e.g. ping or nc from a debug shell).'
bashio::log.error ' 4. Check that the system time is correct (NTP); large time drift can break key handshakes.'
bashio::log.error ' 5. Confirm that WireGuard kernel support / module is available in the host system.'
bashio::log.error ' 6. If DNS names are used for the endpoint, verify DNS resolution from inside the container (e.g. nslookup or dig).'
bashio::exit.nok 'WireGuard start failed. See the log above for details.'
fi
fi
bashio::log.info "WireGuard interface ${wireguard_interface} is up."
# Example usage of the helper: get VPN-protected IP and store it
if vpn_ip="$(_get_public_ip)"; then
echo "${vpn_ip}" > /vpn_ip
bashio::log.info "Detected VPN public IP: ${vpn_ip}"
else
bashio::log.warning 'Unable to determine VPN public IP (all IP services failed).'
fi
# Refresh DNS resolver configuration if resolvconf is present
if command -v resolvconf >/dev/null 2>&1; then
bashio::log.info 'Refreshing DNS resolver configuration via resolvconf -u.'
if ! resolvconf -u >/dev/null 2>&1; then
bashio::log.warning 'resolvconf -u failed. DNS configuration may not have been updated.'
fi
else
bashio::log.debug 'resolvconf not found in PATH; skipping DNS refresh.'
fi
fi
if bashio::config.true 'silent'; then
exec \
s6-notifyoncheck -d -n 300 -w 1000 -c "nc -z localhost ${WEBUI_PORT}" \
s6-setuidgid abc /usr/bin/qbittorrent-nox --webui-port="${WEBUI_PORT}" > /dev/null
else
exec \
s6-notifyoncheck -d -n 300 -w 1000 -c "nc -z localhost ${WEBUI_PORT}" \
s6-setuidgid abc /usr/bin/qbittorrent-nox --webui-port="${WEBUI_PORT}"
fi
fi
# --- Launch qBittorrent ---
# Determine log output based on silent mode
QB_OUTPUT="/dev/stdout"
if bashio::config.true 'silent'; then
QB_OUTPUT="/dev/null"
fi
bashio::log.info "Starting qBittorrent..."
exec \
s6-notifyoncheck -d -n 300 -w 1000 -c "nc -z localhost ${WEBUI_PORT}" \
s6-setuidgid abc /usr/bin/qbittorrent-nox --webui-port="${WEBUI_PORT}" > "${QB_OUTPUT}"