Compare commits

...

52 Commits

Author SHA1 Message Date
github-actions
9d55fccd03 GitHub bot: changelog 2026-02-05 07:15:45 +00:00
Alexandre
27a1a4db7d Update version to 2.39.0-2 in config.yaml 2026-02-05 08:08:07 +01:00
Alexandre
f78a519ed6 Merge pull request #2447 from alexbelgium/codex/fix-failing-build-on-github-actions
Fix browserless_chrome Dockerfile RUN syntax
2026-02-05 08:07:09 +01:00
Alexandre
9b0ac87798 Fix browserless_chrome Dockerfile RUN syntax 2026-02-05 08:06:52 +01:00
GitHub Actions
a0de882aae Revert 'Update version to 2.39.0-2 in config.yaml' [nobuild] 2026-02-05 06:42:51 +00:00
github-actions
396a804433 GitHub bot: changelog 2026-02-05 06:38:36 +00:00
Alexandre
c186bcf653 Update version to 2.39.0-2 in config.yaml 2026-02-05 07:37:51 +01:00
Alexandre
a36fe30a74 Merge pull request #2399 from litinoveweedle/qbittorrent_vpn_overhaul
Qbittorrent vpn overhaul
2026-02-05 07:32:46 +01:00
Alexandre
a70f6beb12 Update qBittorrent version to 5.1.4-7 2026-02-05 07:32:20 +01:00
Alexandre
2c4003448b Merge pull request #2437 from enter360/doc-update-birdnet-go
add common issues for unifi cameras
2026-02-05 07:29:22 +01:00
litinoveweedle
281dfea1b5 add hassio logging level 2026-02-04 21:34:32 +01:00
litinoveweedle
30123292aa implement waiting for opevpn interface to come up 2026-02-04 19:33:05 +01:00
litinoveweedle
6759f66a8a fix openvpn options 2026-02-04 19:14:06 +01:00
litinoveweedle
6aa9613a90 Merge branch 'qbittorrent_vpn_overhaul' of https://github.com/litinoveweedle/alexbelgium-hassio-addons into qbittorrent_vpn_overhaul 2026-02-04 16:20:54 +01:00
litinoveweedle
c6104f5a82 fix vpn check script logic 2026-02-04 16:17:49 +01:00
Alexandre
6c40e39e8f Add changelog for improved vpn script with proper recognition
Enhanced the openvpn and wireguard scripts for improved robustness, security, and compatibility.
2026-02-04 15:57:44 +01:00
litinoveweedle
3e8add95ba remove unused /etc/openvpn directory 2026-02-04 15:19:28 +01:00
litinoveweedle
0fca3e1b5f Merge branch 'qbittorrent_vpn_overhaul' of https://github.com/litinoveweedle/alexbelgium-hassio-addons into qbittorrent_vpn_overhaul 2026-02-04 09:26:52 +01:00
litinoveweedle
76a6a64965 Check for wireguard_config option to avoid assigning string "null" to variable 2026-02-04 09:26:48 +01:00
Alexandre
b04ac63ce7 Check for openvpn_config option to avoid assigning string "null" to variable 2026-02-04 09:19:26 +01:00
Alexandre
9cb4883240 Add execute permission to vpn script 2026-02-04 08:56:17 +01:00
litinoveweedle
5daf3c240f config selection logic fix 2026-02-03 23:16:56 +01:00
litinoveweedle
81287577a2 remove hopefully unnecessary image packages 2026-02-03 23:05:57 +01:00
litinoveweedle
ec4b5417ea fix DNS_server parsing 2026-02-03 22:58:12 +01:00
litinoveweedle
5ac54f5d1c Merge branch 'qbittorrent_vpn_overhaul' of https://github.com/litinoveweedle/alexbelgium-hassio-addons into qbittorrent_vpn_overhaul 2026-02-03 22:38:08 +01:00
litinoveweedle
5647aac528 multiple bug fixes
fix script premature termination as bashio set -e?
fix parsing of the DNS servers
improve ip/host checks
2026-02-03 22:36:46 +01:00
Alexandre
60cb4c9c26 Update vpn 2026-02-03 21:59:11 +01:00
Alexandre
6aec6385ab Merge branch 'master' into qbittorrent_vpn_overhaul 2026-02-03 21:46:09 +01:00
Alexandre
c69440da1a Fix sed command for OpenVPN runtime config 2026-02-03 21:31:19 +01:00
litinoveweedle
8c5171b027 removed host dependency 2026-02-03 21:09:17 +01:00
litinoveweedle
9789b9dab6 added comments 2026-02-03 17:42:40 +01:00
litinoveweedle
333116c8fc improve endpoint hostname validation 2026-02-03 17:34:48 +01:00
litinoveweedle
2e0327fe87 fixes
replaced IFS by mapfile where relevant
2026-02-03 13:00:32 +01:00
Corbin Black
11026d710c add common issues for unifi cameras 2026-02-02 23:42:50 -06:00
litinoveweedle
699400ad40 the real fix to fix vpn script permissions ;-) 2026-01-29 17:21:34 +01:00
litinoveweedle
bfdbd1e289 fixed vpn script execute permissions 2026-01-29 17:18:20 +01:00
litinoveweedle
38302a8428 fix opevpn config path in log message 2026-01-29 17:08:40 +01:00
litinoveweedle
092bf0ab46 fixed find of openvpn configs 2026-01-29 17:04:31 +01:00
litinoveweedle
ffaec60c47 correct check for bashio fs.directory_exists 2026-01-29 16:51:57 +01:00
Alexandre
10b743e6fd Avoid wireguard conf validation message 2026-01-28 14:15:11 +01:00
Alexandre
a82cb73634 No more bin folder 2026-01-28 13:56:39 +01:00
litinoveweedle
22dc8a8351 overhaul of the cont-init vpn scripts 2026-01-27 12:17:30 +01:00
litinoveweedle
80f7efb013 overhaul cont-init vpn scripts 2026-01-27 12:15:16 +01:00
litinoveweedle
b001f1ebca Add wireguard MTU option
+ fix OpenVPN suffix to both conf and ovn
2026-01-26 20:24:29 +01:00
litinoveweedle
af403e5eae fix OpenVPN config suffix 2026-01-26 18:26:14 +01:00
litinoveweedle
3559f52e84 Merge branch 'alexbelgium:master' into qbittorrent_vpn_overhaul 2026-01-26 18:12:25 +01:00
litinoveweedle
38c8929605 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
2026-01-26 18:11:13 +01:00
litinoveweedle
7e0281bc21 remove unnecessary OpeVPN scripts 2026-01-25 17:40:17 +01:00
litinoveweedle
b120e4bcd7 Merge branch 'qbittorrent_vpn_overhaul' of https://github.com/litinoveweedle/alexbelgium-hassio-addons into qbittorrent_vpn_overhaul 2026-01-25 17:24:15 +01:00
litinoveweedle
1a0df6c5c2 improve dns servers handling
fixed routing rule addresses
2026-01-25 17:23:59 +01:00
litinoveweedle
afec1850ba Merge branch 'alexbelgium:master' into qbittorrent_vpn_overhaul 2026-01-25 09:23:47 +01:00
litinoveweedle
47a43c82b4 initial VPN logic implementation
both Wireguard and OpenVPN are  now handled by single service file
This remove dependency to external tools
Simplify vpn routing by using dedicated routing table which is used by qbittorrent torrent client listenning directly on the vpn interface.
To prevent DNS leeks traffic to addon configured DNS servers is forced to use same dedicated routing table
2026-01-25 09:21:49 +01:00
21 changed files with 656 additions and 831 deletions

