mirror of
https://github.com/linuxserver/docker-mods.git
synced 2026-03-23 00:05:28 +08:00
Added mullvad init script
This commit is contained in:
parent
3db855ba2d
commit
a16867844d
4
.github/workflows/BuildImage.yml
vendored
4
.github/workflows/BuildImage.yml
vendored
@ -12,8 +12,8 @@ on:
|
||||
env:
|
||||
GITHUB_REPO: "linuxserver/docker-mods" #don't modify
|
||||
ENDPOINT: "linuxserver/mods" #don't modify
|
||||
BASEIMAGE: "replace_baseimage" #replace
|
||||
MODNAME: "replace_modname" #replace
|
||||
BASEIMAGE: "scratch" #replace
|
||||
MODNAME: "wireguard-mullvad" #replace
|
||||
MOD_VERSION: ${{ inputs.mod_version }} #don't modify
|
||||
MULTI_ARCH: "true" #set to false if not needed
|
||||
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
## Buildstage ##
|
||||
FROM ghcr.io/linuxserver/baseimage-alpine:3.20 AS buildstage
|
||||
|
||||
RUN \
|
||||
echo "**** install packages ****" && \
|
||||
apk add --no-cache \
|
||||
curl && \
|
||||
echo "**** grab rclone ****" && \
|
||||
mkdir -p /root-layer && \
|
||||
if [ $(uname -m) = "x86_64" ]; then \
|
||||
echo "Downloading x86_64 tarball" && \
|
||||
curl -o \
|
||||
/root-layer/rclone.deb -L \
|
||||
"https://downloads.rclone.org/v1.47.0/rclone-v1.47.0-linux-amd64.deb"; \
|
||||
elif [ $(uname -m) = "aarch64" ]; then \
|
||||
echo "Downloading aarch64 tarball" && \
|
||||
curl -o \
|
||||
/root-layer/rclone.deb -L \
|
||||
"https://downloads.rclone.org/v1.47.0/rclone-v1.47.0-linux-arm64.deb"; \
|
||||
fi && \
|
||||
|
||||
# copy local files
|
||||
COPY root/ /root-layer/
|
||||
|
||||
## Single layer deployed image ##
|
||||
FROM scratch
|
||||
|
||||
LABEL maintainer="username"
|
||||
|
||||
# Add files from buildstage
|
||||
COPY --from=buildstage /root-layer/ /
|
||||
@ -1,30 +0,0 @@
|
||||
#!/usr/bin/with-contenv bash
|
||||
|
||||
# This is the init file used for adding os or pip packages to install lists.
|
||||
# It takes advantage of the built-in init-mods-package-install init script that comes with the baseimages.
|
||||
# If using this, we need to make sure we set this init as a dependency of init-mods-package-install so this one runs first
|
||||
|
||||
if ! command -v apprise; then
|
||||
echo "**** Adding apprise and its deps to package install lists ****"
|
||||
echo "apprise" >> /mod-pip-packages-to-install.list
|
||||
## Ubuntu
|
||||
if [ -f /usr/bin/apt ]; then
|
||||
echo "\
|
||||
python3 \
|
||||
python3-pip \
|
||||
runc" >> /mod-repo-packages-to-install.list
|
||||
fi
|
||||
# Alpine
|
||||
if [ -f /sbin/apk ]; then
|
||||
echo "\
|
||||
cargo \
|
||||
libffi-dev \
|
||||
openssl-dev \
|
||||
python3 \
|
||||
python3-dev \
|
||||
python3 \
|
||||
py3-pip" >> /mod-repo-packages-to-install.list
|
||||
fi
|
||||
else
|
||||
echo "**** apprise already installed, skipping ****"
|
||||
fi
|
||||
@ -1 +0,0 @@
|
||||
/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-add-package/run
|
||||
@ -1,8 +0,0 @@
|
||||
#!/usr/bin/with-contenv bash
|
||||
|
||||
# This is an install script that is designed to run after init-mods-package-install
|
||||
# so it can take advantage of packages installed
|
||||
# init-mods-end depends on this script so that later init and services wait until this script exits
|
||||
|
||||
echo "**** Setting up apprise ****"
|
||||
apprise blah blah
|
||||
@ -1 +0,0 @@
|
||||
/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-install/run
|
||||
@ -0,0 +1 @@
|
||||
/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-add-package/run
|
||||
154
root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/run
Executable file
154
root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/run
Executable file
@ -0,0 +1,154 @@
|
||||
#!/usr/bin/with-contenv bash
|
||||
|
||||
fail() {
|
||||
|
||||
echo "[mullvad_setup] Error: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
skip() {
|
||||
|
||||
echo "[mullvad_setup] $1"
|
||||
exit 0
|
||||
}
|
||||
|
||||
config_file_base="/opt/mullvad"
|
||||
target_link="/config/wg_confs/wg0.conf"
|
||||
|
||||
# Verify required environment variables are present. This script cannot run without them.
|
||||
[[ -z "${MULLVAD_ACCOUNT}" ]] && fail "MULLVAD_ACCOUNT environment variable is not set"
|
||||
[[ -z "${MULLVAD_PRIVATE_KEY}" ]] && fail "MULLVAD_PRIVATE_KEY environment variable is not set"
|
||||
[[ -z "${MULLVAD_LOCATION}" ]] && fail "MULLVAD_LOCATION environment variable is not set"
|
||||
|
||||
# Set up the configuration output directory. Create it if it doesn't exist.
|
||||
[[ -d "${config_file_base}" ]] || mkdir -p "${config_file_base}"
|
||||
|
||||
echo "[mullvad_setup] Cleaning config base..."
|
||||
rm -f "${config_file_base}/"*
|
||||
|
||||
echo "[mullvad_setup] Removing old symlink if it exists..."
|
||||
rm -f "$target_link"
|
||||
|
||||
# MULLVAD_LOCATION is a dynamic-value variable.
|
||||
# It may may contain a value fo either:
|
||||
# - a region (e.g. 'gb')
|
||||
# - a city (e.g. 'gb-lon')
|
||||
# - or specific nodes (e.g. 'gb-lon-wg-001,gb-lon-wg-002').
|
||||
if [[ "$MULLVAD_LOCATION" =~ ^[a-z]{2}$ ]]; then
|
||||
|
||||
echo "[mullvad_setup] Location filter set to region '$MULLVAD_LOCATION'."
|
||||
jq_filter=".countries[] | select(.code == \"$MULLVAD_LOCATION\") | .cities[] | .relays[]"
|
||||
|
||||
elif [[ "$MULLVAD_LOCATION" =~ ^[a-z]{2}-[a-z]{3}$ ]]; then
|
||||
|
||||
echo "[mullvad_setup] Location filter set to city '$MULLVAD_LOCATION'."
|
||||
country="${MULLVAD_LOCATION%%-*}"
|
||||
city="${MULLVAD_LOCATION##*-}"
|
||||
|
||||
jq_filter=".countries[] | select(.code == \"$country\") | .cities[] | select(.code == \"$city\") | .relays[]"
|
||||
|
||||
elif [[ "$MULLVAD_LOCATION" =~ ^([a-z]{2}-[a-z]{3}-wg-[0-9]{3},?)+$ ]]; then
|
||||
|
||||
echo "[mullvad_setup] Location filter set to nodes '$MULLVAD_LOCATION'."
|
||||
jq_list=$(echo "$MULLVAD_LOCATION" | sed 's/,/", "/g' | sed 's/^/"/; s/$/"/')
|
||||
jq_filter=".countries[] | .cities[] | .relays[] | select(.hostname | IN($jq_list))"
|
||||
|
||||
else
|
||||
fail "Invalid MULLVAD_LOCATION format. Expected formats: 'gb', 'gb-lon', or 'gb-lon-wg-001,nl-ams-wg-001'."
|
||||
fi
|
||||
|
||||
# The client's tunnel address needs to be obtained.
|
||||
# A call to the Mullvad API is made using the account number and the public key derived from the private key.
|
||||
# The API returns a comma-delimited string with the (ipv4) assigned tunnel address as the first value.
|
||||
echo "[mullvad_setup] Fetching tunnel address from Mullvad API..."
|
||||
wg_pubkey="$(wg pubkey <<<"$MULLVAD_PRIVATE_KEY")"
|
||||
tunnel_address_response="$(curl -sSL https://api.mullvad.net/wg -d account="$MULLVAD_ACCOUNT" --data-urlencode pubkey="$wg_pubkey")" || fail "Could not talk to Mullvad API."
|
||||
tunnel_address="${tunnel_address_response%%,*}"
|
||||
|
||||
# Now the specified node needs to be selected.
|
||||
# The API is called again to get the list of all available nodes, which is then filtered down to matching nodes.
|
||||
# A random node is selected from the filtered list. If only one node matches, that one is selected.
|
||||
echo "[mullvad_setup] Fetching relay information from Mullvad API..."
|
||||
relay_response="$(curl -LsS https://api.mullvad.net/public/relays/wireguard/v1/)" || fail "Unable to connect to Mullvad API."
|
||||
relay_fields=$(echo "$relay_response" | jq -r "$jq_filter | [.hostname, .public_key, .ipv4_addr_in] | @tsv" | sort -R | head) || fail "Failed to parse Mullvad API response. Check your MULLVAD_LOCATION value."
|
||||
|
||||
[[ -z "$relay_fields" ]] && fail "No relays found matching the specified location filter '$MULLVAD_LOCATION'."
|
||||
|
||||
# Optionally, if the user defines a network which needs routing access to services running through wireguared,
|
||||
# a PostUp and PreDown hook is created to add and remove the necessary routing rules when the tunnel is brought up and down.
|
||||
if [[ -n "$LAN_NETWORKS" || -n "${ALLOW_ATTACHED_NETWORKS}" ]]; then
|
||||
|
||||
ip_route_add=""
|
||||
ip_route_delete=""
|
||||
chain_route_add=""
|
||||
chain_route_delete=""
|
||||
|
||||
if [[ -n "$LAN_NETWORKS" ]]; then
|
||||
|
||||
IFS=',' read -ra lan_network_array <<< "${LAN_NETWORKS}"
|
||||
DROUTE=$(ip route | grep default | awk '{print $3}');
|
||||
|
||||
for lan_network_item in $(echo "$LAN_NETWORKS" | tr "," " "); do
|
||||
|
||||
echo "[mullvad_setup] Adding iptables rule for LAN network $lan_network_item"
|
||||
|
||||
lan_network_item=$(echo "${lan_network_item}" | sed -e 's~^[ \t]*~~;s~[ \t]*$~~')
|
||||
|
||||
ip_route_add+="ip route add ${lan_network_item} via ${DROUTE}; "
|
||||
ip_route_delete+="ip route delete ${lan_network_item}; "
|
||||
chain_route_delete+="iptables -D OUTPUT -d ${lan_network_item} -j ACCEPT; "
|
||||
|
||||
if [[ "${lan_network_item}" = "${lan_network_array[0]}" ]]; then
|
||||
chain_route_add+="iptables -I OUTPUT -d ${lan_network_item} -j ACCEPT; "
|
||||
else
|
||||
chain_route_add+="iptables -A OUTPUT -d ${lan_network_item} -j ACCEPT; "
|
||||
fi
|
||||
done
|
||||
|
||||
fi
|
||||
|
||||
# If set, the iptables rules will be amended to allow inbound traffic from services on the same attached network(s).
|
||||
# This allows other containers on the same stack or global network to access services running through the WireGuard tunnel.
|
||||
if [[ "${ALLOW_ATTACHED_NETWORKS:-}" == "true" ]]; then
|
||||
|
||||
attached_networks=$(ip route | grep -e "link" | awk '{print $1}');
|
||||
for attached_network in $attached_networks; do
|
||||
|
||||
echo "[mullvad_setup] Adding iptables rule for attached docker network $attached_network"
|
||||
chain_route_add+="iptables -A OUTPUT -d $attached_network -j ACCEPT; "
|
||||
chain_route_delete+="iptables -D OUTPUT -d $attached_network -j ACCEPT; "
|
||||
|
||||
done
|
||||
fi
|
||||
|
||||
post_up_hook="${ip_route_add}${chain_route_add}iptables -A OUTPUT ! -o %i -m mark ! --mark \$(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT;"
|
||||
pre_down_hook="${ip_route_delete}iptables -D OUTPUT ! -o %i -m mark ! --mark \$(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT; $chain_route_delete"
|
||||
fi
|
||||
|
||||
IFS=$'\t' read -r WG_HOST WG_PUBKEY WG_IPADDR <<< "$relay_fields"
|
||||
echo "[mullvad_setup] Generating config for chosen node $WG_HOST..."
|
||||
|
||||
# The selected node is used to generate the WireGuard configuration file.
|
||||
configuration_file="$config_file_base/$WG_HOST.conf"
|
||||
umask 077
|
||||
rm -f "$configuration_file.tmp"
|
||||
cat > "$configuration_file.tmp" <<-_EOF
|
||||
[Interface]
|
||||
PrivateKey = $MULLVAD_PRIVATE_KEY
|
||||
Address = $tunnel_address
|
||||
DNS = ${MULLVAD_DNS:-"10.64.0.1"}
|
||||
PostUp = $post_up_hook
|
||||
PreDown = $pre_down_hook
|
||||
|
||||
[Peer]
|
||||
PublicKey = $WG_PUBKEY
|
||||
Endpoint = $WG_IPADDR:51820
|
||||
AllowedIPs = 0.0.0.0/0
|
||||
_EOF
|
||||
|
||||
mv "$configuration_file.tmp" "$configuration_file"
|
||||
|
||||
# Finally, a symlink is created to the generated configuration file.
|
||||
# Use of a link allows the container owner to check which node was selected.
|
||||
echo "[mullvad_setup] Symlink created $target_link -> $configuration_file"
|
||||
ln -sf "$configuration_file" "$target_link"
|
||||
@ -0,0 +1 @@
|
||||
/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/run
|
||||
@ -1,7 +0,0 @@
|
||||
#!/usr/bin/with-contenv bash
|
||||
|
||||
# This is an example service that would run for the mod
|
||||
# It depends on init-services, the baseimage hook for start of all longrun services
|
||||
|
||||
exec \
|
||||
s6-setuidgid abc run my app
|
||||
@ -1 +0,0 @@
|
||||
longrun
|
||||
Loading…
x
Reference in New Issue
Block a user