mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-02-05 19:34:52 +01:00
vpn service script improvements & fixes
simplified logging + notify user on the VPN behavior improved DNS servers handling + IPv6 defaults added Wireguard persistent-keepalive option fixed some execution bugg + typos
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# shellcheck shell=bas
|
||||
# shellcheck shell=bash
|
||||
|
||||
# --- WireGuard Specific Logic ---
|
||||
|
||||
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=()
|
||||
|
||||
_parse_config() {
|
||||
local -n config_ref="$1"
|
||||
local config_file="$2"
|
||||
@@ -25,27 +28,42 @@ _parse_config() {
|
||||
}
|
||||
|
||||
_parse_dns() {
|
||||
local -a dns_servers=()
|
||||
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")
|
||||
|
||||
while IFS=',' read -r dns_ip; do
|
||||
if _is_ip_address "${dns_ip}"; then
|
||||
bashio::log.warning "Ignoring invalid DNS server address: ${dns_ip}"
|
||||
continue
|
||||
IFS=',' read -ra dns_conf <<< $(bashio::config 'DNS_server' | tr -d ' ')
|
||||
if [ ${config["IPv4Enabled"]} = "true" ]; then
|
||||
for dns_ip in "${dns_conf[@]}"; do
|
||||
_is_ip_address "${dns_ip}"
|
||||
local is_ip=$?
|
||||
if [ "${is_ip}" -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
|
||||
_is_ip_address "${dns_ip}"
|
||||
local is_ip=$?
|
||||
if [ "${is_ip}" -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
|
||||
dns_servers+=("${dns_ip}")
|
||||
done <<< $(bashio::config 'DNS_server' | tr -d ' ')
|
||||
|
||||
if [ ${#dns_servers[@]} -eq 0 ]; then
|
||||
bashio::log.warning "No valid DNS servers configured. Using addon defaults."
|
||||
dns_servers=("8.8.8.8" "1.1.1.1")
|
||||
fi
|
||||
config["DnsServers"]="${dns_servers[*]}"
|
||||
}
|
||||
|
||||
_cmd() {
|
||||
cmd="$1"
|
||||
bashio::log.info "Executing command: ${cmd}"
|
||||
bashio::log.debug "Executing command: ${cmd}"
|
||||
eval "${cmd}"
|
||||
}
|
||||
|
||||
@@ -72,32 +90,19 @@ _resolvconf() {
|
||||
bashio::log.warning "No original resolv.conf backup found. Leaving as is."
|
||||
fi
|
||||
elif [ "${mode}" = "update" ]; then
|
||||
bashio::log.info "Updating ${resolv_conf} with DNS servers: ${config["DnsServers"]}"
|
||||
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.warn "Overriding ${resolv_conf} with DNS servers: ${config["DnsServers"]}"
|
||||
local valid_dns="false"
|
||||
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 ${config["DnsServers"]}; do
|
||||
_is_ip_address "${dns_ip}"
|
||||
local is_ip=$?
|
||||
if [ "${is_ip}" -eq 1 ] && [ ${config["IPv4Enabled"]} = "true" ]; then
|
||||
echo "nameserver ${dns_ip}"
|
||||
valid_dns="true"
|
||||
elif [ "${is_ip}" -eq 2 ] && [ "${config["IPv6Enabled"]}" = "true" ]; then
|
||||
echo "nameserver ${dns_ip}"
|
||||
valid_dns="true"
|
||||
else
|
||||
bashio::log.warning "Ignoring invalid DNS server address: ${dns_ip}"
|
||||
continue
|
||||
fi
|
||||
for dns_ip in ${dns_servers_ipv4[@]} ${dns_servers_ipv6[@]}; do
|
||||
echo "nameserver ${dns_ip}"
|
||||
done
|
||||
} > "${resolv_conf}"
|
||||
if [ "${valid_dns}" = "false" ]; then
|
||||
bashio::exit.nok "No valid DNS servers could be written to ${resolv_conf}."
|
||||
fi
|
||||
else
|
||||
bashio::exit.nok "Invalid resolvconf mode specified. Use 'update' or 'reset'."
|
||||
fi
|
||||
@@ -127,9 +132,14 @@ _resolve_hostname() {
|
||||
}
|
||||
|
||||
_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, ipv6
|
||||
local ipv4
|
||||
local ipv6
|
||||
|
||||
# add routing rules for local IPs
|
||||
for ipv4 in ${local_ipv4}; do
|
||||
config["IPv4Enabled"]="true"
|
||||
_cmd "ip -4 route add default dev ${config["Interface"]} table ${config["Table"]}" || return 1
|
||||
@@ -141,27 +151,26 @@ _routing_add() {
|
||||
_cmd "ip -6 rule add priority 1 from ${ipv6} table ${config["Table"]}" || return 1
|
||||
done
|
||||
|
||||
local dns_ip
|
||||
for dns_ip in ${config["DnsServers"]}; do
|
||||
_is_ip_address "${dns_ip}"
|
||||
local is_ip=$?
|
||||
if [ "${is_ip}" -eq 0 ]; then
|
||||
bashio::log.warning "Ignoring invalid DNS server address: ${dns_ip}"
|
||||
continue
|
||||
elif [ "${is_ip}" -eq 1 ] && [ ${config["IPv4Enabled"]} = "true" ]; then
|
||||
#_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
|
||||
elif [ "${is_ip}" -eq 2 ] && [ "${config["IPv6Enabled"]}" = "true" ]; then
|
||||
#_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
|
||||
else
|
||||
bashio::log.warning "Failed to add route for DNS server: ${dns_ip}"
|
||||
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
|
||||
|
||||
# Update resolv.conf with VPN DNS servers
|
||||
_resolvconf "update"
|
||||
}
|
||||
|
||||
_routing_del() {
|
||||
bashio::log.info "Removing routing rules for VPN interface ${config["Interface"]}..."
|
||||
|
||||
_resolvconf "reset"
|
||||
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
|
||||
@@ -172,14 +181,19 @@ _routing_del() {
|
||||
}
|
||||
|
||||
_wireguard_up() {
|
||||
bashio::log.warn "Bringing up Wireguard interface on ${config["Interface"]}..."
|
||||
bashio::log.warn "Using Wireguard configuration file: ${config["ConfigFile"]}"
|
||||
bashio::log.warn "This script force Wireguard to ignore any routes and DNS settings."
|
||||
bashio::log.warn "Default route will be inserted into custom routing table: ${config["Table"]}"
|
||||
bashio::log.warn "This routing table will be used for traffic from the VPN interface and to configured DNS servers."
|
||||
bashio::log.warn "Qbittorrent bittorrent client shall be set to use the VPN interface ${config["Interface"]} only."
|
||||
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."
|
||||
|
||||
_cmd "ip link add dev ${config["Interface"]} type wireguard" || return 1
|
||||
for key in "Interface" "ListenPort" "PrivateKey" "PublicKey" "EndpointIP" "EndpointPort" ; 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
|
||||
local allowed_ips=""
|
||||
for local_ip in ${config["Address"]}; do
|
||||
_is_ip_address "${local_ip}"
|
||||
@@ -199,10 +213,12 @@ _wireguard_up() {
|
||||
bashio::log.error "No valid local IP addresses configured."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_cmd "wg set ${config["Interface"]} listen-port ${config["ListenPort"]} private-key ${config["PrivateKey"]}" || return 1
|
||||
_cmd "wg set ${config["Interface"]} peer ${config["PublicKey"]} endpoint ${config["EndpointIP"]}:${config["EndpointPort"]} allowed-ips ${allowed_ips}" || return 1
|
||||
if [ -n "${config["PersistentKeepalive"]}" ]; then
|
||||
_cmd "wg set ${config["Interface"]} peer ${config["PublicKey"]} persistent-keepalive ${config["PersistentKeepalive"]}" || return 1
|
||||
if [ -v config["PersistentKeepalive"] ] && [ -n "${config["PersistentKeepalive"]}" ]; then
|
||||
_cmd "wg set ${config["Interface"]} peer ${config["PublicKey"]} endpoint ${config["EndpointIP"]}:${config["EndpointPort"]} allowed-ips ${allowed_ips} persistent-keepalive ${config["PersistentKeepalive"]}" || return 1
|
||||
else
|
||||
_cmd "wg set ${config["Interface"]} peer ${config["PublicKey"]} endpoint ${config["EndpointIP"]}:${config["EndpointPort"]} allowed-ips ${allowed_ips}" || return 1
|
||||
fi
|
||||
_cmd "ip link set ${config["Interface"]} up" || return 1
|
||||
_routing_add
|
||||
@@ -211,7 +227,7 @@ _wireguard_up() {
|
||||
_wireguard_down() {
|
||||
_routing_del
|
||||
_cmd "ip link set ${config["Interface"]} down" 2>/dev/null || true
|
||||
_cmd "ip link del dev ${config["Interface"]}" 2>/dev/null || true
|
||||
_cmd "ip link del ${config["Interface"]}" 2>/dev/null || true
|
||||
}
|
||||
|
||||
wireguard() {
|
||||
@@ -235,6 +251,8 @@ wireguard() {
|
||||
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}"
|
||||
@@ -252,7 +270,7 @@ wireguard() {
|
||||
config["PrivateKey"]="${WIREGUARD_STATE_DIR}/privatekey"
|
||||
|
||||
if [ "${mode}" = "up" ]; then
|
||||
bashio::log.info "Starting WireGuard interface ${config["Interface"]}..."
|
||||
bashio::log.info "Starting WireGuard on interface ${config["Interface"]}..."
|
||||
if _is_ip_address ${config["EndpointHost"]}; then
|
||||
local endpoint_ips=$(_resolve_hostname ${config["EndpointHost"]})
|
||||
if [ ${#endpoint_ips[@]} -eq 0 ]; then
|
||||
@@ -270,7 +288,7 @@ wireguard() {
|
||||
_wireguard_down
|
||||
done
|
||||
else
|
||||
bashio::log.info "WireGuard endpoint ${config["EndpointHost"]} is a valid IP address. Using as is."
|
||||
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."
|
||||
@@ -280,9 +298,9 @@ wireguard() {
|
||||
_wireguard_down
|
||||
fi
|
||||
elif [ "${mode}" = "down" ]; then
|
||||
bashio::log.info "Stopping WireGuard interface ${config["Interface"]}..."
|
||||
bashio::log.info "Stopping WireGuard on interface ${config["Interface"]}..."
|
||||
_wireguard_down
|
||||
bashio::log.info "WireGuard interface ${config["Interface"]} is 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'."
|
||||
@@ -293,12 +311,10 @@ wireguard() {
|
||||
}
|
||||
|
||||
_openvpn_up() {
|
||||
bashio::log.warn "Bringing up OpenVPN interface on ${config["Interface"]}..."
|
||||
bashio::log.warn "Using OpenVPN configuration file: ${config["ConfigFile"]}"
|
||||
bashio::log.warn "This script force OpenvPN to ignore any routes and DNS settings pushed by the server."
|
||||
bashio::log.warn "Default route will be inserted into custom routing table: ${config["Table"]}"
|
||||
bashio::log.warn "This routing table will be used for traffic from the VPN interface and to configured DNS servers."
|
||||
bashio::log.warn "Qbittorrent bittorrent client shall be set to use the VPN interface ${config["Interface"]} only."
|
||||
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 "${config["MySelf"]} openvpn postup" > ${config["PostUpScript"]}
|
||||
@@ -350,6 +366,8 @@ openvpn() {
|
||||
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}"
|
||||
@@ -359,11 +377,11 @@ openvpn() {
|
||||
|
||||
if [ "${mode}" = "up" ]; then
|
||||
# register up and down scripts
|
||||
bashio::log.info "Starting OpenVPN with configuration file: ${config_file}..."
|
||||
bashio::log.info "Starting OpenVPN on interface ${config["Interface"]}..."
|
||||
_openvpn_up
|
||||
bashio::exit.ok 'OpenVPN started.'
|
||||
elif [ "${mode}" = "down" ]; then
|
||||
bashio::log.info "Stopping OpenVPN..."
|
||||
bashio::log.info "Stopping OpenVPN on interface ${config["Interface"]}..."
|
||||
_openvpn_down
|
||||
bashio::exit.ok 'OpenVPN stopped.'
|
||||
elif [ "${mode}" = "postup" ]; then
|
||||
@@ -382,7 +400,6 @@ 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
|
||||
_parse_dns
|
||||
if [[ "$1" == "wireguard" ]]; then
|
||||
wireguard "$2"
|
||||
elif [[ "$1" == "openvpn" ]]; then
|
||||
|
||||
Reference in New Issue
Block a user