View File

@@ -1,15 +1,27 @@
{
"name": "Example devcontainer for add-on repositories",
"image": "ghcr.io/home-assistant/devcontainer:addons",
"appPort": ["7123:8123", "7357:4357"],
"postStartCommand": "sudo -E bash devcontainer_bootstrap",
"runArgs": ["-e", "GIT_EDITOR=code --wait", "--privileged"],
"image": "ghcr.io/home-assistant/devcontainer:2-addons",
"appPort": [
"7123:8123",
"7357:4357"
],
"postStartCommand": "bash devcontainer_bootstrap",
"runArgs": [
"-e",
"GIT_EDITOR=code --wait",
"--privileged"
],
"workspaceFolder": "/mnt/supervisor/addons/local/${localWorkspaceFolderBasename}",
"workspaceMount": "source=${localWorkspaceFolder},target=${containerWorkspaceFolder},type=bind,consistency=cached",
"containerEnv": {
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
},
"customizations": {
"vscode": {
"extensions": ["timonwong.shellcheck", "esbenp.prettier-vscode"],
"extensions": [
"timonwong.shellcheck",
"esbenp.prettier-vscode"
],
"settings": {
"terminal.integrated.profiles.linux": {
"zsh": {
@@ -24,5 +36,8 @@
}
}
},
"mounts": [ "type=volume,target=/var/lib/docker" ]
}
"mounts": [
"type=volume,target=/var/lib/docker",
"type=volume,target=/mnt/supervisor"
]
}

4
.vscode/tasks.json vendored
View File

@@ -4,7 +4,7 @@
{
"label": "Start Home Assistant",
"type": "shell",
"command": "sudo chmod a+x /usr/bin/supervisor* && sudo -E supervisor_run",
"command": "supervisor_run",
"group": {
"kind": "test",
"isDefault": true
@@ -16,4 +16,4 @@
"problemMatcher": []
}
]
}
}

View File

@@ -74,10 +74,8 @@ cifsdomain: "domain" # optional, allow setting the domain for the smb share
```
- Config.yaml
Additional variables can be configured using the config.yaml file found in /config/db21ed7f_battybirdnet-pi/config.yaml using the Filebrowser addon
Additional variables can be configured in `/config/db21ed7f_battybirdnet-pi/config.yaml` using the File Browser add-on.
- Config_env.yaml
Additional environment variables can be configured there
### Mounting Drives
@@ -169,7 +167,13 @@ Should be avoided as the model uses the whole 0-15khz range
## Common issues
Not yet available
### Unifi Cameras RTSP
Unifi Cameras default issue RTSPS links.These must be updated to be standard RTSP format. `rstp://<CAMERA_IP>:7447/<TOKEN>`.
### UDP Update
If a newly added camera does not report healthy, set the transport configuration in `config.yaml` to `udp`, then restart the add-on.
## Support
@@ -178,4 +182,3 @@ Create an issue on github
---

View File

@@ -1,3 +1,5 @@
## 2.39.0-2 (05-02-2026)
- Minor bugs fixed
## 2.39.0 (2026-02-04)
- Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases)

View File

@@ -70,7 +70,7 @@ RUN chmod 777 /ha_entrypoint.sh
ADD "https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.templates/bashio-standalone.sh" "/usr/local/lib/bashio-standalone.sh"
RUN chmod 0755 /usr/local/lib/bashio-standalone.sh
RUN \ sed -i "2a /./ha_entrypoint.sh" ./scripts/start.sh
RUN sed -i "2a /./ha_entrypoint.sh" ./scripts/start.sh
############
# 5 Labels #

View File

@@ -86,5 +86,5 @@ schema:
slug: browserless_chrome
udev: true
url: https://github.com/alexbelgium/hassio-addons/tree/master/browserless_chrome
version: "2.39.0"
version: "2.39.0-2"
webui: "[PROTO:ssl]://[HOST]:[PORT:3000]/docs"

View File

@@ -1,3 +1,6 @@
## 5.1.4-7 (05-02-2026)
- Rewrite the openvpn and wireguard scripts in order to make them more robust, secure, and compatible with more suppliers @litinoveweedle
## 5.1.4-6 (03-02-2026)
- Minor bugs fixed
## 5.1.4-5 (23-01-2026)

View File

