#!/bin/bash # do not run this as root if [ "$(id -u)" = "0" ]; then echo "Error: Do not run this as root!" exit 1 fi # check if argument is not missing if [ -z "$1" ]; then echo "Error: Missing tag argument. Please provide a tag argument." exit 2 fi # Check if the 'debian' user exists if id "debian" &>/dev/null; then # Check if 'debian' user is in the 'sudo' group if groups "debian" | grep -qw "sudo"; then # Check if the sudoers file already exists if [ ! -f /etc/sudoers.d/debian_nopass ]; then echo "Creating no-password sudo entry for debian user..." # Use echo piped to sudo tee to safely create a sudoers file without using a text editor echo 'debian' | sudo -S sh -c "echo '%sudo ALL=(ALL) NOPASSWD: ALL' | tee /etc/sudoers.d/debian_nopass > /dev/null" # Set correct permissions for the sudoers file echo 'debian' | sudo -S chmod 0440 /etc/sudoers.d/debian_nopass echo "No-password sudo entry created successfully." fi fi fi # set options set -e set -o nounset set -o errtrace set -o pipefail # variables and configuration NAME="whonix" DM="derivative-maker" DB="derivative-binary" BL="build-log" VERSION="$1" VERSION_MASTER="master" VERSION_NUMBER="${VERSION%%-*}" REPOSITORY_URL="https://github.com/Whonix/derivative-maker.git" FLAVOR_GATEWAY="whonix-gateway-xfce" FLAVOR_WORKSTATION="whonix-workstation-xfce" TARGET="utm" ARCH="arm64" TB="open" REPO="true" SIZE_GATEWAY="15G" SIZE_WORKSTATION="25G" WB="whonix-binary-$VERSION" APT="/usr/bin/apt" GIT="/usr/bin/git" GPG="/usr/bin/gpg" # catching errors as they occur function catch() { echo "Error Report" if [ "$1" != "0" ]; then echo "Error $1 occurred on line $2, command: '$3'" exit 11 # Exit on any error caught by this function fi } # SIGINT or SIGTERM function handle_signal() { echo "Error: received SIGINT or SIGTERM exiting" exit 3 } # start text function beginning() { echo "Starting $NAME build for version $VERSION" } # finishing text function bye() { echo "Exiting without errors." echo "Build function finished, please check log" echo "Its recommended that you verify the signing key on the repository and build, more info here:" echo "https://www.whonix.org/wiki/Signing_Key" } # safe exit of the script function safe_exit() { trap bye EXIT exit 0 } function check_apt_sources() { echo "Checking APT sources availability..." if ! sudo "$APT" update &> /dev/null; then echo "Error: APT sources are not reachable. Please check your sources.list and internet connection." exit 4 fi } function check_connectivity() { local HOST="github.com" echo "Checking connectivity to $HOST..." if ! ping -c 1 -W 2 "$HOST" &> /dev/null; then echo "Error: Unable to reach $HOST. Please check your internet connection." exit 5 fi } function repository_not_found() { echo "Error: ${HOME}/${DM} not found" exit 6 } # Set up trap trap 'catch $? $LINENO "$BASH_COMMAND"' EXIT trap handle_signal SIGINT SIGTERM function update() { # updates/upgrades and autoremove packages check_apt_sources sudo "$APT" update -y sudo "$APT" upgrade -y sudo "$APT" autoremove -y } function install() { # install dependencies local PACKAGES=("git" "time" "curl" "apt-cacher-ng" "lsb-release" "dpkg-dev" "fakeroot" "fasttrack-archive-keyring" "safe-rm" "extrepo-offline-data") # Loop through each package for PACKAGE in "${PACKAGES[@]}"; do if ! dpkg-query -W -f='${Status}' "$PACKAGE" 2>/dev/null | grep -q "install ok installed"; then echo "$PACKAGE is not installed. Installing..." sudo DEBIAN_FRONTEND=noninteractive "$APT" install -y "$PACKAGE" else echo "$PACKAGE is already installed." fi done # GPG Key Import Check local KEY_FINGERPRINT="916B8D99C38EAF5E8ADC7A2A8D66066A2EEACCDA" if ! gpg --list-keys "$KEY_FINGERPRINT" > /dev/null 2>&1; then echo "GPG key not found. Importing..." "$GPG" --import /usr/share/extrepo/offline-data/debian/bullseye/whonix.asc else echo "GPG key already imported." fi } function repo_down() { if [ ! -d "${HOME}/${DM}" ]; then # download git repository check_connectivity echo "Git clone $NAME repository" "$GIT" clone --depth=1 --branch "$VERSION_MASTER" --jobs=4 --recurse-submodules --shallow-submodules "$REPOSITORY_URL" cd "${HOME}/${DM}" || repository_not_found fi } function fetch_tag() { cd "${HOME}/${DM}" || repository_not_found # Verify if the current directory is a git repository if ! "$GIT" rev-parse --git-dir > /dev/null 2>&1; then echo "Error: Not a git repository. Please run this script within a git repository." exit 7 fi if [ ! "$($GIT status --porcelain)" = "" ]; then echo "Error: Command git status --porcelain failed at the beginning!" exit 8 fi # Before fetching and checking out: "$GIT" reset --hard "$GIT" clean -fdx # Fetch only the specific tag "$GIT" fetch --depth=1 origin tag "$VERSION" # Checkout the fetched tag "$GIT" checkout "$VERSION" "$GIT" verify-tag "$VERSION" "$GIT" verify-commit "${VERSION}^{commit}" "$GIT" verify-commit HEAD # Reset and clean each submodule "$GIT" submodule foreach --recursive 'git reset --hard && git clean -fdx' # Submodule operations "$GIT" submodule sync --recursive "$GIT" submodule update --init --recursive --jobs=200 # Double-check everything is clean "$GIT" submodule foreach --recursive 'git reset --hard && git clean -fdx' if [ ! "$($GIT status --porcelain)" = "" ]; then echo "Error: Command git status --porcelain failed at the end!" exit 9 fi # verifies correct version is downloaded GIT_VERSION="$($GIT describe --tags --abbrev=0)" if [ "$GIT_VERSION" == "$VERSION" ]; then "$GIT" describe echo "Git version matches the desired version ($VERSION)." else "$GIT" describe echo "Error: Git version does not match the desired version ($VERSION)." exit 10 fi } function build() { cd "${HOME}/${DM}" || repository_not_found echo "Building gateway" "${HOME}/${DM}/${DM}" --flavor "$FLAVOR_GATEWAY" --target "$TARGET" --arch "$ARCH" --tb "$TB" --repo "$REPO" --vmsize "$SIZE_GATEWAY" echo "Building workstation" "${HOME}/${DM}/${DM}" --flavor "$FLAVOR_WORKSTATION" --target "$TARGET" --arch "$ARCH" --tb "$TB" --repo "$REPO" --vmsize "$SIZE_WORKSTATION" } function pack() { mkdir -p "${HOME}/${WB}" cp -vr "${HOME}/${DB}/${VERSION_NUMBER}"/*.utm.tar.gz "${HOME}/${WB}" echo "Build of $NAME version $VERSION is finished" } function main() { beginning update install repo_down fetch_tag build pack } # Script execution starts here echo "Starting $NAME build function" cd "$HOME" || { echo "Error: Failed to change directory to $HOME"; exit 12; } main 2>&1 | tee -a "${HOME}/${BL}" # Copy the build log to the binary easy access folder and complete the script mv "${HOME}/${BL}" "${HOME}/${WB}/" || { echo "Error: Failed to move build log"; exit 13; } cd "$HOME" || { echo "Error: Failed to change directory to $HOME at script end"; exit 14; } # Exit the script safely safe_exit