Files
hassio-addons/qbittorrent/rootfs/usr/local/sbin/vpn
litinoveweedle 7af8610a25 qBittorrent upnp and firewall for VPN
This is implementation of the UPnP port opening for qBittorrent running on VPN. Implementation also includes simple firewall for incoming connections.
2026-03-21 15:24:35 +01:00

616 lines
24 KiB
Plaintext
Executable File

#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
# --- Common Functions ---
declare -A config
config["MySelf"]="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
declare -a dns_servers_ipv4=()
declare -a dns_servers_ipv6=()
log_level="$(bashio::config "log_level")"
[[ "$log_level" == "debug" ]] && bashio::log.warning "--- Debug mode is active ---"
[[ "$log_level" == "debug" ]] && set -x
_parse_config() {
local -n config_ref="$1"
local config_file="$2"
local line
while IFS= read -r line || [[ -n "$line" ]]; do
# Skip comments and empty lines
[[ "$line" =~ ^[#!] ]] && continue
# Extract key and value using regex (trim spaces)
#if [[ "$line" =~ ^[[:space:]]*([^ =]+)[[:space:]]*=[[:space:]]*(.*)[[:space:]]* ]]; then
if [[ "$line" =~ ^[[:space:]]*([^=[:space:]]+)[=[:space:]]+(.*)[[:space:]]* ]]; then
local key="${BASH_REMATCH[1]}"
local value="${BASH_REMATCH[2]}"
if [[ "$key" == "Address" ]]; then
if [[ -n "${config_ref["Address"]:-}" ]]; then
config_ref["Address"]+=",${value}"
else
config_ref["Address"]="${value}"
fi
else
config_ref["$key"]="$value"
fi
fi
done < "$config_file"
}
_parse_dns() {
local dns_ip
local -a dns_conf=()
local -a dns_backup_ipv4=("8.8.8.8" "1.1.1.1")
local -a dns_backup_ipv6=("2001:4860:4860::8888" "2606:4700:4700::1111")
local dns_servers=$(bashio::config 'DNS_server')
mapfile -d ',' -t dns_conf < <(echo "${dns_servers}" | tr -d ' ' | tr -d '\n')
if [ ${config["IPv4Enabled"]} = "true" ]; then
for dns_ip in "${dns_conf[@]}"; do
local result=0
_check_host "${dns_ip}" || result=$?
if [ "${result}" -eq 1 ]; then
dns_servers_ipv4+=("${dns_ip}")
fi
done
if [ ${#dns_servers_ipv4[@]} -eq 0 ]; then
bashio::log.warning "No valid IPv4 DNS servers configured. Using addon defaults ${dns_backup_ipv4[@]}"
dns_servers_ipv4=("${dns_backup_ipv4[@]}")
fi
fi
if [ ${config["IPv6Enabled"]} = "true" ]; then
for dns_ip in "${dns_conf[@]}"; do
local result=0
_check_host "${dns_ip}" || result=$?
if [ "${result}" -eq 2 ]; then
dns_servers_ipv6+=("${dns_ip}")
fi
done
if [ ${#dns_servers_ipv6[@]} -eq 0 ]; then
bashio::log.warning "No valid IPv6 DNS servers configured. Using addon defaults ${dns_backup_ipv6[@]}"
dns_servers_ipv6=("${dns_backup_ipv6[@]}")
fi
fi
}
_cmd() {
cmd="$1"
bashio::log.debug "Executing command: ${cmd}"
eval "${cmd}"
}
_check_host() {
if ipcalc -c -4 "$1" >/dev/null 2>&1; then
return 1 # IPv4
elif ipcalc -c -6 "$1" >/dev/null 2>&1; then
return 2 # IPv6
elif getent ahosts "$1" >/dev/null 2>&1; then
return 3 # resolvable hostnamee
else
return 0 # neither IP nor resolvable hostname
fi
}
_resolvconf() {
local mode=$1
local resolv_conf="/etc/resolv.conf"
local resolv_backup="/etc/resolv.conf.bak"
if [ "${mode}" = "reset" ]; then
bashio::log.info "Resetting ${resolv_conf} to default DNS servers."
if bashio::fs.file_exists "${resolv_backup}"; then
cp "${resolv_backup}" "${resolv_conf}"
else
bashio::log.warning "No original resolv.conf backup found. Leaving as is."
fi
elif [ "${mode}" = "update" ]; then
bashio::log.info "Updating ${resolv_conf} with VPN DNS servers."
if ! bashio::fs.file_exists "${resolv_backup}"; then
bashio::log.debug "Creating backup of original resolv.conf at ${resolv_backup}"
cp "${resolv_conf}" "${resolv_backup}" 2>/dev/null || true
fi
bashio::log.debug "Updating ${resolv_conf} with DNS servers: ${dns_servers_ipv4[*]} ${dns_servers_ipv6[*]}"
{
echo "# Generated by vpn script"
local dns_ip
for dns_ip in ${dns_servers_ipv4[@]} ${dns_servers_ipv6[@]}; do
echo "nameserver ${dns_ip}"
done
} > "${resolv_conf}"
else
bashio::exit.nok "Invalid resolvconf mode specified. Use 'update' or 'reset'."
fi
}
_resolve_hostname() {
local hostname=$1
local -a ips=()
local -a ipv4_candidates=()
local -a ipv6_candidates=()
# Resolve hostname to IPv4
mapfile -t ipv4_candidates < <(getent ahostsv4 "${hostname}" | awk '{print $1}' | uniq)
# Resolve hostname to IPv6
mapfile -t ipv6_candidates < <(getent ahostsv6 "${hostname}" | awk '{print $1}' | uniq)
if [ ${#ipv4_candidates[@]} -gt 0 ]; then
bashio::log.debug "Resolved ${hostname} to ${ipv4_candidates[@]}"
ips+=("${ipv4_candidates[@]}")
fi
if [ ${#ipv6_candidates[@]} -gt 0 ]; then
bashio::log.debug "Resolved ${hostname} to ${ipv6_candidates[@]}"
ips+=("${ipv6_candidates[@]}")
fi
echo "${ips[@]}"
}
_routing_add() {
bashio::log.info "Adding routing rules for VPN interface ${config["Interface"]}..."
local local_ipv4=$(ip addr show ${config["Interface"]} | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1)
local local_ipv6=$(ip addr show ${config["Interface"]} | grep 'inet6 ' | awk '{print $2}' | cut -d'/' -f1)
local ipv4
local ipv6
local dns_ip
# add routing rules for local IPs
for ipv4 in ${local_ipv4}; do
config["IPv4Enabled"]="true"
_cmd "ip -4 rule add priority 1 from ${ipv4} table ${config["Table"]}" || return 1
_cmd "ip -4 rule add priority 1 to ${ipv4}/24 table ${config["Table"]}" || return 1
done
if [ "${config["IPv4Enabled"]}" = "true" ]; then
_cmd "ip -4 route add default dev ${config["Interface"]} table ${config["Table"]}" || return 1
fi
for ipv6 in ${local_ipv6}; do
config["IPv6Enabled"]="true"
_cmd "ip -6 rule add priority 1 from ${ipv6} table ${config["Table"]}" || return 1
_cmd "ip -6 rule add priority 1 to ${ipv6}/64 table ${config["Table"]}" || return 1
done
if [ "${config["IPv6Enabled"]}" = "true" ]; then
_cmd "ip -6 route add default dev ${config["Interface"]} table ${config["Table"]}" || return 1
fi
# get valid DNS servers
_parse_dns
# add routing rules for DNS servers
for dns_ip in "${dns_servers_ipv4[@]}"; do
#_cmd "ip -4 route add ${dns_ip} dev ${config["Interface"]}" || return 1
_cmd "ip -4 rule add priority 1 to ${dns_ip} table ${config["Table"]}" || return 1
done
for dns_ip in "${dns_servers_ipv6[@]}"; do
#_cmd "ip -6 route add ${dns_ip} dev ${config["Interface"]}" || return 1
_cmd "ip -6 rule add priority 1 to ${dns_ip} table ${config["Table"]}" || return 1
done
}
_routing_del() {
bashio::log.info "Removing routing rules for VPN interface ${config["Interface"]}..."
while _cmd "ip -4 rule del priority 1 from all table ${config["Table"]} 2>/dev/null"; do :; done
while _cmd "ip -4 rule del priority 1 to all table ${config["Table"]} 2>/dev/null"; do :; done
while _cmd "ip -4 route del default dev ${config["Interface"]} table ${config["Table"]} 2>/dev/null"; do :; done
while _cmd "ip -6 rule del priority 1 from all table ${config["Table"]} 2>/dev/null"; do :; done
while _cmd "ip -6 rule del priority 1 to all table ${config["Table"]} 2>/dev/null"; do :; done
while _cmd "ip -6 route del default dev ${config["Interface"]} table ${config["Table"]} 2>/dev/null"; do :; done
}
# --- Firewall Specific Functions ---
_firewall_add() {
if [ "${config["IPv4Enabled"]}" = "true" ]; then
_cmd "iptables -N pnat" || return 1
_cmd "iptables -A INPUT -i ${config["Interface"]} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT" || return 1
_cmd "iptables -A INPUT -i ${config["Interface"]} -p icmp -j ACCEPT" || return 1
_cmd "iptables -A INPUT -i ${config["Interface"]} -j pnat" || return 1
_cmd "iptables -A INPUT -i ${config["Interface"]} -j DROP" || return 1
fi
if [ "${config["IPv6Enabled"]}" = "true" ]; then
_cmd "ip6tables -N pnat" || return 1
_cmd "ip6tables -A INPUT -i ${config["Interface"]} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT" || return 1
_cmd "ip6tables -A INPUT -i ${config["Interface"]} -p icmpv6 -j ACCEPT" || return 1
_cmd "ip6tables -A INPUT -i ${config["Interface"]} -j pnat" || return 1
_cmd "ip6tables -A INPUT -i ${config["Interface"]} -j DROP" || return 1
fi
}
_firewall_del() {
if [ "${config["IPv4Enabled"]}" = "true" ]; then
_cmd "iptables -F INPUT" || true
_cmd "iptables -F pnat" || true
_cmd "iptables -X pnat" || true
fi
if [ "${config["IPv6Enabled"]}" = "true" ]; then
_cmd "ip6tables -F INPUT" || true
_cmd "ip6tables -F pnat" || true
_cmd "ip6tables -X pnat" || true
fi
}
# --- WireGuard Specific Logic ---
_wireguard_check() {
local timeout="${1:-20}"
local deadline ts
deadline=$(( $(date +%s) + timeout ))
while [ "$(date +%s)" -lt "${deadline}" ]; do
ping -I "${config["Interface"]}" -c1 -W1 1.1.1.1 >/dev/null 2>&1 || true
ts="$(wg show "${config["Interface"]}" latest-handshakes 2>/dev/null | awk -v pk="${config["PublicKey"]}" '$1==pk{print $2; exit}')"
if [ -n "${ts}" ] && [ "${ts}" -gt 0 ] 2>/dev/null; then
return 0
fi
sleep 1
done
bashio::log.error "WireGuard handshake not established after ${timeout}s (latest-handshake=${ts:-0})."
wg show "${config["Interface"]}" 2>&1 | while IFS= read -r l; do bashio::log.error "${l}"; done
return 1
}
_wireguard_up() {
local local_ip
local -a local_ips=()
local -A local_ip_types=()
local allowed_ip
local -a allowed_ips=()
local -A allowed_ip_types=()
local key
bashio::log.warning "This script force Wireguard to ignore any routes and DNS settings."
bashio::log.warning "Default route will be inserted into custom routing table: ${config["Table"]}"
bashio::log.warning "This routing table will be used for traffic from the VPN interface and to the configured DNS servers."
bashio::log.warning "Qbittorrent bittorrent client shall be set to use the VPN interface ${config["Interface"]} only."
for key in "Interface" "ListenPort" "PrivateKey" "PublicKey" "EndpointIP" "EndpointPort" "Address"; do
if [ ! -v config[$key] ] || [ -z "${config[$key]}" ]; then
bashio::log.error "Missing required WireGuard configuration parameter: ${key}"
return 1
fi
done
_cmd "ip link add ${config["Interface"]} type wireguard" || return 1
mapfile -d ',' -t local_ips < <(echo "${config["Address"]}" | tr -d ' ')
for local_ip in ${local_ips[@]}; do
local result=0
_check_host "${local_ip}" || result=$?
if [ "${result}" -eq 1 ]; then
config["IPv4Enabled"]="true"
local_ip_types["${local_ip}"]="ipv4"
allowed_ip_types["0.0.0.0/0"]="ipv4"
_cmd "ip addr add ${local_ip} dev ${config["Interface"]}" || return 1
elif [ "${result}" -eq 2 ]; then
config["IPv6Enabled"]="true"
local_ip_types["${local_ip}"]="ipv6"
_cmd "ip addr add ${local_ip} dev ${config["Interface"]}" || return 1
else
bashio::log.warning "Ignoring invalid local IP address: ${local_ip}"
fi
done
if [ ${#local_ip_types[@]} -eq 0 ]; then
bashio::log.error "No valid local IP addresses configured for WireGuard interface."
return 1
fi
mapfile -d ',' -t allowed_ips < <(echo "${config["Address"]}" | tr -d ' ')
for allowed_ip in ${allowed_ips[@]}; do
local result=0
_check_host "${allowed_ip}" || result=$?
if [ "${result}" -eq 1 ] && [ "${config["IPv4Enabled"]}" == "true" ]; then
allowed_ip_types["${allowed_ip}"]="ipv4"
#allowed_ip_types["0.0.0.0/0"]="ipv4"
elif [ "${result}" -eq 2 ] && [ "${config["IPv6Enabled"]}" == "true" ]; then
allowed_ip_types["${allowed_ip}"]="ipv6"
#allowed_ip_types["::/0"]="ipv6"
else
bashio::log.error "Invalid allowed IP address: ${allowed_ip}"
return 1
fi
done
if [ ${#allowed_ip_types[@]} -eq 0 ]; then
bashio::log.error "No valid allowed IP addresses configured for WireGuard peer."
return 1
fi
printf -v allowed_ips '%s,' "${!allowed_ip_types[@]}"
allowed_ips="${allowed_ips%,}"
_cmd "wg set ${config["Interface"]} listen-port ${config["ListenPort"]} private-key ${config["PrivateKey"]}" || return 1
local endpoint="${config["EndpointIP"]}:${config["EndpointPort"]}"
if [[ "${config["EndpointIP"]}" == *:* ]]; then
endpoint="[${config["EndpointIP"]}]:${config["EndpointPort"]}"
fi
local peer_cmd="wg set ${config["Interface"]} peer ${config["PublicKey"]} endpoint ${endpoint} allowed-ips ${allowed_ips}"
if [ -n "${config["PresharedKey"]:-}" ]; then
peer_cmd="${peer_cmd} preshared-key ${config["PresharedKey"]}"
fi
if [ -n "${config["PersistentKeepalive"]:-}" ]; then
peer_cmd="${peer_cmd} persistent-keepalive ${config["PersistentKeepalive"]}"
fi
_cmd "${peer_cmd}" || return 1
if [ -v config["MTU"] ] && [ -n "${config["MTU"]}" ]; then
_cmd "ip link set ${config["Interface"]} mtu ${config["MTU"]}" || return 1
fi
_cmd "ip link set ${config["Interface"]} up" || return 1
# Add routing rules for VPN interface and DNS servers
_routing_add || return 1
# Add firewall rules for VPN interface
_firewall_add || return 1
# Update resolv.conf with VPN DNS servers
_resolvconf "update" || return 1
# Wait for handshake to be established before returning success
_wireguard_check 10 || return 1
}
_wireguard_down() {
# Update resolv.conf to remove VPN DNS servers
_resolvconf "reset" || true
# Remove routing rules for VPN interface and DNS servers
_routing_del || true
# Remove firewall rules for VPN interface
_firewall_del || true
_cmd "ip link set ${config["Interface"]} down" 2>/dev/null || true
_cmd "ip link del ${config["Interface"]}" 2>/dev/null || true
}
wireguard() {
local mode=$1
local key
local interface
local config_file
local WIREGUARD_STATE_DIR="/var/run/wireguard"
if ! bashio::fs.file_exists "${WIREGUARD_STATE_DIR}/interface"; then
bashio::exit.nok 'WireGuard runtime configuration not prepared. Please restart the add-on.'
fi
interface=$(cat "${WIREGUARD_STATE_DIR}/interface")
if [ -z "${interface}" ]; then
bashio::exit.nok 'WireGuard runtime configuration not prepared. Please restart the add-on.'
fi
if ! bashio::fs.file_exists "${WIREGUARD_STATE_DIR}/config"; then
bashio::exit.nok 'WireGuard runtime configuration not prepared. Please restart the add-on.'
fi
config_file=$(cat "${WIREGUARD_STATE_DIR}/config")
if [ -z "${config_file}" ]; then
bashio::exit.nok 'WireGuard runtime configuration not prepared. Please restart the add-on.'
fi
bashio::log.info "Using Wireguard configuration file: ${config_file}"
_parse_config config "${config_file}"
config["Interface"]="${interface}"
config["ConfigFile"]="${config_file}"
config["Table"]="${config["Table"]:-1000}"
config["ListenPort"]="${config["ListenPort"]:-51820}"
config["EndpointHost"]="${config["Endpoint"]%:*}"
config["EndpointPort"]="${config["Endpoint"]##*:}"
config["IPv4Enabled"]="false"
config["IPv6Enabled"]="false"
for key in "${!config[@]}"; do
bashio::log.debug "${key}: ${config[$key]}"
done
printf '%s\n' "${config["PrivateKey"]}" > "${WIREGUARD_STATE_DIR}/privatekey"
chmod 600 "${WIREGUARD_STATE_DIR}/privatekey" || true
config["PrivateKey"]="${WIREGUARD_STATE_DIR}/privatekey"
if [ -n "${config["PresharedKey"]:-}" ]; then
printf '%s\n' "${config["PresharedKey"]}" > "${WIREGUARD_STATE_DIR}/presharedkey"
chmod 600 "${WIREGUARD_STATE_DIR}/presharedkey" || true
config["PresharedKey"]="${WIREGUARD_STATE_DIR}/presharedkey"
fi
if [ "${mode}" = "up" ]; then
bashio::log.info "Starting WireGuard on interface ${config["Interface"]}..."
local result=0
_check_host "${config["EndpointHost"]}" || result=$?
if [ "${result}" -eq 0 ]; then
bashio::log.error "WireGuard endpoint ${config["EndpointHost"]} is neither a valid IP address nor a resolvable hostname."
bashio::exit.nok 'WireGuard start failed.'
elif [ "${result}" -eq 3 ]; then
local -a endpoint_ips=()
mapfile -d ' ' -t endpoint_ips < <(_resolve_hostname ${config["EndpointHost"]})
if [ ${#endpoint_ips[@]} -eq 0 ]; then
bashio::log.error "Failed to resolve WireGuard endpoint hostname: ${config["EndpointHost"]}"
bashio::exit.nok 'WireGuard start failed.'
fi
for endpoint_ip in "${endpoint_ips[@]}"; do
bashio::log.info "Resolved WireGuard endpoint hostname ${config["EndpointHost"]} to IP: ${endpoint_ip}"
config["EndpointIP"]="${endpoint_ip}"
if _wireguard_up; then
bashio::log.info "WireGuard interface ${config["Interface"]} is up."
bashio::exit.ok 'WireGuard started.'
fi
bashio::log.error 'WireGuard failed to establish connection.'
_wireguard_down
done
else
bashio::log.debug "WireGuard endpoint ${config["EndpointHost"]} is a valid IP address. Using as is."
config["EndpointIP"]="${config["EndpointHost"]}"
if _wireguard_up; then
bashio::log.info "WireGuard interface ${config["Interface"]} is up."
bashio::exit.ok 'WireGuard started.'
fi
bashio::log.error 'WireGuard failed to establish connection.'
_wireguard_down
fi
elif [ "${mode}" = "down" ]; then
bashio::log.info "Stopping WireGuard on interface ${config["Interface"]}..."
_wireguard_down
bashio::log.info "WireGuard on interface ${config["Interface"]} is down."
bashio::exit.ok 'WireGuard stopped.'
else
bashio::log.error "Invalid WireGuard mode specified. Use 'up' or 'down'."
bashio::exit.nok 'WireGuard start failed.'
fi
bashio::exit.nok 'WireGuard start failed.'
}
# --- OpenVPN Specific Logic ---
_openvpn_check() {
local timeout="${1:-20}"
local deadline ts
deadline=$(( $(date +%s) + timeout ))
while [ "$(date +%s)" -lt "${deadline}" ]; do
if ip link show "${config["Interface"]}" > /dev/null 2>&1 ; then
return 0
fi
sleep 2
done
bashio::log.error "OpenVPN interface ${config["Interface"]} failed to come up after ${timeout}s."
return 1
}
_openvpn_up() {
bashio::log.warning "This script force OpenvPN to ignore any routes and DNS settings pushed by the server."
bashio::log.warning "Default route will be inserted into custom routing table: ${config["Table"]}"
bashio::log.warning "This routing table will be used for traffic from the VPN interface and to the configured DNS servers."
bashio::log.warning "Qbittorrent bittorrent client shall be set to use the VPN interface ${config["Interface"]} only."
# Register this script as OpenVPN up/down handlers to manage routing
echo '#!/bin/bash' > ${config["PostUpScript"]}
echo "${config["MySelf"]} openvpn postup" >> ${config["PostUpScript"]}
chmod 755 ${config["PostUpScript"]}
echo '#!/bin/bash' > ${config["PostDownScript"]}
echo "${config["MySelf"]} openvpn postdown" >> ${config["PostDownScript"]}
chmod 755 ${config["PostDownScript"]}
# Define logging
log_path="/dev/null"
[[ "$log_level" == "debug" ]] && log_path="/proc/1/fd/1"
# Start OpenVPN in the background
_cmd "/usr/sbin/openvpn \
--config "${config["ConfigFile"]}" \
--client \
--daemon \
--log "$log_path" \
--script-security 2 \
--auth-user-pass "${OPENVPN_STATE_DIR}/credentials.conf" \
--auth-retry none \
--up ${config["PostUpScript"]} \
--down ${config["PostDownScript"]} \
--up-delay \
--up-restart \
--route-nopull \
--route-noexec" || return 1
# Wait for the VPN interface to come up
_openvpn_check 30 || return 1
}
_openvpn_down() {
# Terminate OpenVPN process
pkill -f "openvpn --config ${config["ConfigFile"]}" || true
}
_openpvn_postup() {
# Add routing rules for VPN interface and DNS servers
_routing_add || return 1
# Add firewall rules for VPN interface
_firewall_add || return 1
# Update resolv.conf with VPN DNS servers
_resolvconf "update" || return 1
}
_openpvn_postdown() {
# Update resolv.conf to remove VPN DNS servers
_resolvconf "reset" || true
# Remove routing rules for VPN interface and DNS servers
_routing_del || true
# Remove firewall rules for VPN interface
_firewall_del || true
}
openvpn() {
local mode=$1
local interface
local config_file
local OPENVPN_STATE_DIR="/var/run/openvpn"
if ! bashio::fs.file_exists "${OPENVPN_STATE_DIR}/interface"; then
bashio::exit.nok 'OpenVPN runtime configuration not prepared. Please restart the add-on.'
fi
interface=$(cat "${OPENVPN_STATE_DIR}/interface")
if [ -z "${interface}" ]; then
bashio::exit.nok 'OpenVPN runtime configuration not prepared. Please restart the add-on.'
fi
if ! bashio::fs.file_exists "${OPENVPN_STATE_DIR}/config"; then
bashio::exit.nok 'OpenVPN runtime configuration not prepared. Please restart the add-on.'
fi
config_file=$(cat "${OPENVPN_STATE_DIR}/config")
if [ -z "${config_file}" ]; then
bashio::exit.nok 'OpenVPN runtime configuration not prepared. Please restart the add-on.'
fi
bashio::log.warning "Using OpenVPN configuration file: ${config_file}"
_parse_config config "${config_file}"
config["Interface"]="${interface}"
config["ConfigFile"]="${config_file}"
config["Table"]="${config["Table"]:-1000}"
config["PostUpScript"]="${OPENVPN_STATE_DIR}/up.sh"
config["PostDownScript"]="${OPENVPN_STATE_DIR}/down.sh"
if [ "${mode}" = "up" ]; then
# register up and down scripts
bashio::log.info "Starting OpenVPN on interface ${config["Interface"]}..."
if _openvpn_up; then
bashio::log.info "OpenVPN interface ${config["Interface"]} is up."
bashio::exit.ok 'OpenVPN started.'
fi
bashio::log.error 'OpenVPN failed to establish connection.'
_openvpn_down
elif [ "${mode}" = "down" ]; then
bashio::log.info "Stopping OpenVPN on interface ${config["Interface"]}..."
_openvpn_down
bashio::log.info "OpenVPN on interface ${config["Interface"]} is down."
bashio::exit.ok 'OpenVPN stopped.'
elif [ "${mode}" = "postup" ]; then
_openpvn_postup
bashio::exit.ok 'OpenVPN routes added.'
elif [ "${mode}" = "postdown" ]; then
_openpvn_postdown
bashio::exit.ok 'OpenVPN routes deleted.'
else
bashio::log.error "Invalid OpenVPN mode specified. Use 'up', 'down', 'postup', or 'postdown'."
bashio::exit.nok 'OpenVPN start failed.'
fi
bashio::exit.nok 'OpenVPN start failed.'
}
# --- Entry Point ---
if [ $# -ne 2 ]; then
bashio::log.error "Invalid number of arguments. Usage: vpn.sh <wireguard|openvpn> <up|down>"
bashio::exit.nok 'VPN start failed.'
fi
if [[ "$1" == "wireguard" ]]; then
wireguard "$2"
elif [[ "$1" == "openvpn" ]]; then
openvpn "$2"
else
bashio::log.error "Invalid VPN type specified. Use 'wireguard' or 'openvpn'."
bashio::exit.nok 'VPN start failed.'
fi