@@ -92,6 +92,7 @@ RUN \
# Copy local files
COPY rootfs/ /
RUN find /etc -type f \( -name "*.sh" -o -name "run" -o -name "finish" \) -exec chmod +x {} +
RUN chmod +x /usr/local/sbin/vpn
# Uses /bin for compatibility purposes
# hadolint ignore=DL4005
@@ -112,7 +113,7 @@ RUN chmod 744 /ha_automodules.sh && /ha_automodules.sh "$MODULES" && rm /ha_auto
# && chmod a+x /etc/s6-overlay/s6-rc.d/$SCRIPTSNAME/* ; done; fi
# Manual apps
ARG PACKAGES="wireguard-tools iptables ip6tables iptables-legacy"
ARG PACKAGES="ipcalc wireguard-tools"
# Automatic apps & bashio
ADD "https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.templates/ha_autoapps.sh" "/ha_autoapps.sh"
@@ -126,8 +127,6 @@ RUN chmod 744 /ha_autoapps.sh && /ha_autoapps.sh "$PACKAGES" && rm /ha_autoapps.
# 4 Entrypoint #
################
RUN chmod +x /usr/local/bin/* /usr/local/sbin/*
# Add entrypoint
ENV S6_STAGE2_HOOK=/ha_entrypoint.sh
ADD "https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.templates/ha_entrypoint.sh" "/ha_entrypoint.sh"

View File

@@ -79,6 +79,7 @@ map:
- ssl
name: qBittorrent
options:
log_level: info
env_vars: []
DNS_server: 8.8.8.8,1.1.1.1
PGID: "0"
@@ -88,10 +89,7 @@ options:
certfile: fullchain.pem
customUI: vuetorrent
keyfile: privkey.pem
qbit_manage: false
ssl: false
wireguard_enabled: false
wireguard_config: ""
whitelist: localhost,127.0.0.1,172.30.0.0/16,192.168.0.0/16
panel_admin: false
panel_icon: mdi:progress-download
@@ -112,6 +110,7 @@ privileged:
- DAC_READ_SEARCH
- NET_ADMIN
schema:
log_level: list(trace|debug|info|notice|warning|error|fatal)?
env_vars:
- name: match(^[A-Za-z0-9_]+$)
value: str?
@@ -129,8 +128,7 @@ schema:
keyfile: str
localdisks: str?
networkdisks: str?
openvpn_alt_mode: bool?
openvpn_config: str?
openvpn_config: match(^\w+\.(ovpn|conf)$)?
openvpn_enabled: bool?
openvpn_password: str?
openvpn_username: str?
@@ -138,10 +136,10 @@ schema:
run_duration: str?
silent: bool?
ssl: bool
wireguard_config: str?
wireguard_config: match(^\w+\.conf$)?
wireguard_enabled: bool?
whitelist: str?
slug: qbittorrent
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: "5.1.4-6"
version: "5.1.4-7"

View File

@@ -2,269 +2,112 @@
# shellcheck shell=bash
set -e
declare openvpn_config
OPENVPN_STATE_DIR="/var/run/openvpn"
QBT_CONFIG_FILE="/config/qBittorrent/qBittorrent.conf"
declare openvpn_config=""
declare openvpn_runtime_config=""
declare interface_name=""
declare openvpn_username
declare openvpn_password
QBT_CONFIG_FILE="/config/qBittorrent/qBittorrent.conf"
if bashio::config.true 'openvpn_enabled'; then
bashio::log.info "----------------------------"
bashio::log.info "Openvpn enabled, configuring"
bashio::log.info "----------------------------"
# Function to check for files path
function check_path() {
# Get variable
file="$1"
# Double check exists
if [ ! -f "$file" ]; then
bashio::warning "$file not found"
return 1
fi
# Check each lines
cp "$file" /tmpfile
line_number=0
while read -r line; do
# Increment the line number
((line_number = line_number + 1))
# Check if lines starting with auth-user-pass have a valid argument
###################################################################
if [[ "$line" == "auth-user-pass"* ]]; then
# Extract the second argument
file_name="$(echo "$line" | awk -F' ' '{print $2}')"
# If second argument is null or -
if [ -z "$file_name" ] || [[ "$file_name" == -* ]]; then
# Insert to explain why a comment is made
sed -i "${line_number}i # The following line is commented out as does not contain a valid argument" "$file"
# Increment as new line added
((line_number = line_number + 1))
# Comment out the line
sed -i "${line_number}s/^/# /" "$file"
# Go to next line
continue
fi
fi
# Check if the line contains a txt file
#######################################
if [[ ! $line =~ ^"#" ]] && [[ ! $line =~ ^";" ]] && [[ ! $line =~ ^"remote" ]] && [[ "$line" == *" "*"."* ]] || [[ "$line" == "auth-user-pass"* ]]; then
# Extract the txt file name from the line
file_name="$(echo "$line" | awk -F' ' '{print $2}')"
# if contains only numbers and dots it is likely an ip, don't check it
if [[ "$file_name" =~ ^[0-9\.]+$ ]]; then
continue
fi
# Check if the txt file exists
if [[ "$file_name" != *"/etc/openvpn/credentials"* ]] && [ ! -f "$file_name" ]; then
# Check if the txt file exists in the /config/openvpn/ directory
if [ -f "/config/openvpn/${file_name##*/}" ]; then
# Append /config/openvpn/ in front of the original txt file in the ovpn file
sed -i "${line_number}s|$file_name|/config/openvpn/${file_name##*/}|" "$file"
# Print a success message
bashio::log.warning "Appended /config/openvpn/ to ${file_name##*/} in $file"
else
# Print an error message
bashio::log.warning "$file_name is referenced in your ovpn file but does not exist, and can't be found either in the /config/openvpn/ directory"
fi
fi
fi
done < /tmpfile
rm /tmpfile
# Standardize lf
dos2unix "$file"
# Remove custom up & down
sed -i '/^up /s/^/#/' "$file"
sed -i '/^down /s/^/#/' "$file"
# Remove blank lines
sed -i '/^[[:blank:]]*$/d' "$file"
# Ensure config ends with a line feed
sed -i "\$q" "$file"
# Correct paths
sed -i "s=/etc/openvpn=/config/openvpn=g" "$file"
sed -i "s=/config/openvpn/credentials=/etc/openvpn/credentials=g" "$file"
}
#####################
# CONFIGURE OPENVPN #
#####################
# If openvpn_config option used
if bashio::config.has_value "openvpn_config"; then
openvpn_config=$(bashio::config 'openvpn_config')
# If file found
if [ -f /config/openvpn/"$openvpn_config" ]; then
# If correct type
if [[ "$openvpn_config" == *".ovpn" ]] || [[ "$openvpn_config" == *".conf" ]]; then
echo "... configured ovpn file : using /addon_configs/$HOSTNAME/openvpn/$openvpn_config"
else
bashio::exit.nok "Configured ovpn file : $openvpn_config is set but does not end by .ovpn ; it can't be used!"
fi
else
bashio::exit.nok "Configured ovpn file : $openvpn_config not found! Are you sure you added it in /addon_configs/$HOSTNAME/openvpn using the Filebrowser addon ?"
fi
# If openvpn_config not set, but folder is not empty
elif ls /config/openvpn/*.ovpn > /dev/null 2>&1; then
# Look for openvpn files
# Wildcard search for openvpn config files and store results in array
mapfile -t VPN_CONFIGS < <(find /config/openvpn -maxdepth 1 -name "*.ovpn" -print)
# Choose random config
VPN_CONFIG="${VPN_CONFIGS[$RANDOM % ${#VPN_CONFIGS[@]}]}"
# Get the VPN_CONFIG name without the path and extension
openvpn_config="${VPN_CONFIG##*/}"
echo "... Openvpn enabled, but openvpn_config option empty. Selecting a random ovpn file : ${openvpn_config}. Other available files :"
printf '%s\n' "${VPN_CONFIGS[@]}"
# If openvpn_enabled set, config not set, and openvpn folder empty
else
bashio::exit.nok "openvpn_enabled is set, however, your openvpn folder is empty ! Are you sure you added it in /addon_configs/$HOSTNAME/openvpn using the Filebrowser addon ?"
fi
# Send to openvpn script
sed -i "s|/config/openvpn/config.ovpn|/config/openvpn/$openvpn_config|g" /etc/s6-overlay/s6-rc.d/svc-qbittorrent/run
# Check path
check_path /config/openvpn/"${openvpn_config}"
# Set credentials
if bashio::config.has_value "openvpn_username"; then
openvpn_username=$(bashio::config 'openvpn_username')
echo "${openvpn_username}" > /etc/openvpn/credentials
else
bashio::exit.nok "Openvpn is enabled, but openvpn_username option is empty! Exiting"
fi
if bashio::config.has_value "openvpn_password"; then
openvpn_password=$(bashio::config 'openvpn_password')
echo "${openvpn_password}" >> /etc/openvpn/credentials
else
bashio::exit.nok "Openvpn is enabled, but openvpn_password option is empty! Exiting"
fi
# Add credentials file
if grep -q ^auth-user-pass /config/openvpn/"$openvpn_config"; then
# Credentials specified are they custom ?
file_name="$(sed -n "/^auth-user-pass/p" /config/openvpn/"$openvpn_config" | awk -F' ' '{print $2}')"
file_name="${file_name:-null}"
if [[ "$file_name" != *"/etc/openvpn/credentials"* ]] && [[ "$file_name" != "null" ]]; then
if [ -f "$file_name" ]; then
# If credential specified, exists, and is not the addon default
bashio::log.warning "auth-user-pass specified in the ovpn file, addon username and passwords won't be used !"
else
# Credential referenced but doesn't exist
bashio::log.warning "auth-user-pass $file_name is referenced in your ovpn file but does not exist, and can't be found either in the /config/openvpn/ directory. The addon will attempt to use it's own username and password instead."
# Comment previous lines
sed -i '/^auth-user-pass/i # specified auth-user-pass file not found, disabling' /config/openvpn/"$openvpn_config"
sed -i '/^auth-user-pass/s/^/#/' /config/openvpn/"$openvpn_config"
# No credentials specified, using addons username and password
echo "# Please do not remove the line below, it allows using the addon username and password" >> /config/openvpn/"$openvpn_config"
echo "auth-user-pass /etc/openvpn/credentials" >> /etc/openvpn/"$openvpn_config"
fi
else
# Standardize just to be sure
sed -i "/\/etc\/openvpn\/credentials/c auth-user-pass \/etc\/openvpn\/credentials" /config/openvpn/"$openvpn_config"
fi
else
# No credentials specified, using addons username and password
echo "# Please do not remove the line below, it allows using the addon username and password" >> /config/openvpn/"$openvpn_config"
echo "auth-user-pass /etc/openvpn/credentials" >> /config/openvpn/"$openvpn_config"
fi
# Permissions
chmod 755 /config/openvpn/*
chmod 755 /etc/openvpn/*
chmod 600 /etc/openvpn/credentials
chmod 755 /etc/openvpn/up.sh
chmod 755 /etc/openvpn/down.sh
chmod 755 /etc/openvpn/up-qbittorrent.sh
chmod +x /etc/openvpn/up.sh
chmod +x /etc/openvpn/down.sh
chmod +x /etc/openvpn/up-qbittorrent.sh
echo "... openvpn correctly set, qbittorrent will run tunnelled through openvpn"
#########################
# CONFIGURE QBITTORRENT #
#########################
# WITH CONTAINER BINDING
#########################
# If alternative mode enabled, bind container
if bashio::config.true 'openvpn_alt_mode'; then
echo "Using container binding"
# Remove interface
echo "... deleting previous interface settings"
sed -i '/Interface/d' "$QBT_CONFIG_FILE"
# Modify ovpn config
if grep -q route-nopull /config/openvpn/"$openvpn_config"; then
echo "... removing route-nopull from your config.ovpn"
sed -i '/route-nopull/d' /config/openvpn/"$openvpn_config"
fi
# Exit
exit 0
fi
# WITH INTERFACE BINDING
#########################
# Connection with interface binding
echo "Using interface binding in the qBittorrent app"
# Define preferences line
cd /config/qBittorrent/ || exit 1
# If qBittorrent.conf exists
if [ -f "$QBT_CONFIG_FILE" ]; then
# Remove previous line and bind tun0
echo "... deleting previous interface settings"
sed -i '/Interface/d' "$QBT_CONFIG_FILE"
# Bind tun0
echo "... binding tun0 interface in qBittorrent configuration"
sed -i "/\[Preferences\]/ i\Connection\\\Interface=tun0" "$QBT_CONFIG_FILE"
sed -i "/\[Preferences\]/ i\Connection\\\InterfaceName=tun0" "$QBT_CONFIG_FILE"
# Add to ongoing session
sed -i "/\[BitTorrent\]/a \Session\\\Interface=tun0" "$QBT_CONFIG_FILE"
sed -i "/\[BitTorrent\]/a \Session\\\InterfaceName=tun0" "$QBT_CONFIG_FILE"
else
bashio::log.error "qBittorrent config file doesn't exist, openvpn must be added manually to qbittorrent options "
exit 1
fi
# Modify ovpn config
if ! grep -q route-nopull /config/openvpn/"$openvpn_config"; then
echo "... adding route-nopull to your config.ovpn"
sed -i "1a route-nopull" /config/openvpn/"$openvpn_config"
fi
else
##################
# REMOVE OPENVPN #
##################
if ! bashio::config.true 'wireguard_enabled'; then
# Ensure no redirection by removing the direction tag when no VPN is used
if [ -f "$QBT_CONFIG_FILE" ]; then
sed -i '/Interface/d' "$QBT_CONFIG_FILE"
fi
bashio::log.info "Direct connection without VPN enabled"
else
bashio::log.info "OpenVPN disabled. WireGuard handling network binding."
fi
if bashio::fs.directory_exists "${OPENVPN_STATE_DIR}"; then
bashio::log.warning "Previous OpenVPN state directory found, cleaning up."
rm -Rf "${OPENVPN_STATE_DIR}"
fi
if ! bashio::config.true 'openvpn_enabled'; then
bashio::exit.ok 'OpenVPN is disabled.'
elif bashio::config.true 'wireguard_enabled'; then
bashio::exit.nok 'OpenVPN and WireGuard cannot be enabled simultaneously. Disable one of them.'
fi
mkdir -p "${OPENVPN_STATE_DIR}"
bashio::log.info "----------------------------"
bashio::log.info "Openvpn enabled, configuring"
bashio::log.info "----------------------------"
# Set credentials
if bashio::config.has_value "openvpn_username"; then
openvpn_username=$(bashio::config 'openvpn_username')
else
bashio::exit.nok "Openvpn is enabled, but openvpn_username option is empty! Exiting"
fi
if bashio::config.has_value "openvpn_password"; then
openvpn_password=$(bashio::config 'openvpn_password')
else
bashio::exit.nok "Openvpn is enabled, but openvpn_password option is empty! Exiting"
fi
echo -e "${openvpn_username}\n${openvpn_password}" > "${OPENVPN_STATE_DIR}/credentials.conf"
chmod 600 "${OPENVPN_STATE_DIR}/credentials.conf"
if bashio::config.has_value "openvpn_config"; then
openvpn_config="$(bashio::config 'openvpn_config')"
openvpn_config="${openvpn_config##*/}"
fi
if [[ -z "${openvpn_config}" ]]; then
bashio::log.info 'openvpn_config option left empty. Attempting automatic selection.'
mapfile -t configs < <(find /config/openvpn -maxdepth 1 \( -type f -name '*.conf' -o -name '*.ovpn' \) -print)
if [ "${#configs[@]}" -eq 0 ]; then
bashio::exit.nok 'OpenVPN is enabled but no .conf or .ovpn file was found in /config/openvpn.'
elif [ "${#configs[@]}" -eq 1 ]; then
openvpn_config="${configs[0]}"
bashio::log.info "OpenVPN configuration not specified. Using ${openvpn_config##*/}."
elif bashio::fs.file_exists '/config/openvpn/config.conf'; then
openvpn_config='/config/openvpn/config.conf'
bashio::log.info 'Using default OpenVPN configuration config.conf.'
else
bashio::exit.nok "Multiple OpenVPN configuration files detected. Please set the 'openvpn_config' option."
fi
elif bashio::fs.file_exists "/config/openvpn/${openvpn_config}"; then
openvpn_config="/config/openvpn/${openvpn_config}"
else
bashio::exit.nok "OpenVPN configuration '/config/openvpn/${openvpn_config}' not found."
fi
interface_name="$(sed -n "/^dev tun/p" "${openvpn_config}" | awk -F' ' '{print $2}')"
if [[ -z "${interface_name}" ]]; then
bashio::exit.nok "OpenVPN configuration '${openvpn_config}' misses device directive."
elif [[ ${interface_name} = "tun" ]]; then
interface_name='tun0'
elif [[ ${interface_name} = "tap" ]]; then
interface_name='tap0'
fi
openvpn_runtime_config="${OPENVPN_STATE_DIR}/${interface_name}.conf"
cp "${openvpn_config}" "${openvpn_runtime_config}"
chmod 600 "${openvpn_runtime_config}"
dos2unix "${openvpn_runtime_config}" >/dev/null 2>&1 || true
sed -i '/^[[:space:]]*[;#]/d' "${openvpn_runtime_config}"
sed -i 's/#.*//' "${openvpn_runtime_config}"
sed -i '/^[[:space:]]*$/d' "${openvpn_runtime_config}"
sed -i '/^[[:blank:]]*$/d' "${openvpn_runtime_config}"
sed -i '/^up/d' "${openvpn_runtime_config}"
sed -i '/^down/d' "${openvpn_runtime_config}"
sed -i '/^route/d' "${openvpn_runtime_config}"
sed -i '/^auth-user-pass /d' "${openvpn_runtime_config}"
sed -i '/^cd /d' "${openvpn_runtime_config}"
sed -i '/^chroot /d' "${openvpn_runtime_config}"
sed -i '$q' "${openvpn_runtime_config}"
bashio::log.info 'Prepared OpenVPN runtime configuration for initial connection attempt.'
echo "${openvpn_runtime_config}" > "${OPENVPN_STATE_DIR}/config"
echo "${interface_name}" > "${OPENVPN_STATE_DIR}/interface"
bashio::log.info "Using interface binding in the qBittorrent app"
if bashio::fs.file_exists "${QBT_CONFIG_FILE}"; then
sed -i '/Interface/d' "${QBT_CONFIG_FILE}"
sed -i "/\\[Preferences\\]/ i\\Connection\\\\Interface=${interface_name}" "${QBT_CONFIG_FILE}"
sed -i "/\\[Preferences\\]/ i\\Connection\\\\InterfaceName=${interface_name}" "${QBT_CONFIG_FILE}"
sed -i "/\\[BitTorrent\\]/a \\Session\\\\Interface=${interface_name}" "${QBT_CONFIG_FILE}"
sed -i "/\\[BitTorrent\\]/a \\Session\\\\InterfaceName=${interface_name}" "${QBT_CONFIG_FILE}"
else
bashio::log.warning "qBittorrent config file not found. Bind the client manually to interface ${interface_name}."
fi
bashio::log.info "OpenVPN prepared with interface ${interface_name} using configuration ${openvpn_config##*/}."

