diff --git a/.github/workflows/BuildImage.yml b/.github/workflows/BuildImage.yml index 4715e56..bd8fab4 100644 --- a/.github/workflows/BuildImage.yml +++ b/.github/workflows/BuildImage.yml @@ -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 diff --git a/Dockerfile.complex b/Dockerfile.complex deleted file mode 100644 index 3ed07b0..0000000 --- a/Dockerfile.complex +++ /dev/null @@ -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/ / diff --git a/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-add-package/run b/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-add-package/run deleted file mode 100755 index 063b570..0000000 --- a/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-add-package/run +++ /dev/null @@ -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 diff --git a/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-add-package/up b/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-add-package/up deleted file mode 100644 index 6414139..0000000 --- a/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-add-package/up +++ /dev/null @@ -1 +0,0 @@ -/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-add-package/run \ No newline at end of file diff --git a/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-install/run b/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-install/run deleted file mode 100755 index 59a4b77..0000000 --- a/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-install/run +++ /dev/null @@ -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 diff --git a/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-install/up b/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-install/up deleted file mode 100644 index 03d298d..0000000 --- a/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-install/up +++ /dev/null @@ -1 +0,0 @@ -/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-install/run \ No newline at end of file diff --git a/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-add-package/dependencies.d/init-mods b/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-add-package/dependencies.d/init-mods similarity index 100% rename from root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-add-package/dependencies.d/init-mods rename to root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-add-package/dependencies.d/init-mods diff --git a/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-install/dependencies.d/init-mods-package-install b/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-add-package/run old mode 100644 new mode 100755 similarity index 100% rename from root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-install/dependencies.d/init-mods-package-install rename to root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-add-package/run diff --git a/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-add-package/type b/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-add-package/type similarity index 100% rename from root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-add-package/type rename to root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-add-package/type diff --git a/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-add-package/up b/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-add-package/up new file mode 100644 index 0000000..564b80b --- /dev/null +++ b/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-add-package/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-add-package/run \ No newline at end of file diff --git a/root/etc/s6-overlay/s6-rc.d/init-mods-end/dependencies.d/init-mod-imagename-modname-install b/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/dependencies.d/init-mods-package-install similarity index 100% rename from root/etc/s6-overlay/s6-rc.d/init-mods-end/dependencies.d/init-mod-imagename-modname-install rename to root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/dependencies.d/init-mods-package-install diff --git a/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/run b/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/run new file mode 100755 index 0000000..82fdb85 --- /dev/null +++ b/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/run @@ -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" \ No newline at end of file diff --git a/root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-install/type b/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/type similarity index 100% rename from root/etc/s6-overlay/s6-rc.d/init-mod-imagename-modname-install/type rename to root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/type diff --git a/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/up b/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/up new file mode 100644 index 0000000..8cb8e3c --- /dev/null +++ b/root/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-mod-wireguard-mullvad-install/run \ No newline at end of file diff --git a/root/etc/s6-overlay/s6-rc.d/init-mods-package-install/dependencies.d/init-mod-imagename-modname-add-package b/root/etc/s6-overlay/s6-rc.d/init-mods-end/dependencies.d/init-mod-wireguard-mullvad-install similarity index 100% rename from root/etc/s6-overlay/s6-rc.d/init-mods-package-install/dependencies.d/init-mod-imagename-modname-add-package rename to root/etc/s6-overlay/s6-rc.d/init-mods-end/dependencies.d/init-mod-wireguard-mullvad-install diff --git a/root/etc/s6-overlay/s6-rc.d/svc-mod-imagename-modname/dependencies.d/init-services b/root/etc/s6-overlay/s6-rc.d/init-mods-package-install/dependencies.d/init-mod-wireguard-mullvad-add-package similarity index 100% rename from root/etc/s6-overlay/s6-rc.d/svc-mod-imagename-modname/dependencies.d/init-services rename to root/etc/s6-overlay/s6-rc.d/init-mods-package-install/dependencies.d/init-mod-wireguard-mullvad-add-package diff --git a/root/etc/s6-overlay/s6-rc.d/svc-mod-imagename-modname/run b/root/etc/s6-overlay/s6-rc.d/svc-mod-imagename-modname/run deleted file mode 100755 index 02ffe39..0000000 --- a/root/etc/s6-overlay/s6-rc.d/svc-mod-imagename-modname/run +++ /dev/null @@ -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 diff --git a/root/etc/s6-overlay/s6-rc.d/svc-mod-imagename-modname/type b/root/etc/s6-overlay/s6-rc.d/svc-mod-imagename-modname/type deleted file mode 100644 index 1780f9f..0000000 --- a/root/etc/s6-overlay/s6-rc.d/svc-mod-imagename-modname/type +++ /dev/null @@ -1 +0,0 @@ -longrun \ No newline at end of file diff --git a/root/etc/s6-overlay/s6-rc.d/user/contents.d/init-mod-imagename-modname-add-package b/root/etc/s6-overlay/s6-rc.d/user/contents.d/init-mod-wireguard-mullvad-add-package similarity index 100% rename from root/etc/s6-overlay/s6-rc.d/user/contents.d/init-mod-imagename-modname-add-package rename to root/etc/s6-overlay/s6-rc.d/user/contents.d/init-mod-wireguard-mullvad-add-package diff --git a/root/etc/s6-overlay/s6-rc.d/user/contents.d/init-mod-imagename-modname-install b/root/etc/s6-overlay/s6-rc.d/user/contents.d/init-mod-wireguard-mullvad-install similarity index 100% rename from root/etc/s6-overlay/s6-rc.d/user/contents.d/init-mod-imagename-modname-install rename to root/etc/s6-overlay/s6-rc.d/user/contents.d/init-mod-wireguard-mullvad-install diff --git a/root/etc/s6-overlay/s6-rc.d/user/contents.d/svc-mod-imagename-modname b/root/etc/s6-overlay/s6-rc.d/user/contents.d/svc-mod-wireguard-mullvad similarity index 100% rename from root/etc/s6-overlay/s6-rc.d/user/contents.d/svc-mod-imagename-modname rename to root/etc/s6-overlay/s6-rc.d/user/contents.d/svc-mod-wireguard-mullvad