From cc179a05d07b2dc33230503cff5bdae515dbfa04 Mon Sep 17 00:00:00 2001 From: Donald Zou Date: Thu, 7 Nov 2024 23:47:08 +0800 Subject: [PATCH] Update --- Dockerfile | 69 +++++++++----- entrypoint.sh | 235 ++++++++++++++++++++++++++++++++++++++++++++++ src/entrypoint.sh | 34 ------- 3 files changed, 283 insertions(+), 55 deletions(-) create mode 100644 entrypoint.sh delete mode 100644 src/entrypoint.sh diff --git a/Dockerfile b/Dockerfile index ad73bb2..18291fb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,31 +1,58 @@ -# Pull from small Debian stable image. -FROM alpine:latest AS builder - +FROM alpine:latest LABEL maintainer="dselen@nerthus.nl" -WORKDIR /opt/wireguarddashboard/src +# Declaring environment variables, change Peernet to an address you like, standard is a 24 bit subnet. +ARG wg_net="10.0.0.1" +ARG wg_port="51820" -RUN apk update && \ - apk add --no-cache sudo gcc musl-dev rust cargo linux-headers +# Following ENV variables are changable on container runtime because /entrypoint.sh handles that. See compose.yaml for more info. +ENV TZ="Europe/Amsterdam" +ENV global_dns="1.1.1.1" +ENV enable="none" +ENV isolate="none" +ENV public_ip="0.0.0.0" -COPY ./docker/alpine/builder.sh /opt/wireguarddashboard/src/ -COPY ./docker/alpine/requirements.txt /opt/wireguarddashboard/src/ -RUN chmod u+x /opt/wireguarddashboard/src/builder.sh -RUN /opt/wireguarddashboard/src/builder.sh +# Doing package management operations, such as upgrading +RUN apk update \ + && apk add --no-cache bash git tzdata \ + iptables ip6tables openrc curl wireguard-tools \ + sudo py3-psutil py3-bcrypt \ + && apk upgrade +# Using WGDASH -- like wg_net functionally as a ARG command. But it is needed in entrypoint.sh so it needs to be exported as environment variable. +ENV WGDASH=/opt/wireguarddashboard -FROM alpine:latest -WORKDIR /opt/wireguarddashboard/src +# Removing the Linux Image package to preserve space on the image, for this reason also deleting apt lists, to be able to install packages: run apt update. -COPY ./src /opt/wireguarddashboard/src/ -COPY --from=builder /opt/wireguarddashboard/src/venv /opt/wireguarddashboard/src/venv -COPY --from=builder /opt/wireguarddashboard/src/log /opt/wireguarddashboard/src/log/ +# Doing WireGuard Dashboard installation measures. Modify the git clone command to get the preferred version, with a specific branch for example. +RUN mkdir /data \ + && mkdir /configs \ + && mkdir -p ${WGDASH}/src +COPY ./src ${WGDASH}/src -RUN apk update && \ - apk add --no-cache wireguard-tools sudo && \ - apk add --no-cache iptables ip6tables && \ - chmod u+x /opt/wireguarddashboard/src/entrypoint.sh +# Generate basic WireGuard interface. Echoing the WireGuard interface config for readability, adjust if you want it for efficiency. +# Also setting the pipefail option, verbose: https://github.com/hadolint/hadolint/wiki/DL4006. +SHELL ["/bin/bash", "-o", "pipefail", "-c"] +RUN out_adapt=$(ip -o -4 route show to default | awk '{print $NF}') \ + && echo -e "[Interface]\n\ +Address = ${wg_net}/24\n\ +PrivateKey =\n\ +PostUp = iptables -t nat -I POSTROUTING 1 -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE\n\ +PostUp = iptables -I FORWARD -i wg0 -o wg0 -j DROP\n\ +PreDown = iptables -t nat -D POSTROUTING -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE\n\ +PreDown = iptables -D FORWARD -i wg0 -o wg0 -j DROP\n\ +ListenPort = ${wg_port}\n\ +SaveConfig = true\n\ +DNS = ${global_dns}" > /configs/wg0.conf.template \ + && chmod 600 /configs/wg0.conf.template -HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD curl -f http://localhost:10086/signin || exit 1 +# Defining a way for Docker to check the health of the container. In this case: checking the gunicorn process. +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD sh -c 'pgrep gunicorn > /dev/null && pgrep tail > /dev/null' || exit 1 -ENTRYPOINT ["/opt/wireguarddashboard/src/entrypoint.sh"] \ No newline at end of file +# Copy the basic entrypoint.sh script. +COPY entrypoint.sh /entrypoint.sh + +# Exposing the default WireGuard Dashboard port for web access. +EXPOSE 10086 +ENTRYPOINT ["/bin/bash", "/entrypoint.sh"] \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..08ed769 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,235 @@ +#!/bin/bash + +echo "------------------------- START ----------------------------" +echo "Starting the WireGuard Dashboard Docker container." + +ensure_installation() { + # When using a custom directory to store the files, this part moves over and makes sure the installation continues. + echo "Quick-installing..." + + [ ! -d "/data/db" ] && echo "Creating database dir" && mkdir /data/db + ln -s /data/db "${WGDASH}/src/db" + + [ ! -f "/data/wg-dashboard.ini" ] && echo "Creating wg-dashboard.ini file" && touch /data/wg-dashboard.ini + ln -s /data/wg-dashboard.ini "${WGDASH}/src/wg-dashboard.ini" + + python3 -m venv "${WGDASH}"/src/venv + . "${WGDASH}/src/venv/bin/activate" + + mv /usr/lib/python3.12/site-packages/psutil* "${WGDASH}"/src/venv/lib/python3.12/site-packages + mv /usr/lib/python3.12/site-packages/bcrypt* "${WGDASH}"/src/venv/lib/python3.12/site-packages + + chmod +x "${WGDASH}"/src/wgd.sh + cd "${WGDASH}"/src || exit + ./wgd.sh install + + echo "Looks like the installation succeeded." + + # This first step is to ensure the wg0.conf file exists, and if not, then its copied over from the ephemeral container storage. + # This is done so WGDashboard it works out of the box + + if [ ! -f "/etc/wireguard/wg0.conf" ]; then + echo "Standard wg0 Configuration file not found, grabbing template." + cp -a "/configs/wg0.conf.template" "/etc/wireguard/wg0.conf" + + echo "Setting a secure private key." # SORRY 4 BE4 - Daan + + local privateKey + privateKey=$(wg genkey) + sed -i "s|^PrivateKey *=.*$|PrivateKey = ${privateKey}|g" /etc/wireguard/wg0.conf + + echo "Done setting template." + else + echo "Existing wg0 configuration file found, using that." + fi +} + +set_envvars() { + printf "\n------------- SETTING ENVIRONMENT VARIABLES ----------------\n" + + # Path to the configuration file (exists because of previous function). + local config_file="/opt/wireguarddashboard/src/wg-dashboard.ini" + + # Check if the file is empty + if [ ! -s "$config_file" ]; then + echo "Config file is empty. Creating [Peers] section." + + # Create [Peers] section with initial values + { + echo "[Peers]" + echo "remote_endpoint = ${public_ip}" + echo "peer_global_dns = ${global_dns}" + } > "$config_file" + + else + echo "Config file is not empty, enforcing environment variables." + + # Check and update the DNS if it has changed + current_dns=$(grep "peer_global_dns = " "$config_file" | awk '{print $NF}') + if [ "${global_dns}" != "$current_dns" ]; then + echo "Changing default DNS." + sed -i "s/^peer_global_dns = .*/peer_global_dns = ${global_dns}/" "$config_file" + else + echo "DNS is set correctly." + fi + + # Determine the public IP and update if necessary + if [ "${public_ip}" = "0.0.0.0" ]; then + default_ip=$(curl -s ifconfig.me) + + echo "Trying to fetch the Public-IP using ifconfig.me: ${default_ip}" + sed -i "s/^remote_endpoint = .*/remote_endpoint = ${default_ip}/" "$config_file" + else + current_ip=$(grep "remote_endpoint = " "$config_file" | awk '{print $NF}') + + if [ "${public_ip}" != "$current_ip" ]; then + echo "Setting the Public-IP using given variable: ${public_ip}" + + sed -i "s/^remote_endpoint = .*/remote_endpoint = ${public_ip}/" "$config_file" + fi + + fi + + fi +} + +# === CORE SERVICES === +start_core() { + printf "\n---------------------- STARTING CORE -----------------------\n" + + echo "Activating Python venv and executing the WireGuard Dashboard service." + + . "${WGDASH}"/src/venv/bin/activate + cd "${WGDASH}"/src || return + bash wgd.sh start + + # Isolated peers feature, first converting the existing configuration files and the given names to arrays. + # + # WILL BE REMOVED IN FUTURE WHEN WGDASHBOARD ITSELF SUPPORTS THIS!! + # + + local configurations=(/etc/wireguard/*) + IFS=',' read -r -a do_isolate <<< "${isolate}" + non_isolate=() + + # Checking if there are matches between the two arrays. + for config in "${configurations[@]}"; do + config=$(echo "$config" | sed -e 's|.*/etc/wireguard/||' -e 's|\.conf$||') + + local found + found=false + + for interface in "${do_isolate[@]}"; do + + if [[ "$config" == "$interface" ]]; then + found=true + break + fi + + done + + if [ "$found" = false ]; then + non_isolate+=("$config") + fi + + done + + # Isolating the matches. + for interface in "${do_isolate[@]}"; do + + if [ "$interface" = "none" ] || [ "$interface" = "" ]; then + echo "Found: $interface, stopping isolation checking." + break + else + if [ -f "/etc/wireguard/${interface}.conf" ]; then + echo "Isolating interface:" "$interface" + + upblocking=$(grep -c "PostUp = iptables -I FORWARD -i ${interface} -o ${interface} -j DROP" /etc/wireguard/"${interface}".conf) + downblocking=$(grep -c "PreDown = iptables -D FORWARD -i ${interface} -o ${interface} -j DROP" /etc/wireguard/"${interface}".conf) + + if [ "$upblocking" -lt 1 ] && [ "$downblocking" -lt 1 ]; then + sed -i "/PostUp =/a PostUp = iptables -I FORWARD -i ${interface} -o ${interface} -j DROP" /etc/wireguard/"${interface}".conf + sed -i "/PreDown =/a PreDown = iptables -D FORWARD -i ${interface} -o ${interface} -j DROP" /etc/wireguard/"${interface}".conf + fi + + else + echo "Configuration for $interface in enforce isolation does not seem to exist, continuing." + fi + + fi + + done + + # Removing isolation for the configurations that did not match. + for interface in "${non_isolate[@]}"; do + + if [ -f "/etc/wireguard/${interface}.conf" ]; then + echo "Removing isolation, if isolation is present for:" "$interface" + + sed -i "/PostUp = iptables -I FORWARD -i ${interface} -o ${interface} -j DROP/d" /etc/wireguard/"${interface}".conf + sed -i "/PreDown = iptables -D FORWARD -i ${interface} -o ${interface} -j DROP/d" /etc/wireguard/"${interface}".conf + else + echo "Configuration for $interface in removing isolation does not seem to exist, continuing." + fi + + done + + # The following section takes care of enabling wireguard interfaces on startup. Using arrays and given arguments. + # + # WILL BE REMOVED IN FUTURE WHEN WGDASHBOARD ITSELF SUPPORTS THIS!! + # + + IFS=',' read -r -a enable_array <<< "${enable}" + + for interface in "${enable_array[@]}"; do + + if [ "$interface" = "none" ]; then + echo "Found: $interface, stopping enabling checking." + break + else + echo "Enabling interface:" "$interface" + + local fileperms + fileperms=$(stat -c "%a" /etc/wireguard/"${interface}".conf) + if [ "$fileperms" -eq 644 ]; then + echo "Configuration is world accessible, adjusting." + chmod 600 "/etc/wireguard/${interface}.conf" + fi + + if [ -f "/etc/wireguard/${interface}.conf" ]; then + wg-quick up "$interface" + else + echo "No corresponding configuration file found for $interface doing nothing." + fi + + fi + + done +} + +ensure_blocking() { + sleep 1s + echo -e "\nEnsuring container continuation." + + # Find and tail the latest error and access logs if they exist + local logdir="/opt/wireguarddashboard/src/log" + + latestErrLog=$(find "$logdir" -name "error_*.log" -type f -print | sort -r | head -n 1) + latestAccLog=$(find "$logdir" -name "access_*.log" -type f -print | sort -r | head -n 1) + + # Only tail the logs if they are found + if [ -n "$latestErrLog" ] || [ -n "$latestAccLog" ]; then + tail -f "$latestErrLog" "$latestAccLog" + else + echo "No log files found to tail." + fi + + # Blocking command to keep the container running as a last resort. + sleep infinity +} + +# Execute functions for the WireGuard Dashboard services, then set the environment variables +ensure_installation +set_envvars +start_core +ensure_blocking \ No newline at end of file diff --git a/src/entrypoint.sh b/src/entrypoint.sh deleted file mode 100644 index c8056f2..0000000 --- a/src/entrypoint.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -echo "Starting the WireGuard Dashboard Docker container." - -clean_up() { - # Cleaning out previous data such as the .pid file and starting the WireGuard Dashboard. Making sure to use the python venv. - echo "Looking for remains of previous instances..." - if [ -f "/opt/wireguarddashboard/app/src/gunicorn.pid" ]; then - echo "Found old .pid file, removing." - rm /opt/wireguarddashboard/app/src/gunicorn.pid - else - echo "No remains found, continuing." - fi -} -ensure_blocking() { - sleep 1s - echo "Ensuring container continuation." - - # This function checks if the latest error log is created and tails it for docker logs uses. - if find "/opt/wireguarddashboard/src/log" -mindepth 1 -maxdepth 1 -type f | read -r; then - latestErrLog=$(find /opt/wireguarddashboard/src/log -name "error_*.log" | head -n 1) - latestAccLog=$(find /opt/wireguarddashboard/src/log -name "access_*.log" | head -n 1) - tail -f "${latestErrLog}" "${latestAccLog}" - fi - - # Blocking command in case of erroring. So the container does not quit. - sleep infinity -} - -{ date; clean_up; printf "\n\n"; } >> ./log/install.txt - -chmod u+x /opt/wireguarddashboard/src/wgd.sh -/opt/wireguarddashboard/src/wgd.sh install -/opt/wireguarddashboard/src/wgd.sh docker_start -ensure_blocking