View File

@@ -6,37 +6,32 @@ WIREGUARD_STATE_DIR="/var/run/wireguard"
QBT_CONFIG_FILE="/config/qBittorrent/qBittorrent.conf"
declare wireguard_config=""
declare wireguard_runtime_config=""
declare configured_name
declare interface_name=""
mkdir -p "${WIREGUARD_STATE_DIR}"
if ! bashio::config.true 'wireguard_enabled'; then
rm -f "${WIREGUARD_STATE_DIR}/config" "${WIREGUARD_STATE_DIR}/interface"
exit 0
if bashio::fs.directory_exists "${WIREGUARD_STATE_DIR}"; then
bashio::log.warning "Previous WireGuard state directory found, cleaning up."
rm -Rf "${WIREGUARD_STATE_DIR}"
fi
if bashio::config.true 'openvpn_enabled'; then
if ! bashio::config.true 'wireguard_enabled'; then
bashio::exit.ok 'WireGuard is disabled.'
elif bashio::config.true 'openvpn_enabled'; then
bashio::exit.nok 'OpenVPN and WireGuard cannot be enabled simultaneously. Disable one of them.'
fi
if bashio::config.true 'openvpn_alt_mode'; then
bashio::log.warning 'The openvpn_alt_mode option is ignored when WireGuard is enabled.'
fi
mkdir -p "${WIREGUARD_STATE_DIR}"
if bashio::config.has_value 'wireguard_config'; then
configured_name="$(bashio::config 'wireguard_config')"
configured_name="${configured_name##*/}"
if [[ -z "${configured_name}" ]]; then
bashio::log.info 'wireguard_config option left empty. Attempting automatic selection.'
elif bashio::fs.file_exists "/config/wireguard/${configured_name}"; then
wireguard_config="/config/wireguard/${configured_name}"
else
bashio::exit.nok "WireGuard configuration '/config/wireguard/${configured_name}' not found."
fi
fi
bashio::log.info "------------------------------"
bashio::log.info "Wireguard enabled, configuring"
bashio::log.info "------------------------------"
if [ -z "${wireguard_config:-}" ]; then
mapfile -t configs < <(find /config/wireguard -maxdepth 1 -type f -name '*.conf' -print)
if bashio::config.has_value "wireguard_config"; then
wireguard_config="$(bashio::config 'wireguard_config')"
wireguard_config="${wireguard_config##*/}"
fi
if [[ -z "${wireguard_config}" ]]; then
bashio::log.info 'wireguard_config option left empty. Attempting automatic selection.'
mapfile -t configs < <(find /config/wireguard -maxdepth 1 -type f -name '*.conf' -print)
if [ "${#configs[@]}" -eq 0 ]; then
bashio::exit.nok 'WireGuard is enabled but no .conf file was found in /config/wireguard.'
elif [ "${#configs[@]}" -eq 1 ]; then
@@ -48,10 +43,12 @@ if [ -z "${wireguard_config:-}" ]; then
else
bashio::exit.nok "Multiple WireGuard configuration files detected. Please set the 'wireguard_config' option."
fi
elif bashio::fs.file_exists "/config/wireguard/${wireguard_config}"; then
wireguard_config="/config/wireguard/${wireguard_config}"
else
bashio::exit.nok "WireGuard configuration '/config/wireguard/${wireguard_config}' not found."
fi
dos2unix "${wireguard_config}" >/dev/null 2>&1 || true
interface_name="$(basename "${wireguard_config}" .conf)"
if [[ -z "${interface_name}" ]]; then
interface_name='wg0'
@@ -60,12 +57,26 @@ fi
wireguard_runtime_config="${WIREGUARD_STATE_DIR}/${interface_name}.conf"
cp "${wireguard_config}" "${wireguard_runtime_config}"
chmod 600 "${wireguard_runtime_config}" 2>/dev/null || true
chmod 600 "${wireguard_runtime_config}"
dos2unix "${wireguard_runtime_config}" >/dev/null 2>&1 || true
sed -i '/^[[:space:]]*[;#]/d' "${wireguard_runtime_config}"
sed -i 's/#.*//' "${wireguard_runtime_config}"
sed -i '/^[[:space:]]*$/d' "${wireguard_runtime_config}"
sed -i '/^[[:blank:]]*$/d' "${wireguard_runtime_config}"
sed -i '/DNS/d' "${wireguard_runtime_config}"
sed -i '/PostUp/d' "${wireguard_runtime_config}"
sed -i '/PostDown/d' "${wireguard_runtime_config}"
sed -i '/SaveConfig/d' "${wireguard_runtime_config}"
sed -i "\$q" "${wireguard_runtime_config}"
bashio::log.info 'Prepared WireGuard runtime configuration for initial connection attempt.'
echo "${wireguard_runtime_config}" > "${WIREGUARD_STATE_DIR}/config"
echo "${interface_name}" > "${WIREGUARD_STATE_DIR}/interface"
bashio::log.info "Using interface binding in the qBittorrent app"
if bashio::fs.file_exists "${QBT_CONFIG_FILE}"; then
sed -i '/Interface/d' "${QBT_CONFIG_FILE}"
sed -i "/\\[Preferences\\]/ i\\Connection\\\\Interface=${interface_name}" "${QBT_CONFIG_FILE}"

View File

@@ -1,36 +0,0 @@
#!/bin/sh
# shellcheck disable=SC2154,SC2004,SC2059,SC2086
# Copyright (c) 2006-2007 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# Contributed by Roy Marples (uberlord@gentoo.org)
# If we have a service specific script, run this now
if [ -x /etc/openvpn/"${RC_SVCNAME}"-down.sh ]; then
/etc/openvpn/"${RC_SVCNAME}"-down.sh "$@"
fi
# Restore resolv.conf to how it was
if [ "${PEER_DNS}" != "no" ]; then
if [ -x /sbin/resolvconf ]; then
/sbin/resolvconf -d "${dev}"
elif [ -e /etc/resolv.conf-"${dev}".sv ]; then
# Important that we cat instead of move incase resolv.conf is
# a symlink and not an actual file
cat /etc/resolv.conf-"${dev}".sv > /etc/resolv.conf
rm -f /etc/resolv.conf-"${dev}".sv
fi
fi
if [ -n "${RC_SVCNAME}" ]; then
# Re-enter the init script to start any dependant services
if /etc/init.d/"${RC_SVCNAME}" --quiet status; then
export IN_BACKGROUND=true
if [ -d /var/run/s6/container_environment ]; then printf "%s" "true" > /var/run/s6/container_environment/IN_BACKGROUND; fi
printf "%s\n" "IN_BACKGROUND=\"true\"" >> ~/.bashrc
/etc/init.d/"${RC_SVCNAME}" --quiet stop
fi
fi
exit 0
# vim: ts=4 :

View File

@@ -1,9 +0,0 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
set -e
WEBUI_PORT=${WEBUI_PORT:-8080}
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}"

View File

@@ -1,97 +0,0 @@
#!/bin/sh
# shellcheck disable=SC2154,SC2004,SC2059,SC2086
# launch qbittorrent
/etc/openvpn/up-qbittorrent.sh "${4}" &
# Copyright (c) 2006-2007 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# Contributed by Roy Marples (uberlord@gentoo.org)
# Setup our resolv.conf
# Vitally important that we use the domain entry in resolv.conf so we
# can setup the nameservers are for the domain ONLY in resolvconf if
# we're using a decent dns cache/forwarder like dnsmasq and NOT nscd/libc.
# nscd/libc users will get the VPN nameservers before their other ones
# and will use the first one that responds - maybe the LAN ones?
# non resolvconf users just the the VPN resolv.conf
# FIXME:- if we have >1 domain, then we have to use search :/
# We need to add a flag to resolvconf to say
# "these nameservers should only be used for the listed search domains
# if other global nameservers are present on other interfaces"
# This however, will break compatibility with Debians resolvconf
# A possible workaround would be to just list multiple domain lines
# and try and let resolvconf handle it
if [ "${PEER_DNS}" != "no" ]; then
NS=
DOMAIN=
SEARCH=
i=1
while true; do
eval opt=\$foreign_option_${i}
[ -z "${opt}" ] && break
if [ "${opt}" != "${opt#dhcp-option DOMAIN *}" ]; then
if [ -z "${DOMAIN}" ]; then
DOMAIN="${opt#dhcp-option DOMAIN *}"
else
SEARCH="${SEARCH}${SEARCH:+ }${opt#dhcp-option DOMAIN *}"
fi
elif [ "${opt}" != "${opt#dhcp-option DNS *}" ]; then
NS="${NS}nameserver ${opt#dhcp-option DNS *}\n"
fi
i=$((${i} + 1))
done
if [ -n "${NS}" ]; then
DNS="# Generated by openvpn for interface ${dev}\n"
if [ -n "${SEARCH}" ]; then
DNS="${DNS}search ${DOMAIN} ${SEARCH}\n"
elif [ -n "${DOMAIN}" ]; then
DNS="${DNS}domain ${DOMAIN}\n"
fi
DNS="${DNS}${NS}"
if [ -x /sbin/resolvconf ]; then
printf "${DNS}" | /sbin/resolvconf -a "${dev}"
else
# Preserve the existing resolv.conf
if [ -e /etc/resolv.conf ]; then
cp /etc/resolv.conf /etc/resolv.conf-"${dev}".sv
fi
printf "${DNS}" > /etc/resolv.conf
chmod 644 /etc/resolv.conf
fi
fi
fi
# Below section is Gentoo specific
# Quick summary - our init scripts are re-entrant and set the RC_SVCNAME env var
# as we could have >1 openvpn service
if [ -n "${RC_SVCNAME}" ]; then
# If we have a service specific script, run this now
if [ -x /etc/openvpn/"${RC_SVCNAME}"-up.sh ]; then
/etc/openvpn/"${RC_SVCNAME}"-up.sh "$@"
fi
# Re-enter the init script to start any dependant services
if ! /etc/init.d/"${RC_SVCNAME}" --quiet status; then
export IN_BACKGROUND=true
if [ -d /var/run/s6/container_environment ]; then printf "%s" "true" > /var/run/s6/container_environment/IN_BACKGROUND; fi
printf "%s\n" "IN_BACKGROUND=\"true\"" >> ~/.bashrc
/etc/init.d/${RC_SVCNAME} --quiet start
fi
fi
###############
# ALLOW WEBUI #
###############
ip route add 10.0.0.0/8 via 172.30.32.1
ip route add 192.168.0.0/16 via 172.30.32.1
ip route add 172.16.0.0/12 via 172.30.32.1
exit 0
# vim: ts=4 :

View File

@@ -10,125 +10,6 @@ if bashio::config.true 'silent'; then
sed -i 's|/proc/1/fd/1 hassio;|off;|g' /etc/nginx/nginx.conf
fi
# --- 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
# Allow sysctl failures on read-only hosts while keeping the interface up
if echo "${output}" | grep -qi 'net\.ipv4\.conf\.all\.src_valid_mark=1'; then
if echo "${output}" | grep -qiE 'read-only file system|operation not permitted'; then
if ip link show "${wireguard_interface}" >/dev/null 2>&1; then
bashio::log.warning 'WireGuard applied but sysctl net.ipv4.conf.all.src_valid_mark=1 could not be set (read-only). Continuing.'
status=0
return 0
fi
fi
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 ---
openvpn_enabled=false
@@ -148,24 +29,9 @@ if [[ "${openvpn_enabled}" == true && "${wireguard_enabled}" == true ]]; then
fi
if [[ "${openvpn_enabled}" == true ]]; then
exec /usr/sbin/openvpn \
--config /config/openvpn/config.ovpn \
--script-security 2 \
--up /etc/openvpn/up.sh \
--down /etc/openvpn/down.sh \
--pull-filter ignore "route-ipv6" \
--pull-filter ignore "ifconfig-ipv6" \
--pull-filter ignore "tun-ipv6" \
--pull-filter ignore "redirect-gateway ipv6" \
--pull-filter ignore "dhcp-option DNS6" \
&
/usr/local/sbin/vpn openvpn up
elif [[ "${wireguard_enabled}" == true ]]; then
# Run modularized WireGuard setup
_setup_wireguard
/usr/local/sbin/vpn wireguard up
fi
# --- Launch qBittorrent ---

View File

@@ -129,13 +129,6 @@ if bashio::config.true 'openvpn_enabled'; then
vpn_openvpn=true
fi
if [[ "${vpn_openvpn}" == true ]] && ! bashio::config.true 'openvpn_alt_mode'; then
VPN_INTERFACE="tun0"
bashio::log.info "VPN monitor set to query external IP through interface ${VPN_INTERFACE} (interface binding)."
else
VPN_INTERFACE=""
fi
if bashio::config.true 'wireguard_enabled'; then
vpn_wireguard=true
fi
@@ -151,6 +144,19 @@ if [[ "${vpn_openvpn}" == true && "${vpn_wireguard}" == true ]]; then
exit 1
fi
if [[ "${vpn_openvpn}" == true ]]; then
VPN_INTERFACE=$(cat "/var/run/openvpn/interface")
bashio::log.info "VPN monitor set to query external IP through interface ${VPN_INTERFACE} (interface binding)."
elif [[ "${vpn_wireguard}" == true ]]; then
VPN_INTERFACE=$(cat "/var/run/wireguard/interface")
bashio::log.info "VPN monitor set to query external IP through interface ${VPN_INTERFACE} (interface binding)."
fi
if [[ -z "${VPN_INTERFACE}" ]] || ! ip link show "${VPN_INTERFACE}" > /dev/null 2>&1 ; then
bashio::log.error "VPN interface not found."
bashio::addon.stop
exit 1
fi
REAL_IP="$(read_real_ip)"
if [[ -n "${REAL_IP}" ]]; then

View File

@@ -1,86 +0,0 @@
#!/bin/sh
set -eu
STATE_DIR="/var/run/wireguard/resolvconf"
BACKUP_FILE="${STATE_DIR}/resolv.conf.backup"
mkdir -p "${STATE_DIR}"
if [ "$#" -eq 0 ]; then
exit 0
fi
command="$1"
shift || true
restore_backup() {
if [ -f "${BACKUP_FILE}" ]; then
cat "${BACKUP_FILE}" > /etc/resolv.conf
fi
}
apply_dns() {
iface="$1"
shift || true
# Skip optional arguments such as -m <metric> or -x
while [ "$#" -gt 0 ]; do
case "$1" in
-m|-p|-w)
shift 2 || true
;;
-x|-y|-Z)
shift 1 || true
;;
--)
shift
break
;;
*)
break
;;
esac
done
tmp_file="${STATE_DIR}/${iface}.conf"
cat > "${tmp_file}"
if [ ! -f "${BACKUP_FILE}" ]; then
cp /etc/resolv.conf "${BACKUP_FILE}" 2>/dev/null || true
fi
{
echo "# Generated by WireGuard add-on resolvconf shim"
cat "${tmp_file}"
} > /etc/resolv.conf
}
case "${command}" in
-a)
if [ "$#" -eq 0 ]; then
exit 0
fi
apply_dns "$@"
;;
-d)
if [ "$#" -gt 0 ]; then
rm -f "${STATE_DIR}/$1.conf"
fi
restore_backup
;;
-u)
latest_conf="$(find "${STATE_DIR}" -maxdepth 1 -type f -name '*.conf' -print | head -n 1 || true)"
if [ -n "${latest_conf}" ] && [ -f "${latest_conf}" ]; then
{
echo "# Generated by WireGuard add-on resolvconf shim"
cat "${latest_conf}"
} > /etc/resolv.conf
else
restore_backup
fi
;;
*)
# Treat other commands as successful no-ops to remain compatible with wg-quick.
exit 0
;;
esac

View File

@@ -1,75 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
REAL_IP6TABLES_RESTORE="/sbin/ip6tables-restore"
if [[ ! -x "${REAL_IP6TABLES_RESTORE}" ]]; then
REAL_IP6TABLES_RESTORE="/usr/sbin/ip6tables-restore"
fi
cleanup() {
local exit_code=$?
[[ -n "${RULES_FILE:-}" && -f "${RULES_FILE}" ]] && rm -f "${RULES_FILE}"
[[ -n "${SANITIZED_FILE:-}" && -f "${SANITIZED_FILE}" ]] && rm -f "${SANITIZED_FILE}"
return $exit_code
}
trap cleanup EXIT
RULES_FILE="$(mktemp)"
cat > "${RULES_FILE}"
ipv6_unavailable() {
local message="$1"
[[ $message =~ [Tt]able[[:space:]]does[[:space:]]not[[:space:]]exist ]] && return 0
[[ $message =~ address[[:space:]]family[[:space:]]not[[:space:]]supported ]] && return 0
[[ $message =~ can[[:punct:]]t[[:space:]]initialize[[:space:]]ip6tables[[:space:]]table ]] && return 0
[[ $message =~ IPv6[[:space:]]support[[:space:]]not[[:space:]]available ]] && return 0
return 1
}
# First attempt with the original ruleset
output=""
if output="$(${REAL_IP6TABLES_RESTORE} "$@" < "${RULES_FILE}" 2>&1)"; then
[[ -n "${output}" ]] && printf '%s\n' "${output}" >&2
exit 0
fi
status=$?
# Retry without comment matches if the kernel is missing the comment module
SANITIZED_FILE="$(mktemp)"
sed -E 's/-m[[:space:]]+comment[[:space:]]+--comment[[:space:]]+"[^"]*"//g' "${RULES_FILE}" > "${SANITIZED_FILE}"
retry_output=""
if retry_output="$(${REAL_IP6TABLES_RESTORE} "$@" < "${SANITIZED_FILE}" 2>&1)"; then
printf '%s\n' "ip6tables-restore failed with comment matches; reapplied without comments." >&2
printf '%s\n' "Original error: ${output}" >&2
[[ -n "${retry_output}" ]] && printf '%s\n' "${retry_output}" >&2
exit 0
fi
retry_status=$?
# Final fallback: try legacy backend if available
legacy_output=""
for legacy in /sbin/ip6tables-restore-legacy /usr/sbin/ip6tables-restore-legacy; do
if [[ -x "${legacy}" ]]; then
if legacy_output="$(${legacy} "$@" < "${RULES_FILE}" 2>&1)"; then
printf '%s\n' "ip6tables-restore failed; succeeded using legacy backend." >&2
printf '%s\n' "Original error: ${output}" >&2
[[ -n "${legacy_output}" ]] && printf '%s\n' "${legacy_output}" >&2
exit 0
fi
fi
done
if ipv6_unavailable "${output}" || ipv6_unavailable "${retry_output}" || ipv6_unavailable "${legacy_output}"; then
printf '%s\n' "IPv6 firewall support not detected; skipping IPv6 ruleset restore and continuing." >&2
printf '%s\n' "Original error: ${output}" >&2
[[ -n "${retry_output}" ]] && printf '%s\n' "Sanitized retry error: ${retry_output}" >&2
[[ -n "${legacy_output}" ]] && printf '%s\n' "Legacy backend error: ${legacy_output}" >&2
exit 0
fi
printf '%s\n' "ip6tables-restore failed and fallbacks were unsuccessful." >&2
printf '%s\n' "Original error: ${output}" >&2
printf '%s\n' "Sanitized retry error: ${retry_output}" >&2
exit ${retry_status}

View File

@@ -1,54 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
REAL_IPTABLES_RESTORE="/sbin/iptables-restore"
if [[ ! -x "${REAL_IPTABLES_RESTORE}" ]]; then
REAL_IPTABLES_RESTORE="/usr/sbin/iptables-restore"
fi
cleanup() {
local exit_code=$?
[[ -n "${RULES_FILE:-}" && -f "${RULES_FILE}" ]] && rm -f "${RULES_FILE}"
[[ -n "${SANITIZED_FILE:-}" && -f "${SANITIZED_FILE}" ]] && rm -f "${SANITIZED_FILE}"
return $exit_code
}
trap cleanup EXIT
RULES_FILE="$(mktemp)"
cat > "${RULES_FILE}"
# First attempt with the original ruleset
if output="$(${REAL_IPTABLES_RESTORE} "$@" < "${RULES_FILE}" 2>&1)"; then
[[ -n "${output}" ]] && printf '%s\n' "${output}" >&2
exit 0
fi
status=$?
# Retry without comment matches if the kernel is missing the comment module
SANITIZED_FILE="$(mktemp)"
sed -E 's/-m[[:space:]]+comment[[:space:]]+--comment[[:space:]]+"[^"]*"//g' "${RULES_FILE}" > "${SANITIZED_FILE}"
if retry_output="$(${REAL_IPTABLES_RESTORE} "$@" < "${SANITIZED_FILE}" 2>&1)"; then
printf '%s\n' "iptables-restore failed with comment matches; reapplied without comments." >&2
printf '%s\n' "Original error: ${output}" >&2
[[ -n "${retry_output}" ]] && printf '%s\n' "${retry_output}" >&2
exit 0
fi
retry_status=$?
# Final fallback: try legacy backend if available
for legacy in /sbin/iptables-restore-legacy /usr/sbin/iptables-restore-legacy; do
if [[ -x "${legacy}" ]]; then
if legacy_output="$(${legacy} "$@" < "${RULES_FILE}" 2>&1)"; then
printf '%s\n' "iptables-restore failed; succeeded using legacy backend." >&2
printf '%s\n' "Original error: ${output}" >&2
[[ -n "${legacy_output}" ]] && printf '%s\n' "${legacy_output}" >&2
exit 0
fi
fi
done
printf '%s\n' "iptables-restore failed and fallbacks were unsuccessful." >&2
printf '%s\n' "Original error: ${output}" >&2
printf '%s\n' "Sanitized retry error: ${retry_output}" >&2
exit ${retry_status}

View File

@@ -1,17 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
REAL_SYSCTL="/sbin/sysctl"
if [[ ! -x "${REAL_SYSCTL}" ]]; then
REAL_SYSCTL="/usr/sbin/sysctl"
fi
if [[ "$#" -ge 2 && "$1" == "-q" && "$2" == "net.ipv4.conf.all.src_valid_mark=1" ]]; then
if "${REAL_SYSCTL}" "$@" >/dev/null 2>&1; then
exit 0
fi
# Suppress failure for this specific key to keep wg-quick from aborting in unprivileged environments.
exit 0
fi
exec "${REAL_SYSCTL}" "$@"

View File

@@ -0,0 +1,453 @@
#!/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=()
_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]}"
config_ref["$key"]="$value"
fi
done < "$config_file"
}
_parse_dns() {
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 IPv6
mapfile -t ipv6_candidates < <(getent ahostsv6 "${hostname}" | awk '{print $1}' | uniq)
# Resolve hostname to IPv4
mapfile -t ipv4_candidates < <(getent ahostsv4 "${hostname}" | awk '{print $1}' | uniq)
if [ ${#ipv6_candidates[@]} -gt 0 ]; then
bashio::log.debug "Resolved ${hostname} to ${ipv6_candidates[@]}"
ips+=("${ipv6_candidates[@]}")
fi
if [ ${#ipv4_candidates[@]} -gt 0 ]; then
bashio::log.debug "Resolved ${hostname} to ${ipv4_candidates[@]}"
ips+=("${ipv4_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
# 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
_cmd "ip -4 rule add priority 1 from ${ipv4} table ${config["Table"]}" || return 1
done
for ipv6 in ${local_ipv6}; do
config["IPv6Enabled"]="true"
_cmd "ip -6 route add default dev ${config["Interface"]} table ${config["Table"]}" || return 1
_cmd "ip -6 rule add priority 1 from ${ipv6} table ${config["Table"]}" || return 1
done
# 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
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
}
# --- WireGuard Specific Logic ---
_wireguard_up() {
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" ; 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=""
local -a local_ips=()
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
allowed_ips="${allowed_ips},0.0.0.0/0"
_cmd "ip addr add ${local_ip} dev ${config["Interface"]}" || return 1
elif [ "${result}" -eq 2 ]; then
allowed_ips="${allowed_ips},::/0"
_cmd "ip addr add ${local_ip} dev ${config["Interface"]}" || return 1
else
bashio::log.warning "Ignoring invalid local IP address: ${local_ip}"
fi
done
allowed_ips="${allowed_ips#,}"
if [ -z "${allowed_ips}" ]; then
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
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
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
_routing_add
}
_wireguard_down() {
_routing_del
_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 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
echo ${config["PrivateKey"]} > ${WIREGUARD_STATE_DIR}/privatekey
config["PrivateKey"]="${WIREGUARD_STATE_DIR}/privatekey"
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_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"]}
# Start OpenVPN in the background
_cmd "/usr/sbin/openvpn \
--config "${config["ConfigFile"]}" \
--client \
--daemon \
--log /dev/null \
--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 slow OpenVPN interface to come up
for i in {1..10}; 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."
return 1
}
_openvpn_down() {
# Terminate OpenVPN process
pkill -f "openvpn --config ${config["ConfigFile"]}" || true
_routing_del
}
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
_routing_add
bashio::exit.ok 'OpenVPN routes added.'
elif [ "${mode}" = "postdown" ]; then
_routing_del
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