hshqlib.sh
Rama main · réplica de lectura del repositorio original.
#!/bin/bash
HSHQ_LIB_SCRIPT_VERSION=249
LOG_LEVEL=info
# Copyright (C) 2023 HomeServerHQ <drdoug@homeserverhq.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
set -e
function init()
{
# Version 74 Update
# Changing to the home directory fixes issues with docker compose (v2)
# since the command 'stat .' (inside of docker compose) results in a
# permission denied error when running script from root as another user,
# i.e. 'sudo -u <USER> bash hshq.sh'
# Basically, docker compose is dependent on which directory it is
# executed from, regardless if a specified file is in a readable directory.
cd ~
IS_STACK_DEBUG=false
USERNAME=$(id -u -n)
PRIOR_HSHQ_VERSION=0
LAST_RELAYSERVER_VERSION_UPDATE=239
IS_AUTO_INSTALL=false
HSHQ_INSTALL_VARIANT=base
loadVersionVars
IS_RUST_UTILS=false
if [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^26\.04. ]]; then
IS_RUST_UTILS=true
fi
IS_DESKTOP_ENV=false
if [ -d $HOME/Desktop ]; then
IS_DESKTOP_ENV=true
fi
IS_CONSOLE_ENV=false
# Leave this set to true for a bit, to allow for users to update.
# Eventually, we'll turn it back off and only restart as needed.
IS_RESTART_SCRIPTSERVER=true
LOCK_UTILS_FILENAME=lockUtils.sh
LOCKHOLDER_FILENAME=lastCaller
ALL_LOCKS="hshqopen networkchecks"
CONFIG_FILE_DEFAULT_FILENAME=config.cnf
ENCRYPTED_CONFIG_FILE_DEFAULT_FILENAME=config.enc
ENCRYPTED_CONFIG_FILE_BACKUP_FILENAME=config-enc.bak
ENC_PW_FILE=/tmp/encpw.enc
TMP_PW_CHECKDIR=/tmp/pwcheckdir
IS_CONFIG_FILE_UNENCRYPTED=false
RS_INSTALL_SETUP_SCRIPT_NAME=setupRS.sh
RS_INSTALL_FRESH_SCRIPT_NAME=installRS.sh
RS_INSTALL_TRANSFER_SCRIPT_NAME=transferRS.sh
RS_INSTALL_VALIDATION_SCRIPT_NAME=validateRS.sh
RS_INSTALL_VALIDATION_LIB_SCRIPT_NAME=validateLib.sh
RS_UPDATE_UTILS_SCRIPT_NAME=rsUtilsScript.sh
RS_UPDATE_SCRIPT_NAME=rsUpdateScript.sh
NUKE_SCRIPT_NAME=nuke.sh
RELAYSERVER_HSHQ_FULL_LOG_NAME=hshqRSInstall.log
RELAYSERVER_HSHQ_TIMESTAMP_LOG_NAME=hshqRSInstallTS.log
RELAYSERVER_NOT_READY_FILE=notready
RELAYSERVER_SCRIPTS_UPLOADED_FILE=uploaded
RELAYSERVER_INSTALL_COMPLETE_FILE=installed
RELAYSERVER_INSTALL_FAILED_FILE=failed
RELAYSERVER_SWAP_SIZE=2G
HOMESERVER_SWAP_SIZE=16G
HSHQ_LOG_FILE=/var/log/hshq.log
HSHQ_INSTALL_NOTES_FILENAME='HomeServer_Install_Notes.txt'
SCRIPTSERVER_FULL_STACKLIST_FILENAME=fullStackList.txt
SCRIPTSERVER_OPTIONAL_STACKLIST_FILENAME=optionalStackList.txt
SCRIPTSERVER_UPDATE_STACKLIST_FILENAME=updateStackList.txt
SCRIPTSERVER_REDIS_STACKLIST_FILENAME=redisStackList.txt
ALLDBS_IMPORT_FILENAME=DBCImport.txt
DNS_ZONE_FILENAME_BASE=DNSZoneInfo
DOCKER_VERSION_UBUNTU_2204=5:29.2.1-1~ubuntu.22.04~jammy
DOCKER_VERSION_UBUNTU_2404=5:29.4.0-1~ubuntu.24.04~noble
DOCKER_VERSION_UBUNTU_2604=5:29.4.0-1~ubuntu.24.04~noble
DOCKER_VERSION_DEBIAN_12=5:29.1.4-1~debian.12~bookworm
DOCKER_VERSION_CHECK_UBUNTU_2204=29.2.1
DOCKER_VERSION_CHECK_UBUNTU_2404=29.4.0
DOCKER_VERSION_CHECK_UBUNTU_2604=29.4.0
DOCKER_VERSION_CHECK_DEBIAN_12=29.1.4
NET_PRIVATEIP_BRIDGE_NAME=brdockpriv
NET_PRIVATEIP_SUBNET=172.16.1.0/24
NET_PRIVATEIP_SUBNET_PREFIX=172.16.1
NET_MAILU_EXT_BRIDGE_NAME=brmailuext
NET_MAILU_EXT_SUBNET=172.16.2.0/24
NET_MAILU_EXT_SUBNET_PREFIX=172.16.2
NET_MAILU_INT_BRIDGE_NAME=brmailuint
NET_MAILU_INT_SUBNET=172.16.3.0/24
NET_MAILU_INT_SUBNET_PREFIX=172.16.3
NET_WEBPROXY_BRIDGE_NAME=brdockproxy
NET_WEBPROXY_SUBNET=172.16.4.0/22
NET_WEBPROXY_SUBNET_PREFIX=172.16.4
NET_EXTERNAL_BRIDGE_NAME=brdockext
NET_EXTERNAL_SUBNET=172.16.8.0/21
NET_EXTERNAL_SUBNET_PREFIX=172.16.8
NET_INTERNALMAIL_BRIDGE_NAME=brdockmail
NET_INTERNALMAIL_SUBNET=172.16.16.0/22
NET_INTERNALMAIL_SUBNET_PREFIX=172.16.16
NET_DBS_BRIDGE_NAME=brdockdbs
NET_DBS_SUBNET=172.16.20.0/22
NET_DBS_SUBNET_PREFIX=172.16.20
NET_LDAP_BRIDGE_NAME=brdockldap
NET_LDAP_SUBNET=172.16.24.0/22
NET_LDAP_SUBNET_PREFIX=172.16.24
NET_AIPRIVATE_SUBNET=172.16.28.0/22
NET_AIPRIVATE_SUBNET_PREFIX=172.16.28
NET_AIPRIVATE_BRIDGE_NAME=brdockaipriv
NET_AILIMITED_SUBNET=172.16.32.0/24
NET_AILIMITED_SUBNET_PREFIX=172.16.32
NET_AILIMITED_BRIDGE_NAME=brdockailim
RELAYSERVER_NET_EXTERNAL_BRIDGE_NAME=brdockext
RELAYSERVER_NET_EXTERNAL_SUBNET=172.16.1.0/24
RELAYSERVER_NET_EXTERNAL_SUBNET_PREFIX=172.16.1
RELAYSERVER_NET_WEBPROXY_BRIDGE_NAME=brdockproxy
RELAYSERVER_NET_WEBPROXY_SUBNET=172.16.2.0/24
RELAYSERVER_NET_WEBPROXY_SUBNET_PREFIX=172.16.2
RELAYSERVER_MAIL_RELAY_BRIDGE_NAME=brdockmrelay
RELAYSERVER_MAIL_RELAY_SUBNET=172.16.3.0/24
RELAYSERVER_MAIL_RELAY_SUBNET_PREFIX=172.16.3
RELAYSERVER_CLIENT_DNS_BRIDGE_NAME=brdockcdns
RELAYSERVER_CLIENT_DNS_SUBNET=172.16.4.0/24
RELAYSERVER_CLIENT_DNS_SUBNET_PREFIX=172.16.4
ADGUARD_DNS_PORT=53
CADDY_HTTP_PORT=80
CADDY_HTTPS_PORT=443
COTURN_PRIMARY_PORT=3478
COTURN_SECONDARY_PORT=5349
COTURN_COMMS_MIN_PORT=14100
COTURN_COMMS_MAX_PORT=14200
JELLYFIN_PORT=7359
JITSI_COLIBRI_PORT=8020
JITSI_JVB_PORT=4443
JITSI_MEET_PORT=10000
KHOJ_VNC_PORT=55901
MAILU_PORT_1=25
MAILU_PORT_2=110
MAILU_PORT_3=143
MAILU_PORT_4=465
MAILU_PORT_5=587
MAILU_PORT_6=993
MAILU_PORT_7=995
PEERTUBE_RDP_PORT=1935
QBITTORRENT_PORT=6881
SYNCTHING_DISC_PORT=21027
SYNCTHING_SYNC_PORT=22000
SYNCTHING_LOCAL_WEB_PORT=8384
UPNP_PORT=1900
VNC_SERVER_PORT=5901
WAZUH_PORT_1=1514
WAZUH_PORT_2=1515
WAZUH_PORT_3=514
WAZUH_PORT_4=55000
WAZUH_PORT_5=9200
WAZUH_AGENT_VERSION=4.14.7-1
PAPERLESS_ADMIN_ID=2
DEFAULT_UNFOUND_IP_ADDRESS=169.254.84.48
DEFAULT_UNFOUND_IP_SUBNET=169.254.0.0/16
MAX_DOCKER_PULL_TRIES=20
MENU_WIDTH=69
MENU_HEIGHT=24
MENU_INT_HEIGHT=10
ENABLE_STACK_DELETE=false
SUDO_NORMAL_TIMEOUT=15
SUDO_LONG_TIMEOUT=1440
SUDO_LONG_TIMEOUT_FILENAME=sudohshqinstall
SUDO_MAX_RETRIES=20
MAILX_TIMEOUT=15
NETPLAN_ORIGIN_HINT=88-hshq
NETPLAN_APPLY_WAIT=5
MIN_NETWORKCHECK_INTERVAL=5
STACK_VERSION_PREFIX=#HSHQManaged
HSHQ_ADMIN_NAME="HSHQ Admin"
IS_HSHQ_DEV_FILENAME=hshq.dev
IS_HSHQ_TEST_FILENAME=hshq.test
IS_HSHQ_DEV_TEST=false
if [ -f $HOME/hshq/$IS_HSHQ_DEV_FILENAME ] || [ -f $HOME/hshq/$IS_HSHQ_TEST_FILENAME ]; then
IS_HSHQ_DEV_TEST=true
fi
HSHQ_WRAP_URL=https://homeserverhq.com/hshq.sh
HSHQ_WRAP_VER_URL=https://homeserverhq.com/ver/getwrapversion
HSHQ_WRAP_DEV_URL=https://homeserverhq.com/hshq-dev.sh
HSHQ_WRAP_DEV_VER_URL=https://homeserverhq.com/ver/getwrapversion-dev
HSHQ_WRAP_TEST_URL=https://homeserverhq.com/hshq-test.sh
HSHQ_WRAP_TEST_VER_URL=https://homeserverhq.com/ver/getwrapversion-test
HSHQ_LIB_URL=https://homeserverhq.com/hshqlib.sh
HSHQ_LIB_VER_URL=https://homeserverhq.com/ver/getversion
HSHQ_LIB_DEV_URL=https://homeserverhq.com/hshqlib-dev.sh
HSHQ_LIB_DEV_VER_URL=https://homeserverhq.com/ver/getversion-dev
HSHQ_LIB_TEST_URL=https://homeserverhq.com/hshqlib-test.sh
HSHQ_LIB_TEST_VER_URL=https://homeserverhq.com/ver/getversion-test
HSHQ_RELEASES_URL=https://homeserverhq.com/releases
HSHQ_SIG_BASE_URL=https://homeserverhq.com/signatures/
HSHQ_GPG_FINGERPRINT=5B9C33067C71ABCFCE1ACF8A7F46128ABB7C1E42
HSHQ_GPG_FINGERPRINT_SHORT=7F46128ABB7C1E42
HSHQ_LIB_TMP=$HOME/hshqlib.tmp
HSHQ_WRAP_TMP=$HOME/hshqwrap.tmp
HSHQ_LIB_FILENAME=hshqlib.sh
HSHQ_NEW_LIB_FILENAME=hshqlib.new
HSHQ_WRAP_FILENAME=hshq.sh
HSHQ_ASSETS_GIT_REPO=https://github.com/homeserverhq/assets.git
APPLICATION_FIRST_LINE="###################### Application Begin #######################"
APPLICATION_LAST_LINE="####################### Application End ########################"
INVITATION_FIRST_LINE="####################### Invitation Begin #######################"
INVITATION_LAST_LINE="######################## Invitation End ########################"
initServiceDefaults
loadPinnedDockerImages
loadDirectoryStructure
CONFIG_FILE_DEFAULT_LOCATION=$HSHQ_CONFIG_DIR
HSHQ_DB=$HSHQ_CONFIG_DIR/hshq.db
HSHQ_INSTALL_CFG=$HSHQ_BASE_DIR/installConfig.txt
HSHQ_CUSTOM_PRE_IPTABLES_SCRIPT=$HSHQ_SCRIPTS_DIR/root/userCustomPreIPTables.sh
HSHQ_CUSTOM_POST_IPTABLES_SCRIPT=$HSHQ_SCRIPTS_DIR/root/userCustomPostIPTables.sh
HSHQ_PLAINTEXT_ROOT_CONFIG=$HSHQ_CONFIG_DIR/ptRootConfig.conf
HSHQ_PLAINTEXT_USER_CONFIG=$HSHQ_CONFIG_DIR/ptUserConfig.conf
SYSTEM_STATE_FILENAME=$HSHQ_CONFIG_DIR/systemstate
SS_INIT=init
SS_INSTALLING=installing
SS_RESTORING=restoring
SS_BOOTING=booting
SS_TRANSFERRING=transferring
SS_UPDATING=updating
SS_REMOVING=removing
SS_RUNNING=running
RESTORE_SCREEN_NAME=hshqRestore
UTILS_LIST="wget|wget whiptail|whiptail awk|gawk screen|screen pwgen|pwgen argon2|argon2 dig|dnsutils htpasswd|apache2-utils ssh|ssh sshd|openssh-server sshpass|sshpass wg|wireguard-tools qrencode|qrencode openssl|openssl faketime|faketime bc|bc sipcalc|sipcalc jq|jq git|git http|httpie sqlite3|sqlite3 curl|curl sha1sum|coreutils lsb_release|lsb-release nano|nano cron|cron ping|iputils-ping route|net-tools grepcidr|grepcidr networkd-dispatcher|networkd-dispatcher certutil|libnss3-tools update-ca-certificates|ca-certificates gpg|gnupg python3|python3 pip3|python3-pip unzip|unzip hwinfo|hwinfo netplan|netplan.io netplan|openvswitch-switch uuidgen|uuid-runtime aa-enforce|apparmor-utils logrotate|logrotate yq|yq iwlist|wireless-tools sudo|sudo gcc|build-essential gio|libglib2.0-bin ffmpeg|ffmpeg php|php-cli gnupg2|gnupg2 apt-transport-https|apt-transport-https software-properties-common|software-properties-common python3-werkzeug|python3-werkzeug inotifywait|inotify-tools"
DESKTOP_APT_LIST=""
APT_REMOVE_LIST="xxd needrestart bsd-mailx"
RELAYSERVER_UTILS_LIST="curl|curl awk|gawk whiptail|whiptail nano|nano screen|screen htpasswd|apache2-utils pwgen|pwgen git|git http|httpie jq|jq sqlite3|sqlite3 wg|wireguard-tools qrencode|qrencode route|net-tools sipcalc|sipcalc mailx|mailutils ipset|ipset uuidgen|uuid-runtime grepcidr|grepcidr networkd-dispatcher|networkd-dispatcher aa-enforce|apparmor-utils logrotate|logrotate"
hshqlogo=$(cat << EOFLG
#===============================================================#
# ▀█ █ ▄▄ ▄▄ ▄▄ ▄▄▄ █▀▀▀█ ▄▄▄ ▄▄▄ ▄ ▄ ▄▄▄ ▄▄▄ █ █ █▀▀█ #
# █▀▀█ █ █ █ █ █ ▄▄ ▀▀▀▄▄ ▄▄ ▄▄▄▀ ▀▄ ▄▀ ▄▄ ▄▄▄▀ █▀▀█ █ █ #
# █ █ ▀▄▄▀ █ █ ▄▄▄ █▄▄▄█ ▄▄▄ ▄▄▄▄ ▀█▀ ▄▄▄ ▄▄▄▄ █ █ █▄▄█▄ #
#===============================================================#
EOFLG
)
hshqwarninglogo=$(cat << EOFLG
#===============================================================#
# ▀█ █ ▄▄ ▄▄ ▄▄ ▄▄▄ █▀▀▀█ ▄▄▄ ▄▄▄ ▄ ▄ ▄▄▄ ▄▄▄ █ █ █▀▀█ #
# █▀▀█ █ █ █ █ █ ▄▄ ▀▀▀▄▄ ▄▄ ▄▄▄▀ ▀▄ ▄▀ ▄▄ ▄▄▄▀ █▀▀█ █ █ #
# █ █ ▀▄▄▀ █ █ ▄▄▄ █▄▄▄█ ▄▄▄ ▄▄▄▄ ▀█▀ ▄▄▄ ▄▄▄▄ █ █ █▄▄█▄ #
#===============================================================#
***** WARNING - Config File is UNENCRYPTED! *****
EOFLG
)
if [ -d $HSHQ_SCRIPTS_DIR/source ]; then
for curSource in $HSHQ_SCRIPTS_DIR/source/*; do
if [ -f "$curSource" ]; then
source "$curSource"
fi
done
fi
}
function main()
{
init
checkWrapperVersion
IS_CONSOLE_ENV=true
IS_PERFORM_INSTALL=false
IS_NEW_LIB=false
CONNECTING_IP=""
IS_PERFORM_RESTORE=false
IS_PERFORM_UPDATE=false
case "$1" in
"install")
IS_PERFORM_INSTALL=true
CONNECTING_IP=$2
;;
"-a")
CONNECTING_IP=$2
;;
"run")
IS_NEW_LIB=$2
CONNECTING_IP=$3
;;
"restore")
CONNECTING_IP=$2
IS_PERFORM_RESTORE=true
;;
"update")
IS_PERFORM_UPDATE=true
;;
*)
;;
esac
if [ -z "$CONNECTING_IP" ]; then
CONNECTING_IP=$(getConnectingIPAddress)
fi
if [ -z "$CONNECTING_IP" ] && [ -f ~/hshq/cip.txt ]; then
CONNECTING_IP=$(cat ~/hshq/cip.txt)
fi
if [ "$IS_PERFORM_UPDATE" = "true" ]; then
setSystemState $SS_UPDATING
checkUpdateVersion
moveScriptServerPerformUpdate
if [ "$IS_RESTART_SCRIPTSERVER" = "true" ]; then
sudo systemctl daemon-reload
sudo systemctl restart runScriptServer.service
fi
setSystemState $SS_RUNNING
return
fi
if ! [ -z "$USER_SUDO_PW" ]; then
sudo -k
echo "$USER_SUDO_PW" | sudo -S -v -p "" > /dev/null 2>&1
if [ $? -ne 0 ]; then
USER_SUDO_PW=""
fi
fi
if [ "$IS_PERFORM_INSTALL" = "true" ]; then
mkdir -p $TMP_PW_CHECKDIR
read -r -s -p "" sudoPWKey
USER_SUDO_PW="$(openssl enc -d -aes256 -pbkdf2 -in $ENC_PW_FILE -pass pass:$sudoPWKey)"
rm -f $ENC_PW_FILE
refreshSudo
rm -fr $TMP_PW_CHECKDIR
clear
stty echo
if [ "$USERNAME" = "root" ]; then
strErr="main - Cannot install as root user, exiting..."
logHSHQEvent error "$strErr"
echo "$strErr"
exit 1
fi
performBaseInstallation
exit 0
fi
if [ "$IS_PERFORM_RESTORE" = "true" ]; then
mkdir -p $TMP_PW_CHECKDIR
read -r -s -p "" sudoPWKey
USER_SUDO_PW="$(openssl enc -d -aes256 -pbkdf2 -in $ENC_PW_FILE -pass pass:$sudoPWKey)"
rm -f $ENC_PW_FILE
refreshSudo
rm -fr $TMP_PW_CHECKDIR
clear
stty echo
if [ "$USERNAME" = "root" ]; then
echo "Cannot install as root user, exiting..."
exit 1
fi
performFullRestore
exit 0
fi
set +e
# Mitigate any race conditions after an update
sleep 2
tgLock="$(tryGetLock hshqopen User-Console)"
# Additional measures against race conditions
if ! [ "$tgLock" = "true" ]; then
sleep 2
tgLock="$(tryGetLock hshqopen User-Console)"
fi
if ! [ "$tgLock" = "true" ]; then
checkRes="$(getLockOpenMsg hshqopen)"
showNoYesMessageBox "Script Running" "It appears that this script is already running in another session ($checkRes). This situation could occur if another scheduled process is currently running or the script exited prematurely with an error. Unknown and damaging consequences can occur if running two different instances at the same time. Are you certain that you wish to continue?"
mbres=$?
if [ $mbres -eq 0 ]; then
exit
fi
sudo -k
USER_SUDO_PW=""
refreshSudo
if [ $? -ne 0 ]; then
exit 3
fi
forceGetLock hshqopen "User-Console"
fi
set -e
checkConfigAvailable
is_hshq_installed=false
if ! [ -z "$CONFIG_FILE" ] && [ -f $CONFIG_FILE ]; then
isInstalled=$(getConfigVarFromFile IS_INSTALLED $CONFIG_FILE)
if [ "$isInstalled" = "true" ]; then
is_hshq_installed=true
IS_CONFIG_FILE_UNENCRYPTED=true
set +e
fixConfigV130
set -e
source $CONFIG_FILE
else
is_hshq_installed=false
fi
elif ! [ -z "$ENC_CONFIG_FILE" ] && [ -f $ENC_CONFIG_FILE ]; then
# Encrypted file found, assume installed.
is_hshq_installed=true
fi
set +e
mainMenuResult=1
while [[ $mainMenuResult -ne 0 ]]
do
if [ "$is_hshq_installed" = "true" ]; then
if [ "$IS_NEW_LIB" = "true" ]; then
checkLoadConfig
fi
showInstalledMenu
else
showNotInstalledMenu
fi
mainMenuResult=$?
done
performExitFunctions true
}
function showNotInstalledMenu()
{
set +e
checkSupportedHostOS
chmod 544 ~/hshq.sh
refreshSudo
setSudoTimeoutInstall
if [[ "$(isProgramInstalled sshd)" = "false" ]]; then
sudo DEBIAN_FRONTEND=noninteractive apt update > /dev/null 2>&1
echo "Installing openssh-server, please wait..."
performAptInstall openssh-server > /dev/null 2>&1
fi
notinstalledmenu=$(cat << EOF
$(getLogo)
EOF
)
menures=$(whiptail --title "Select an option" --menu "$notinstalledmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT \
"1" "Perform Base Installation" \
"2" "Edit Configuration" \
"3" "Install Dependencies" \
"4" "Update APT Mirror" \
"5" "Uninstall and Remove Everything" \
"6" "Restore From Backup" \
"7" "Exit" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
menures=0
fi
set -e
case $menures in
0)
return 0 ;;
1)
menuPerformBaseInstall ;;
2)
checkWorkingInternet
checkUpdateAptSources false
set -e
checkLoadConfig
initConfig
nano $CONFIG_FILE
loadConfigVars true
set +e
return 1 ;;
3)
checkWorkingInternet
checkUpdateAptSources false
set -e
installDependencies
pullBaseServicesDockerImages
sudo reboot ;;
4)
set +e
checkWorkingInternet
refreshSudo
checkUpdateAptSources true
return 1 ;;
5)
nukeHSHQ
set +e
return 1 ;;
6)
checkWorkingInternet
checkUpdateAptSources false
showRestoreMenu
set +e
return 1 ;;
7)
return 0 ;;
esac
}
function menuPerformBaseInstall()
{
checkWorkingInternet
checkUpdateAptSources false
clear
checkDockerPre
set -e
clear
echo "Please wait..."
checkLoadConfig
initConfig
initCertificateAuthority
if [ "$IS_DESKTOP_ENV" = "true" ]; then
PRIMARY_VPN_SETUP_TYPE=none
updatePlaintextRootConfigVar PRIMARY_VPN_SETUP_TYPE $PRIMARY_VPN_SETUP_TYPE
else
setupVPNConnection
if [ $? -ne 0 ]; then
exit
fi
fi
if [ "$IS_INSTALLED" = "true" ] || [ "$IS_INSTALLING" = "true" ]; then
showMessageBox "Error" "Already installed or existing installation is in progress."
set +e
exit
fi
initInstallation
}
function checkDockerPre()
{
set +e
sudo docker ps > /dev/null 2>&1
if [ $? -ne 0 ]; then
if [ "$DISTRO_ID" = "debian" ] && [[ "$DISTRO_VERSION" =~ ^12. ]]; then
# Docker must be installed and system rebooted
showMessageBox "WARNING" "It appears that docker is not installed, and the OS Linux version is Debian 12. Due to recent changes, docker must be installed beforehand and the system rebooted in order to continue. After the system has rebooted, please log back in with this same user ($USERNAME) and restart the installation (bash hshq.sh). Press okay to proceed."
upgradeHostOS
sudo DEBIAN_FRONTEND=noninteractive apt dist-upgrade -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'
installDocker
sudo reboot
fi
fi
}
function upgradeHostOS()
{
if [ "$IS_DISABLE_HOST_UPGRADE" = "true" ]; then
return
fi
sudo DEBIAN_FRONTEND=noninteractive apt update
sudo DEBIAN_FRONTEND=noninteractive apt upgrade -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'
}
function checkWorkingInternet()
{
set +e
timeout 10 ping -c 1 1.1.1.1 > /dev/null 2>&1
if [ $? -ne 0 ]; then
showMessageBox "ERROR" "You do not appear to have a working internet connection. Please check your network settings and try again."
exit 2
fi
}
function verifyAptSource()
{
if ([ "$DISTRO_ID" = "debian" ] && [[ "$DISTRO_VERSION" =~ ^12. ]]) || ([ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]); then
if test -f /etc/apt/sources.list; then
echo "true"
return
fi
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^24. ]]; then
if test -f /etc/apt/sources.list.d/ubuntu.sources; then
echo "true"
return
fi
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^26. ]]; then
if test -f /etc/apt/sources.list.d/ubuntu.sources; then
echo "true"
return
fi
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^1\.1. ]]; then
if test -f /etc/apt/sources.list; then
echo "true"
return
fi
elif [ "$DISTRO_ID" = "linuxmint" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]; then
if test -f /etc/apt/sources.list.d/official-package-repositories.list; then
echo "true"
return
fi
fi
echo "false"
}
function backupOrigAptSource()
{
if ([ "$DISTRO_ID" = "debian" ] && [[ "$DISTRO_VERSION" =~ ^12. ]]) || ([ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]); then
if ! test -f /etc/apt/sources.list.orig; then
sudo cp /etc/apt/sources.list /etc/apt/sources.list.orig
fi
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^24. ]]; then
if ! test -f /etc/apt/sources.list.d/ubuntu.sources.orig; then
sudo cp /etc/apt/sources.list.d/ubuntu.sources /etc/apt/sources.list.d/ubuntu.sources.orig
fi
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^26. ]]; then
if ! test -f /etc/apt/sources.list.d/ubuntu.sources.orig; then
sudo cp /etc/apt/sources.list.d/ubuntu.sources /etc/apt/sources.list.d/ubuntu.sources.orig
fi
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^1\.1. ]]; then
if ! test -f /etc/apt/sources.list.orig; then
sudo cp /etc/apt/sources.list /etc/apt/sources.list.orig
fi
elif [ "$DISTRO_ID" = "linuxmint" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]; then
if ! test -f /etc/apt/sources.list.d/official-package-repositories.list.orig; then
sudo cp /etc/apt/sources.list.d/official-package-repositories.list /etc/apt/sources.list.d/official-package-repositories.list.orig
fi
fi
}
function checkIfOrigAptSource()
{
if ([ "$DISTRO_ID" = "debian" ] && [[ "$DISTRO_VERSION" =~ ^12. ]]) || ([ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]); then
if test -f /etc/apt/sources.list.orig; then
echo "true"
else
echo "false"
fi
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^24. ]]; then
if test -f /etc/apt/sources.list.d/ubuntu.sources.orig; then
echo "true"
else
echo "false"
fi
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^26. ]]; then
if test -f /etc/apt/sources.list.d/ubuntu.sources.orig; then
echo "true"
else
echo "false"
fi
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^1\.1. ]]; then
if test -f /etc/apt/sources.list.orig; then
echo "true"
else
echo "false"
fi
elif [ "$DISTRO_ID" = "linuxmint" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]; then
if test -f /etc/apt/sources.list.d/official-package-repositories.list.orig; then
echo "true"
else
echo "false"
fi
fi
}
function getDefaultAptSource()
{
if ([ "$DISTRO_ID" = "debian" ] && [[ "$DISTRO_VERSION" =~ ^12. ]]) || ([ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]); then
grep "^deb" /etc/apt/sources.list.orig | head -n 1 | cut -d" " -f2
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^24. ]]; then
grep "^URIs" /etc/apt/sources.list.d/ubuntu.sources.orig | head -n 1 | cut -d" " -f2
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^26. ]]; then
grep "^URIs" /etc/apt/sources.list.d/ubuntu.sources.orig | head -n 1 | cut -d" " -f2
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^1\.1. ]]; then
grep "^deb" /etc/apt/sources.list.orig | head -n 1 | cut -d" " -f2
elif [ "$DISTRO_ID" = "linuxmint" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]; then
grep "^URIs" /etc/apt/sources.list.d/official-package-repositories.list.orig | head -n 1 | cut -d" " -f2
fi
}
function backupAptSource()
{
if ([ "$DISTRO_ID" = "debian" ] && [[ "$DISTRO_VERSION" =~ ^12. ]]) || ([ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]); then
sudo cp -f /etc/apt/sources.list /etc/apt/sources.list.backup
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^24. ]]; then
sudo cp -f /etc/apt/sources.list.d/ubuntu.sources /etc/apt/sources.list.d/ubuntu.sources.backup
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^26. ]]; then
sudo cp -f /etc/apt/sources.list.d/ubuntu.sources /etc/apt/sources.list.d/ubuntu.sources.backup
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^1\.1. ]]; then
sudo cp -f /etc/apt/sources.list /etc/apt/sources.list.backup
elif [ "$DISTRO_ID" = "linuxmint" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]; then
sudo cp -f /etc/apt/sources.list.d/official-package-repositories.list /etc/apt/sources.list.d/official-package-repositories.list.backup
fi
}
function removeBackupAptSource()
{
if ([ "$DISTRO_ID" = "debian" ] && [[ "$DISTRO_VERSION" =~ ^12. ]]) || ([ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]); then
sudo rm -f /etc/apt/sources.list.backup
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^24. ]]; then
sudo rm -f /etc/apt/sources.list.d/ubuntu.sources.backup
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^26. ]]; then
sudo rm -f /etc/apt/sources.list.d/ubuntu.sources.backup
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^1\.1. ]]; then
sudo rm -f /etc/apt/sources.list.backup
elif [ "$DISTRO_ID" = "linuxmint" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]; then
sudo rm -f /etc/apt/sources.list.d/official-package-repositories.list.backup
fi
}
function restoreBackupAptSource()
{
if ([ "$DISTRO_ID" = "debian" ] && [[ "$DISTRO_VERSION" =~ ^12. ]]) || ([ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]); then
sudo cp -f /etc/apt/sources.list.backup /etc/apt/sources.list
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^24. ]]; then
sudo cp -f /etc/apt/sources.list.d/ubuntu.sources.backup /etc/apt/sources.list.d/ubuntu.sources
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^26. ]]; then
sudo cp -f /etc/apt/sources.list.d/ubuntu.sources.backup /etc/apt/sources.list.d/ubuntu.sources
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^1\.1. ]]; then
sudo cp -f /etc/apt/sources.list.backup /etc/apt/sources.list
elif [ "$DISTRO_ID" = "linuxmint" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]; then
sudo cp -f /etc/apt/sources.list.d/official-package-repositories.list.backup /etc/apt/sources.list.d/official-package-repositories.list
fi
}
function restoreOrigAptSource()
{
if ([ "$DISTRO_ID" = "debian" ] && [[ "$DISTRO_VERSION" =~ ^12. ]]) || ([ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]); then
sudo cp -f /etc/apt/sources.list.orig /etc/apt/sources.list
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^24. ]]; then
sudo cp -f /etc/apt/sources.list.d/ubuntu.sources.orig /etc/apt/sources.list.d/ubuntu.sources
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^26. ]]; then
sudo cp -f /etc/apt/sources.list.d/ubuntu.sources.orig /etc/apt/sources.list.d/ubuntu.sources
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^1\.1. ]]; then
sudo cp -f /etc/apt/sources.list.orig /etc/apt/sources.list
elif [ "$DISTRO_ID" = "linuxmint" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]; then
sudo cp -f /etc/apt/sources.list.d/official-package-repositories.list.orig /etc/apt/sources.list.d/official-package-repositories.list
fi
}
function replaceAptMirror()
{
aptUrl="$1"
if [ "$DISTRO_ID" = "debian" ] && [[ "$DISTRO_VERSION" =~ ^12. ]]; then
sudo sed -i "/security/! s|deb [a-z]*://[^ ]* |deb ${aptUrl} |g" /etc/apt/sources.list
sudo sed -i "/security/! s|deb-src [a-z]*://[^ ]* |deb-src ${aptUrl} |g" /etc/apt/sources.list
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]; then
sudo sed -i "s|deb [a-z]*://[^ ]* |deb ${aptUrl} |g" /etc/apt/sources.list
sudo sed -i "s|deb-src [a-z]*://[^ ]* |deb-src ${aptUrl} |g" /etc/apt/sources.list
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^24. ]]; then
sudo sed -i "s|^URIs.*|URIs: ${aptUrl} |g" /etc/apt/sources.list.d/ubuntu.sources
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^26. ]]; then
sudo sed -i "s|^URIs.*|URIs: ${aptUrl} |g" /etc/apt/sources.list.d/ubuntu.sources
elif [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^1\.1. ]]; then
sudo sed -i "s|deb [a-z]*://[^ ]* |deb ${aptUrl} |g" /etc/apt/sources.list
sudo sed -i "s|deb-src [a-z]*://[^ ]* |deb-src ${aptUrl} |g" /etc/apt/sources.list
elif [ "$DISTRO_ID" = "linuxmint" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]; then
sudo sed -i "/linuxmint/! s|deb [a-z]*://[^ ]* |deb ${aptUrl} |g" /etc/apt/sources.list.d/official-package-repositories.list
fi
}
function checkUpdateAptSources()
{
isForceCheck="$1"
set +e
if [ "$IS_AUTO_INSTALL" = "true" ]; then
return
fi
if ! [ "$(verifyAptSource)" = "true" ]; then
showMessageBox "ERROR" "There was a problem with your apt sources list. Proceeding with current settings..."
return
fi
if [ "$(checkIfOrigAptSource)" = "true" ] && ! [ "$isForceCheck" = "true" ]; then
performAptUpdate false
return
fi
if ! [ "$isForceCheck" = "true" ]; then
showYesNoMessageBox "Find Mirrors" "Would you like to check for nearby apt mirrors? This process will take around 1-2 minutes to complete, but it will likely save a lot more time in the future when performing Linux host updates."
if [ $? -ne 0 ]; then
backupOrigAptSource
performAptUpdate false
return
fi
fi
backupOrigAptSource
default_apt_src="$(getDefaultAptSource)"
backupAptSource
echo "========================================================================"
echo " Calculating the closest apt mirrors."
echo " This could take a minute or two, please wait..."
echo "========================================================================"
mirrorResults="$(getFastestMirrors)"
if [ -z "$mirrorResults" ]; then
showMessageBox "ERROR" "There was a problem obtaining mirror info, continuing with current settings..."
removeBackupAptSource
performAptUpdate false
return
fi
OLDIFS=$IFS
IFS=$(echo -en "\n\b")
mirrorResultsArr=($(echo "$mirrorResults"))
IFS=$OLDIFS
aptupdatemenu=$(cat << EOF
$(getLogo)
Select your desired apt sources mirror from the list below.
To keep the current settings, select Cancel.
EOF
)
uas_menu_items=( --title "Select Mirror" --radiolist "$aptupdatemenu" $(($MENU_HEIGHT+2)) $MENU_WIDTH $MENU_INT_HEIGHT )
curNum=1
for curMirror in "${mirrorResultsArr[@]}"
do
uas_menu_items+=( "$(echo $curMirror | xargs)" )
uas_menu_items+=( "|" )
if [ $curNum -eq 1 ]; then
uas_menu_items+=( "on" )
else
uas_menu_items+=( "off" )
fi
((curNum++))
done
while true;
do
selItem=$(whiptail "${uas_menu_items[@]}" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
restoreBackupAptSource
removeBackupAptSource
performAptUpdate false
return
fi
selAptSource=$(echo "$selItem" | xargs | rev | cut -d" " -f1 | rev)
replaceAptMirror "$selAptSource"
tmpOutput=/tmp/aptOut.txt
rm -f $tmpOutput
performAptUpdate false 2>&1 | tee $tmpOutput
grep -e "^Err:" -e "Failed" $tmpOutput > /dev/null 2>&1
chkRes=$?
rm -f $tmpOutput
if [ $chkRes -eq 0 ]; then
errmenu=$(cat << EOF
$(getLogo)
There were some errors found with the selected mirror.
Select Retry to select a different mirror, or select
Revert to restore the original default.
EOF
)
restoreOrigAptSource
if (whiptail --title "Error" --yesno "$errmenu" $MENU_HEIGHT $MENU_WIDTH --yes-button "Retry" --no-button "Revert" ); then
continue
else
performAptUpdate false
break
fi
else
echo -e "========================================================================\n"
echo -e "The selected mirror appears to be in good working order. (See above)\n"
isBreakMainLoop=false
while true;
do
echo "Enter 'confirm': To proceed with this option"
echo " 'retry': To select a different mirror from the list"
echo " 'revert': To restore the prior settings and continue"
echo " 'orig': To restore the original default and continue"
echo " 'exit': To restore the prior settings and exit the HSHQ utility"
echo
read -r -p "Your selection: " selMirrorOption
if [ "$selMirrorOption" = "confirm" ]; then
isBreakMainLoop=true
break
elif [ "$selMirrorOption" = "retry" ]; then
restoreBackupAptSource
isBreakMainLoop=false
break
elif [ "$selMirrorOption" = "revert" ]; then
restoreBackupAptSource
isBreakMainLoop=true
performAptUpdate false
break
elif [ "$selMirrorOption" = "orig" ]; then
restoreOrigAptSource
isBreakMainLoop=true
performAptUpdate false
break
elif [ "$selMirrorOption" = "exit" ]; then
restoreBackupAptSource
removeBackupAptSource
performExitFunctions true
exit
else
echo "Unknown response, please retry."
fi
done
if [ "$isBreakMainLoop" = "true" ]; then
break
fi
fi
done
removeBackupAptSource
}
function performAptUpdate()
{
clear
isQuietUpdate="$1"
if [ "$isQuietUpdate" = "true" ]; then
echo "Performing apt update, please wait..."
sudo DEBIAN_FRONTEND=noninteractive apt update > /dev/null
else
sudo DEBIAN_FRONTEND=noninteractive apt update
fi
}
function getFastestMirrors()
{
# Some of the concepts of this function were adopted from
# https://github.com/vegardit/fast-apt-mirror.sh/blob/v1/fast-apt-mirror.sh
set +e
distID=""
last_modified_path=""
if [ "$DISTRO_ID" = "debian" ]; then
distID="debian"
elif [ "$DISTRO_ID" = "ubuntu" ] || [ "$DISTRO_ID" = "linuxmint" ]; then
distID="ubuntu"
else
# Fatal
exit 1
fi
cd ~
backupOrigAptSource
default_mirror="$(getDefaultAptSource)"
which netselect > /dev/null 2>&1
if [ $? -ne 0 ]; then
netselect_version='0.3.ds1-30.1'
wget http://ftp.debian.org/debian/pool/main/n/netselect/netselect_${netselect_version}_${DISTRO_ARCH}.deb > /dev/null 2>&1
sudo apt install ./netselect_${netselect_version}_${DISTRO_ARCH}.deb > /dev/null 2>&1
rm -f ./netselect_${netselect_version}_${DISTRO_ARCH}.deb
fi
rm -f /tmp/mirrors.txt
outputMirrorsList
OLDIFS=$IFS
IFS=$(echo -en "\n\b")
fastestMirrorsArr=($(sudo netselect -s50 -t4 $(grep "^$distID" /tmp/mirrors.txt | cut -d" " -f2) 2>/dev/null))
IFS=$OLDIFS
rm -f /tmp/mirrors.txt
mirrorsDomainList=$(echo "$default_mirror" | tr -s "/" | cut -d"/" -f2)
mirrorsURLsList="$default_mirror"
maxMirrors=15
curMirrorCount=1
curDTSeconds=$(date +%s)
last_modified_path="ls-lR.gz"
for curMirror in "${fastestMirrorsArr[@]}"
do
curURL=$(echo "$curMirror" | xargs | cut -d" " -f2)
curDomain=$(echo "$curURL" | tr -s "/" | cut -d"/" -f2)
echo "$mirrorsDomainList" | grep "$curDomain" > /dev/null 2>&1
if [ $? -ne 0 ]; then
mirrorsDomainList="$mirrorsDomainList $curDomain"
# Check last modified date
lastModified=$(curl --max-time 3 -sSfL --head "$curURL${last_modified_path}" 2>/dev/null | grep -i "last-modified" | cut -d" " -f2- | LANG=C date -f- -u +%s)
if ! [ -z "$lastModified" ] && [ "$(checkValidNumber $lastModified)" = "true" ] && [ $lastModified -gt 0 ]; then
secsDiff=$(($curDTSeconds-$lastModified))
# 2592000 is equivalent to last 30 days
if [ $secsDiff -ge 2592000 ]; then
continue
fi
else
continue
fi
mirrorsURLsList="$mirrorsURLsList\n$curURL"
((curMirrorCount++))
if [ $curMirrorCount -ge $maxMirrors ]; then
break
fi
fi
done
echo -e "$mirrorsURLsList" | xargs -P 5 -i bash -c "curl -r 0-5120000 --max-time 5 -sf -w '%{speed_download} {}\n' -o /dev/null {}ls-lR.gz 2>/dev/null" | awk '$1 > 0' | awk '{printf "%.2f MB/s %s\n",$1/1048576,$2}' | sort -rg | head -n 7
}
function outputMirrorsList()
{
# We'll just manage the list right here
# to reduce the potential points of failure
cat <<EOFML > /tmp/mirrors.txt
debian http://ftp.am.debian.org/debian/
debian http://ftp.au.debian.org/debian/
debian http://ftp.at.debian.org/debian/
debian http://ftp.by.debian.org/debian/
debian http://ftp.be.debian.org/debian/
debian http://ftp.br.debian.org/debian/
debian http://ftp.bg.debian.org/debian/
debian http://ftp.ca.debian.org/debian/
debian http://ftp.cl.debian.org/debian/
debian http://ftp.cn.debian.org/debian/
debian http://ftp.hr.debian.org/debian/
debian http://ftp.cz.debian.org/debian/
debian http://ftp.dk.debian.org/debian/
debian http://ftp.fi.debian.org/debian/
debian http://ftp.fr.debian.org/debian/
debian http://ftp2.de.debian.org/debian/
debian http://ftp.de.debian.org/debian/
debian http://ftp.hk.debian.org/debian/
debian http://ftp.is.debian.org/debian/
debian http://ftp.it.debian.org/debian/
debian http://ftp.jp.debian.org/debian/
debian http://ftp.kr.debian.org/debian/
debian http://ftp.lt.debian.org/debian/
debian http://ftp.md.debian.org/debian/
debian http://ftp.nl.debian.org/debian/
debian http://ftp.nc.debian.org/debian/
debian http://ftp.nz.debian.org/debian/
debian http://ftp.no.debian.org/debian/
debian http://ftp.pl.debian.org/debian/
debian http://ftp.pt.debian.org/debian/
debian http://ftp.sk.debian.org/debian/
debian http://ftp.si.debian.org/debian/
debian http://ftp.es.debian.org/debian/
debian http://ftp.se.debian.org/debian/
debian http://ftp.ch.debian.org/debian/
debian http://ftp.tw.debian.org/debian/
debian http://ftp.tr.debian.org/debian/
debian http://ftp.uk.debian.org/debian/
debian http://ftp.us.debian.org/debian/
debian http://mirror.abmanagement.al/debian/
debian http://debian.unnoba.edu.ar/debian/
debian http://mirror.sitsa.com.ar/debian/
debian http://mirrors.asnet.am/debian/
debian http://debian.mirror.digitalpacific.com.au/debian/
debian http://debian.mirror.serversaustralia.com.au/debian/
debian http://mirror.aarnet.edu.au/debian/
debian http://mirror.amaze.com.au/debian/
debian http://mirror.gsl.icu/debian/
debian http://mirror.linux.org.au/debian/
debian http://mirror.overthewire.com.au/debian/
debian http://mirror.realcompute.io/debian/
debian http://mirrors.xtom.au/debian/
debian http://debian.anexia.at/debian/
debian http://debian.lagis.at/debian/
debian http://debian.mur.at/debian/
debian http://debian.sil.at/debian/
debian http://ftp.tu-graz.ac.at/mirror/debian/
debian http://mirror.alwyzon.net/debian/
debian http://mirror.ourhost.az/debian/
debian http://mirror.limda.net/debian/
debian http://mirror.xeonbd.com/debian/
debian http://ftp.byfly.by/debian/
debian http://mirror.datacenter.by/debian/
debian http://debian-mirror.behostings.net/debian/
debian http://ftp.belnet.be/debian/
debian http://mirror.as35701.net/debian/
debian http://mirror.unix-solutions.be/debian/
debian http://debian.c3sl.ufpr.br/debian/
debian http://debian.pop-sc.rnp.br/debian/
debian http://mirror.blue3.com.br/debian/
debian http://mirrors.ic.unicamp.br/debian/
debian http://mirror.uepg.br/debian/
debian http://mirror.ufscar.br/debian/
debian http://debian.a1.bg/debian/
debian http://debian.ipacct.com/debian/
debian http://debian.ludost.net/debian/
debian http://debian.mnet.bg/debian/
debian http://debian.telecoms.bg/debian/
debian http://ftp.uni-sofia.bg/debian/
debian http://mirrors.netix.net/debian/
debian http://mirror.telepoint.bg/debian/
debian http://mirror.sabay.com.kh/debian/
debian http://debian.linux.n0c.ca/debian/
debian http://debian.mirror.rafal.ca/debian/
debian http://mirror.csclub.uwaterloo.ca/debian/
debian http://mirror.dst.ca/debian/
debian http://mirror.estone.ca/debian/
debian http://mirror.it.ubc.ca/debian/
debian http://debian-mirror.puq.apoapsis.cl/debian/
debian http://debian.redlibre.cl/debian/
debian http://mirror.hnd.cl/debian/
debian http://mirror.insacom.cl/debian/
debian http://mirror.ufro.cl/debian/
debian http://mirror.lzu.edu.cn/debian/
debian http://mirror.nju.edu.cn/debian/
debian http://mirror.nyist.edu.cn/debian/
debian http://mirrors.163.com/debian/
debian http://mirrors.bfsu.edu.cn/debian/
debian http://mirrors.hit.edu.cn/debian/
debian http://mirrors.jlu.edu.cn/debian/
debian http://mirrors.neusoft.edu.cn/debian/
debian http://mirrors.tuna.tsinghua.edu.cn/debian/
debian http://mirrors.ustc.edu.cn/debian/
debian http://mirrors.zju.edu.cn/debian/
debian http://mirrors.ucr.ac.cr/debian/
debian http://debian.carnet.hr/debian/
debian http://debian.iskon.hr/debian/
debian http://debian.superhosting.cz/debian/
debian http://ftp.cvut.cz/debian/
debian http://ftp.debian.cz/debian/
debian http://ftp.sh.cvut.cz/debian/
debian http://ftp.zcu.cz/debian/
debian http://merlin.fit.vutbr.cz/debian/
debian http://mirror.dkm.cz/debian/
debian http://mirror.it4i.cz/debian/
debian http://mirrors.nic.cz/debian/
debian http://mirror.one.com/debian/
debian http://mirrors.dotsrc.org/debian/
debian http://mirrors.rackhosting.com/debian/
debian http://mirrors.xtom.ee/debian/
debian http://debian.web.trex.fi/debian/
debian http://www.nic.funet.fi/debian/
debian http://apt.tetaneutral.net/debian/
debian http://debian.apt-mirror.de/debian/
debian http://debian.mirrors.ovh.net/debian/
debian http://debian.obspm.fr/debian/
debian http://debian.polytech-lille.fr/debian/
debian http://debian.proxad.net/debian/
debian http://debian.univ-tlse2.fr/debian/
debian http://deb-mir1.naitways.net/debian/
debian http://deb.syxpi.fr/debian/
debian http://ftp.ec-m.fr/debian/
debian http://ftp.lip6.fr/pub/linux/distributions/debian/
debian http://ftp.rezopole.net/debian/
debian http://ftp.univ-pau.fr/linux/mirrors/debian/
debian http://ftp.u-picardie.fr/debian/
debian http://ftp.u-strasbg.fr/debian/
debian http://miroir.univ-lorraine.fr/debian/
debian http://mirror.debian.ikoula.com/debian/
debian http://mirror.gitoyen.net/debian/
debian http://mirror.ibcp.fr/debian/
debian http://mirror.johnnybegood.fr/debian/
debian http://mirror.plusserver.com/debian/debian/
debian http://mirrors.ircam.fr/pub/debian/
debian http://debian.grena.ge/debian/
debian http://debian.charite.de/debian/
debian http://debian.inf.tu-dresden.de/debian/
debian http://debian.intergenia.de/debian/
debian http://debian.mirror.iphh.net/debian/
debian http://debian.mirror.lrz.de/debian/
debian http://debian.netcologne.de/debian/
debian http://debian.tu-bs.de/debian/
debian http://de.mirrors.clouvider.net/debian/
debian http://drmirror.physi.uni-heidelberg.de/debian/
debian http://ftp.fau.de/debian/
debian http://ftp.gwdg.de/debian/
debian http://ftp.halifax.rwth-aachen.de/debian/
debian http://ftp.hosteurope.de/mirror/ftp.debian.org/debian/
debian http://ftp-stud.hs-esslingen.de/debian/
debian http://ftp.stw-bonn.de/debian/
debian http://ftp.tu-chemnitz.de/debian/
debian http://ftp.tu-clausthal.de/debian/
debian http://ftp.uni-bayreuth.de/debian/
debian http://ftp.uni-hannover.de/debian/debian/
debian http://ftp.uni-kl.de/debian/
debian http://ftp.uni-mainz.de/debian/
debian http://ftp.uni-stuttgart.de/debian/
debian http://ftp.wrz.de/debian/
debian http://mirror.23m.com/debian/
debian http://mirror.creoline.net/debian/
debian http://mirror.de.leaseweb.net/debian/
debian http://mirror.dogado.de/debian/
debian http://mirror.eu.oneandone.net/debian/
debian http://mirror.informatik.tu-freiberg.de/debian/
debian http://mirror.ipb.de/debian/
debian http://mirror.netzwerge.de/debian/
debian http://mirror.plusline.net/debian/
debian http://mirrors.xtom.de/debian/
debian http://mirror.united-gameserver.de/debian/
debian http://mirror.wtnet.de/debian/
debian http://packages.hs-regensburg.de/debian/
debian http://pubmirror.plutex.de/debian/
debian http://debian.otenet.gr/debian/
debian http://mirror.xtom.com.hk/debian/
debian http://ftp.bme.hu/debian/
debian http://repo.jztkft.hu/debian/
debian http://is.mirror.flokinet.net/debian/
debian http://mirror.cse.iitk.ac.in/debian/
debian http://mirror.nitc.ac.in/debian/
debian http://mirrors.iitd.ac.in/debian/
debian http://kartolo.sby.datautama.net.id/debian/
debian http://kebo.pens.ac.id/debian/
debian http://mirror.poliwangi.ac.id/debian/
debian http://mirror.unair.ac.id/debian/
debian http://mr.heru.id/debian/
debian http://mirror.aminidc.com/debian/
debian http://mirrors.pardisco.co/debian/
debian http://debian.interhost.co.il/debian/
debian http://debian.connesi.it/debian/
debian http://debian.dynamica.it/debian/
debian http://debian.mirror.garr.it/debian/
debian http://ftp.linux.it/debian/
debian http://giano.com.dist.unige.it/debian/
debian http://mirror.units.it/debian/
debian http://debian-mirror.sakura.ne.jp/debian/
debian http://ftp.jaist.ac.jp/debian/
debian http://ftp.kddilabs.jp/pub/debian/
debian http://ftp.nara.wide.ad.jp/debian/
debian http://ftp.riken.jp/Linux/debian/debian/
debian http://ftp.yz.yamagata-u.ac.jp/debian/
debian http://mirrors.xtom.jp/debian/
debian http://repo.jing.rocks/debian/
debian http://mirror.hoster.kz/debian/
debian http://mirror.ps.kz/debian/
debian http://debian.mirror.ac.ke/debian/
debian http://debian.mirror.liquidtelecom.com/debian/
debian http://ftp.kaist.ac.kr/debian/
debian http://ftp.lanet.kr/debian/
debian http://mirror.siwoo.org/debian/
debian http://debian.koyanet.lv/debian/
debian http://mirror.cloudhosting.lv/debian/
debian http://mirror.veesp.com/debian/
debian http://debian.mirror.vu.lt/debian/
debian http://mirror.litnet.lt/debian/
debian http://mirror.vpsnet.com/debian/
debian http://debian.mirror.root.lu/debian/
debian http://mirror.a1.mk/debian/
debian http://mirror-mk.interspace.com/debian/
debian http://lidsol.fi-b.unam.mx/debian/
debian http://mirror.as43289.net/debian/
debian http://mirror.marwan.ma/debian/
debian http://debian.snt.utwente.nl/debian/
debian http://mirror.ams.macarne.com/debian/
debian http://mirror.bgp.rodeo/debian/
debian http://mirror.duocast.net/debian/
debian http://mirror.i3d.net/debian/
debian http://mirror.nforce.com/debian/
debian http://mirror.nl.cdn-perfprod.com/debian/
debian http://mirror.nl.datapacket.com/debian/
debian http://mirror.nl.leaseweb.net/debian/
debian http://mirror.nl.mirhosting.net/debian/
debian http://mirrors.hostiserver.com/debian/
debian http://mirrors.xtom.nl/debian/
debian http://mirror.tngnet.com/debian/
debian http://mirror.vpgrp.io/debian/
debian http://nl.mirror.flokinet.net/debian/
debian http://nl.mirrors.clouvider.net/debian/
debian http://debian.nautile.nc/debian/
debian http://mirror.lagoon.nc/debian/
debian http://linux.purple-cat.net/debian/
debian http://mirror.fsmg.org.nz/debian/
debian http://ftp.uio.no/debian/
debian http://ftp.agh.edu.pl/debian/
debian http://ftp.psnc.pl/debian/
debian http://ftp.task.gda.pl/debian/
debian http://debian.uevora.pt/debian/
debian http://ftp.eq.uc.pt/software/Linux/debian/
debian http://ftp.rnl.tecnico.ulisboa.pt/pub/debian/
debian http://mirror.leitecastro.com/debian/
debian http://mirrors.upr.edu/debian/
debian http://debian.mithril.re/debian/
debian http://mirror.flo.c-f.ro/debian/
debian http://mirror.linux.ro/debian/
debian http://mirror.ro.cdn-perfprod.com/debian/
debian http://mirrors.hosterion.ro/debian/
debian http://mirrors.hostico.ro/debian/
debian http://mirrors.nav.ro/debian/
debian http://mirrors.nxthost.com/debian/
debian http://mirrors.pidginhost.com/debian/
debian http://ro.mirror.flokinet.net/debian/
debian http://ftp.psn.ru/debian/
debian http://mirror.corbina.net/debian/
debian http://mirror.docker.ru/debian/
debian http://mirror.hyperdedic.ru/debian/
debian http://mirror.kpfu.ru/debian/
debian http://mirrors.powernet.com.ru/debian/
debian http://mirror.truenetwork.ru/debian/
debian http://repository.su/debian/
debian http://mirror.pmf.kg.ac.rs/debian/
debian http://mirror.coganng.com/debian/
debian http://mirror.djvg.sg/debian/
debian http://mirror.sg.gs/debian/
debian http://ossmirror.mycloud.services/debian/
debian http://deb.bbxnet.sk/debian/
debian http://ftp.antik.sk/debian/
debian http://ftp.debian.sk/debian/
debian http://debian.mirror.ac.za/debian/
debian http://ftp.is.co.za/debian/
debian http://debian.grn.cat/debian/
debian http://debian.redimadrid.es/debian/
debian http://debian.redparra.com/debian/
debian http://debian.uvigo.es/debian/
debian http://ftp.caliu.cat/debian/
debian http://ftp.cica.es/debian/
debian http://ftp.udc.es/debian/
debian http://mirror.raiolanetworks.com/debian/
debian http://repo.ifca.es/debian/
debian http://softlibre.unizar.es/debian/
debian http://ulises.hostalia.com/debian/
debian http://debian.lth.se/debian/
debian http://debian.mirror.su.se/debian/
debian http://ftp.acc.umu.se/debian/
debian http://ftpmirror1.infania.net/debian/
debian http://mirror.braindrainlan.nu/debian/
debian http://mirrors.glesys.net/debian/
debian http://debian.ethz.ch/debian/
debian http://linuxsoft.cern.ch/debian/
debian http://mirror1.infomaniak.com/debian/
debian http://mirror2.infomaniak.com/debian/
debian http://mirror.init7.net/debian/
debian http://mirror.iway.ch/debian/
debian http://mirror.sinavps.ch/debian/
debian http://pkg.adfinis-on-exoscale.ch/debian/
debian http://debian.ccns.ncku.edu.tw/debian/
debian http://debian.csie.ntu.edu.tw/debian/
debian http://debian.cs.nycu.edu.tw/debian/
debian http://ftp.tku.edu.tw/debian/
debian http://mirror.twds.com.tw/debian/
debian http://opensource.nchc.org.tw/debian/
debian http://tw1.mirror.blendbyte.net/debian/
debian http://ftp.debianclub.org/debian/
debian http://mirror.applebred.net/debian/
debian http://mirror.kku.ac.th/debian/
debian http://mirrors.cloudforest.co.th/debian/
debian http://ftp.linux.org.tr/debian/
debian http://debian.netforce.hosting/debian/
debian http://debian.volia.net/debian/
debian http://distrohub.kyiv.ua/debian/
debian http://mirror.mirohost.net/debian/
debian http://mirror.ukrnames.com/debian/
debian http://debian.mirrors.uk2.net/debian/
debian http://debian.mirror.uk.sargasso.net/debian/
debian http://free.hands.com/debian/
debian http://ftp.ticklers.org/debian/
debian http://mirror.cov.ukservers.com/debian/
debian http://mirror.lchost.net/debian/
debian http://mirror.mythic-beasts.com/debian/
debian http://mirror.ox.ac.uk/debian/
debian http://mirror.positive-internet.com/debian/
debian http://mirrors.coreix.net/debian/
debian http://mirrorservice.org/sites/ftp.debian.org/debian/
debian http://mirror.vinehost.net/debian/
debian http://uk.mirrors.clouvider.net/debian/
debian http://atl.mirrors.clouvider.net/debian/
debian http://debian-archive.trafficmanager.net/debian/
debian http://debian.cc.lehigh.edu/debian/
debian http://debian.csail.mit.edu/debian/
debian http://debian.cs.binghamton.edu/debian/
debian http://debian.mirror.constant.com/debian/
debian http://debian.osuosl.org/debian/
debian http://debian.uchicago.edu/debian/
debian http://la.mirrors.clouvider.net/debian/
debian http://lethe.chinstrap.org/debian/
debian http://mirror.0x626b.com/debian/
debian http://mirror.cogentco.com/debian/
debian http://mirror.dal.nexril.net/debian/
debian http://mirror.keystealth.org/debian/
debian http://mirror.rustytel.net/debian/
debian http://mirrors.accretive-networks.net/debian/
debian http://mirrors.bloomu.edu/debian/
debian http://mirrors.bmcc.edu/debian/
debian http://mirrors.iu13.net/debian/
debian http://mirrors.lug.mtu.edu/debian/
debian http://mirrors.mwcampbell.us/debian/
debian http://mirrors.namecheap.com/debian/
debian http://mirrors.ocf.berkeley.edu/debian/
debian http://mirror.steadfast.net/debian/
debian http://mirrors.vcea.wsu.edu/debian/
debian http://mirrors.wikimedia.org/debian/
debian http://mirrors.xtom.com/debian/
debian http://mirror.timkevin.us/debian/
debian http://mirror.us.leaseweb.net/debian/
debian http://mirror.us.mirhosting.net/debian/
debian http://mirror.us.oneandone.net/debian/
debian http://nyc.mirrors.clouvider.net/debian/
debian http://plug-mirror.rcac.purdue.edu/debian/
debian http://repo.ialab.dsu.edu/debian/
debian http://us.mirror.ahrefs.org/debian/
debian http://debian.repo.cure.edu.uy/debian/
debian http://debian.xtdv.net/debian/
debian http://mirror.bizflycloud.vn/debian/
ubuntu http://mirror.sitsa.com.ar/ubuntu/
ubuntu http://ubuntu.zero.com.ar/ubuntu/
ubuntu http://ubuntu.unc.edu.ar/ubuntu/
ubuntu http://mirrors.asnet.am/ubuntu/
ubuntu http://ubuntu.mirror.gnc.am/ubuntu/
ubuntu http://mirrors.teamcloud.am/mirrors/ubuntu/
ubuntu http://mirror.aarnet.edu.au/pub/ubuntu/archive/
ubuntu http://mirror.internet.asn.au/pub/ubuntu/archive/
ubuntu http://mirror.datamossa.io/ubuntu/archive/
ubuntu http://mirror.realcompute.io/ubuntu/
ubuntu http://ubuntu.mirror.serversaustralia.com.au/ubuntu/
ubuntu http://ftp.iinet.net.au/pub/ubuntu/
ubuntu ftp://ftp.iinet.net.au/pub/ubuntu
ubuntu http://mirror.as24220.net/pub/ubuntu-archive/
ubuntu ftp://mirror.as24220.net/pub/ubuntu-archive/
ubuntu http://mirror.as24220.net/pub/ubuntu/
ubuntu ftp://mirror.as24220.net/pub/ubuntu/
ubuntu http://mirror.internode.on.net/pub/ubuntu/ubuntu/
ubuntu ftp://mirror.internode.on.net/pub/ubuntu/ubuntu/
ubuntu http://mirror.netspace.net.au/pub/ubuntu/
ubuntu ftp://mirror.netspace.net.au/pub/ubuntu/
ubuntu http://mirror.overthewire.com.au/ubuntu/
ubuntu ftp://mirror.overthewire.com.au/ubuntu/
ubuntu http://mirror.alwyzon.net/ubuntu/
ubuntu http://mirror.easyname.at/ubuntu-archive/
ubuntu ftp://mirror.easyname.at/ubuntu-archive/
ubuntu http://ubuntu.anexia.at/ubuntu/
ubuntu http://mirror.kumi.systems/ubuntu/
ubuntu ftp://mirror.kumi.systems/ubuntu/
ubuntu http://mirror.kumi.systems/ubuntu-ports/
ubuntu ftp://mirror.kumi.systems/ubuntu-ports/
ubuntu http://ubuntu.lagis.at/ubuntu/
ubuntu ftp://ubuntu.lagis.at/ubuntu/
ubuntu http://mirror.datacenter.az/ubuntu/
ubuntu http://mirror.ourhost.az/ubuntu/
ubuntu http://aze.archive.ubuntu.com/ubuntu/
ubuntu ftp://aze.archive.ubuntu.com/ubuntu/
ubuntu http://mirror.yer.az/ubuntu/archive/
ubuntu http://mirror.gotmyhost.com/ubuntu-archive/
ubuntu http://mirror.limda.net/ubuntu-archive/
ubuntu http://mirror.xeonbd.com/ubuntu-archive/
ubuntu http://ftp.byfly.by/ubuntu/
ubuntu ftp://ftp.byfly.by/ubuntu/
ubuntu http://mirror.datacenter.by/ubuntu/
ubuntu ftp://mirror.datacenter.by/ubuntu/
ubuntu http://ftp.belnet.be/ubuntu/
ubuntu ftp://ftp.belnet.be/ubuntu/
ubuntu http://mirror.unix-solutions.be/ubuntu/
ubuntu http://mirrors-ubuntu.behostings.com/ubuntu/
ubuntu http://archive.ubuntu.mirror.ba/ubuntu/
ubuntu http://ubuntu-mirror.bhtelecom.ba/ubuntu/
ubuntu http://mirror.uepg.br/ubuntu/
ubuntu http://ubuntu.c3sl.ufpr.br/ubuntu/
ubuntu ftp://ubuntu.c3sl.ufpr.br/ubuntu/
ubuntu http://ubuntu.mirror.letscloud.io/
ubuntu http://mirror.ufam.edu.br/ubuntu/
ubuntu http://mirror.ufscar.br/ubuntu/
ubuntu http://mirror.unesp.br/ubuntu/
ubuntu http://sft.if.usp.br/ubuntu/
ubuntu ftp://sft.if.usp.br/ubuntu/
ubuntu http://ubuntu-archive.locaweb.com.br/ubuntu/
ubuntu http://ubuntu.letscloud.io/ubuntu/
ubuntu http://ubuntu.mti.mt.gov.br/
ubuntu http://mirror.telepoint.bg/ubuntu/
ubuntu ftp://mirror.telepoint.bg/ubuntu/
ubuntu http://mirrors.neterra.net/ubuntu/archive/
ubuntu ftp://mirrors.neterra.net/ubuntu/archive/
ubuntu http://mirrors.netix.net/ubuntu/archive/
ubuntu ftp://mirrors.netix.net/ubuntu/archive/
ubuntu http://ubuntu.ipacct.com/ubuntu/
ubuntu ftp://ubuntu.ipacct.com/ubuntu/
ubuntu http://mirrors.storpool.com/ubuntu/archive/
ubuntu http://ubuntu.linux-bg.org/ubuntu/
ubuntu ftp://ubuntu.linux-bg.org/ubuntu/
ubuntu http://mirror.bg.host.ag/ubuntu/
ubuntu ftp://mirror.bg.host.ag/ubuntu/
ubuntu http://mirror.sabay.com.kh/ubuntu/
ubuntu http://mirrors.layeronline.com/ubuntu/
ubuntu http://ubuntu.linux.n0c.ca/ubuntuarchive/
ubuntu http://mirror.csclub.uwaterloo.ca/ubuntu/
ubuntu ftp://mirror.csclub.uwaterloo.ca/ubuntu/
ubuntu http://mirror.it.ubc.ca/ubuntu/
ubuntu http://mirror.reenigne.net/ubuntu/
ubuntu http://muug.ca/mirror/ubuntu/
ubuntu ftp://ftp.muug.ca/mirror/ubuntu/
ubuntu http://mirror.0xem.ma/ubuntu/
ubuntu http://mirror.hep.gg/ubuntu/
ubuntu http://archive.ubuntu.mirror.rafal.ca/ubuntu/
ubuntu ftp://archive.ubuntu.mirror.rafal.ca/ubuntu/
ubuntu http://gpl.savoirfairelinux.net/pub/mirrors/ubuntu/
ubuntu http://mirror.ca-tr.kamatera.com/ubuntu/
ubuntu http://mirror.rcg.sfu.ca/mirror/ubuntu/
ubuntu http://ubuntu.mirror.globo.tech/
ubuntu ftp://ubuntu.mirror.globo.tech/
ubuntu http://mirror.its.dal.ca/ubuntu/
ubuntu ftp://mirror.its.dal.ca/ubuntu/
ubuntu http://ubuntu.mirror.rafal.ca/ubuntu/
ubuntu http://mirror.hnd.cl/ubuntu/
ubuntu http://ubuntu-mirror.puq.apoapsis.cl/ubuntu/
ubuntu http://mirror.uchile.cl/ubuntu/
ubuntu http://elmirror.cl/ubuntu/
ubuntu http://mirror.ufro.cl/ubuntu/
ubuntu http://mirror1.grupocg.cl/ubuntu/
ubuntu http://mirrors.tuna.tsinghua.edu.cn/ubuntu/
ubuntu http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/
ubuntu http://mirror.nju.edu.cn/ubuntu/
ubuntu http://mirror.nyist.edu.cn/ubuntu/
ubuntu http://mirror.nyist.edu.cn/ubuntu-ports/
ubuntu http://mirrors.ustc.edu.cn/ubuntu/
ubuntu http://mirrors.sdu.edu.cn/ubuntu/
ubuntu http://repo.huaweicloud.com/ubuntu/
ubuntu http://ftp.sjtu.edu.cn/ubuntu/
ubuntu ftp://ftp.sjtu.edu.cn/ubuntu/
ubuntu http://mirror.bjtu.edu.cn/ubuntu/
ubuntu http://mirrors.cn99.com/ubuntu/
ubuntu http://mirrors.huaweicloud.com/repository/ubuntu/
ubuntu http://mirrors.hust.edu.cn/ubuntu/
ubuntu http://mirrors.jcut.edu.cn/ubuntu/
ubuntu http://mirrors.jlu.edu.cn/ubuntu/
ubuntu http://mirrors.jxust.edu.cn/ubuntu/
ubuntu http://mirrors.njupt.edu.cn/ubuntu/
ubuntu http://mirrors.sjtug.sjtu.edu.cn/ubuntu/
ubuntu http://mirrors.sohu.com/ubuntu/
ubuntu ftp://mirrors.sohu.com/ubuntu/
ubuntu http://mirrors.xjtu.edu.cn/ubuntu/
ubuntu http://mirrors.yun-idc.com/ubuntu/
ubuntu ftp://mirrors.yun-idc.com/ubuntu/
ubuntu http://mirrors.zju.edu.cn/ubuntu/
ubuntu http://mirrors.cqu.edu.cn/ubuntu/
ubuntu http://edgeuno-bog2.mm.fcix.net/ubuntu/
ubuntu http://mirror.unimagdalena.edu.co/ubuntu/
ubuntu http://mirrors.atlas.net.co/ubuntu/
ubuntu http://mirrors.united.cd/ubuntu/
ubuntu http://ubuntu.ucr.ac.cr/ubuntu/
ubuntu ftp://ubuntu.ucr.ac.cr/ubuntu/
ubuntu http://ubuntu.grad.hr/ubuntu/
ubuntu ftp://ubuntu.grad.hr/ubuntu/
ubuntu http://mirror.library.ucy.ac.cy/linux/ubuntu/archive/
ubuntu http://cz.archive.ubuntu.com/ubuntu/
ubuntu http://ftp.linux.cz/pub/linux/ubuntu/
ubuntu ftp://ftp.linux.cz/pub/linux/ubuntu/
ubuntu http://ftp.sh.cvut.cz/ubuntu/
ubuntu ftp://ftp.sh.cvut.cz/ubuntu/
ubuntu http://mirror.it4i.cz/ubuntu/
ubuntu http://ftp.cvut.cz/ubuntu/
ubuntu ftp://ftp.cvut.cz/ubuntu/
ubuntu http://ucho.ignum.cz/ubuntu/
ubuntu ftp://ucho.ignum.cz/ubuntu/
ubuntu http://mirrors.dotsrc.org/ubuntu/
ubuntu ftp://mirrors.dotsrc.org/ubuntu/
ubuntu http://mirrors.fibianet.dk/ubuntu/
ubuntu http://mirror.group.one/ubuntu/
ubuntu http://mirror.one.com/ubuntu/
ubuntu ftp://mirror.one.com/ubuntu/
ubuntu http://ftp.klid.dk/ftp/ubuntu/
ubuntu ftp://ftp.klid.dk/ubuntu/
ubuntu http://mirror.cedia.org.ec/ubuntu/
ubuntu ftp://mirror.cedia.org.ec/ubuntu/
ubuntu http://mirrors.xtom.ee/ubuntu/
ubuntu http://mirror.5i.fi/ubuntu/
ubuntu http://ubuntu.web.trex.fi/ubuntu/
ubuntu ftp://mirror.clientvps.com/ubuntu/
ubuntu http://mirror.fi.ossplanet.net/ubuntu/
ubuntu http://mirror.ubuntu.ikoula.com/
ubuntu http://ubuntu.lafibre.info/ubuntu/
ubuntu http://ubuntu.univ-reims.fr/ubuntu/
ubuntu ftp://ubuntu.univ-reims.fr/ubuntu/
ubuntu http://ubuntu.mirrors.ovh.net/ubuntu/
ubuntu http://mirror.plusserver.com/ubuntu/ubuntu/
ubuntu http://miroir.univ-lorraine.fr/ubuntu/
ubuntu http://mirror.its-tps.fr/ubuntu/
ubuntu http://mirror.johnnybegood.fr/ubuntu/
ubuntu http://mirrors.ircam.fr/pub/ubuntu/archive/
ubuntu ftp://mirrors.ircam.fr/pub/ubuntu/archive/
ubuntu http://ubuntu.syxpi.fr/ubuntu/
ubuntu http://ubuntu.univ-nantes.fr/ubuntu/
ubuntu ftp://ubuntu.univ-nantes.fr/ubuntu/
ubuntu http://ftp.u-picardie.fr/mirror/ubuntu/ubuntu/
ubuntu http://distrib-coffee.ipsl.jussieu.fr/pub/linux/ubuntu/
ubuntu ftp://distrib-coffee.ipsl.jussieu.fr/pub/linux/ubuntu/
ubuntu http://ubuntu.grena.ge/ubuntu/
ubuntu http://ftp.uni-stuttgart.de/ubuntu/
ubuntu http://ftp.halifax.rwth-aachen.de/ubuntu/
ubuntu ftp://ftp.halifax.rwth-aachen.de/ubuntu/
ubuntu http://mirror.dogado.de/ubuntu/
ubuntu http://mirror.netcologne.de/ubuntu/
ubuntu ftp://mirror.netcologne.de/ubuntu/
ubuntu http://ubuntu.mirror.lrz.de/ubuntu/
ubuntu http://de.mirrors.clouvider.net/ubuntu/
ubuntu http://debian.charite.de/ubuntu/
ubuntu http://ftp.stw-bonn.de/ubuntu/
ubuntu ftp://ftp.stw-bonn.de/ubuntu/
ubuntu http://mirror.creoline.net/ubuntu/
ubuntu http://mirror.de.leaseweb.net/ubuntu/
ubuntu ftp://mirror.de.leaseweb.net/ubuntu/
ubuntu http://mirror.ipb.de/ubuntu/
ubuntu http://mirror.wtnet.de/ubuntu/
ubuntu http://mirrors.xtom.de/ubuntu/
ubuntu http://ubuntu.mirror.tudos.de/ubuntu/
ubuntu http://ftp.fau.de/ubuntu/
ubuntu ftp://ftp.fau.de/ubuntu/
ubuntu http://drmirror.physi.uni-heidelberg.de/ubuntu/
ubuntu http://ftp-stud.hs-esslingen.de/ubuntu/
ubuntu ftp://ftp-stud.hs-esslingen.de/ubuntu/
ubuntu http://mirror.netzwerge.de/ubuntu/
ubuntu http://mirror.serverloft.eu/ubuntu/ubuntu/
ubuntu http://artfiles.org/ubuntu.com/
ubuntu ftp://artfiles.org/ubuntu.com/
ubuntu ftp://ftp.fu-berlin.de/linux/ubuntu/
ubuntu http://ftp.rz.tu-bs.de/pub/mirror/ubuntu-packages/
ubuntu ftp://ftp.rz.tu-bs.de/pub/mirror/ubuntu-packages/
ubuntu http://ftp.tu-chemnitz.de/pub/linux/ubuntu-ports/
ubuntu ftp://ftp.tu-chemnitz.de/pub/linux/ubuntu-ports/
ubuntu http://ftp.tu-ilmenau.de/mirror/ubuntu/
ubuntu ftp://ftp.tu-ilmenau.de/mirror/ubuntu/
ubuntu http://ftp.uni-bayreuth.de/linux/ubuntu/ubuntu/
ubuntu ftp://ftp.uni-bayreuth.de/pub/linux/ubuntu/ubuntu/
ubuntu http://ftp.uni-mainz.de/ubuntu/
ubuntu ftp://ftp.uni-mainz.de/ubuntu/
ubuntu http://ftp5.gwdg.de/pub/linux/debian/ubuntu/
ubuntu ftp://ftp5.gwdg.de/pub/linux/debian/ubuntu/
ubuntu http://mirror.23m.com/ubuntu/
ubuntu http://mirror.daniel-jost.net/ubuntu/
ubuntu ftp://mirror.daniel-jost.net/ubuntu/
ubuntu http://mirror.eu-fr.kamatera.com/ubuntu/
ubuntu http://mirror.informatik.tu-freiberg.de/ubuntu/
ubuntu http://mirror.kamp.de/ubuntu/
ubuntu http://mirror.kkg.berlin/ubuntu/
ubuntu http://mirror.liquidrp.de/ubuntu/
ubuntu http://mirror.scaleuptech.com/ubuntu/
ubuntu ftp://mirror.scaleuptech.com/ubuntu/
ubuntu http://mirror.thunderhosting.cloud/ubuntu/
ubuntu http://mirror.united-gameserver.de/ubuntu/
ubuntu http://packages.oth-regensburg.de/ubuntu/
ubuntu http://suse.uni-leipzig.de/pub/releases.ubuntu.com/ubuntu/
ubuntu ftp://suse.uni-leipzig.de/pub/releases.ubuntu.com/ubuntu/
ubuntu http://ftp.hosteurope.de/mirror/archive.ubuntu.com/
ubuntu ftp://ftp.hosteurope.de/mirror/archive.ubuntu.com/
ubuntu http://ftp.tu-chemnitz.de/pub/linux/ubuntu/
ubuntu http://ftp.cc.uoc.gr/mirrors/linux/ubuntu/packages/
ubuntu ftp://ftp.cc.uoc.gr/mirrors/linux/ubuntu/packages/
ubuntu http://ftp.ntua.gr/ubuntu/
ubuntu ftp://ftp.ntua.gr/ubuntu/
ubuntu http://ubuntu.otenet.gr/
ubuntu ftp://ftp.otenet.gr/ubuntu/
ubuntu http://mirror.greennet.gl/ubuntu/
ubuntu http://hk.mirrors.thegigabit.com/ubuntu/
ubuntu http://mirror-hk.koddos.net/ubuntu/
ubuntu http://mirror.01link.hk/ubuntu/
ubuntu http://mirror.as.kamatera.com/ubuntu/
ubuntu http://repo.jztkft.hu/ubuntu/
ubuntu http://mirror.niif.hu/ubuntu/
ubuntu http://quantum-mirror.hu/mirrors/pub/ubuntu/
ubuntu http://mirrors.sth.sze.hu/ubuntu/
ubuntu http://ubuntu.hysing.is/ubuntu/
ubuntu http://is.mirror.flokinet.net/ubuntu/
ubuntu http://mirrors.opensource.is/ubuntu/
ubuntu http://repos.del.extreme-ix.org/ubuntu/
ubuntu http://in.mirror.coganng.com/ubuntu/
ubuntu ftp://in.mirror.coganng.com/ubuntu/
ubuntu http://in.mirror.coganng.com/ubuntu-ports/
ubuntu ftp://in.mirror.coganng.com/ubuntu-ports/
ubuntu http://mirror.bharatdatacenter.com/ubuntu/
ubuntu ftp://mirror.bharatdatacenter.com/ubuntu/
ubuntu http://mirror.cse.iitk.ac.in/ubuntu/
ubuntu ftp://mirror.cse.iitk.ac.in/ubuntu/
ubuntu http://mirror.ietlucknow.ac.in/ubuntu/
ubuntu http://mirror.nitc.ac.in/ubuntu/
ubuntu http://mirrors.nxtgen.com/ubuntu-mirror/ubuntu/
ubuntu http://repo.extreme-ix.org/ubuntu/
ubuntu http://ftp.iitm.ac.in/ubuntu/
ubuntu ftp://ftp.iitm.ac.in/ubuntu
ubuntu http://cdn.repo.cloudeka.id/ubuntu/
ubuntu http://cdn.ubuntu.repo.cloudeka.id/ubuntu/
ubuntu http://linux.domainesia.com/ubuntu/ubuntu-archive/
ubuntu http://mirror.faizuladib.com/ubuntu/
ubuntu http://mirror.repository.id/ubuntu/
ubuntu http://mr.heru.id/ubuntu/
ubuntu http://mirror.deace.id/ubuntu/
ubuntu http://mirror-gw.elastis.id/ubuntu/
ubuntu http://mirror.biznetgio.com/ubuntu/
ubuntu http://mirror.cepatcloud.id/ubuntu/
ubuntu http://mirror.citrahost.com/ubuntu/
ubuntu http://mirror.dewabiz.com/ubuntu/
ubuntu http://mirror.gi.co.id/ubuntu/
ubuntu http://mirror.papua.go.id/ubuntu/
ubuntu http://mirror.poliwangi.ac.id/ubuntu/
ubuntu http://mirror.unair.ac.id/ubuntu/
ubuntu http://repo.ugm.ac.id/ubuntu/
ubuntu http://repo.usk.ac.id/ubuntu/
ubuntu http://suro.ubaya.ac.id/ubuntu/
ubuntu ftp://suro.ubaya.ac.id/mirror/ubuntu/
ubuntu http://archive.ubuntu.mirrors.zagrio.net/
ubuntu ftp://mirrors.zagrio.net/ubuntu/archive/
ubuntu http://mirror.iranserver.com/ubuntu/
ubuntu http://ir.archive.ubuntu.com/ubuntu/
ubuntu ftp://ir.archive.ubuntu.com/ubuntu/
ubuntu http://ubuntu.mobinhost.com/ubuntu/
ubuntu ftp://ubuntu.mobinhost.com/ubuntu/
ubuntu http://ubuntu.pishgaman.net/ubuntu/
ubuntu http://mirror.aminidc.com/ubuntu/
ubuntu ftp://mirror.aminidc.com/ubuntu/
ubuntu http://mirrors.pardisco.co/ubuntu/
ubuntu http://archive.ubuntu.petiak.ir/ubuntu/
ubuntu ftp://archive.ubuntu.petiak.ir/ubuntu/
ubuntu http://ubuntu.pars.host/
ubuntu http://ubuntu.parsvds.com/ubuntu/
ubuntu http://mirror.faraso.org/ubuntu/
ubuntu http://mirrors.ubuntu.dimit.cloud/
ubuntu http://repo.iut.ac.ir/repo/Ubuntu/
ubuntu http://mirror.webworld.ie/ubuntu/
ubuntu http://mirror.il-ha.kamatera.com/ubuntu/
ubuntu http://ubuntu-archive.interhost.co.il/ubuntu/
ubuntu http://mirror.il-pt.kamatera.com/ubuntu/
ubuntu http://mirror.il-rh.kamatera.com/ubuntu/
ubuntu http://mirror.il-ta.kamatera.com/ubuntu/
ubuntu http://mirror.il.kamatera.com/ubuntu/
ubuntu http://rep-ubuntu-il.upress.io/ubuntu/
ubuntu http://mirror.isoc.org.il/pub/ubuntu/
ubuntu ftp://mirror.isoc.org.il/ubuntu/
ubuntu http://it1.mirror.vhosting-it.com/ubuntu/
ubuntu http://it2.mirror.vhosting-it.com/ubuntu/
ubuntu http://giano.com.dist.unige.it/ubuntu/
ubuntu http://ftp.udx.icscoe.jp/Linux/ubuntu/
ubuntu http://mirror.hashy0917.net/ubuntu/
ubuntu http://mirror.hashy0917.net/ubuntu-ports/
ubuntu http://archive.g4t1.pro/ubuntu/
ubuntu http://ftp.tsukuba.wide.ad.jp/Linux/ubuntu/
ubuntu http://linux.yz.yamagata-u.ac.jp/ubuntu/
ubuntu http://mirror.nishi.network/ubuntu/
ubuntu http://mirror.nishi.network/ubuntu-ports/
ubuntu http://repo.jing.rocks/ubuntu/
ubuntu http://repo.jing.rocks/ubuntu-ports/
ubuntu http://ftp.jaist.ac.jp/pub/Linux/ubuntu/
ubuntu ftp://ftp.jaist.ac.jp/pub/Linux/ubuntu/
ubuntu http://ftp.riken.jp/Linux/ubuntu/
ubuntu ftp://ftp.riken.jp/Linux/ubuntu/
ubuntu http://ubuntutym.u-toyama.ac.jp/ubuntu/
ubuntu http://ftp.sakura.ad.jp/ubuntu/
ubuntu http://jp.mirror.coganng.com/ubuntu/
ubuntu ftp://jp.mirror.coganng.com/ubuntu/
ubuntu http://jp.mirror.coganng.com/ubuntu-ports/
ubuntu ftp://jp.mirror.coganng.com/ubuntu-ports/
ubuntu http://mirror.hoster.kz/ubuntu/
ubuntu ftp://mirror.hoster.kz/ubuntu/
ubuntu http://mirror.ps.kz/ubuntu/
ubuntu ftp://mirror.ps.kz/ubuntu/
ubuntu http://mirror.amuksa.com/ubuntu/
ubuntu http://mirror.kakao.com/ubuntu/
ubuntu http://mirror.krfoss.org/ubuntu/
ubuntu http://mirror.siwoo.org/ubuntu/
ubuntu http://ftp.daum.net/ubuntu/
ubuntu ftp://ftp.daum.net/ubuntu/
ubuntu http://ftp.lanet.kr/ubuntu/
ubuntu ftp://ftp.lanet.kr/ubuntu/
ubuntu http://ftp.lanet.kr/ubuntu-ports/
ubuntu ftp://ftp.lanet.kr/ubuntu-ports/
ubuntu http://mirror.jeonnam.school/ubuntu/
ubuntu http://mirror.zzunipark.com/ubuntu/
ubuntu http://mirror.zzunipark.com/ubuntu-ports/
ubuntu http://mirror.yuki.net.uk/ubuntu-ports/
ubuntu http://mir.linux.kg/ubuntu/
ubuntu http://mirror.cloudhosting.lv/ubuntu/
ubuntu http://ubuntu-arch.linux.edu.lv/ubuntu/
ubuntu ftp://ubuntu-arch.linux.edu.lv/lv.archive.ubuntu.com/ubuntu/
ubuntu http://ubuntu.koyanet.lv/ubuntu/
ubuntu http://mirror.bacloud.com/ubuntu-mirror/archive/
ubuntu http://mirror.vpsnet.com/ubuntu/
ubuntu http://ubuntu.mirror.vu.lt/ubuntu/
ubuntu ftp://ubuntu.mirror.vu.lt/ubuntu/
ubuntu http://ftp.litnet.lt/ubuntu/
ubuntu ftp://ftp.litnet.lt/ubuntu/
ubuntu http://ubuntu.mirror.root.lu/ubuntu/
ubuntu ftp://mirror.root.lu/ubuntu/ubuntu/
ubuntu http://mirror.a1.mk/ubuntu/
ubuntu http://mirror.t-home.mk/ubuntu/
ubuntu http://ubuntu.dts.mg/ubuntu/
ubuntu http://mirrors.gbnetwork.com/ubuntu/
ubuntu http://mirrors.ipserverone.com/ubuntu/
ubuntu http://ubuntu.mirror.thegigabit.com/
ubuntu http://ubuntu.tuxuri.com/ubuntu/
ubuntu http://ubuntu-mirror.cloud.mu/ubuntu/
ubuntu http://ubuntu-mirror.cloud.mu/ubuntu-ports/
ubuntu http://mirror.as43289.net/ubuntu/
ubuntu ftp://mirror.as43289.net/ubuntu/
ubuntu http://mirror.ihost.md/ubuntu/
ubuntu http://mirrors.mivocloud.com/ubuntu/
ubuntu ftp://mirrors.mivocloud.com/ubuntu/
ubuntu http://mirror.hosthink.net/ubuntu/
ubuntu http://mirror.datacenter.mn/ubuntu/
ubuntu http://mirror.marwan.ma/ubuntu/
ubuntu http://mirrors.ygn.mmix.net.mm/ubuntu/
ubuntu http://download.nust.na/pub/ubuntu/ubuntu/
ubuntu http://ubuntu.ntc.net.np/ubuntu/
ubuntu http://mirror.ams.macarne.com/ubuntu/
ubuntu http://mirror.i3d.net/pub/ubuntu/
ubuntu http://mirror.nl.datapacket.com/ubuntu/
ubuntu http://nl.archive.ubuntu.com/ubuntu/
ubuntu http://ftp.nluug.nl/os/Linux/distr/ubuntu/
ubuntu ftp://ftp.nluug.nl/pub/os/Linux/distr/ubuntu/
ubuntu http://ftp.snt.utwente.nl/pub/os/linux/ubuntu/
ubuntu ftp://ftp.snt.utwente.nl/pub/os/linux/ubuntu/
ubuntu http://mirror.lyrahosting.com/ubuntuarchive/
ubuntu ftp://mirror.lyrahosting.com/ubuntuarchive/
ubuntu http://mirror.nforce.com/pub/linux/ubuntu/
ubuntu ftp://mirror.nforce.com/pub/linux/ubuntu/
ubuntu http://mirror.nl.altushost.com/ubuntu/
ubuntu http://mirror.nl.leaseweb.net/ubuntu/
ubuntu ftp://mirror.nl.leaseweb.net/ubuntu/
ubuntu http://mirror.serverion.com/ubuntu/
ubuntu ftp://mirror.serverion.com/ubuntu/
ubuntu http://mirror.vpgrp.io/ubuntu/
ubuntu http://mirrors.xtom.nl/ubuntu/
ubuntu http://nl.mirrors.clouvider.net/ubuntu/
ubuntu http://ubuntu.mirror.intermax.nl/
ubuntu http://ubuntu.mirror.true.nl/ubuntu/
ubuntu http://ubuntu.mirror.wearetriple.com/archive/
ubuntu http://mirror.transip.net/ubuntu/ubuntu/
ubuntu ftp://mirror.transip.net/ubuntu/ubuntu/
ubuntu http://mirrors.as215248.net/ubuntu/
ubuntu http://nl3.archive.ubuntu.com/ubuntu/
ubuntu http://ftp.tudelft.nl/archive.ubuntu.com/
ubuntu ftp://ftp.tudelft.nl/pub/Linux/archive.ubuntu.com/
ubuntu http://mirror.eu.kamatera.com/ubuntu/
ubuntu http://mirror.hostnet.nl/ubuntu/archive/
ubuntu http://mirror.nl.as215296.net/ubuntu/
ubuntu http://mirror.previder.nl/ubuntu/
ubuntu http://mirrors.evoluso.com/ubuntu/
ubuntu http://nl.mirror.flokinet.net/ubuntu/
ubuntu http://osmirror.rug.nl/ubuntu/
ubuntu ftp://osmirror.rug.nl/ubuntu/
ubuntu ftp://ftpserv.tudelft.nl/pub/Linux/archive.ubuntu.com/
ubuntu http://ubuntu.lagoon.nc/ubuntu/
ubuntu ftp://ubuntu.lagoon.nc/ubuntu/
ubuntu http://archive.ubuntu.nautile.nc/ubuntu/
ubuntu http://mirror.fsmg.org.nz/ubuntu/
ubuntu http://ubuntu.btit.nz/ubuntu/
ubuntu ftp://ubuntu.btit.nz/ubuntu/
ubuntu http://no.mirrors.blix.com/ubuntu/
ubuntu http://no.archive.ubuntu.com/ubuntu/
ubuntu http://ubuntu.hi.no/archive/
ubuntu http://ftp.uninett.no/ubuntu/
ubuntu ftp://ftp.uninett.no/ubuntu/
ubuntu http://ubuntu.uib.no/archive/
ubuntu ftp://ubuntu.uib.no/pub/Linux/Distributions/ubuntu/archive/
ubuntu http://mirror.virtury.com/ubuntu/
ubuntu http://mirror.rise.ph/ubuntu/
ubuntu ftp://mirror.rise.ph/ubuntu/
ubuntu http://ftp.icm.edu.pl/pub/Linux/ubuntu/
ubuntu ftp://ftp.icm.edu.pl/pub/Linux/ubuntu/
ubuntu http://ftp.psnc.pl/linux/ubuntu/
ubuntu http://ubuntu.task.gda.pl/ubuntu/
ubuntu ftp://ubuntu.task.gda.pl/ubuntu/
ubuntu http://ftp.agh.edu.pl/ubuntu/
ubuntu ftp://ftp.agh.edu.pl/ubuntu/
ubuntu http://mirror.chmuri.net/ubuntu/
ubuntu http://ubuntu.man.lodz.pl/ubuntu/
ubuntu ftp://ubuntu.man.lodz.pl/ubuntu/
ubuntu http://mirror.leitecastro.com/ubuntu/
ubuntu http://mirrors.ptisp.pt/ubuntu/
ubuntu http://mirrors.up.pt/ubuntu/
ubuntu ftp://mirrors.up.pt/pub/ubuntu/
ubuntu http://cesium.di.uminho.pt/pub/ubuntu-archive/
ubuntu ftp://cesium.di.uminho.pt/pub/ubuntu-archive/
ubuntu http://archive.ubuntumirror.dei.uc.pt/ubuntu/
ubuntu ftp://archive.ubuntumirror.dei.uc.pt/ubuntu/
ubuntu http://ftp.rnl.tecnico.ulisboa.pt/pub/ubuntu/archive/
ubuntu ftp://ftp.rnl.tecnico.ulisboa.pt/pub/ubuntu/archive/
ubuntu http://glua.ua.pt/pub/ubuntu/
ubuntu ftp://glua.ua.pt/pub/ubuntu/
ubuntu http://mirrors.webhs.pt/ubuntu/
ubuntu http://mirrors.hosterion.ro/ubuntu/
ubuntu http://mirror.efect.ro/ubuntu/archive/
ubuntu http://mirrors.nav.ro/ubuntu/
ubuntu ftp://mirrors.nav.ro/ubuntu/
ubuntu http://mirror.flo.c-f.ro/ubuntu/
ubuntu http://mirror.flokinet.net/ubuntu/
ubuntu http://mirrors.chroot.ro/ubuntu/
ubuntu ftp://mirrors.chroot.ro/ubuntu/
ubuntu http://mirrors.hostico.ro/ubuntu/archive/
ubuntu http://mirrors.pidginhost.com/ubuntu/
ubuntu ftp://mirrors.pidginhost.com/ubuntu/
ubuntu http://ubuntu.mirrors.linux.ro/archive/
ubuntu ftp://ftp.linux.ro/ubuntu/archive/
ubuntu http://mirror.linux-ia64.org/ubuntu/
ubuntu http://mirror.neftm.ru/ubuntu/
ubuntu http://mirror.timeweb.ru/ubuntu/
ubuntu http://mirror.truenetwork.ru/ubuntu/
ubuntu ftp://mirror.truenetwork.ru/ubuntu/
ubuntu http://mirror.docker.ru/ubuntu/
ubuntu http://mirror.corbina.net/ubuntu/
ubuntu ftp://mirror.corbina.net/ubuntu/
ubuntu http://mirror.hyperdedic.ru/ubuntu/
ubuntu http://mirror.yandex.ru/ubuntu/
ubuntu ftp://mirror.yandex.ru/ubuntu/
ubuntu http://mirrors.powernet.com.ru/ubuntu/
ubuntu ftp://mirrors.powernet.com.ru/pub/ubuntu/archive/
ubuntu http://mirror.logol.ru/ubuntu/
ubuntu ftp://mirror.logol.ru/ubuntu/
ubuntu http://mirror.maeen.sa/apt-mirror/
ubuntu http://mirror1.sox.rs/ubuntu/ubuntu/
ubuntu http://ossmirror.mycloud.services/os/linux/ubuntu/
ubuntu http://mirror.sg.gs/ubuntu/
ubuntu http://kambing.uu.sg/ubuntu/
ubuntu http://mirror.0x.sg/ubuntu/
ubuntu ftp://mirror.0x.sg/ubuntu/
ubuntu http://mirror.soonkeat.sg/ubuntu/
ubuntu http://mirror.aktkn.sg/ubuntu/
ubuntu http://mirror.coganng.com/ubuntu/
ubuntu ftp://mirror.coganng.com/ubuntu/
ubuntu http://mirror.coganng.com/ubuntu-ports/
ubuntu ftp://mirror.coganng.com/ubuntu-ports/
ubuntu http://sg-mirrors.vhost.vn/ubuntu/
ubuntu http://mirror.vnet.sk/ubuntu/
ubuntu http://ftp.energotel.sk/pub/linux/ubuntu/
ubuntu ftp://ftp.energotel.sk/pub/linux/ubuntu/
ubuntu http://tux.rainside.sk/ubuntu/
ubuntu ftp://tux.rainside.sk/ubuntu/
ubuntu http://ftp.arnes.si/pub/mirrors/ubuntu/
ubuntu ftp://ftp.arnes.si/mirrors/ubuntu/
ubuntu http://ubuntu.mirror.ac.za/ubuntu/
ubuntu ftp://ubuntu.mirror.ac.za/ubuntu/
ubuntu http://mirror.hostafrica.co.za/ubuntu/
ubuntu http://ubuntu.mirror.rain.co.za/ubuntu/
ubuntu http://mirror.raiolanetworks.com/ubuntu/
ubuntu http://ftp.udc.es/ubuntu/
ubuntu ftp://ftp.udc.es/ubuntu/
ubuntu http://ubuntu.cica.es/ubuntu/
ubuntu ftp://ubuntu.cica.es/ubuntu/
ubuntu http://ftp.caliu.cat/pub/distribucions/ubuntu/archive/
ubuntu http://ftp.csuc.cat/ubuntu/archive/
ubuntu ftp://ftp.csuc.cat/ubuntu/archive/
ubuntu http://ubuntu.uvigo.es/
ubuntu ftp://ftp.uvigo.es/ubuntu/
ubuntu http://dafi.inf.um.es/ubuntu/
ubuntu ftp://dafi.inf.um.es/ubuntu/
ubuntu http://ubuntu.grn.cat/ubuntu/
ubuntu ftp://ubuntu.grn.cat/ubuntu/
ubuntu http://ftp.acc.umu.se/ubuntu/
ubuntu ftp://ftp.acc.umu.se/ubuntu/
ubuntu http://ftp.ludd.ltu.se/mirrors/ubuntu/
ubuntu http://mirror.bahnhof.net/ubuntu/
ubuntu ftp://mirror.bahnhof.net/ubuntu/
ubuntu http://mirror.zetup.net/ubuntu/
ubuntu http://ftp.lysator.liu.se/ubuntu/
ubuntu ftp://ftp.lysator.liu.se/ubuntu/
ubuntu http://ftpmirror1.infania.net/ubuntu/
ubuntu ftp://ftpmirror1.infania.net/ubuntu/
ubuntu http://mirror.siati.ch/ubuntu-archive/
ubuntu http://mirror.infomaniak.ch/ubuntu/
ubuntu ftp://mirror.infomaniak.ch/ubuntu/
ubuntu http://mirror.init7.net/ubuntu/
ubuntu http://mirror.solnet.ch/ubuntu/
ubuntu http://pkg.adfinis-on-exoscale.ch/ubuntu/
ubuntu http://ubuntu.ethz.ch/ubuntu/
ubuntu ftp://ubuntu.ethz.ch/ubuntu/
ubuntu http://mirror.gofoss.xyz/ubuntu/
ubuntu http://mirror.gofoss.xyz/ubuntu-ports/
ubuntu http://pkg.adfinis.com/ubuntu/
ubuntu http://mirror.twds.com.tw/ubuntu/
ubuntu http://mirror.twds.com.tw/ubuntu-ports/
ubuntu http://free.nchc.org.tw/ubuntu/
ubuntu ftp://free.nchc.org.tw/ubuntu
ubuntu http://ftp.tw.debian.org/ubuntu/
ubuntu ftp://ftp.tw.debian.org/ubuntu/
ubuntu http://ubuntu.ccns.ncku.edu.tw/ubuntu/
ubuntu http://ftp.ubuntu-tw.net/ubuntu/
ubuntu ftp://ftp.ubuntu-tw.net/ubuntu/
ubuntu http://mirror.ossplanet.net/ubuntu/
ubuntu ftp://mirror.ossplanet.net/ubuntu/
ubuntu http://ftp.tku.edu.tw/ubuntu/
ubuntu ftp://ftp.tku.edu.tw/ubuntu/
ubuntu http://tw1.mirror.blendbyte.net/ubuntu/
ubuntu http://ubuntu.cs.nctu.edu.tw/ubuntu/
ubuntu ftp://ubuntu.cs.nctu.edu.tw/ubuntu/
ubuntu http://ftp.mirror.tw/pub/ubuntu/ubuntu/
ubuntu ftp://ftp.mirror.tw/pub/ubuntu/ubuntu/
ubuntu http://mirror.aptus.co.tz/pub/ubuntuarchive/
ubuntu http://deb-mirror.habari.co.tz/ubuntu/
ubuntu http://mirror.kku.ac.th/ubuntu/
ubuntu ftp://mirror.kku.ac.th/ubuntu/
ubuntu http://mirrors.cloudforest.co.th/ubuntu/
ubuntu http://mirror1.totbb.net/ubuntu/
ubuntu http://mirrors.bangmod.cloud/ubuntu/
ubuntu http://mirror1.ku.ac.th/ubuntu/
ubuntu http://mirror.ati.tn/ubuntu/
ubuntu http://mirror.ekiphost.com/ubuntu/
ubuntu http://mirror.sh.com.tr/ubuntu/
ubuntu http://mirror.domainhizmetleri.com/ubuntu/
ubuntu http://mirror.verinomi.com/ubuntu/ubuntu-archive/
ubuntu http://mirror.kapteyan.com.tr/ubuntu/
ubuntu http://mirror.rabisu.com/ubuntu/ubuntu-archive/
ubuntu http://mirror.renu.ac.ug/ubuntu/
ubuntu http://ubuntu.ip-connect.vn.ua/
ubuntu ftp://ubuntu.ip-connect.vn.ua/mirror/ubuntu/
ubuntu http://ubuntu.mirrors.omnilance.com/ubuntu/
ubuntu http://distrohub.kyiv.ua/ubuntu/
ubuntu http://mirror.mirohost.net/ubuntu/
ubuntu ftp://mirror.mirohost.net/ubuntu/
ubuntu http://ubuntu.netforce.hosting/ubuntu/
ubuntu ftp://ubuntu.netforce.hosting/ubuntu/
ubuntu http://ubuntu.org.ua/ubuntu/
ubuntu ftp://ubuntu.org.ua/ubuntu/
ubuntu http://mirror.cov.ukservers.com/ubuntu/
ubuntu ftp://mirror.cov.ukservers.com/ubuntu/
ubuntu http://mirror.katapult.io/ubuntu/
ubuntu http://mirror.server.net/ubuntu/
ubuntu http://mirror.vorboss.net/ubuntu-archive/
ubuntu http://mirrors.ukfast.co.uk/sites/archive.ubuntu.com/
ubuntu ftp://mirrors.ukfast.co.uk/archive.ubuntu.com/
ubuntu http://www.mirrorservice.org/sites/archive.ubuntu.com/ubuntu/
ubuntu ftp://ftp.mirrorservice.org/sites/archive.ubuntu.com/ubuntu/
ubuntu http://uk.mirrors.clouvider.net/ubuntu/
ubuntu http://archive.ubuntu.moon127.net/
ubuntu http://mirror.as29550.net/archive.ubuntu.com/
ubuntu ftp://mirror.as29550.net/archive.ubuntu.com/
ubuntu http://mirror.bytemark.co.uk/ubuntu/
ubuntu ftp://mirror.bytemark.co.uk/ubuntu/
ubuntu http://mirror.eu-lo.kamatera.com/ubuntu/
ubuntu http://mirror.freethought-internet.co.uk/ubuntu/
ubuntu ftp://mirror.freethought-internet.co.uk/ubuntu/
ubuntu http://mirror.ox.ac.uk/sites/archive.ubuntu.com/ubuntu/
ubuntu ftp://mirror.ox.ac.uk/sites/archive.ubuntu.com/ubuntu/
ubuntu http://mirror.vinehost.net/ubuntu/
ubuntu ftp://mirror.vinehost.net/ubuntu/
ubuntu http://mirrors.coreix.net/ubuntu/
ubuntu http://mirrors.gethosted.online/ubuntu/
ubuntu ftp://mirrors.gethosted.online/ubuntu/
ubuntu http://mirrors.melbourne.co.uk/ubuntu/
ubuntu ftp://mirrors.melbourne.co.uk/ubuntu/
ubuntu http://ports.ubuntu.moon127.net/
ubuntu http://ubuntu.mirrors.uk2.net/ubuntu/
ubuntu ftp://ubuntu.mirrors.uk2.net/ubuntu/
ubuntu http://archive.ubuntu.com/ubuntu/
ubuntu ftp://archive.ubuntu.com/ubuntu/
ubuntu http://mirror.enzu.com/ubuntu/
ubuntu ftp://mirror.enzu.com/ubuntu/
ubuntu http://mirror.pilotfiber.com/ubuntu/
ubuntu http://dal.mirrors.serverside.com/ubuntu/
ubuntu http://mirror.math.princeton.edu/pub/ubuntu/
ubuntu http://mirror.pit.teraswitch.com/ubuntu/
ubuntu http://mirror.us.mirhosting.net/ubuntu/
ubuntu ftp://mirror.us.mirhosting.net/ubuntu/
ubuntu http://mirrors.iu13.net/ubuntu/
ubuntu http://atl.mirrors.clouvider.net/ubuntu/
ubuntu http://dal.mirrors.clouvider.net/ubuntu/
ubuntu http://la.mirrors.clouvider.net/ubuntu/
ubuntu http://mirror.arizona.edu/ubuntu/
ubuntu http://mirror.brightridge.com/ubuntuarchive/
ubuntu http://mirror.hoobly.com/ubuntu/
ubuntu http://mirror.its.umich.edu/ubuntu/
ubuntu http://mirror.linux.ncsu.edu/ubuntu/
ubuntu http://mirror.lstn.net/ubuntu/
ubuntu http://mirror.nodesdirect.com/ubuntu/
ubuntu ftp://mirror.nodesdirect.com/ubuntu/
ubuntu http://mirror.servaxnet.com/ubuntu/
ubuntu http://mirror.timkevin.us/ubuntu/
ubuntu http://mirror.ubuntu.serverforge.org/
ubuntu http://mirror.umd.edu/ubuntu/
ubuntu http://mirror.us.leaseweb.net/ubuntu/
ubuntu ftp://mirror.us.leaseweb.net/ubuntu/
ubuntu http://mirrors.bloomu.edu/ubuntu/
ubuntu ftp://mirrors.bloomu.edu/ubuntu/
ubuntu http://mirrors.egr.msu.edu/ubuntu/
ubuntu http://mirrors.rit.edu/ubuntu/
ubuntu ftp://mirrors.rit.edu/ubuntu/
ubuntu http://mirrors.usinternet.com/ubuntu/archive/
ubuntu http://mirrors.xtom.com/ubuntu/
ubuntu http://nyc.mirrors.clouvider.net/ubuntu/
ubuntu http://plug-mirror.rcac.purdue.edu/ubuntu/
ubuntu ftp://plug-mirror.rcac.purdue.edu/ubuntu/
ubuntu http://ubuntu.cs.utah.edu/ubuntu/
ubuntu ftp://ubuntu.cs.utah.edu/ubuntu/ubuntu/
ubuntu http://ubuntu.mirror.constant.com/
ubuntu http://ubuntu.mirror.shastacoe.net/ubuntu/
ubuntu http://mirrors.wikimedia.org/ubuntu/
ubuntu http://mirrors.gigenet.com/ubuntu/
ubuntu http://mirror.rustytel.net/ubuntu/
ubuntu http://mirror.siena.edu/ubuntu/
ubuntu http://mirrors.cmich.edu/ubuntu/
ubuntu http://mirrors.us.kernel.org/ubuntu/
ubuntu ftp://mirrors.us.kernel.org/ubuntu/
ubuntu http://repo.ialab.dsu.edu/ubuntu/
ubuntu http://ubuntu.osuosl.org/ubuntu/
ubuntu ftp://ubuntu.osuosl.org/pub/ubuntu/
ubuntu http://ftp.usf.edu/pub/ubuntu/
ubuntu ftp://ftp.usf.edu/pub/ubuntu/
ubuntu http://mirror.cc.vt.edu/pub2/ubuntu/
ubuntu ftp://mirror.cc.vt.edu/pub2/ubuntu/
ubuntu http://mirror.clarkson.edu/ubuntu/
ubuntu http://mirror.cogentco.com/pub/linux/ubuntu/
ubuntu http://mirror.cs.jmu.edu/pub/ubuntu/
ubuntu http://mirror.dal.nexril.net/ubuntu/
ubuntu http://mirror.math.ucdavis.edu/ubuntu/
ubuntu http://mirror.metrocast.net/ubuntu/
ubuntu http://mirror.mia.velocihost.net/ubuntu/
ubuntu http://mirror.mrjester.net/ubuntu/archive/
ubuntu http://mirror.nerdmilio.com/ubuntu/
ubuntu http://mirror.steadfastnet.com/ubuntu/
ubuntu http://mirror.team-cymru.com/ubuntu/
ubuntu ftp://mirror.team-cymru.com/ubuntu/
ubuntu http://mirror.team-cymru.org/ubuntu/
ubuntu ftp://mirror.team-cymru.org/ubuntu/
ubuntu http://mirror.uoregon.edu/ubuntu/
ubuntu ftp://mirror.uoregon.edu/ubuntu/
ubuntu http://mirror.us-midwest-1.nexcess.net/ubuntu/
ubuntu http://mirror.us-ny2.kamatera.com/ubuntu/
ubuntu http://mirror.us-sc.kamatera.com/ubuntu/
ubuntu http://mirror.us-tx.kamatera.com/ubuntu/
ubuntu http://mirrors.accretive-networks.net/ubuntu/
ubuntu http://mirrors.arpnetworks.com/Ubuntu/
ubuntu http://mirrors.cat.pdx.edu/ubuntu/
ubuntu http://mirrors.liquidweb.com/ubuntu/
ubuntu http://mirrors.lug.mtu.edu/ubuntu/
ubuntu ftp://mirrors.lug.mtu.edu/ubuntu/
ubuntu http://mirrors.maine.edu/ubuntu/
ubuntu http://mirrors.mit.edu/ubuntu/
ubuntu http://mirrors.namecheap.com/ubuntu/
ubuntu ftp://mirrors.namecheap.com/ubuntu/
ubuntu http://mirrors.ocf.berkeley.edu/ubuntu/
ubuntu ftp://mirrors.ocf.berkeley.edu/ubuntu/
ubuntu http://mirrors.ocf.berkeley.edu/ubuntu-ports/
ubuntu http://mirrors.sarak.as/ubuntu/
ubuntu ftp://mirrors.sarak.as/ubuntu/
ubuntu http://mirrors.sonic.net/ubuntu/
ubuntu ftp://mirrors.sonic.net/ubuntu/
ubuntu http://mirrors.xmission.com/ubuntu/
ubuntu ftp://mirrors.xmission.com/ubuntu/
ubuntu http://pubmirrors.dal.corespace.com/ubuntu/
ubuntu ftp://pubmirrors.dal.corespace.com/ubuntu/
ubuntu http://ubuntu.mirror.frontiernet.net/ubuntu/
ubuntu ftp://ubuntu.mirror.frontiernet.net/ubuntu/
ubuntu http://ubuntu.mirrors.pair.com/archive/
ubuntu ftp://ubuntu.mirrors.pair.com/archive/
ubuntu http://ubuntu.phoenixnap.com/ubuntu/
ubuntu http://www.gtlib.gatech.edu/pub/ubuntu/
ubuntu ftp://ftp.gtlib.gatech.edu/pub/ubuntu
ubuntu http://mirror.d.umn.edu/ubuntu/
ubuntu http://mirrors.vcea.wsu.edu/ubuntu/
ubuntu http://ubuntu.securedservers.com/
ubuntu http://ftp.ussg.iu.edu/linux/ubuntu/
ubuntu http://mirror.vcu.edu/pub/gnu+linux/ubuntu/
ubuntu ftp://mirror.vcu.edu/pub/gnu+linux/ubuntu/
ubuntu http://ubuntu.repo.cure.edu.uy/ubuntu/
ubuntu http://repos.interior.edu.uy/ubuntu/
ubuntu http://mirror.dc.uz/ubuntu/
ubuntu ftp://mirror.dc.uz/ubuntu/
ubuntu http://mirror.azvps.vn/ubuntu/
ubuntu http://ubuntu.vpsttt.com/ubuntu/
ubuntu http://opensource.xtdv.net/ubuntu/
ubuntu http://mirror.bizflycloud.vn/ubuntu/
ubuntu http://mirror.clearsky.vn/ubuntu/
ubuntu http://mirror.vietnix.vn/ubuntu/
ubuntu http://mirror.viettelcloud.vn/ubuntu/
ubuntu http://mirrors.tino.org/ubuntu/
ubuntu http://vn-mirrors.techhost.vn/ubuntu/
ubuntu http://vn-mirrors.vhost.vn/ubuntu/
ubuntu http://mirrors.nhanhoa.com/ubuntu/
EOFML
}
function getLogo()
{
if [ "$IS_INSTALLED" = "true" ] && [ "$IS_CONFIG_FILE_UNENCRYPTED" = "true" ]; then
echo "$hshqwarninglogo"
else
echo "$hshqlogo"
fi
}
function showInstalledMenu()
{
installedmenu=$(cat << EOF
$(getLogo)
EOF
)
menures=$(whiptail --title "Select an option" --menu "$installedmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT \
"1" "Services" \
"2" "System Utils" \
"3" "Exit" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
menures=0
fi
case $menures in
0)
return 0 ;;
1)
checkLoadConfig
showStacksMenu
set +e
return 1 ;;
2)
set +e
showAllUtilsMenu
set +e
return 1 ;;
# 2)
# checkLoadConfig
# if ! [ "$IS_INSTALLED" = "true" ]; then
# showMessageBox "System Not Installed" "You must perform a system installation first before using this utility."
# exit
# fi
# set +e
# showNetworkMenu
# set +e
# return 1 ;;
# 2)
# showHSHQUtilsMenu
# set +e
# return 1 ;;
# 3)
# showSysUtilsMenu
# set +e
# return 1 ;;
# 4)
# return 0 ;;
esac
}
function showRestoreMenu()
{
set +e
precheckRestoreResult="$(performRestorePrecheck)"
if ! [ -z "$precheckRestoreResult" ]; then
showMessageBox "ERROR" "$precheckRestoreResult"
return
fi
checkPerformSystemPrep
if [ $? -ne 0 ]; then
return
fi
restoremenu=$(cat << EOF
$(getLogo)
EOF
)
menures=$(whiptail --title "Select an option" --menu "$restoremenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT \
"1" "Restore From Encrypted Duplicati Backup" \
"2" "Restore From Unencrypted Backup" \
"3" "Exit" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
menures=0
fi
case $menures in
0)
return 0 ;;
1)
showRestoreEncryptedMenu ;;
2)
showRestoreUnencryptedMenu ;;
3)
return 0 ;;
esac
}
function showRestoreEncryptedMenu()
{
restoremenu=$(cat << EOF
$(getLogo)
EOF
)
menures=$(whiptail --title "Select an option" --menu "$restoremenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT \
"1" "Mount Drive" \
"2" "Select Directory" \
"3" "Exit" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
menures=0
fi
case $menures in
0)
return 0 ;;
1)
showRestoreMountDriveMenu ;;
2)
showRestoreSelectEncryptedDirectoryMenu ;;
3)
return 0 ;;
esac
}
function showRestoreUnencryptedMenu()
{
set +e
performRestorePostcheck
if [ $? -ne 0 ]; then
return
fi
confirmRestore=$(promptUserInputMenu "" "Confirm" "The main restore process is ready to start. This will take awhile to complete, as all of the docker images must be re-downloaded. You will be prompted shortly for your config decrypt password. Enter the word 'restore' below to continue:")
if [ $? -ne 0 ] || [ -z "$confirmRestore" ] || ! [ "$confirmRestore" = "restore" ]; then
showMessageBox "Incorrect Confirmation" "The text did not match, returning..."
return
fi
cp $HSHQ_LIB_SCRIPT $HOME/$HSHQ_LIB_FILENAME
rm -fr $HSHQ_DATA_DIR/*
mv $HSHQ_RESTORE_DIR/* $HSHQ_DATA_DIR/
rm -fr $HSHQ_RESTORE_DIR
# Only restore hshqlib script if version is less than 85
hshqlib_orig_version=$(sed -n 2p $HSHQ_LIB_SCRIPT | cut -d"=" -f2)
if [ $hshqlib_orig_version -lt 85 ]; then
mv $HOME/$HSHQ_LIB_FILENAME $HSHQ_LIB_SCRIPT
else
rm -f $HOME/$HSHQ_LIB_FILENAME
fi
scName=$RESTORE_SCREEN_NAME
initScreen $scName "bash $HSHQ_LIB_SCRIPT restore $CONNECTING_IP"
if [ $? -ne 0 ]; then
showMessageBox "ERROR" "There was a problem starting the restore process, exiting..."
exit 1
fi
screen -r $scName
exit 0
}
function performFullRestore()
{
refreshSudo
createHSHQLog
loadConfigConsole
forceGetLock networkchecks performFullRestore
setSystemState $SS_RESTORING
IS_INSTALLED=false
IS_INSTALLING=true
is_draw_pb=false
set +e
origUsername=$(echo $HSHQ_BASE_DIR | cut -d"/" -f3)
curUID=$(id -u)
curGID=$(id -g)
if ! [ "$USERNAME" = "$origUsername" ] || ! [ "$curUID" = "$USERID" ] || ! [ "$curGID" = "$GROUPID" ]; then
msgmenu=$(cat << EOF
$(getLogo)
The current username(UID:GID) does not match the original.
Current: $USERNAME ($curUID:$curGID)
Original: $origUsername ($USERID:$GROUPID)
The restore cannot be performed.
Please ensure to have the exact same username
and UID:GID as the Original.
EOF
)
whiptail --title "ERROR" --msgbox "$msgmenu" $MENU_HEIGHT $MENU_WIDTH
return
fi
setSudoTimeoutInstall
mkdir -p $HSHQ_NONBACKUP_DIR
mkdir -p $HSHQ_BUILD_DIR
# Set hostname
new_hostname="HomeServer-$(echo $HOMESERVER_DOMAIN | sed 's/\./-/g')"
if [ -z "$(cat /etc/hosts | grep $new_hostname)" ]; then
echo "127.0.1.1 $new_hostname" | sudo tee -a /etc/hosts
fi
sudo hostnamectl set-hostname $new_hostname
rm -f $HOME/dead.letter
installDependencies
echo "Pulling docker images..."
restorePullDockerImages
rm -f $HOME/script-server.zip
# Set timezone
sudo timedatectl set-timezone "$TZ"
set +e
# Setup primary network interface
prevPrimaryIface=$(sqlite3 $HSHQ_DB "select InterfaceName from connections where ConnectionType = 'primary' and NetworkType = 'home_network';")
isFound=false
availInterfacesArr=($(echo "$(getAllNetworkInterfacesCSV)" | tr "," "\n"))
for curInterface in "${availInterfacesArr[@]}"
do
if [ "$prevPrimaryIface" = "$curInterface" ]; then
isFound=true
break
fi
done
if [ "$isFound" = "false" ]; then
default_iface=$(getDefaultIface)
checkDB=$(sqlite3 $HSHQ_DB "select InterfaceName from connections where InterfaceName = '$default_iface';")
if [ -z "$checkDB" ]; then
addHSInterface $default_iface true
else
sudo sqlite3 $HSHQ_DB "update connections set ConnectionType = 'secondary' where NetworkType = 'home_network';"
sudo sqlite3 $HSHQ_DB "update connections set ConnectionType = 'primary' where InterfaceName = '$default_iface';"
fi
fi
set -e
# This should cover all possibilities
curPrimaryIfaceName=$(sqlite3 $HSHQ_DB "select InterfaceName from connections where ConnectionType = 'primary' and NetworkType = 'home_network';")
curPrimaryIfaceIP=$(sqlite3 $HSHQ_DB "select IPAddress from connections where ConnectionType = 'primary' and NetworkType = 'home_network';")
HOMESERVER_HOST_PRIMARY_INTERFACE_NAME=$curPrimaryIfaceName
updatePlaintextRootConfigVar HOMESERVER_HOST_PRIMARY_INTERFACE_NAME $HOMESERVER_HOST_PRIMARY_INTERFACE_NAME
HOMESERVER_HOST_PRIMARY_INTERFACE_IP=$curPrimaryIfaceIP
updatePlaintextRootConfigVar HOMESERVER_HOST_PRIMARY_INTERFACE_IP $HOMESERVER_HOST_PRIMARY_INTERFACE_IP
find $HSHQ_SSL_DIR -name "*-ca.crt" -print0 | xargs -0 -I {} sudo cp {} /usr/local/share/ca-certificates/
sudo update-ca-certificates
CURRENT_SSH_PORT=$(sudo grep "^Port\|^#Port" /etc/ssh/sshd_config | cut -d" " -f2)
if [ -z "$CURRENT_SSH_PORT" ]; then
CURRENT_SSH_PORT=22
fi
# Change SSH port on host
sudo sed -i "s|^#*Port .*$|Port ${SSH_PORT}|g" /etc/ssh/sshd_config
sudo sed -i "s|^Port .*$|Port ${SSH_PORT}|g" /etc/ssh/sshd_config
updateMOTD
performSuggestedSecUpdates
installMailUtils
performClearIPTables true
checkUpdateAllIPTables performFullRestore
outputScripts
sudo cp $HSHQ_WIREGUARD_DIR/vpn/*.conf /etc/wireguard/ > /dev/null 2>&1
performAptInstall python3-tornado > /dev/null 2>&1
sudo rm -f /etc/systemd/system/runScriptServer.service
sudo ln -s $HSHQ_SCRIPTS_DIR/root/runScriptServer.service /etc/systemd/system/runScriptServer.service
sudo systemctl daemon-reload
sudo systemctl enable runScriptServer
sudo systemctl start runScriptServer
set -e
if [ -d $HSHQ_STACKS_DIR/wazuh ]; then
installWazuhAgent
fi
set +e
sudo docker ps -q | xargs sudo docker stop > /dev/null 2>&1
sudo docker container prune -f > /dev/null 2>&1
createDockerNetworks
enableAllWGVPNInterfaces
createWGDockerNetworks
outputEnvPortainer
startPortainer
docker volume create --driver local -o device=$HSHQ_STACKS_DIR/caddy-common/primary-certs -o o=bind -o type=none caddy-primary-certs
createClientDNSNetworksOnRestore
prepAdguardInstallation
restartAllStacks "duplicati,syncthing" true doNothing false doNothing
sudo docker container prune -f
addDomainAndWildcardAdguardHS $HOMESERVER_DOMAIN $HOMESERVER_HOST_PRIMARY_INTERFACE_IP
# Add RelayServer fingerprint
if [ "$PRIMARY_VPN_SETUP_TYPE" = "host" ]; then
perfRemoteAction -m ssh -p $RELAYSERVER_SSH_PORT -o "-T -o ConnectTimeout=10 -o 'StrictHostKeyChecking accept-new'" -u $RELAYSERVER_REMOTE_USERNAME -h $RELAYSERVER_SUB_RELAYSERVER.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN -c "echo \"Successfully connected to RelayServer! \"" -f
fi
wgDockInternetUpAll
postInstallation restore
setSystemState $SS_RUNNING
performExitFunctions
releaseLock networkchecks performFullRestore false
set +e
sudo systemctl daemon-reload
sudo systemctl restart ssh
sudo systemctl restart sshd
clear
echo -e "\n\n\n\n################################################################\n"
echo -e " HomeServer Restore Process Complete! \n"
echo -e " The Duplicati and Syncthing stacks have been intentionally"
echo -e " skipped from the stack restart process, as you may still"
echo -e " need to re-configure your backup drives, etc. You may also"
echo -e " have to manually restart some stacks due to any issues in"
echo -e " the docker startup process. Double check everything is"
echo -e " working correctly in Portainer."
echo -e " --------------------- "
echo -e " If the ip address of the primary network interface is"
echo -e " different, then the caddy-home-xxx stack likely had"
echo -e " issues when restarting the stack. However, the networking"
echo -e " checks background process will automatically resolve that"
echo -e " issue within a minute or two.\n"
echo -e " ***** IMPORTANT ***** "
echo -e " If you use Duplicati as your primary backup method, then"
echo -e " the first thing you need to do after starting the Duplicati"
echo -e " stack is to run the 'Recreate (delete and repair)' function."
echo -e " This can be found by going to your HSHQ Backup, then go to"
echo -e " Advanced -> Database. DO NOT run the 'Repair' function,"
echo -e " even if prompted. It will cause permanent data loss to your"
echo -e " backup history, and you will have to either recover the"
echo -e " missing files or start a fresh new backup profile."
echo -e "\n################################################################\n\n"
read -r -p " Press enter to continue."
screen -S $RESTORE_SCREEN_NAME -X quit
}
function restorePullDockerImages()
{
set +e
OLDIFS=$IFS
IFS=$(echo -en "\n\b")
imgList=($(sudo find $HSHQ_STACKS_DIR/portainer/compose -name docker-compose.yml -print0 | xargs -0 sudo grep -hr "^[[:blank:]]*image:"))
imgOnlyList=()
for curImgItem in "${imgList[@]}"
do
imgOnlyList+=($(echo "$curImgItem" | xargs | cut -d" " -f2))
done
IFS=$OLDIFS
uniqs_arr=($(for tmpImg in "${imgOnlyList[@]}"; do echo "${tmpImg}"; done | sort -u))
for curImg in "${uniqs_arr[@]}"
do
echo "Pulling $curImg..."
buildOrPullImage "$curImg" 5 > /dev/null 2>&1
done
# Create nonbackup directories
stackList=($(sudo find $HSHQ_STACKS_DIR/portainer/compose -name docker-compose.yml -print0 | xargs -0 sudo grep -hr "^#HSHQManaged" | cut -d" " -f2))
for curStackItem in "${stackList[@]}"
do
checkCreateNonbackupDirByStack "$curStackItem"
done
restoreRemainingNonBackupDirectories
}
function restoreRemainingNonBackupDirectories()
{
set +e
OLDIFS=$IFS
IFS=$(echo -en "\n\b")
dirList=($(sudo find $HSHQ_STACKS_DIR/portainer/compose -name docker-compose.yml -print0 | xargs -0 sudo grep -hr "\${PORTAINER_HSHQ_NONBACKUP_DIR}" | cut -d"}" -f2 | cut -d":" -f1 | cut -d" " -f1))
for curDir in "${dirList[@]}"
do
mkdir -p ${HSHQ_NONBACKUP_DIR}$curDir
done
IFS=$OLDIFS
}
function showRestoreMountDriveMenu()
{
findmnt | grep $HSHQ_BACKUP_DIR > /dev/null 2>&1
if [ $? -eq 0 ]; then
showMessageBox "ERROR" "There is already a filesystem mounted to $HSHQ_BACKUP_DIR. Please unmount this filesystem first, returning..."
return
fi
dbackmenu=$(cat << EOF
$(getLogo)
EOF
)
OLDIFS=$IFS
IFS=$(echo -en "\n\b")
diskarr=($(sudo hwinfo --disk --short | tail +2))
curListNum=1
scsbm_menu_items=( --title "Select Disk/Partition" --radiolist "$dbackmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT )
for curDisk in "${diskarr[@]}"
do
curDiskID=$(echo "$curDisk" | xargs | cut -d" " -f1)
curDiskName=$(echo "$curDisk" | xargs | cut -d" " -f2-)
findmnt | grep $curDiskID > /dev/null 2>&1
if [ $? -eq 0 ]; then
continue
fi
curDiskPartitions=($(lsblk --noheadings --raw -o NAME,SIZE,TYPE $curDiskID))
for curDiskPart in "${curDiskPartitions[@]}"
do
curDiskPartName=$(echo $curDiskPart | cut -d" " -f1)
curDiskPartSize=$(echo $curDiskPart | cut -d" " -f2)
curDiskPartType=$(echo $curDiskPart | cut -d" " -f3)
if [ "$curDiskPartType" = "part" ]; then
scsbm_menu_items+=( "$curDiskPartName $curDiskPartSize ($curDiskName)" )
scsbm_menu_items+=( "|" )
scsbm_menu_items+=( "off" )
((curListNum++))
fi
done
done
IFS=$OLDIFS
if [ $curListNum -le 1 ]; then
showMessageBox "ERROR" "There are no available disks for this operation, returning..."
return
fi
while true;
do
selDiskItem=$(whiptail "${scsbm_menu_items[@]}" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
return
fi
if [ -z "$selDiskItem" ]; then
showMessageBox "ERROR" "You did not make a selection, please try again."
else
break
fi
done
selDisk=$(echo "$selDiskItem" | cut -d" " -f1)
if [ -z "$selDisk" ]; then
showMessageBox "ERROR" "No partition was selected, returning..."
return
fi
mountDir=$(promptUserInputMenu "$HSHQ_BACKUP_DIR" "Select Directory" "Enter the directory where you would like to mount this partition:")
if [ "$mountDir" = "$HSHQ_BACKUP_DIR" ]; then
mkdir -p $HSHQ_BACKUP_DIR
fi
if [ $(checkValidDirectory "$mountDir") = "false" ]; then
showMessageBox "ERROR" "Invalid directory name, returning..."
return
fi
findmnt | grep "$mountDir" > /dev/null 2>&1
if [ $? -eq 0 ]; then
showMessageBox "ERROR" "There is already a disk mounted to this directory, returning..."
return
fi
if [ -d "$mountDir" ]; then
if ! [ -z "$(ls -A $mountDir)" ]; then
showMessageBox "ERROR" "The selected directory is not empty, returning..."
return
fi
fi
if ! [ -d "$mountDir" ]; then
showYesNoMessageBox "ERROR" "This directory does not exist, would you like to create it now?"
if [ $? -ne 0 ]; then
return
fi
mkdir -p $mountDir
if [ $? -ne 0 ]; then
showMessageBox "ERROR" "There was an error creating this directory, likely a permission error, returning..."
return
fi
fi
sudo mount /dev/$selDisk $mountDir
if [ $? -ne 0 ]; then
showMessageBox "ERROR" "There was an error mounting this partition, returning..."
return
fi
showRestoreDuplicatiRestoreMenu "$mountDir" "/dev/$selDisk"
}
function showRestoreSelectEncryptedDirectoryMenu()
{
backupDir=$(promptUserInputMenu "$HSHQ_BACKUP_DIR" "Select Directory" "Enter the top-level directory of the encrypted data:")
showRestoreDuplicatiRestoreMenu "$backupDir"
}
function showRestoreDuplicatiRestoreMenu()
{
set +e
backupDirTL="$1"
umountDisk="$2"
curListNum=0
dbackupmenu=$(cat << EOF
$(getLogo)
EOF
)
OLDIFS=$IFS
IFS=$(echo -en "\n\b")
dupback_menu_items=( --title "Select Directory" --radiolist "$dbackupmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT )
dirarr=($(sudo ls $backupDirTL))
cd ~
for curDir in "${dirarr[@]}"
do
if [ "$curDir" = "lost+found" ]; then
continue
fi
if sudo test -d "$backupDirTL/$curDir"; then
dupback_menu_items+=( "$curDir" )
dupback_menu_items+=( "|" )
dupback_menu_items+=( "off" )
((curListNum++))
fi
done
IFS=$OLDIFS
if [ $curListNum -lt 1 ]; then
showMessageBox "ERROR" "There are no subdirectories for this operation, returning..."
return
fi
selBackupDir=$(whiptail "${dupback_menu_items[@]}" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
return
fi
if [ -z "$selBackupDir" ]; then
showMessageBox "ERROR" "No directory was selected, returning..."
return
fi
if [ -d "$HSHQ_RESTORE_DIR" ]; then
if ! [ -z "$(ls -A $HSHQ_RESTORE_DIR)" ]; then
showMessageBox "ERROR" "The restore directory ($HSHQ_RESTORE_DIR) is not empty, returning..."
return 1
fi
else
mkdir -p $HSHQ_RESTORE_DIR
fi
dup_pw=$(promptPasswordMenu "Enter Passphrase" "Enter the passphrase that you used to encrypt your Duplicati backup data: ")
confirmRestore=$(promptUserInputMenu "" "Confirm" "The decryption process is ready to start. It could take anywhere from 15 minutes to many hours to decrypt the data from the encrypted state, depending on how much data. After this step, you will be prompted for additional information. So check back in an hour or so to complete the full server restore process. Enter the word 'decrypt' below to continue:")
if [ $? -ne 0 ] || [ -z "$confirmRestore" ] || ! [ "$confirmRestore" = "decrypt" ]; then
showMessageBox "Incorrect Confirmation" "The text did not match, returning..."
return
fi
sudo rm -fr /tmp/dupconfig
mkdir /tmp/dupconfig
docker container stop duplicati-restore > /dev/null 2>&1
docker container rm duplicati-restore > /dev/null 2>&1
docker run -d --name=duplicati-restore -v /tmp/dupconfig:/config -v $backupDirTL/$selBackupDir:/backup -v $HSHQ_RESTORE_DIR:/restore $(getScriptImageByContainerName duplicati) > /dev/null 2>&1
echo "Recreating duplicati database..."
#docker exec -it duplicati-restore bash -c "mono /app/duplicati/Duplicati.CommandLine.exe repair /backup --dbpath=/config/hshqrestore.sqlite --passphrase=$dup_pw"
docker exec -it duplicati-restore bash -c "/app/duplicati/duplicati-cli repair /backup --dbpath=/config/hshqrestore.sqlite --passphrase=$dup_pw"
is_repair_dup_error=$?
if [ $is_repair_dup_error -ne 0 ]; then
docker container stop duplicati-restore > /dev/null 2>&1
docker container rm duplicati-restore > /dev/null 2>&1
sudo rm -fr /tmp/dupconfig
if ! [ -z "$umountDisk" ]; then
echo "Unmounting disk ($umountDisk)..."
sudo umount $umountDisk
fi
showMessageBox "ERROR" "There was a problem recreating the database, the password may be incorrect. Returning..."
return 2
fi
echo "Database recreated, restoring files..."
#docker exec -it duplicati-restore bash -c "mono /app/duplicati/Duplicati.CommandLine.exe restore /backup --dbpath=/config/hshqrestore.sqlite --restore-path=/restore --overwrite=true --restore-permissions --passphrase=$dup_pw"
docker exec -it duplicati-restore bash -c "/app/duplicati/duplicati-cli restore /backup --dbpath=/config/hshqrestore.sqlite --restore-path=/restore --overwrite=true --restore-permissions --passphrase=$dup_pw"
docker container stop duplicati-restore > /dev/null 2>&1
docker container rm duplicati-restore > /dev/null 2>&1
sudo rm -fr /tmp/dupconfig
echo "Data Successfully Restored!"
if ! [ -z "$umountDisk" ]; then
echo "Unmounting disk ($umountDisk)..."
sudo umount $umountDisk
fi
showRestoreUnencryptedMenu
}
function performRestorePrecheck()
{
if [ -d $HSHQ_ASSETS_DIR ]; then
echo "The HSHQ assets directory ($HSHQ_ASSETS_DIR) already exists. Cannot perform restore."
fi
if [ -d $HSHQ_CONFIG_DIR ]; then
echo "The HSHQ config directory ($HSHQ_CONFIG_DIR) already exists. Cannot perform restore."
fi
if [ -d $HSHQ_RELAYSERVER_DIR ]; then
echo "The HSHQ relayserver directory ($HSHQ_RELAYSERVER_DIR) already exists. Cannot perform restore."
fi
if [ -d $HSHQ_SCRIPTS_DIR ]; then
echo "The HSHQ scripts directory ($HSHQ_SCRIPTS_DIR) already exists. Cannot perform restore."
fi
if [ -d $HSHQ_SECRETS_DIR ]; then
echo "The HSHQ secrets directory ($HSHQ_SECRETS_DIR) already exists. Cannot perform restore."
fi
if [ -d $HSHQ_SSL_DIR ]; then
echo "The HSHQ ssl directory ($HSHQ_SSL_DIR) already exists. Cannot perform restore."
fi
if [ -d $HSHQ_STACKS_DIR ]; then
echo "The HSHQ stacks directory ($HSHQ_STACKS_DIR) already exists. Cannot perform restore."
fi
if [ -d $HSHQ_WIREGUARD_DIR ]; then
echo "The HSHQ wireguard directory ($HSHQ_WIREGUARD_DIR) already exists. Cannot perform restore."
fi
}
function performRestorePostcheck()
{
if ! [ -d "$HSHQ_RESTORE_DIR" ]; then
showMessageBox "ERROR" "The restore directory ($HSHQ_RESTORE_DIR) does not exist, returning..."
return 1
fi
if ! [ -d "$HSHQ_RESTORE_DIR/assets" ]; then
showMessageBox "ERROR" "The restore (assets) directory ($HSHQ_RESTORE_DIR/assets) does not exist, returning..."
return 1
fi
if ! [ -d "$HSHQ_RESTORE_DIR/config" ]; then
showMessageBox "ERROR" "The restore (config) directory ($HSHQ_RESTORE_DIR/config) does not exist, returning..."
return 1
fi
if ! [ -d "$HSHQ_RESTORE_DIR/lib" ]; then
showMessageBox "ERROR" "The restore (lib) directory ($HSHQ_RESTORE_DIR/lib) does not exist, returning..."
return 1
fi
if ! [ -d "$HSHQ_RESTORE_DIR/scripts" ]; then
showMessageBox "ERROR" "The restore (scripts) directory ($HSHQ_RESTORE_DIR/scripts) does not exist, returning..."
return 1
fi
if ! [ -d "$HSHQ_RESTORE_DIR/secrets" ]; then
showMessageBox "ERROR" "The restore (secrets) directory ($HSHQ_RESTORE_DIR/secrets) does not exist, returning..."
return 1
fi
if ! [ -d "$HSHQ_RESTORE_DIR/ssl" ]; then
showMessageBox "ERROR" "The restore (ssl) directory ($HSHQ_RESTORE_DIR/ssl) does not exist, returning..."
return 1
fi
if ! [ -d "$HSHQ_RESTORE_DIR/stacks" ]; then
showMessageBox "ERROR" "The restore (stacks) directory ($HSHQ_RESTORE_DIR/stacks) does not exist, returning..."
return 1
fi
if ! [ -d "$HSHQ_RESTORE_DIR/wireguard" ]; then
showMessageBox "ERROR" "The restore (wireguard) directory ($HSHQ_RESTORE_DIR/wireguard) does not exist, returning..."
return 1
fi
}
function createClientDNSNetworksOnRestore()
{
echo "Creating ClientDNS networks..."
if [ -z "$PORTAINER_TOKEN" ]; then
PORTAINER_TOKEN="$(getPortainerToken -u $PORTAINER_ADMIN_USERNAME -p $PORTAINER_ADMIN_PASSWORD)"
fi
rsi_numTries=1
rsi_totalTries=5
rsi_retVal=1
rstackIDsQry=""
while [ $rsi_numTries -le $rsi_totalTries ]
do
rstackIDsQry=$(http --check-status --ignore-stdin --verify=no --timeout=300 --print="b" GET https://127.0.0.1:$PORTAINER_LOCAL_HTTPS_PORT/api/stacks?filters={\"EndpointId\":$PORTAINER_ENDPOINT_ID} "Authorization: Bearer $PORTAINER_TOKEN")
rsi_retVal=$?
if [ $rsi_retVal -eq 0 ]; then
break
fi
((rsi_numTries++))
done
if [ $rsi_retVal -ne 0 ]; then
echo "ERROR: Could not get list of stack IDs in Portainer..." 1>&2
return
fi
rstackIDs=($(echo $rstackIDsQry | jq -r '.[] | select(.Status == 1) | .Id'))
rstackNames=($(echo $rstackIDsQry | jq -r '.[] | select(.Status == 1) | .Name'))
numItems=$((${#rstackIDs[@]} - 1))
for curID in $(seq 0 $numItems);
do
echo ${rstackNames[$curID]} | grep "^clientdns-" > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "Found ClientDNS stack: (${rstackIDs[$curID]}) ${rstackNames[$curID]}"
cdns_stack_name=$(echo echo ${rstackNames[$curID]} | cut -d"-" -f2)
clientdns_subnet=$(getConfigVarFromFile CLIENTDNS_SUBNET_PREFIX $HSHQ_STACKS_DIR/portainer/compose/${rstackIDs[$curID]}/stack.env root).0/24
docker network create -o com.docker.network.bridge.name=brcd-${cdns_stack_name} --driver=bridge --subnet $clientdns_subnet cdns-${cdns_stack_name} > /dev/null
fi
done
}
function showAllUtilsMenu()
{
set +e
utilmenu=$(cat << EOF
$(getLogo)
EOF
)
menures=$(whiptail --title "Select an option" --menu "$utilmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT \
"1" "Edit Encrypted Config File" \
"2" "Edit Plaintext Root Config File" \
"3" "Edit Plaintext User Config File" \
"4" "Update APT Mirror" \
"5" "Release All Locks" \
"6" "Update IP and Refresh Firewall" \
"7" "Configure Simple Backup" \
"8" "Uninstall and Remove Everything" \
"9" "Exit" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
menures=0
fi
set -e
case $menures in
0)
set +e
return 1 ;;
1)
set +e
showYesNoMessageBox "WARNING" "Be VERY careful editing this file. You could very easily break things. The configuration file will be opened with the nano utility, so ensure you know how to use this before proceeding.\n\nDo you wish to continue?"
if [ $? -ne 0 ]; then
return 1
fi
checkLoadConfig
nano $CONFIG_FILE
loadConfigVars true
set +e
return 1 ;;
2)
set +e
showYesNoMessageBox "WARNING" "Be VERY careful editing this file. You could very easily break things. The configuration file will be opened with the nano utility, so ensure you know how to use this before proceeding.\n\nDo you wish to continue?"
if [ $? -ne 0 ]; then
return 1
fi
checkLoadConfig
sudo nano $HSHQ_PLAINTEXT_ROOT_CONFIG
loadConfigVars true
set +e
return 1 ;;
3)
set +e
showYesNoMessageBox "WARNING" "Be VERY careful editing this file. You could very easily break things. The configuration file will be opened with the nano utility, so ensure you know how to use this before proceeding.\n\nDo you wish to continue?"
if [ $? -ne 0 ]; then
return 1
fi
checkLoadConfig
nano $HSHQ_PLAINTEXT_USER_CONFIG
loadConfigVars true
set +e
return 1 ;;
4)
set +e
refreshSudo
checkUpdateAptSources true
return 1 ;;
5)
set +e
sudo -k
USER_SUDO_PW=""
refreshSudo
releaseAllLocks true
return 1 ;;
6)
checkLoadConfig
checkHostAllInterfaceIPChanges true false User-Console
checkUpdateAllIPTables User-Console
set +e
return 1 ;;
7)
checkLoadConfig
showSimpleBackupMenu
set +e
return 1 ;;
8)
nukeHSHQ ;;
9)
return 0 ;;
esac
}
function showHSHQUtilsMenu()
{
set +e
utilmenu=$(cat << EOF
$(getLogo)
EOF
)
menures=$(whiptail --title "Select an option" --menu "$utilmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT \
"1" "Edit Encrypted Config File" \
"2" "Edit Plaintext Root Config File" \
"3" "Edit Plaintext User Config File" \
"5" "Reset Caddy Data" \
"6" "Restart All Stacks" \
"7" "Email Vaultwarden Credentials" \
"8" "Email Root CA" \
"9" "Exit" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
menures=0
fi
set -e
case $menures in
0)
set +e
return 1 ;;
1)
checkLoadConfig
nano $CONFIG_FILE
loadConfigVars true
set +e
return 1 ;;
2)
checkLoadConfig
sudo nano $HSHQ_PLAINTEXT_ROOT_CONFIG
loadConfigVars true
set +e
return 1 ;;
3)
checkLoadConfig
nano $HSHQ_PLAINTEXT_USER_CONFIG
loadConfigVars true
set +e
return 1 ;;
4)
checkLoadConfig
if ! [ "$IS_INSTALLED" = "true" ]; then
showMessageBox "System Not Installed" "You must perform a system installation first before using this utility."
exit
fi
generateCertDialog
set +e
return 1 ;;
5)
checkLoadConfig
if ! [ "$IS_INSTALLED" = "true" ]; then
showMessageBox "System Not Installed" "You must perform a system installation first before using this utility."
exit
fi
showResetCaddyMenu
set +e
return 1 ;;
6)
checkLoadConfig
restartAllStacksDialog ;;
7)
checkLoadConfig
emailVaultwardenCredentials ;;
8)
checkLoadConfig
sendRootCAEmail false ;;
9)
return 0 ;;
esac
}
function showSysUtilsMenu()
{
set +e
utilmenu=$(cat << EOF
$(getLogo)
EOF
)
menures=$(whiptail --title "Select an option" --menu "$utilmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT \
"1" "Update Linux OS and Reboot" \
"2" "Download All Docker Images" \
"3" "Release All Locks" \
"4" "Check For IP Address Changes" \
"5" "Refresh Firewall" \
"6" "Configure Simple Backup" \
"7" "Uninstall and Remove Everything" \
"8" "Exit" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
menures=0
fi
set -e
case $menures in
0)
set +e
return 1 ;;
1)
performExitFunctions true
set +e
sudo apt update && sudo apt upgrade -y && sudo apt autoremove -y && sudo reboot ;;
2)
pullDockerImages
set +e
return 1 ;;
3)
sudo -k
set +e
USER_SUDO_PW=""
refreshSudo
releaseAllLocks
return 1 ;;
4)
checkLoadConfig
checkHostAllInterfaceIPChanges true false User-Console
set +e
return 1 ;;
5)
checkLoadConfig
checkUpdateAllIPTables User-Console
set +e
return 1 ;;
6)
checkLoadConfig
showSimpleBackupMenu
set +e
return 1 ;;
7)
nukeHSHQ ;;
8)
return 0 ;;
esac
}
function showResetCaddyMenu()
{
set +e
caddymenu=$(cat << EOF
$(getLogo)
Select the Caddy instances that you wish to reset:
EOF
)
caddy_arr=($(docker ps -a --filter name=caddy- --format "{{.Names}}"))
menu_items=""
for curcaddy in "${caddy_arr[@]}"
do
menu_items=${menu_items}"$curcaddy | OFF "
done
if [ "$PRIMARY_VPN_SETUP_TYPE" = "host" ]; then
menu_items=${menu_items}"RelayServer | OFF "
fi
sel_caddy=($(whiptail --title "Select Instances" --checklist "$caddymenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT $menu_items 3>&1 1>&2 2>&3))
showYesNoMessageBox "Confirm Reset" "Perform this action on the selected Caddy server(s), Continue?"
if [ $? -ne 0 ]; then
set -e
return 0
fi
set -e
for curcaddy in "${sel_caddy[@]}"
do
curcaddy=$(echo $curcaddy | tr -d \")
if [ "$curcaddy" = "RelayServer" ]; then
resetCaddyDataRelayServer true
else
sudo $HSHQ_SCRIPTS_DIR/userasroot/resetCaddyContainer.sh $curcaddy
fi
done
}
function checkPerformSystemPrep()
{
isPrepped=true
for util in $UTILS_LIST; do
if [[ "$(isProgramInstalled $util)" = "false" ]]; then
isPrepped=false
break
fi
done
if [ "$isPrepped" = "false" ]; then
showYesNoMessageBox "Prep System" "The system must be prepped with updates and required utilities. The system will automatically reboot upon completion and you will need to re-run this utility. Do you wish to continue?"
if [ $? -ne 0 ]; then
return 1
fi
sudo apt update && sudo apt upgrade -y && sudo apt autoremove -y
installDependencies
pullBaseServicesDockerImages
sudo reboot
fi
}
function checkWrapperVersion()
{
set +e
curWrapV=$(sed -n 2p $HSHQ_WRAP_SCRIPT | cut -d"=" -f2)
if ! [ -z "$curWrapV" ]; then
if [ $curWrapV -lt 5 ]; then
showMessageBox "Update Wrapper" "It appears you are using a deprecated version of the wrapper script. Please run the command: 'wget -q4N https://homeserverhq.com/hshq.sh' to obtain the latest version as soon as possible."
fi
fi
set -e
}
function loadVersionVars()
{
DISTRO_ID=$(cat /etc/*-release 2> /dev/null | grep "^ID=" | cut -d"=" -f2 | xargs | tr '[:upper:]' '[:lower:]')
DISTRO_NAME=$(cat /etc/*-release 2> /dev/null | grep "^NAME=" | cut -d"=" -f2 | xargs)
DISTRO_VERSION=$(cat /etc/*-release 2> /dev/null | grep "^VERSION=" | cut -d"=" -f2 | xargs)
DISTRO_CODENAME=$(cat /etc/*-release 2> /dev/null | grep "^VERSION_CODENAME=" | cut -d"=" -f2 | xargs)
DISTRO_ARCH=$(dpkg --print-architecture)
}
function checkSupportedHostOS()
{
IS_DISTRO_EXP=false
if [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^22\.04. ]]; then
return
fi
if [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^24\.04. ]]; then
return
fi
if [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^26\.04. ]]; then
return
fi
if [ "$DISTRO_ID" = "ubuntu" ] && [[ "$DISTRO_VERSION" =~ ^1\.1. ]] && [ "$DISTRO_NAME" = "AnduinOS" ]; then
return
fi
if [ "$DISTRO_ID" = "debian" ] && [[ "$DISTRO_VERSION" =~ ^12. ]]; then
return
fi
if [ "$DISTRO_ID" = "linuxmint" ] && [[ "$DISTRO_VERSION" =~ ^22. ]]; then
return
fi
if [ "$IS_DISTRO_EXP" = "true" ]; then
showYesNoMessageBox "Confirm Experimental" "This distribution of Linux is on the experimental list. It has not yet been thoroughly tested. Do you wish to continue?"
if [ $? -eq 0 ]; then
touch $HOME/hshq/$IS_HSHQ_TEST_FILENAME
IS_HSHQ_DEV_TEST=true
return
fi
else
echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
echo "@ Unsupported Host Operating System @"
echo "@ @"
echo "@ This installation only supports the following Linux distribution(s): @"
echo "@ @"
echo "@ - Ubuntu 22.04 (Jammy Jellyfish) @"
echo "@ - Ubuntu 24.04 (Noble Numbat) @"
echo "@ - AnduinOS 1.1 (Noble Numbat) @"
echo "@ - Debian 12 (Bookworm) @"
echo "@ - Mint 22 (Wilma) @"
echo "@ @"
echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
fi
releaseLock hshqopen "checkSupportedHostOS" false
exit 2
}
function updateSysctl()
{
isPerfUpdate=$1
set +e
sudo rm -f /etc/sysctl.d/88-hshq.conf
sudo tee /etc/sysctl.d/88-hshq.conf >/dev/null <<EOFSC
kernel.panic = 10
fs.file-max = 10000000
fs.nr_open = 10000000
fs.inotify.max_user_instances = 8192
fs.inotify.max_user_watches = 524288
vm.swappiness = 10
net.core.somaxconn = 65536
net.mptcp.enabled = 0
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_loose = 0
net.netfilter.nf_conntrack_tcp_timeout_established = 1800
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 10
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 20
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 20
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 20
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 20
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 10
net.ipv4.route.flush = 1
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_rfc1337 = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5
net.ipv4.tcp_timestamps = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.ip_forward = 1
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
# Reserve for Coturn and any other ports above 10000
net.ipv4.ip_local_reserved_ports = $JITSI_MEET_PORT,$KHOJ_VNC_PORT,$COTURN_COMMS_MIN_PORT-$COTURN_COMMS_MAX_PORT,$SYNCTHING_SYNC_PORT,$SYNCTHING_DISC_PORT,$WAZUH_PORT_4
# See https://community.home-assistant.io/t/zeroconf-error/153883
# Bigger buffers (to make 40Gb more practical). These are maximums, but the default is unaffected.
net.core.wmem_max=268435456
net.core.rmem_max=268435456
net.core.netdev_max_backlog=10000
# Force IGMP v2 (required by CBF switch)
net.ipv4.conf.all.force_igmp_version=2
net.ipv4.conf.default.force_igmp_version=2
# Increase the ARP cache table
net.ipv4.neigh.default.gc_thresh3=4096
net.ipv4.neigh.default.gc_thresh2=2048
net.ipv4.neigh.default.gc_thresh1=1024
# Increase number of multicast groups permitted
net.ipv4.igmp_max_memberships=1024
# See https://documentation.wazuh.com/current/deployment-options/docker/docker-installation.html
vm.max_map_count = 262144
EOFSC
if ! [ "$isPerfUpdate" = "false" ]; then
sudo sysctl --system > /dev/null 2>&1
fi
}
function createSwapfile()
{
set +e
sudo grep -q swap /etc/fstab > /dev/null 2>&1
if [ $? -ne 0 ] && ! [ -f /swapfile ]; then
sudo fallocate -l $HOMESERVER_SWAP_SIZE /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo "# Swap file" | sudo tee -a /etc/fstab > /dev/null 2>&1
echo "/swapfile none swap sw 0 0" | sudo tee -a /etc/fstab > /dev/null 2>&1
fi
}
function updateLogrotateConfig()
{
ulrc_curE=${-//[^e]/}
set +e
# Set max size of syslog to 5G
grep maxsize /etc/logrotate.d/rsyslog > /dev/null 2>&1
if [ $? -ne 0 ]; then
sudo sed -i "/weekly$/a\ maxsize 5G" /etc/logrotate.d/rsyslog
fi
# Move logrotate into hourly
if [ -f /etc/cron.daily/logrotate ]; then
sudo mv /etc/cron.daily/logrotate /etc/cron.hourly
fi
# Set conditions for docker container logs
sudo tee /etc/logrotate.d/docker >/dev/null <<EOFLR
/var/log/docker/*.log {
weekly
maxsize 100M
rotate 4
missingok
notifempty
}
EOFLR
sudo systemctl restart logrotate
if ! [ -z "$ulrc_curE" ]; then
set -e
fi
}
function updateMOTD()
{
cat <<EOFMD > $HOME/88-hshq
#!/bin/bash
echo
echo " #===============================================================#"
echo " # ▀█ █ ▄▄ ▄▄ ▄▄ ▄▄▄ █▀▀▀█ ▄▄▄ ▄▄▄ ▄ ▄ ▄▄▄ ▄▄▄ █ █ █▀▀█ #"
echo " # █▀▀█ █ █ █ █ █ ▄▄ ▀▀▀▄▄ ▄▄ ▄▄▄▀ ▀▄ ▄▀ ▄▄ ▄▄▄▀ █▀▀█ █ █ #"
echo " # █ █ ▀▄▄▀ █ █ ▄▄▄ █▄▄▄█ ▄▄▄ ▄▄▄▄ ▀█▀ ▄▄▄ ▄▄▄▄ █ █ █▄▄█▄ #"
echo " #===============================================================#"
echo
echo " Linux OS: \$(lsb_release -d | cut -d":" -f2 | cut -d"(" -f1 | xargs)"
if [ -f $HSHQ_LIB_SCRIPT ]; then
echo " HSHQ Version: \$(sed -n 2p $HSHQ_LIB_SCRIPT | cut -d'=' -f2)"
fi
printf " Memory Usage: %.1f%% of \$(free -h | awk '/Mem:/{print \$2}')\n" \$((10000 - \$((10**4 * \$(grep "MemAvailable" /proc/meminfo | xargs | cut -d" " -f2) / \$(grep "MemTotal" /proc/meminfo | xargs | cut -d" " -f2)))))e-2
totSwap=\$(grep "SwapTotal" /proc/meminfo | xargs | cut -d" " -f2)
if [ \$totSwap -gt 0 ]; then
printf " Swap Usage: %.1f%% of \$(free -h | awk '/Swap:/{print \$2}')\n" \$((10000 - \$((10**4 * \$(grep "SwapFree" /proc/meminfo | xargs | cut -d" " -f2) / \$totSwap))))e-2
fi
echo
echo " Disks: "
echo " -----------------------------------------------------------------"
echo " Filesystem Size Used Avail Use% Mounted on"
echo " -----------------------------------------------------------------"
df -hP | grep "^/dev" | awk '{printf " %-21s %-7s %-7s %-7s %-7s %-8s\n", \$1, \$2, \$3, \$4, \$5, \$6}'
echo
# Let's only show zombie count if greater than 10
zombie_count=\$(ps aux | grep "defunct" | wc -l)
if [ \$zombie_count -gt 10 ]; then
echo " => There are \$zombie_count zombie processes."
echo
fi
echo " Enter 'bash $HSHQ_WRAP_FILENAME' to run the HomeServerHQ script."
echo
EOFMD
chmod 755 $HOME/88-hshq
sudo chown root:root $HOME/88-hshq
sudo chmod -x /etc/update-motd.d/*
sudo mv $HOME/88-hshq /etc/update-motd.d/
if [ -f /etc/motd ]; then
sudo mv /etc/motd /etc/motd.old
fi
}
function performSuggestedSecUpdates()
{
disa_curE=${-//[^e]/}
set +e
# This function continues the process of reducing the failed checks in the Wazuh SCA scan
# It's mostly low hanging fruit, but reducing the list of exceptions will help to focus
# on the more important ones.
grep "enabled=1" /etc/default/apport > /dev/null 2>&1
if [ $? -eq 0 ]; then
sudo sed -i "s/enabled=1/enabled=0/" /etc/default/apport
sudo systemctl stop apport
sudo systemctl --now disable apport
sudo systemctl daemon-reload
#sudo systemctl mask apport
fi
echo "Authorized uses only." | sudo tee /etc/issue > /dev/null 2>&1
echo "Authorized uses only." | sudo tee /etc/issue.net > /dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt remove --purge -y telnet > /dev/null 2>&1
# Assume the caller will restart sshd - don't want to risk breaking the installation process
sudo sed -i "s/^#MaxAuthTries.*/MaxAuthTries 4/" /etc/ssh/sshd_config
sudo sed -i "s/^#Banner none.*/Banner \/etc\/issue.net/" /etc/ssh/sshd_config
sudo sed -i "s/^#MaxStartups.*/MaxStartups 10:30:60/" /etc/ssh/sshd_config
sudo sed -i "s/^#LoginGraceTime.*/LoginGraceTime 60/" /etc/ssh/sshd_config
sudo sed -i "s/^#AllowTcpForwarding yes.*/AllowTcpForwarding no/" /etc/ssh/sshd_config
sudo chown root:root /etc/crontab
sudo chmod og-rwx /etc/crontab
sudo chown root:root /etc/cron.hourly/
sudo chmod og-rwx /etc/cron.hourly/
sudo chown root:root /etc/cron.daily/
sudo chmod og-rwx /etc/cron.daily/
sudo chown root:root /etc/cron.weekly/
sudo chmod og-rwx /etc/cron.weekly/
sudo chown root:root /etc/cron.monthly/
sudo chmod og-rwx /etc/cron.monthly/
sudo chown root:root /etc/cron.d/
sudo chmod og-rwx /etc/cron.d/
if ! [ -z "$disa_curE" ]; then
set -e
fi
}
function performAptInstall()
{
if [ "$1" = "yq" ]; then
installYQ
return
fi
sudo DEBIAN_FRONTEND=noninteractive apt install -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' $1
}
function installYQ()
{
sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
sudo chmod a+x /usr/local/bin/yq
}
function installDependencies()
{
set +e
createHSHQLog
sudo DEBIAN_FRONTEND=noninteractive apt update > /dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt remove --purge -y needrestart > /dev/null 2>&1
upgradeHostOS
sudo DEBIAN_FRONTEND=noninteractive apt autoremove -y > /dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt remove --purge -y needrestart > /dev/null 2>&1
# Install utils
installHostNTPServer
for util in $UTILS_LIST; do
if [[ "$(isProgramInstalled $util)" = "false" ]]; then
lib_name=$(echo $util | cut -d"|" -f2)
echo "Installing $lib_name, please wait..."
performAptInstall $lib_name
fi
done
if ! [ -f /etc/rsyslog.d/docker-logs.conf ]; then
# Install Rsyslog
sudo systemctl status rsyslog > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Installing rsyslog, please wait..."
performAptInstall rsyslog
sudo systemctl enable rsyslog
sudo systemctl start rsyslog
fi
fi
# Some host tuning
updateSysctl true
updateLogrotateConfig
sudo sed -i "s|^ClientAliveInterval .*$|ClientAliveInterval 15|g" /etc/ssh/sshd_config
sudo sed -i "s|^ClientAliveCountMax .*$|ClientAliveCountMax 3|g" /etc/ssh/sshd_config
sudo sed -i "s|^PermitEmptyPasswords .*$|PermitEmptyPasswords no|g" /etc/ssh/sshd_config
sudo sed -i "s|^#*ClientAliveInterval .*$|ClientAliveInterval 15|g" /etc/ssh/sshd_config
sudo sed -i "s|^#*ClientAliveCountMax .*$|ClientAliveCountMax 3|g" /etc/ssh/sshd_config
sudo sed -i "s|^#*PermitEmptyPasswords .*$|PermitEmptyPasswords no|g" /etc/ssh/sshd_config
sudo sed -i "s/^#DefaultLimitNOFILE.*/DefaultLimitNOFILE=65536:524288/g" /etc/systemd/system.conf
sudo sed -i "s/^#DefaultLimitNOFILE.*/DefaultLimitNOFILE=65536:524288/g" /etc/systemd/user.conf
sudo sed -i "s/^DefaultLimitNOFILE.*/DefaultLimitNOFILE=65536:524288/g" /etc/systemd/system.conf
sudo sed -i "s/^DefaultLimitNOFILE.*/DefaultLimitNOFILE=65536:524288/g" /etc/systemd/user.conf
sudo grep "* - nofile" /etc/security/limits.conf >/dev/null
if [ $? -ne 0 ]; then
echo "* - nofile 65536" | sudo tee -a /etc/security/limits.conf >/dev/null
fi
sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target > /dev/null 2>&1
if [ "$IS_DESKTOP_ENV" = "true" ]; then
for util in $DESKTOP_APT_LIST; do
if [[ "$(isProgramInstalled $util)" = "false" ]]; then
lib_name=$(echo $util | cut -d"|" -f2)
echo "Installing $lib_name, please wait..."
performAptInstall $lib_name
fi
done
fi
installDocker
curl -fsSL https://gvisor.dev/archive.key | sudo gpg --yes --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" | sudo tee /etc/apt/sources.list.d/gvisor.list > /dev/null 2>&1
sudo apt-get update > /dev/null 2>&1
performAptInstall runsc > /dev/null 2>&1
for rem_util in $APT_REMOVE_LIST; do
sudo DEBIAN_FRONTEND=noninteractive apt remove --purge -y $rem_util
done
sudo DEBIAN_FRONTEND=noninteractive apt autoremove -y
set -e
}
function removeBSDMailx()
{
sudo DEBIAN_FRONTEND=noninteractive apt remove --purge -y bsd-mailx
sudo tee /etc/apt/preferences.d/bsd-mailx >/dev/null <<EOFSM
Package: bsd-mailx
Pin: release *
Pin-Priority: -1
EOFSM
sudo apt update > /dev/null 2>&1
}
function installMailUtils()
{
set +e
echo "Removing bsd-mailx, please wait..."
removeBSDMailx
echo "Installing ssmtp, please wait..."
performAptInstall ssmtp
echo "Installing mailx, please wait..."
performAptInstall mailutils
sudo tee /etc/ssmtp/ssmtp.conf >/dev/null <<EOFSM
root=$EMAIL_ADMIN_EMAIL_ADDRESS
mailhub=${SUB_POSTFIX}.${HOMESERVER_DOMAIN}:$MAILU_PORT_5
hostname=$(cat /etc/hostname)
TLS_CA_FILE=/etc/ssl/certs/ca-certificates.crt
UseSTARTTLS=yes
FromLineOverride=no
AuthUser=$EMAIL_SMTP_EMAIL_ADDRESS
AuthPass=$EMAIL_SMTP_PASSWORD
EOFSM
sudo tee /etc/ssmtp/revaliases >/dev/null <<EOFSM
root:$EMAIL_SMTP_EMAIL_ADDRESS
$USERNAME:$EMAIL_SMTP_EMAIL_ADDRESS
EOFSM
getent group mailsenders >/dev/null || sudo groupadd mailsenders
sudo usermod -aG mailsenders $USERNAME
sudo chown root:mailsenders /usr/bin/mail.mailutils
sudo update-alternatives --set mailx /usr/bin/mail.mailutils
}
function installSQLite344()
{
if [ -d $HSHQ_NONBACKUP_DIR/sqlite344 ]; then
return
fi
echo "Installing sqlite (v3.44) from source, this will take a few minutes..."
cd $HSHQ_NONBACKUP_DIR
wget https://www.sqlite.org/2023/sqlite-autoconf-3440000.tar.gz > /dev/null 2>&1
tar xvfz sqlite-autoconf-3440000.tar.gz > /dev/null 2>&1
rm -f sqlite-autoconf-3440000.tar.gz
sudo mv sqlite-autoconf-3440000 sqlite344
cd sqlite344
./configure > /dev/null 2>&1
make -j $(nproc) > /dev/null 2>&1
sudo make install > /dev/null 2>&1
cd ~
$HSHQ_NONBACKUP_DIR/sqlite344/sqlite3 --version > /dev/null 2>&1
rtv=$?
if [ $rtv -ne 0 ]; then
return $rtv
else
echo "Sqlite (v3.44) installation complete!"
fi
}
function checkLoadConfig()
{
set -e
if ! [ -z "$ENC_CONFIG_FILE" ] && ! [ -f "$ENC_CONFIG_FILE" ]; then
ENC_CONFIG_FILE=""
fi
if [ -z "$CONFIG_FILE" ] || ! [ -f "$CONFIG_FILE" ]; then
DEF_CONFIG_FILE=$CONFIG_FILE_DEFAULT_LOCATION/$CONFIG_FILE_DEFAULT_FILENAME
if [ -f $DEF_CONFIG_FILE ]; then
CONFIG_FILE=$DEF_CONFIG_FILE
ENC_CONFIG_FILE=""
else
CONFIG_FILE=""
fi
fi
if [ -z "$ENC_CONFIG_FILE" ] && [ -z "$CONFIG_FILE" ]; then
if [ -f $CONFIG_FILE_DEFAULT_LOCATION/$ENCRYPTED_CONFIG_FILE_DEFAULT_FILENAME ]; then
ENC_CONFIG_FILE=$CONFIG_FILE_DEFAULT_LOCATION/$ENCRYPTED_CONFIG_FILE_DEFAULT_FILENAME
fi
fi
set +e
if ! [ -z "$ENC_CONFIG_FILE" ]; then
showYesNoMessageBox "Encrypted Config Found" "Encrypted configuration file found. Do you wish to decrypt it?"
if [ $? -ne 0 ]; then
releaseLock hshqopen "checkLoadConfig" false
exit 1
fi
# Prompt for config password, test it.
# Check if sudo, if not, get it and test it.
# If either fails, exit.
confirmUnlockCreds
if [ $? -ne 0 ]; then
releaseLock hshqopen "checkLoadConfig" false
exit 1
fi
decryptConfigFile "$ENC_CONFIG_FILE"
ENC_CONFIG_FILE=""
fi
confirmUnlockCreds
if [ $? -ne 0 ]; then
releaseLock hshqopen "checkLoadConfig" false
exit 1
fi
set -e
if ! [ -z "$CONFIG_FILE" ] && [ -f $CONFIG_FILE ]; then
loadConfigVars true
fi
}
function loadConfigConsole()
{
ENC_CONFIG_FILE=$CONFIG_FILE_DEFAULT_LOCATION/$ENCRYPTED_CONFIG_FILE_DEFAULT_FILENAME
if ! [ -f $ENC_CONFIG_FILE ]; then
echo "Config file does not exist, exiting..."
exit 1
fi
if ! [ -z "$USER_CONFIG_PW" ]; then
checkDecryptConfigFile > /dev/null 2>&1
if [ $? -ne 0 ]; then
USER_CONFIG_PW=""
fi
fi
while [ -z "$USER_CONFIG_PW" ]
do
read -r -s -p "Enter your config decrypt password: " USER_CONFIG_PW
retVal=$?
if [ $retVal -ne 0 ]; then
return 3
fi
checkDecryptConfigFile > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "The password is incorrect, please re-enter it."
USER_CONFIG_PW=""
fi
done
decryptAndLoad false
}
function confirmUnlockCreds()
{
set +e
refreshSudo
if [ $? -ne 0 ]; then
return 3
fi
if [ -f $CONFIG_FILE_DEFAULT_LOCATION/$CONFIG_FILE_DEFAULT_FILENAME ]; then
CONFIG_FILE=$CONFIG_FILE_DEFAULT_LOCATION/$CONFIG_FILE_DEFAULT_FILENAME
IS_CONFIG_FILE_UNENCRYPTED=true
return
fi
if ! [ -f $CONFIG_FILE_DEFAULT_LOCATION/$ENCRYPTED_CONFIG_FILE_DEFAULT_FILENAME ]; then
# Assume this is pre-install, so just return
return
fi
promptTestDecryptConfigFile
if [ $? -ne 0 ]; then
return 3
fi
}
function refreshSudo()
{
rsu_curE=${-//[^e]/}
set +e
sudo -k
if ! [ -z "$USER_SUDO_PW" ]; then
echo "$USER_SUDO_PW" | sudo -S -v -p "" > /dev/null 2>&1
if [ $? -ne 0 ]; then
USER_SUDO_PW=""
fi
fi
while [ -z "$USER_SUDO_PW" ]
do
if [ "$IS_CONSOLE_ENV" = "true" ]; then
USER_SUDO_PW=$(promptPasswordMenu "Enter Password" "Enter the sudo password for $USERNAME: ")
retVal=$?
if [ $retVal -ne 0 ]; then
exit 3
fi
echo "$USER_SUDO_PW" | sudo -S -v -p "" > /dev/null 2>&1
if [ $? -ne 0 ]; then
showMessageBox "Incorrect Password" "The password is incorrect, please re-enter it."
USER_SUDO_PW=""
fi
else
echo
read -r -s -p "Enter the sudo password for $USERNAME: " USER_SUDO_PW
echo
echo "$USER_SUDO_PW" | sudo -S -v -p "" > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "The password is incorrect, please re-enter it."
USER_SUDO_PW=""
fi
fi
done
if ! [ -z "$rsu_curE" ]; then
set -e
fi
}
function checkConfigAvailable()
{
if [ -f $CONFIG_FILE_DEFAULT_LOCATION/$CONFIG_FILE_DEFAULT_FILENAME ]; then
CONFIG_FILE=$CONFIG_FILE_DEFAULT_LOCATION/$CONFIG_FILE_DEFAULT_FILENAME
IS_CONFIG_FILE_UNENCRYPTED=true
return
fi
if [ -f $CONFIG_FILE_DEFAULT_LOCATION/$ENCRYPTED_CONFIG_FILE_DEFAULT_FILENAME ]; then
ENC_CONFIG_FILE=$CONFIG_FILE_DEFAULT_LOCATION/$ENCRYPTED_CONFIG_FILE_DEFAULT_FILENAME
IS_CONFIG_FILE_UNENCRYPTED=false
return
fi
}
function initConfig()
{
if [ "$IS_INSTALLING" = "true" ]; then
showMessageBox "Active Installation" "There is an active installation in progress, exiting..."
exit 1
fi
if [ "$IS_INSTALLED" = "true" ]; then
return 0
fi
if [[ "$(isProgramInstalled sqlite3)" = "false" ]]; then
echo "Installing sqlite3, please wait..."
performAptInstall sqlite3 > /dev/null 2>&1
fi
if [[ "$(isProgramInstalled sipcalc)" = "false" ]]; then
echo "Installing sipcalc, please wait..."
performAptInstall sipcalc > /dev/null 2>&1
fi
if [[ "$(isProgramInstalled jq)" = "false" ]]; then
echo "Installing jq, please wait..."
performAptInstall jq > /dev/null 2>&1
fi
if [ -z "$CONFIG_FILE" ]; then
# Create new directories and config file
createInitialEnv
loadConfigVars true
fi
set +e
if [ -z "$IS_ACCEPT_DEFAULTS" ]; then
showYesNoMessageBox "Accept Defaults?" "Do you wish to use defaults where applicable?"
mbres=$?
if [ $mbres -eq 0 ]; then
IS_ACCEPT_DEFAULTS=yes
else
IS_ACCEPT_DEFAULTS=no
fi
fi
total_ram=$(free --giga | grep "Mem:" | xargs | cut -d" " -f2)
if [ $total_ram -lt 4 ]; then
showMessageBox "Insufficient Memory" "Total memory is $total_ram GB. You must have at least 4 GB to perform the installation, exiting..."
exit 1
elif [ $total_ram -lt 8 ]; then
showYesNoMessageBox "Insufficient Memory" "Total memory is $total_ram GB. You should have at least 8 GB to perform the installation. The process could fail or your server could crash. Are you sure you want to continue?"
mbres=$?
if [ $mbres -ne 0 ]; then
exit 1
fi
fi
# Need to allow for docker images already downloaded, so look at total disk space.
# Obviously 120 != 150, but this allows a little leeway.
# The partitioning structure could be different than expected, so user can bypass this warning.
total_disk_space=$(($(df / | tail -1 | xargs | cut -d" " -f2) / 1048576))
if [ $total_disk_space -lt 11 ]; then
showMessageBox "Insufficient Disk Space" "You must have at least 16GB of disk space to perform the base installation, exiting..."
exit 1
elif [ $total_disk_space -lt 120 ]; then
showYesNoMessageBox "Insufficient Disk Space" "Total size of disk is $total_disk_space GB. You should have at least 150 GB of available space to perform the installation. The process could fail or your server could crash. Are you sure you want to continue?"
mbres=$?
if [ $mbres -ne 0 ]; then
exit 1
fi
fi
if [[ "$(isProgramInstalled grepcidr)" = "false" ]]; then
echo "Installing grepcidr, please wait..."
performAptInstall grepcidr > /dev/null 2>&1
fi
is_add_error=false
default_iface=$(getDefaultIface)
add_interface=""
while [ -z "$add_interface" ]
do
if [ "$IS_ACCEPT_DEFAULTS" = "yes" ] && [ "$is_add_error" = "false" ]; then
add_interface=$default_iface
else
add_interface=$(promptUserInputMenu "$default_iface" "Enter Primary Interface" "Enter the primary network interface. The interface for the default route has been entered for you.")
if [ $? -ne 0 ]; then
exit 5
fi
if [ -z "$add_interface" ]; then
showMessageBox "Interface Error" "The interface cannot be empty."
add_interface=""
continue
elif [ $(checkValidStringUpperLowerNumbers "$add_interface" "-") = "false" ]; then
showMessageBox "Interface Error" "The interface contains invalid characters. It must consist of a-z (upper or lowercase), 0-9, and/or -"
add_interface=""
continue
elif [ $(getStringLength "$add_interface") -gt 15 ]; then
showMessageBox "Interface Error" "The interface name is too long, it must be 15 characters or less."
add_interface=""
continue
fi
fi
set +e
addHSInterface $add_interface true
retVal=$?
if [ $retVal -ne 0 ] && [ $retVal -ne 1 ]; then
ip_addr=$(getIPAddressOfInterface $add_interface)
is_private=$(checkIsIPPrivate "$ip_addr")
chk_subnet=$(getSubnetOfInterface "$add_interface" "$ip_addr" "$is_private")
if ! [ -z "$chk_subnet" ] && [ "$(checkNetworkIntersect $chk_subnet 10.0.0.0/8)" = "true" ] && [ $(echo "$chk_subnet" | cut -d"/" -f2) -eq 8 ]; then
showMessageBox "Default Interface Error" "You are using the ENTIRE 10.0.0.0/8 network for your home network - a terribly poor design choice. You will not be able to host a VPN or network with anyone. If you want to bypass these safeguards and proceed, then change your settings in /etc/netplan to a static IP and a smaller network and restart the installation."
exit 6
else
showMessageBox "Default Interface Error" "There was an error with the default interface"
fi
add_interface=""
is_add_error=true
fi
set -e
done
if [ "$(checkIsIPPrivate $HOMESERVER_HOST_PRIMARY_INTERFACE_IP)" = "false" ]; then
if [ -z "$CONNECTING_IP" ]; then
showMessageBox "Error with Connecting IP" "Could not determine connecting IP, exiting..."
exit 1
else
addHomeNetIP ${CONNECTING_IP}/32 false
fi
fi
if [ -z "$USERID" ]; then
USERID=$(id -u)
updatePlaintextRootConfigVar USERID $USERID
fi
if [ -z "$GROUPID" ]; then
GROUPID=$(id -g)
updatePlaintextRootConfigVar GROUPID $GROUPID
fi
if [ -z "$XDG_RUNTIME_DIR" ]; then
XDG_RUNTIME_DIR="/run/user/$USERID"
updateConfigVar XDG_RUNTIME_DIR $XDG_RUNTIME_DIR
fi
if ! [ "$IS_AUTO_INSTALL" = "true" ] && [ "$HSHQ_APP_TYPE" = "home" ]; then
selecttypemenu=$(cat << EOF
$(getLogo)
Is this server for your home or business?
EOF
)
menures=$(whiptail --title "Select an option" --menu "$selecttypemenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT \
"1" "Home" \
"2" "Business" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
menures=1
fi
case $menures in
1)
HSHQ_APP_TYPE=home ;;
2)
HSHQ_APP_TYPE=business ;;
esac
fi
updatePlaintextRootConfigVar HSHQ_APP_TYPE $HSHQ_APP_TYPE
if [ -z "$DISABLED_SERVICES" ]; then
if [ "$HSHQ_APP_TYPE" = "business" ]; then
if [ $total_ram -lt 8 ]; then
DISABLED_SERVICES=$DS_MEM_LOW
elif [ $total_ram -lt 12 ]; then
DISABLED_SERVICES=$BDS_MEM_12
elif [ $total_ram -lt 16 ]; then
DISABLED_SERVICES=$BDS_MEM_16
elif [ $total_ram -lt 22 ]; then
DISABLED_SERVICES=$BDS_MEM_22
elif [ $total_ram -lt 28 ]; then
DISABLED_SERVICES=$BDS_MEM_28
else
DISABLED_SERVICES=$BDS_MEM_HIGH
fi
else
if [ $total_ram -lt 8 ]; then
DISABLED_SERVICES=$DS_MEM_LOW
elif [ $total_ram -lt 12 ]; then
DISABLED_SERVICES=$DS_MEM_12
elif [ $total_ram -lt 16 ]; then
DISABLED_SERVICES=$DS_MEM_16
elif [ $total_ram -lt 22 ]; then
DISABLED_SERVICES=$DS_MEM_22
elif [ $total_ram -lt 28 ]; then
DISABLED_SERVICES=$DS_MEM_28
else
DISABLED_SERVICES=$DS_MEM_HIGH
fi
fi
updatePlaintextRootConfigVar DISABLED_SERVICES $DISABLED_SERVICES
fi
replaceType="HomeServer"
if [ "$HSHQ_APP_TYPE" = "business" ]; then
replaceType="company"
fi
while [ -z "$HOMESERVER_DOMAIN" ]
do
HOMESERVER_DOMAIN=$(promptUserInputMenu "" "Enter Domain" "Enter your $replaceType domain name, i.e. example.com. You must own this domain to route external emails and/or post services on the public internet: ")
if [ -z "$HOMESERVER_DOMAIN" ]; then
showMessageBox "Domain Empty" "The domain cannot be empty"
elif [ $(checkValidString "$HOMESERVER_DOMAIN" ".-") = "false" ]; then
showMessageBox "Invalid Character(s)" "The domain contains invalid character(s). It must consist of a-z (lowercase), 0-9, and/or -."
HOMESERVER_DOMAIN=""
elif [ $(checkValidBaseDomain "$HOMESERVER_DOMAIN") = "false" ]; then
showMessageBox "Invalid Domain Name" "Invalid domain name. The base domain must be of the format 'example.com'. It cannot start or end with a period."
HOMESERVER_DOMAIN=""
elif [ $(getDomainTLD "$HOMESERVER_DOMAIN") = "test" ]; then
showMessageBox "Invalid Domain" "The specific TLD .test actually causes some issues. Change it to .tester or something else."
HOMESERVER_DOMAIN=""
fi
done
updatePlaintextRootConfigVar HOMESERVER_DOMAIN $HOMESERVER_DOMAIN
new_hostname="HomeServer-$(echo $HOMESERVER_DOMAIN | sed 's/\./-/g')"
if [ -z "$(cat /etc/hosts | grep $new_hostname)" ]; then
echo "127.0.1.1 $new_hostname" | sudo tee -a /etc/hosts
fi
sudo hostnamectl set-hostname $new_hostname
rm -f $HOME/dead.letter
EXT_DOMAIN_PREFIX=""
while [ -z "$EXT_DOMAIN_PREFIX" ]
do
if [ "$IS_ACCEPT_DEFAULTS" = "yes" ]; then
EXT_DOMAIN_PREFIX="external"
else
EXT_DOMAIN_PREFIX=$(promptUserInputMenu "external" "External Domain Prefix" "Enter the prefix for your domain to be reached externally:")
fi
if [ -z "$EXT_DOMAIN_PREFIX" ]; then
showMessageBox "Prefix Empty" "The Prefix cannot be empty"
elif [ $(checkValidString "$EXT_DOMAIN_PREFIX" "-") = "false" ]; then
showMessageBox "Invalid Character(s)" "The prefix contains invalid character(s). It must consist of a-z (lowercase), 0-9, and/or -"
EXT_DOMAIN_PREFIX=""
else
updatePlaintextRootConfigVar EXT_DOMAIN_PREFIX $EXT_DOMAIN_PREFIX
fi
done
INT_DOMAIN_PREFIX=""
while [ -z "$INT_DOMAIN_PREFIX" ]
do
if [ "$IS_ACCEPT_DEFAULTS" = "yes" ]; then
INT_DOMAIN_PREFIX="internal"
else
INT_DOMAIN_PREFIX=$(promptUserInputMenu "internal" "Internal Domain Prefix" "Enter the prefix for your RelayServer to be reached internally (if applicable):")
fi
if [ -z "$INT_DOMAIN_PREFIX" ]; then
showMessageBox "Prefix Empty" "The Prefix cannot be empty"
elif [ $(checkValidString "$INT_DOMAIN_PREFIX" "-") = "false" ]; then
showMessageBox "Invalid Character(s)" "The prefix contains invalid character(s). It must consist of a-z (lowercase), 0-9, and/or -"
INT_DOMAIN_PREFIX=""
else
updatePlaintextRootConfigVar INT_DOMAIN_PREFIX $INT_DOMAIN_PREFIX
fi
done
while [ -z "$HOMESERVER_NAME" ]
do
HOMESERVER_NAME=$(promptUserInputMenu "" "Enter Name" "Enter your $replaceType name with desired formatting, i.e. capitalization, spaces, etc. No special characters except for commas, hyphens, or periods. This will appear in window titles, certificates, among other things: ")
if [ -z "$HOMESERVER_NAME" ]; then
showMessageBox "Name Empty" "The name cannot be empty"
elif [ $(checkValidStringUpperLowerNumbers "$HOMESERVER_NAME" "[:space:],.-") = "false" ]; then
showMessageBox "Invalid Character(s)" "The name contains invalid character(s)."
HOMESERVER_NAME=""
fi
done
updatePlaintextRootConfigVar HOMESERVER_NAME "$HOMESERVER_NAME"
while [ -z "$HOMESERVER_ABBREV" ]
do
HOMESERVER_ABBREV=$(promptUserInputMenu "" "Enter Abbrev" "Enter an abbreviation for your $replaceType, 3 to 10 lowercase alpha-numeric characters. This will be used as a default prefix for admin usernames as well as some other uses: ")
if [ -z "$HOMESERVER_ABBREV" ]; then
showMessageBox "Abbrev Empty" "The abbreviation cannot be empty."
elif [ $(checkValidString "$HOMESERVER_ABBREV") = "false" ]; then
showMessageBox "Invalid Character(s)" "The abbreviation contains invalid character(s). It must consist of a-z (lowercase) and/or 0-9"
HOMESERVER_ABBREV=""
elif [ ${#HOMESERVER_ABBREV} -lt 3 ] || [ ${#HOMESERVER_ABBREV} -gt 10 ]; then
showMessageBox "Invalid Length" "The abbreviation must be 3 to 10 characters"
HOMESERVER_ABBREV=""
fi
done
updatePlaintextRootConfigVar HOMESERVER_ABBREV $HOMESERVER_ABBREV
while [ -z "$HOMESERVER_CURRENCY_CODE" ]
do
if [ "$IS_AUTO_INSTALL" = "true" ]; then
HOMESERVER_CURRENCY_CODE="USD"
break
fi
HOMESERVER_CURRENCY_CODE=$(promptUserInputMenu "USD" "Enter Currency Code" "Enter your currency code: ")
if [ -z "$HOMESERVER_CURRENCY_CODE" ]; then
showMessageBox "Code Empty" "The code cannot be empty"
elif [ $(checkValidCurrencyCode "$HOMESERVER_CURRENCY_CODE") = "false" ]; then
showMessageBox "Invalid Code" "The code is invalid."
HOMESERVER_CURRENCY_CODE=""
fi
done
updatePlaintextRootConfigVar HOMESERVER_CURRENCY_CODE "$HOMESERVER_CURRENCY_CODE"
priorTZ=$(readlink /etc/localtime | sed 's|.*/zoneinfo/||')
while [ -z "$TZ" ]
do
TZ=$(promptUserInputMenu "$priorTZ" "Enter Time Zone" "Enter your time zone. For a list of all time zones, visit https://en.wikipedia.org/wiki/List_of_tz_database_time_zones and enter the value in the [TZ database name] column: ")
if [ $? -ne 0 ]; then
exit 2
fi
if [ -z "$TZ" ]; then
showMessageBox "Time Zone Empty" "The time zone cannot be empty"
elif [ $(checkValidStringUpperLowerNumbers "$TZ" "/_+-") = "false" ]; then
showMessageBox "Invalid Character(s)" "The timezone contains invalid character(s)."
TZ=""
else
set +e
sudo timedatectl set-timezone "$TZ" > /dev/null 2>&1
if [ $? -ne 0 ]; then
showMessageBox "Invalid Time Zone" "The time zone is invalid. Please enter a valid time zone."
TZ=""
sudo timedatectl set-timezone "$priorTZ" > /dev/null 2>&1
fi
set -e
fi
done
updatePlaintextRootConfigVar TZ $TZ
echo "$TZ" | sudo tee /etc/timezone > /dev/null 2>&1
sudo chmod 0644 /etc/timezone
set +e
CURRENT_SSH_PORT=$(grep "^Port" /etc/ssh/sshd_config)
if [ $? -eq 0 ]; then
CURRENT_SSH_PORT=$(echo $CURRENT_SSH_PORT | cut -d " " -f2)
if ! [ "$CURRENT_SSH_PORT" = "22" ]; then
SSH_PORT=$CURRENT_SSH_PORT
fi
else
CURRENT_SSH_PORT=22
fi
updatePlaintextRootConfigVar CURRENT_SSH_PORT $CURRENT_SSH_PORT
while [ -z "$SSH_PORT" ]
do
SSH_PORT=$(promptUserInputMenu $((9000 + $RANDOM % 999)) "Enter SSH Port" "It is highly advised to change your default SSH port (22), since bots will constantly probe port 22. A random port between 9000-9999 has been generated for you. Ensure you remember this port in order to log back in.")
if [ -z "$SSH_PORT" ]; then
showMessageBox "SSH Port Empty" "The SSH port cannot be empty"
elif [ "$(checkValidNumber $SSH_PORT)" = "false" ]; then
showMessageBox "Invalid Character(s)" "The port contains invalid character(s). It must be a number."
SSH_PORT=""
elif [ $SSH_PORT -eq 22 ]; then
# No change, we'll allow it
showMessageBox "Huh" "Okay, whatever...you do you."
SSH_PORT="22"
elif [ $SSH_PORT -lt 1024 ] || [ $SSH_PORT -gt 65536 ]; then
showMessageBox "Invalid Character(s)" "The port number is invalid, it must be between 1024-65536."
SSH_PORT=""
else
checkAvailableHomeServerPort $SSH_PORT
if [ $? -ne 0 ]; then
showMessageBox "Invalid Port" "The port is invalid. It either conflicts with an existing used port or is less than 1024."
SSH_PORT=""
fi
fi
done
updatePlaintextRootConfigVar SSH_PORT $SSH_PORT
# Change SSH port on host
sudo sed -i "s|^#*Port .*$|Port ${SSH_PORT}|g" /etc/ssh/sshd_config
sudo sed -i "s|^Port .*$|Port ${SSH_PORT}|g" /etc/ssh/sshd_config
while [ -z "$PORTAINER_LOCAL_HTTPS_PORT" ] || [ "$PORTAINER_LOCAL_HTTPS_PORT" = "$SSH_PORT" ]
do
tmp_port=$((9000 + $RANDOM % 999))
while [ "$tmp_port" = "$SSH_PORT" ]
do
tmp_port=$((9000 + $RANDOM % 999))
done
if [ "$IS_ACCEPT_DEFAULTS" = "yes" ]; then
PORTAINER_LOCAL_HTTPS_PORT=$tmp_port
else
PORTAINER_LOCAL_HTTPS_PORT=$(promptUserInputMenu $tmp_port "Enter Local Portainer Port" "Enter a local port to use for portainer. A random port between 9000-9999 has been generated for you.")
if [ -z "$PORTAINER_LOCAL_HTTPS_PORT" ] || [ "$PORTAINER_LOCAL_HTTPS_PORT" = "$SSH_PORT" ]; then
showMessageBox "Portainer Port Error" "The port cannot be empty or the same as the SSH port"
elif [ "$(checkValidNumber $PORTAINER_LOCAL_HTTPS_PORT)" = "false" ]; then
showMessageBox "Invalid Character(s)" "The port contains invalid character(s). It must be a number."
PORTAINER_LOCAL_HTTPS_PORT=""
elif [ $PORTAINER_LOCAL_HTTPS_PORT -lt 1024 ] || [ $PORTAINER_LOCAL_HTTPS_PORT -gt 65536 ]; then
showMessageBox "Invalid Character(s)" "The port number is invalid, it must be between 1024-65536."
PORTAINER_LOCAL_HTTPS_PORT=""
fi
if ! [ -z "$PORTAINER_LOCAL_HTTPS_PORT" ]; then
checkAvailableHomeServerPort $PORTAINER_LOCAL_HTTPS_PORT
if [ $? -ne 0 ]; then
showMessageBox "Invalid Port" "The port is invalid. It either conflicts with an existing used port or is less than 1024."
PORTAINER_LOCAL_HTTPS_PORT=""
fi
fi
fi
updatePlaintextRootConfigVar PORTAINER_LOCAL_HTTPS_PORT $PORTAINER_LOCAL_HTTPS_PORT
DOCKERUSER_HOMESERVER_HOST_ALLOW_PORTS_DEFAULT=$DOCKERUSER_HOMESERVER_HOST_ALLOW_PORTS_DEFAULT,$PORTAINER_LOCAL_HTTPS_PORT
updatePlaintextUserConfigVar DOCKERUSER_HOMESERVER_HOST_ALLOW_PORTS_DEFAULT $DOCKERUSER_HOMESERVER_HOST_ALLOW_PORTS_DEFAULT
done
set -e
# Do the boring things that must be done
while ! [ "$IS_AUTO_INSTALL" = "true" ] && [ -z "$FIRST_USER_USERNAME" ]
do
FIRST_USER_USERNAME=$(promptUserInputMenu "" "Enter First User" "Enter the username for your first LDAP/email account: ")
if [ -z "$FIRST_USER_USERNAME" ]; then
showMessageBox "Username Empty" "The username cannot be empty"
elif [ $(checkValidString "$FIRST_USER_USERNAME") = "false" ]; then
showMessageBox "Invalid Character(s)" "The username contains invalid character(s). It must consist of a-z (lowercase) and/or 0-9"
FIRST_USER_USERNAME=""
fi
done
curFullName="$(echo $(getent passwd $USERNAME) | cut -d":" -f5 | cut -d"," -f1)"
if [ -z "$curFullName" ]; then
curFullName="${FIRST_USER_USERNAME^}"
fi
while ! [ "$IS_AUTO_INSTALL" = "true" ] && [ -z "$FIRST_USER_FIRSTNAME" ]
do
FIRST_USER_FIRSTNAME=$(promptUserInputMenu "$curFullName" "Enter First Name" "Enter the first name for your first LDAP/email user account: ")
if [ -z "$FIRST_USER_FIRSTNAME" ]; then
showMessageBox "Name Empty" "The name cannot be empty"
elif [ $(checkValidStringUpperLowerNumbers "$FIRST_USER_FIRSTNAME" "[:space:].,-") = "false" ]; then
showMessageBox "Invalid Character(s)" "The name contains invalid character(s). It must consist of a-z, A-Z, 0-9, spaces, periods, commas, and/or hyphens."
FIRST_USER_FIRSTNAME=""
fi
done
while ! [ "$IS_AUTO_INSTALL" = "true" ] && [ -z "$FIRST_USER_LASTNAME" ]
do
FIRST_USER_LASTNAME=$(promptUserInputMenu "" "Enter Last Name" "Enter the last name for your first LDAP/email user account: ")
if [ -z "$FIRST_USER_LASTNAME" ]; then
showMessageBox "Name Empty" "The name cannot be empty"
elif [ $(checkValidStringUpperLowerNumbers "$FIRST_USER_LASTNAME" "[:space:].,-") = "false" ]; then
showMessageBox "Invalid Character(s)" "The name contains invalid character(s). It must consist of a-z, A-Z, 0-9, spaces, periods, commas, and/or hyphens."
FIRST_USER_LASTNAME=""
fi
done
if ! [ "$IS_AUTO_INSTALL" = "true" ] && [ -z "$FIRST_USER_PASSWORD" ]; then
tmp_pw1=""
tmp_pw2=""
set +e
while [ -z "$tmp_pw1" ] || ! [ "$tmp_pw1" = "$tmp_pw2" ]
do
tmp_pw1=$(promptPasswordMenu "Create Password" "Create a password for your LDAP/email user ($FIRST_USER_USERNAME) account: ")
if [ $? -ne 0 ]; then exit; fi
if [ -z "$tmp_pw1" ]; then
showMessageBox "Password Empty" "The password cannot be empty, please try again."
tmp_pw1=abc
tmp_pw2=def
continue
fi
if [ $(checkValidPassword "$tmp_pw1" 8) = "false" ]; then
showMessageBox "Password Invalid" "The password is too short or contains invalid characters. It must contain at least 8 characters and consist of uppercase letters, lowercase letters, and numbers. It can only contain the following symbols: ~!@#%^&*()-_+=<>?.,"
tmp_pw1=abc
tmp_pw2=def
continue
fi
tmp_pw2=$(promptPasswordMenu "Confirm Password" "Enter the password again to confirm: ")
if [ $? -ne 0 ]; then exit; fi
if ! [ "$tmp_pw1" = "$tmp_pw2" ]; then
showMessageBox "Password Mismatch" "The passwords do not match, please try again."
fi
done
FIRST_USER_PASSWORD="$tmp_pw1"
tmp_pw1=""
tmp_pw2=""
set -e
fi
while [ -z "$ADMIN_USERNAME_BASE" ] || [ "$ADMIN_USERNAME_BASE" = "admin" ]
do
if [ "$IS_ACCEPT_DEFAULTS" = "yes" ]; then
ADMIN_USERNAME_BASE=$HOMESERVER_ABBREV"admin"
else
ADMIN_USERNAME_BASE=$(promptUserInputMenu $HOMESERVER_ABBREV"admin" "Enter Admin Base" "Enter the base admin username (it cannot be \"admin\"): ")
fi
if [ $(checkValidString "$ADMIN_USERNAME_BASE") = "false" ]; then
showMessageBox "Invalid Character(s)" "The username contains invalid character(s). It must consist of a-z (lowercase) and/or 0-9"
ADMIN_USERNAME_BASE=""
fi
updatePlaintextRootConfigVar ADMIN_USERNAME_BASE $ADMIN_USERNAME_BASE
done
if [[ "$(isProgramInstalled pwgen)" = "false" ]]; then
echo "Installing pwgen, please wait..."
performAptInstall pwgen > /dev/null 2>&1
fi
if [[ "$(isProgramInstalled uuidgen)" = "false" ]]; then
echo "Installing uuidgen, please wait..."
performAptInstall uuid-runtime > /dev/null 2>&1
fi
while [ -z "$EMAIL_ADMIN_USERNAME" ]
do
if [ "$IS_ACCEPT_DEFAULTS" = "yes" ]; then
EMAIL_ADMIN_USERNAME="mailmanager"
else
EMAIL_ADMIN_USERNAME=$(promptUserInputMenu "mailmanager" "Enter Admin Email" "Enter the admin email username: ")
fi
if [ $(checkValidString "$EMAIL_ADMIN_USERNAME") = "false" ]; then
showMessageBox "Invalid Character(s)" "The username contains invalid character(s). It must consist of a-z (lowercase) and/or 0-9"
EMAIL_ADMIN_USERNAME=""
fi
updateConfigVar EMAIL_ADMIN_USERNAME $EMAIL_ADMIN_USERNAME
done
while [ -z "$EMAIL_JOINT_USERNAME" ]
do
if [ "$IS_ACCEPT_DEFAULTS" = "yes" ]; then
EMAIL_JOINT_USERNAME="info"
else
EMAIL_JOINT_USERNAME=$(promptUserInputMenu "info" "Enter Shared Email" "Enter the shared email username: ")
fi
if [ $(checkValidString "$EMAIL_JOINT_USERNAME") = "false" ]; then
showMessageBox "Invalid Character(s)" "The username contains invalid character(s). It must consist of a-z (lowercase) and/or 0-9"
EMAIL_JOINT_USERNAME=""
fi
updateConfigVar EMAIL_JOINT_USERNAME $EMAIL_JOINT_USERNAME
done
initServicesCredentials
addUserShareDirectories $NEXTCLOUD_ADMIN_USERNAME
addUserShareDirectories $SPEAKR_ADMIN_USERNAME
}
function initInstallation()
{
if [[ "$(isProgramInstalled screen)" = "false" ]]; then
echo "Installing screen, please wait..."
performAptInstall screen > /dev/null 2>&1
fi
if [[ "$(isProgramInstalled nano)" = "false" ]]; then
echo "Installing nano, please wait..."
performAptInstall nano > /dev/null 2>&1
fi
# Show info to user and prompt for confirmation
if [ "$PRIMARY_VPN_SETUP_TYPE" = "host" ]; then
rs_wg_user_conf=$(sudo cat $HSHQ_WIREGUARD_DIR/users/${RELAYSERVER_WG_VPN_NETNAME}-user1.conf)
fi
clear
strInstallConfig=""
strInstallConfig="${strInstallConfig}################################################################\n"
strInstallConfig="${strInstallConfig}========================= General Info =========================\n"
strInstallConfig="${strInstallConfig}- Root CA URL: http://$SUB_FILES.$HOMESERVER_DOMAIN/ca.crt\n"
strInstallConfig="${strInstallConfig}- Home Page: https://$SUB_HSHQHOME.$HOMESERVER_DOMAIN\n"
strInstallConfig="${strInstallConfig}- Email Admin Username: $EMAIL_ADMIN_EMAIL_ADDRESS\n"
strInstallConfig="${strInstallConfig}- Email Admin Password: $EMAIL_ADMIN_PASSWORD\n"
strInstallConfig="${strInstallConfig}- HomeServer Current SSH Port: $CURRENT_SSH_PORT\n"
strInstallConfig="${strInstallConfig}- HomeServer New SSH Port: $SSH_PORT\n"
strInstallConfig="${strInstallConfig}- Portainer IP URL: https://$HOMESERVER_HOST_PRIMARY_INTERFACE_IP:$PORTAINER_LOCAL_HTTPS_PORT\n"
strInstallConfig="${strInstallConfig}- Portainer Username: $PORTAINER_ADMIN_USERNAME\n"
strInstallConfig="${strInstallConfig}- Portainer Password: $PORTAINER_ADMIN_PASSWORD\n"
strInstallConfig="${strInstallConfig}- Script-server IP URL: https://$HOMESERVER_HOST_PRIMARY_INTERFACE_IP:$SCRIPTSERVER_LOCALHOST_PORT\n"
strInstallConfig="${strInstallConfig}- Script-server Username: $SCRIPTSERVER_ADMIN_USERNAME\n"
strInstallConfig="${strInstallConfig}- Script-server Password: $SCRIPTSERVER_ADMIN_PASSWORD\n"
if [ "$PRIMARY_VPN_SETUP_TYPE" = "host" ]; then
strInstallConfig="${strInstallConfig}- RelayServer IP Address: $RELAYSERVER_SERVER_IP\n"
strInstallConfig="${strInstallConfig}- RelayServer Current SSH Port: $RELAYSERVER_CURRENT_SSH_PORT\n"
strInstallConfig="${strInstallConfig}- RelayServer New SSH Port: $RELAYSERVER_SSH_PORT\n"
strInstallConfig="${strInstallConfig}- RelayServer Linux Username: $RELAYSERVER_REMOTE_USERNAME\n"
fi
strInstallConfig="${strInstallConfig}================================================================\n\n"
if [ "$PRIMARY_VPN_SETUP_TYPE" = "host" ] || [ "$PRIMARY_VPN_SETUP_TYPE" = "join" ]; then
strInstallConfig="${strInstallConfig}$(getDNSRecordsInfo $HOMESERVER_DOMAIN)\n\n"
fi
if [ "$PRIMARY_VPN_SETUP_TYPE" = "host" ]; then
strInstallConfig="${strInstallConfig}================= User WireGuard Configuration =================\n"
strInstallConfig="${strInstallConfig}""""$rs_wg_user_conf""""\n"
strInstallConfig="${strInstallConfig}================================================================\n\n"
fi
#strInstallConfig="${strInstallConfig}=========================== Root CA ============================\n"
#strInstallConfig="${strInstallConfig}""""$(cat "$HSHQ_SSL_DIR/${CERTS_ROOT_CA_NAME}.crt")""""\n"
#strInstallConfig="${strInstallConfig}================================================================\n"
strInstallConfig="${strInstallConfig} INSTRUCTIONS \n"
strInstallConfig="${strInstallConfig}The system is prepped for installation. It will take around\n"
strInstallConfig="${strInstallConfig}10-15 minutes to perform the base installation, depending on\n"
strInstallConfig="${strInstallConfig}hardware specs and download speeds. Copy all of this info\n"
strInstallConfig="${strInstallConfig}before proceeding. Ensure to scroll to the top and retain\n"
strInstallConfig="${strInstallConfig}everything between the ##### borders. Simply select the\n"
strInstallConfig="${strInstallConfig}information with your mouse, and it will automatically be\n"
strInstallConfig="${strInstallConfig}copied to the clipboard.\n\n"
strInstallConfig="${strInstallConfig}The process will run continuously uninterrupted in a 'screen'\n"
strInstallConfig="${strInstallConfig}If you lose connection, then enter 'screen -r hshqInstall' to\n"
strInstallConfig="${strInstallConfig}resume the screen if you want to continue to view the process.\n"
strInstallConfig="${strInstallConfig}To exit the screen, press CTRL-a, then release both keys,\n"
strInstallConfig="${strInstallConfig}then press d to detach.\n\n"
strInstallConfig="${strInstallConfig}The system will automatically reboot upon completion.\n\n"
strInstallConfig="${strInstallConfig}If you want to completely remove everything, run the utility\n"
strInstallConfig="${strInstallConfig}inside this script (System Utils -> Uninstall and Remove\n"
strInstallConfig="${strInstallConfig}Everything) and follow the prompts. To do the same on the\n"
strInstallConfig="${strInstallConfig}RelayServer, enter the command 'bash nuke.sh', and follow\n"
strInstallConfig="${strInstallConfig}the prompts.\n\n"
if [ "$PRIMARY_VPN_SETUP_TYPE" = "host" ]; then
strInstallConfig="${strInstallConfig}The installation on the RelayServer will also automatically\n"
strInstallConfig="${strInstallConfig}be initiated as well. Ensure to log in to the RelayServer\n"
strInstallConfig="${strInstallConfig}as user ($RELAYSERVER_REMOTE_USERNAME) to monitor the progress of the installation,\n"
strInstallConfig="${strInstallConfig}by entering the command 'screen -r hshqInstall'.\n\n"
fi
strInstallConfig="${strInstallConfig}################################################################\n\n"
echo -e "${strInstallConfig}"
while true;
do
echo -e "________________________________________________________________________\n"
if [ "$IS_DESKTOP_ENV" = "true" ]; then
hdir=/home/$USERNAME/Desktop
is_keep_config=y
echo -e "The above information has been saved to your Desktop,"
echo -e "i.e. $hdir/$HSHQ_INSTALL_NOTES_FILENAME."
else
hdir=/home/$USERNAME
echo -e "Do you want to retain the above information in a file in the home directory,"
echo -e "i.e. ($hdir/$HSHQ_INSTALL_NOTES_FILENAME)? Note that this"
echo -e "information is very sensitive and you should delete the file as soon"
read -r -p "as you are finished with it. Enter 'y' or 'n': " is_keep_config
fi
if [ "$is_keep_config" = "y" ]; then
final_prompt="After reading the above section, enter 'install' or 'exit': "
rm -f "$hdir/$HSHQ_INSTALL_NOTES_FILENAME"
echo -e "\n" > "$hdir/$HSHQ_INSTALL_NOTES_FILENAME"
echo -e "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" >> "$hdir/$HSHQ_INSTALL_NOTES_FILENAME"
echo -e "@@@@@@@@ You should permanently delete this file @@@@@@@@" >> "$hdir/$HSHQ_INSTALL_NOTES_FILENAME"
echo -e "@@@@@@@@ as soon as you are finished with it @@@@@@@@" >> "$hdir/$HSHQ_INSTALL_NOTES_FILENAME"
echo -e "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n\n" >> "$hdir/$HSHQ_INSTALL_NOTES_FILENAME"
echo -e "${strInstallConfig}" >> "$hdir/$HSHQ_INSTALL_NOTES_FILENAME"
chmod 0400 "$hdir/$HSHQ_INSTALL_NOTES_FILENAME"
break
elif [ "$is_keep_config" = "n" ]; then
final_prompt="After reading/copying the above section, enter 'install' or 'exit': "
break
else
echo "Unknown response, please try again."
fi
done
while true;
do
if [ "$IS_AUTO_INSTALL" = "true" ]; then
break
fi
echo -e "\n________________________________________________________________________\n"
read -r -p "$final_prompt" is_install
if [ "$is_install" = "install" ]; then
break
elif [ "$is_install" = "exit" ]; then
echo "Exiting..."
releaseLock hshqopen "initConfig" false
exit 1
else
echo "Unknown response, please try again."
fi
done
if [ "$PRIMARY_VPN_SETUP_TYPE" = "host" ]; then
echo "Starting installation on RelayServer..."
sleep 1
isRSReady=false
set +e
countRSPreRetries=0
maxRSPreRetries=30
while true;
do
perfRemoteAction -m ssh -p $RELAYSERVER_CURRENT_SSH_PORT -o "-T -o ConnectTimeout=10 -o 'StrictHostKeyChecking accept-new'" -u $RELAYSERVER_REMOTE_USERNAME -h $RELAYSERVER_SERVER_IP -c "sleep 1;if [ -f ~/$RELAYSERVER_NOT_READY_FILE ]; then exit 1; fi" -r 1 -b 1
if [ $? -eq 0 ]; then
break
fi
if [ $countRSPreRetries -ge $maxRSPreRetries ]; then
echo "Max retries reached. Something has likely gone wrong with the RelayServer pre-installation script, exiting..."
exit 2
fi
((countRSPreRetries++))
echo "($countRSPreRetries of $maxRSPreRetries) RelayServer not ready for installation, sleeping 30 seconds then will retry..."
sleep 30
done
sleep 1
perfRemoteAction -m ssh -p $RELAYSERVER_CURRENT_SSH_PORT -o "-T -o ConnectTimeout=10 -o 'StrictHostKeyChecking accept-new'" -u $RELAYSERVER_REMOTE_USERNAME -h $RELAYSERVER_SERVER_IP -c "bash $RS_INSTALL_FRESH_SCRIPT_NAME -s" -f -i "$USER_RELAY_SUDO_PW"
fi
set -e
refreshSudo
echo "Starting installation on HomeServer..."
sleep 1
releaseLock hshqopen "preInstallation" false
truncate -s 0 $HSHQ_LOG_FILE
scName=hshqInstall
initScreen $scName "bash $HSHQ_LIB_SCRIPT install $CONNECTING_IP"
if [ $? -ne 0 ]; then
showMessageBox "ERROR" "There was a problem starting the installation, exiting..."
exit 1
fi
screen -r $scName
exit 0
}
function setSudoTimeoutInstall()
{
sudo tee $HOME/$SUDO_LONG_TIMEOUT_FILENAME >/dev/null <<EOFSU
Defaults timestamp_timeout=$SUDO_LONG_TIMEOUT
EOFSU
sudo chmod 0600 $HOME/$SUDO_LONG_TIMEOUT_FILENAME
sudo mv $HOME/$SUDO_LONG_TIMEOUT_FILENAME /etc/sudoers.d/
}
function removeSudoTimeoutInstall()
{
sudo rm -f /etc/sudoers.d/$SUDO_LONG_TIMEOUT_FILENAME
}
function performBaseInstallation()
{
logHSHQEvent info "Installation Started!"
checkRes="$(acquireAllLocks)"
if ! [ -z "$checkRes" ]; then
strErr="performBaseInstallation - The HSHQ script is already open or running in a different instance ($checkRes). If you are certain that it is not, then please remove the directories: /tmp/hshqopen and /tmp/networkchecks, then restart the installation."
logHSHQEvent error "$strErr"
echo "$strErr"
exit 7
fi
set +e
checkLoadConfig
setSudoTimeoutInstall
if [ "$IS_INSTALLED" = "true" ] || [ "$IS_INSTALLING" = "true" ]; then
strErr="performBaseInstallation - Already installed or existing installation is in progress, exiting..."
logHSHQEvent error "$strErr"
echo "$strErr"
exit 1
fi
IS_INSTALLING=true
setSystemState $SS_INSTALLING
updateConfigVar IS_INSTALLING $IS_INSTALLING
set +e
upgradeHostOS
sudo DEBIAN_FRONTEND=noninteractive apt autoremove -y
set -e
outputScripts
echo "Setting MOTD..."
updateMOTD
createSwapfile
performSuggestedSecUpdates
logHSHQEvent info "performBaseInstallation - Install Dependencies"
installMailUtils
installDependencies
clear
echo -e "\n\n##########################################"
echo -e " Starting Base HSHQ Installation"
echo -e "##########################################\n\n"
enable_trapping
setup_scroll_area
is_draw_pb=true
draw_progress_bar 0
set +e
if [ "$LOG_LEVEL" = "debug" ]; then
echo "Clearing IPTables..."
fi
performClearIPTables true
if [ "$LOG_LEVEL" = "debug" ]; then
echo "performBaseInstallation-Early IPTables..."
fi
checkUpdateAllIPTables performBaseInstallation-Early
performInstallVariantsPre
set -e
draw_progress_bar 5
pullBaseServicesDockerImages
draw_progress_bar 50
logHSHQEvent info "performBaseInstallation - Init DH Params"
initDHParams
draw_progress_bar 53
getUpdateAssets
if [ $? -ne 0 ]; then
echo "FATAL: Could not update image assets, exiting..."
exit 9
fi
sudo mkdir -p /usr/share/icons/HSHQ
sudo cp -f $HSHQ_ASSETS_DIR/icons/* /usr/share/icons/HSHQ/
draw_progress_bar 55
logHSHQEvent info "performBaseInstallation - Starting Stack Installs"
installBaseStacks
draw_progress_bar 80
set +e
if [ "$PRIMARY_VPN_SETUP_TYPE" = "host" ] || [ "$PRIMARY_VPN_SETUP_TYPE" = "join" ]; then
if [ "$PRIMARY_VPN_SETUP_TYPE" = "host" ]; then
logHSHQEvent info "performBaseInstallation - Configure Docker WireGuard Network"
connectPrimaryInternet
elif [ "$PRIMARY_VPN_SETUP_TYPE" = "join" ]; then
dns_file=$HOME/dns.tmp
if [ -f $dns_file ]; then
docker container stop heimdall >/dev/null
updateHomeServerDNS $dns_file
rm -f $dns_file
docker container start heimdall >/dev/null
fi
fi
draw_progress_bar 81
logHSHQEvent info "performBaseInstallation - Connect Services To VPN"
connectPrimaryVPN true
draw_progress_bar 83
echo "Waiting 30s for services to initialize..."
sleep 30
fi
draw_progress_bar 85
clearQueryLogAndStatsAdguardHS
set +e
if [ "$PRIMARY_VPN_SETUP_TYPE" = "host" ]; then
curl https://$SUB_ADGUARD.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN > /dev/null 2>&1
if [ $? -eq 0 ]; then
clearQueryLogAndStatsAdguardRS
fi
fi
set -e
logHSHQEvent info "performBaseInstallation - Post Installation"
draw_progress_bar 86
checkUpdateAllIPTables performBaseInstallation-Late
set -e
draw_progress_bar 90
postInstallation install
}
function trustDesktopIcon()
{
set +e
dtIcon=$1
which gio > /dev/null 2>&1
if [ $? -eq 0 ]; then
gio set $HOME/Desktop/$dtIcon metadata::trusted true > /dev/null 2>&1
gio set -t string $HOME/Desktop/$dtIcon metadata::xfce-exe-checksum "$(sha256sum $HOME/Desktop/$dtIcon | awk '{print $1}')" > /dev/null 2>&1
dbus-launch --exit-with-session gio set $HOME/Desktop/$dtIcon metadata::trusted true > /dev/null 2>&1
dbus-launch --exit-with-session gio set -t string $HOME/Desktop/$dtIcon metadata::xfce-exe-checksum "$(sha256sum $HOME/Desktop/$dtIcon | awk '{print $1}')" > /dev/null 2>&1
nautilus -q > /dev/null 2>&1
fi
}
function postInstallation()
{
isInstallOrRestore="$1"
refreshSudo
echo "Performing post-installation tasks..."
sudo DEBIAN_FRONTEND=noninteractive apt remove --purge -y avahi-daemon > /dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt remove --purge -y avahi-autoipd > /dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt autoremove --purge -y
if [ "$isInstallOrRestore" = "install" ]; then
echo "Emailing Root CA..."
sendRootCAEmail true
draw_progress_bar 91
if ! [ "$IS_AUTO_INSTALL" = "true" ]; then
emailFormattedCredentials true
fi
draw_progress_bar 95
fi
# Need to wait until emails have been sent before changing permissions.
sudo chmod 750 /usr/bin/mail.mailutils
sudo rm -f /etc/skel/hshq.sh
if [ "$IS_DESKTOP_ENV" = "true" ]; then
echo "Configuring Desktop environment..."
set +e
if ! [ -f /usr/share/icons/HSHQ/Homepage.png ]; then
sudo cp /usr/share/icons/HSHQ/HomepageHSHQ.png /usr/share/icons/HSHQ/Homepage.png
fi
rm -f ~/Desktop/InstallHSHQ.desktop
rm -f ~/Desktop/HomePage.desktop
cat <<EOFHP > ~/Desktop/HomePage.desktop
[Desktop Entry]
Name=$HOMESERVER_NAME
Exec=firefox $SUB_HSHQHOME.$HOMESERVER_DOMAIN
Comment=Opens your HomeServer home page
Terminal=false
Icon=/usr/share/icons/HSHQ/Homepage.png
Type=Application
Categories=HomeServerHQ
EOFHP
chmod 755 ~/Desktop/HomePage.desktop
rm -f ~/Desktop/HSHQConsole.desktop
which terminator > /dev/null 2>&1
if [ $? -eq 0 ]; then
# Specify terminator as console if possible
cat <<EOFHP > ~/Desktop/HSHQConsole.desktop
[Desktop Entry]
Name=HSHQ Console Util
Exec=/usr/bin/terminator -x bash -ic "~/hshq.sh"
Comment=Runs the HSHQ console-based management utility
Icon=/usr/share/icons/HSHQ/ConsoleHSHQ.png
Type=Application
Categories=HomeServerHQ
EOFHP
else
cat <<EOFHP > ~/Desktop/HSHQConsole.desktop
[Desktop Entry]
Name=HSHQ Console Util
Exec=bash -ic "~/hshq.sh"
Terminal=true
Comment=Runs the HSHQ console-based management utility
Icon=/usr/share/icons/HSHQ/ConsoleHSHQ.png
Type=Application
Categories=HomeServerHQ
EOFHP
fi
chmod 755 ~/Desktop/HSHQConsole.desktop
rm -f ~/Desktop/HSHQScriptServer.desktop
cat <<EOFHP > ~/Desktop/HSHQScriptServer.desktop
[Desktop Entry]
Name=HSHQ Web Util
Exec=firefox https://127.0.0.1:$SCRIPTSERVER_LOCALHOST_PORT
Comment=Runs the HSHQ web-based management utility
Terminal=false
Icon=/usr/share/icons/HSHQ/ScriptServerHSHQ.png
Type=Application
Categories=HomeServerHQ
EOFHP
chmod 755 ~/Desktop/HSHQScriptServer.desktop
rm -f ~/Desktop/HSHQHelp.desktop
cat <<EOFHP > ~/Desktop/HSHQHelp.desktop
[Desktop Entry]
Name=Help!
Exec=firefox forum.homeserverhq.com
Comment=Ask for help on the HomeServerHQ Forum
Terminal=false
Icon=/usr/share/icons/HSHQ/HelpHSHQ.png
Type=Application
Categories=HomeServerHQ
EOFHP
chmod 755 ~/Desktop/HSHQHelp.desktop
trustDesktopIcon "HomePage.desktop"
trustDesktopIcon "HSHQConsole.desktop"
trustDesktopIcon "HSHQScriptServer.desktop"
trustDesktopIcon "HSHQHelp.desktop"
sudo mkdir -p /usr/share/applications
sudo cp -f ~/Desktop/HomePage.desktop /usr/share/applications/
sudo cp -f ~/Desktop/HSHQConsole.desktop /usr/share/applications/
sudo cp -f ~/Desktop/HSHQScriptServer.desktop /usr/share/applications/
sudo cp -f ~/Desktop/HSHQHelp.desktop /usr/share/applications/
draw_progress_bar 96
which gsettings > /dev/null 2>&1
if [ $? -eq 0 ]; then
# Spell check doesn't belong in a basic text editor,
# especially if it's insanely intrusive.
gsettings set org.gnome.TextEditor spellcheck false > /dev/null 2>&1
# Just because
gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' > /dev/null 2>&1
fi
replaceHomePageDesktopIcon $(pwgen -c -n 8 1)
draw_progress_bar 97
addRootCertificateToApps
draw_progress_bar 98
configureFirefox
set -e
fi
draw_progress_bar 99
initCronJobs
removeSudoTimeoutInstall
rm -f ~/dead.letter
rm -f $HSHQ_BASE_DIR/cip.txt
if [ "$isInstallOrRestore" = "install" ]; then
performInstallVariantsPost
addPrimaryUser "$LDAP_ADMIN_USER_USERNAME" "$LDAP_ADMIN_USER_PASSWORD" "${HOMESERVER_ABBREV^^}" "Admin" true
# > /dev/null 2>&1
if ! [ -z "$FIRST_USER_USERNAME" ]; then
echo "Adding first user($FIRST_USER_USERNAME)..."
addPrimaryUser "$FIRST_USER_USERNAME" "$FIRST_USER_PASSWORD" "$FIRST_USER_FIRSTNAME" "$FIRST_USER_LASTNAME" false > /dev/null 2>&1
fi
IS_INSTALLED=true
updateConfigVar IS_INSTALLED $IS_INSTALLED
IS_INSTALLING=false
updateConfigVar IS_INSTALLING $IS_INSTALLING
encryptConfigFile
draw_progress_bar 100
destroy_scroll_area
releaseLock hshqopen "postInstallation" false
sanitizeHSHQLog
setSystemState $SS_RUNNING
logHSHQEvent info "Installation Complete!"
echo -e "\n\n\n\n################################################################\n"
echo " HomeServer Installation Complete!"
echo " The system will automatically reboot in 60 seconds..."
echo -e "\n################################################################\n\n"
sleep 60
logHSHQEvent info "postInstallation - Rebooting"
sudo cp $HSHQ_LOG_FILE $HSHQ_BASE_DIR/hshqInstall.log
sudo chown $USERNAME:$USERNAME $HSHQ_BASE_DIR/hshqInstall.log
chmod 0400 $HSHQ_BASE_DIR/hshqInstall.log
sudo truncate -s 0 $HSHQ_LOG_FILE
sudo reboot
fi
}
function sanitizeHSHQLog()
{
sudo sed -i 's/\x1b//g' $HSHQ_LOG_FILE
sudo sed -i 's/\^//g' $HSHQ_LOG_FILE
sudo sed -i 's/\x1b//g' $HSHQ_LOG_FILE
sudo sed -i "s|$USERNAME|hshquser|g" $HSHQ_LOG_FILE
sudo sed -i "s|$LDAP_ADMIN_USER_USERNAME|hshqadmin|g" $HSHQ_LOG_FILE
sudo sed -i "s|$HOMESERVER_DOMAIN|hshqexample.com|g" $HSHQ_LOG_FILE
}
function showSimpleBackupMenu()
{
set +e
utilmenu=$(cat << EOF
$(getLogo)
EOF
)
menures=$(whiptail --title "Select an option" --menu "$utilmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT \
"1" "Configure Backup" \
"2" "Mount Existing Backup Disk" \
"3" "Exit" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
menures=0
fi
case $menures in
0)
return 1 ;;
1)
showConfigureSimpleBackupMenu
return 1 ;;
2)
showMountBackupDriveMenu
return 1 ;;
3)
return 0 ;;
esac
}
function showConfigureSimpleBackupMenu()
{
set +e
showYesNoMessageBox "Continue?" "This is a very basic utility to set up your local encrypted backup. It will search for any unmounted disks and prompt you with a list. After making your selection and confirming, it will wipe all data and partitions on that disk, create a single partition, automount the filesystem on boot, then configure your backup in Duplicati for this disk. There are infinite ways you can configure your backup, and infinite tools to accomplish it. This utility is merely one simple way. If you decide to manually set up your own backup process, the directory you need to focus on is $HSHQ_DATA_DIR. The full system can be restored from the contents of this directory. Do you wish to continue?"
if [ $? -ne 0 ]; then
return
fi
sudo cat /etc/fstab | grep $HSHQ_BACKUP_DIR > /dev/null 2>&1
if [ $? -eq 0 ]; then
showMessageBox "ERROR" "There is already a filesystem mounted to $HSHQ_BACKUP_DIR in /etc/fstab. Please unmount this filesystem and remove it from /etc/fstab, returning..."
return
fi
sudo findmnt | grep $HSHQ_BACKUP_DIR > /dev/null 2>&1
if [ $? -eq 0 ]; then
showMessageBox "ERROR" "There is already a filesystem mounted to $HSHQ_BACKUP_DIR. Please unmount this filesystem first, returning..."
return
fi
chkfstab=$(sudo findmnt --verify 2> /dev/null)
if [ $? -ne 0 ]; then
showMessageBox "ERROR" "There is an error with /etc/fstab: ${chkfstab}\nPlease fix this issue and retry."
return
fi
if ! sudo test -f $HSHQ_STACKS_DIR/duplicati/config/Duplicati-server.sqlite; then
showMessageBox "ERROR" "Could not find the Duplicati database, returning..."
return
fi
sudo sqlite3 $HSHQ_STACKS_DIR/duplicati/config/Duplicati-server.sqlite "select TargetURL from Backup;" | grep /backups/ > /dev/null 2>&1
if [ $? -eq 0 ]; then
showMessageBox "ERROR" "There is already a backup in Duplicati that uses the /backups directory, returning..."
return
fi
dtok="$(getDuplicatiToken)"
if [ -z "$dtok" ]; then
showMessageBox "ERROR" "The Duplicati auth token is empty, returning..."
return
fi
dbackmenu=$(cat << EOF
$(getLogo)
EOF
)
OLDIFS=$IFS
IFS=$(echo -en "\n\b")
diskarr=($(sudo hwinfo --disk --short | tail +2))
curListNum=1
scsbm_menu_items=( --title "Select Disk" --radiolist "$dbackmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT )
for curDisk in "${diskarr[@]}"
do
curDiskID=$(echo "$curDisk" | xargs | cut -d" " -f1)
curDiskName=$(echo "$curDisk" | xargs | cut -d" " -f2-)
findmnt | grep $curDiskID > /dev/null 2>&1
if [ $? -eq 0 ]; then
continue
fi
curDiskSize=$(lsblk -o SIZE --noheadings --raw -d $curDiskID)
curDiskParts=$(sudo partx -g $curDiskID 2> /dev/null | wc -l)
scsbm_menu_items+=( "$curDiskID $curDiskSize ($curDiskParts Partitions) $curDiskName" )
scsbm_menu_items+=( "|" )
scsbm_menu_items+=( "off" )
((curListNum++))
done
IFS=$OLDIFS
if [ $curListNum -le 1 ]; then
showMessageBox "ERROR" "There are no available disks for this operation, returning..."
return
fi
while true;
do
selDiskItem=$(whiptail "${scsbm_menu_items[@]}" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
return
fi
if [ -z "$selDiskItem" ]; then
showMessageBox "ERROR" "You did not make a selection, press spacebar on your desired option. Please try again."
else
break
fi
done
selDisk=$(echo "$selDiskItem" | cut -d" " -f1)
if [ -z "$selDisk" ]; then
showMessageBox "ERROR" "No disk was selected, returning..."
return
fi
confirmFormat=$(promptUserInputMenu "" "Confirm Format" "WARNING - This operation will entirely ERASE the contents of the selected drive. To confirm, enter the word 'format' below:")
if [ $? -ne 0 ]; then
return
fi
if [ $? -ne 0 ] || [ -z "$confirmFormat" ] || ! [ "$confirmFormat" = "format" ]; then
showMessageBox "Incorrect Confirmation" "The text did not match, returning..."
return
fi
while true;
do
backupHour=$(promptUserInputMenu "" "Daily Backup Time" "Enter the hour of the day(0-23) that you wish to perform the daily backup. This can be modified later in Duplicati:")
if [ $? -ne 0 ]; then
return
fi
if [[ $backupHour =~ ^[0-9]+$ ]] && [ $backupHour -ge 0 ] && [ $backupHour -le 23 ]; then
break
else
showMessageBox "Invalid Number" "The value is not a valid number."
fi
done
tmp_pw1=""
tmp_pw2=""
while [ -z "$tmp_pw1" ] || ! [ "$tmp_pw1" = "$tmp_pw2" ]
do
tmp_pw1=$(promptPasswordMenu "Create Password" "Create a password to encrypt/decrypt your backup files. ENSURE you remember this or you will be IRREVERSIBLY locked out of your backup files (unless you have a quantum super-computer) and you will NOT be able to recover your data: ")
if [ $? -ne 0 ]; then
return
fi
if [ -z "$tmp_pw1" ]; then
showMessageBox "Password Empty" "The password cannot be empty, please try again."
continue
fi
if [ $(checkValidPassword "$tmp_pw1" 16) = "false" ]; then
showMessageBox "Weak Password" "The password is invalid or is too weak. It must contain at least 16 characters and consist of uppercase letters, lowercase letters, and numbers. It can only contain the following symbols: ~!@#%^&*()-_+=<>?.,"
tmp_pw1=""
tmp_pw2=""
continue
fi
tmp_pw2=$(promptPasswordMenu "Confirm Password" "Enter the password again to confirm: ")
if [ $? -ne 0 ]; then
return
fi
if ! [ "$tmp_pw1" = "$tmp_pw2" ]; then
showMessageBox "Password Mismatch" "The passwords do not match, please try again."
fi
done
backupPW="$tmp_pw1"
tmp_pw1=""
tmp_pw2=""
confirmFinal=$(promptUserInputMenu "" "Confirm" "The backup configuration is ready. To perform all actions, including erasing and formatting the selected disk, enter the word 'confirm' below:")
if [ $? -ne 0 ] || [ -z "$confirmFinal" ] || ! [ "$confirmFinal" = "confirm" ]; then
showMessageBox "Incorrect Confirmation" "The text did not match, returning..."
return
fi
docker container stop duplicati > /dev/null 2>&1
docker container stop syncthing > /dev/null 2>&1
sudo sfdisk --delete $selDisk > /dev/null
retV=$?
if [ $retV -ne 0 ] && [ $retV -ne 1 ]; then
docker container start duplicati > /dev/null 2>&1
docker container start syncthing > /dev/null 2>&1
showMessageBox "ERROR" "Encountered an error attempting to delete the partitions from the disk, exiting..."
return
fi
# A little sleep never hurts
sleep 1
echo ';' | sudo sfdisk $selDisk > /dev/null
if [ $? -ne 0 ]; then
docker container start duplicati > /dev/null 2>&1
docker container start syncthing > /dev/null 2>&1
showMessageBox "ERROR" "Encountered an error attempting to create the new partition, exiting..."
return
fi
sleep 1
newPart=$(sudo fdisk -l $selDisk | grep '^/dev' | cut -d' ' -f1)
sleep 1
sudo mkfs -F -t ext4 $newPart > /dev/null
if [ $? -ne 0 ]; then
docker container start duplicati > /dev/null 2>&1
docker container start syncthing > /dev/null 2>&1
showMessageBox "ERROR" "Encountered an error attempting to create the new filesystem, exiting..."
return
fi
sleep 1
sudo mount $newPart $HSHQ_BACKUP_DIR > /dev/null
if [ $? -ne 0 ]; then
docker container start duplicati > /dev/null 2>&1
docker container start syncthing > /dev/null 2>&1
showMessageBox "ERROR" "Encountered an error attempting to mount the filesystem, exiting..."
return
fi
sleep 1
sudo mkdir $HSHQ_BACKUP_DIR/$HOMESERVER_DOMAIN
docker container start duplicati > /dev/null 2>&1
docker container start syncthing > /dev/null 2>&1
newPartID=$(sudo blkid -s UUID -o value $newPart)
sudo cp /etc/fstab /etc/fstab.old
echo "# Backup Drive" | sudo tee -a /etc/fstab > /dev/null 2>&1
if [ -z "$newPartID" ]; then
echo "$newPart $HSHQ_BACKUP_DIR ext4 defaults,nofail,x-systemd.device-timeout=15 0 0" | sudo tee -a /etc/fstab > /dev/null 2>&1
else
echo "UUID=$newPartID $HSHQ_BACKUP_DIR ext4 defaults,nofail,x-systemd.device-timeout=15 0 0" | sudo tee -a /etc/fstab > /dev/null 2>&1
fi
sudo systemctl daemon-reload > /dev/null 2>&1
sudo findmnt --verify | grep "Success, no errors or warnings detected" > /dev/null 2>&1
if [ $? -ne 0 ]; then
showMessageBox "ERROR" "There was a problem adding the requisite entry to /etc/fstab. Your backup disk will not be auto-mounted after a reboot."
sudo mv /etc/fstab.old /etc/fstab
else
sudo rm -f /etc/fstab.old
fi
mydate=$(date --date="$(date +%Y-%m-%d) $backupHour:00")
dtnow=$(date)
if [ $(($(date -d "$dtnow" +%s) - $(date -d "$mydate" +%s))) -gt 0 ]; then
mydate=$(date -d "$mydate + 1 day")
fi
utcdate=$(TZ="UTC" date --date="$mydate")
cat <<EOFBU > $HOME/hshq-backup.json
{
"CreatedByVersion": "2.0.7.1",
"Schedule": {
"Time": "$(TZ="UTC" date --date="$utcdate" +%FT%TZ)",
"Repeat": "1D",
"LastRun": "0001-01-01T05:51:00Z",
"Rule": "AllowedWeekDays=Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday",
"AllowedDays": [
"mon",
"tue",
"wed",
"thu",
"fri",
"sat",
"sun"
]
},
"Backup": {
"Name": "HSHQ Backup",
"Description": "",
"Tags": [],
"TargetURL": "file:///backups/$HOMESERVER_DOMAIN",
"Sources": [
"/source/"
],
"Settings": [
{
"Filter": "",
"Name": "encryption-module",
"Value": "aes",
"Argument": null
},
{
"Filter": "",
"Name": "compression-module",
"Value": "zip",
"Argument": null
},
{
"Filter": "",
"Name": "dblock-size",
"Value": "50mb",
"Argument": null
},
{
"Filter": "",
"Name": "passphrase",
"Value": "$backupPW",
"Argument": null
},
{
"Filter": "",
"Name": "retention-policy",
"Value": "1W:1D,4W:1W,12M:1M",
"Argument": null
},
{
"Filter": "",
"Name": "ignore-advisory-locking",
"Value": "true",
"Argument": null
}
],
"Filters": [],
"Metadata": {},
"IsTemporary": false
},
"DisplayNames": {
"/source/": "source"
}
}
EOFBU
sleep 3
echo "Getting Duplicati auth token..."
dtok="$(getDuplicatiToken)"
if [ -z "$dtok" ]; then
showMessageBox "ERROR" "ERROR: The Duplicati auth token is empty, returning..."
else
echo "Got token, importing backup configuration..."
fi
docker exec -i -e DTOK="$dtok" duplicati sh -c 'curl -sS --fail-with-body --max-time 300 -X POST -H "Authorization: Bearer $DTOK" -H "Content-Type: application/json" --data-binary @- http://localhost:8200/api/v1/backups' < "$HOME/hshq-backup.json"
#http --check-status --ignore-stdin POST https://$SUB_DUPLICATI.$HOMESERVER_DOMAIN/api/v1/backups "Authorization: Bearer $dtok" @$HOME/hshq-backup.json > /dev/null 2>&1
#docker exec duplicati bash -c "mono /app/duplicati/Duplicati.CommandLine.ConfigurationImporter.exe /config/hshq-backup.json --import-metadata=false --server-datafolder=/config"
uplRetval=$?
sudo rm -f $HOME/hshq-backup.json
if [ $uplRetval -ne 0 ]; then
showMessageBox "ERROR" "ERROR: The backup job was not uploaded to Duplicati. Please report this error on Github or the Forum."
else
showMessageBox "SUCCESS" "Your backup is configured. It will run automatically starting at $mydate, or you can trigger it manually. You will recieve a daily email in your admin email account ($EMAIL_ADMIN_EMAIL_ADDRESS) after each backup. You can view/edit the configuration in https://$SUB_DUPLICATI.$HOMESERVER_DOMAIN ."
fi
}
function getDuplicatiToken()
{
maxDupTries=10
curDupTries=0
isSuccess=false
dupToken=""
while [ $curDupTries -lt $maxDupTries ]
do
dupToken=$(docker exec -e DUPLICATI_ADMIN_PASSWORD="$DUPLICATI_ADMIN_PASSWORD" duplicati sh -c 'curl -sS --fail-with-body --max-time 300 -X POST -H "Content-Type: application/json" -d "$(jq -nc --arg p "$DUPLICATI_ADMIN_PASSWORD" "{password: \$p}")" http://localhost:8200/api/v1/auth/login' | jq -r .AccessToken)
#dupToken=$(http --check-status --ignore-stdin --timeout=300 https://$SUB_DUPLICATI.$HOMESERVER_DOMAIN/api/v1/auth/login password=$DUPLICATI_ADMIN_PASSWORD | jq -r .AccessToken)
if [ $? -eq 0 ] && ! [ -z "$dupToken" ]; then
isSuccess=true
break
fi
((curDupTries++))
sleep 3
done
if [ "$isSuccess" = "true" ]; then
echo "$dupToken"
fi
}
function showMountBackupDriveMenu()
{
findmnt | grep $HSHQ_BACKUP_DIR > /dev/null 2>&1
if [ $? -eq 0 ]; then
showMessageBox "ERROR" "There is already a filesystem mounted to $HSHQ_BACKUP_DIR. Please unmount this filesystem first, returning..."
return
fi
sudo cat /etc/fstab | grep $HSHQ_BACKUP_DIR > /dev/null 2>&1
if [ $? -eq 0 ]; then
showMessageBox "ERROR" "There is already a filesystem mounted to $HSHQ_BACKUP_DIR in /etc/fstab. Please unmount this filesystem and remove it from /etc/fstab, returning..."
return
fi
mkdir -p $HSHQ_BACKUP_DIR
if ! [ -z "$(ls -A $HSHQ_BACKUP_DIR)" ]; then
showMessageBox "ERROR" "The backup directory ($HSHQ_BACKUP_DIR) is not empty, returning..."
return
fi
dbackmenu=$(cat << EOF
$(getLogo)
EOF
)
OLDIFS=$IFS
IFS=$(echo -en "\n\b")
diskarr=($(sudo hwinfo --disk --short | tail +2))
curListNum=1
scsbm_menu_items=( --title "Select Disk/Partition" --radiolist "$dbackmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT )
for curDisk in "${diskarr[@]}"
do
curDiskID=$(echo "$curDisk" | xargs | cut -d" " -f1)
curDiskName=$(echo "$curDisk" | xargs | cut -d" " -f2-)
findmnt | grep $curDiskID > /dev/null 2>&1
if [ $? -eq 0 ]; then
continue
fi
curDiskPartitions=($(lsblk --noheadings --raw -o NAME,SIZE,TYPE $curDiskID))
for curDiskPart in "${curDiskPartitions[@]}"
do
curDiskPartName=$(echo $curDiskPart | cut -d" " -f1)
curDiskPartSize=$(echo $curDiskPart | cut -d" " -f2)
curDiskPartType=$(echo $curDiskPart | cut -d" " -f3)
if [ "$curDiskPartType" = "part" ]; then
scsbm_menu_items+=( "$curDiskPartName $curDiskPartSize ($curDiskName)" )
scsbm_menu_items+=( "|" )
scsbm_menu_items+=( "off" )
((curListNum++))
fi
done
done
IFS=$OLDIFS
if [ $curListNum -le 1 ]; then
showMessageBox "ERROR" "There are no available disks for this operation, returning..."
return
fi
while true;
do
selDiskItem=$(whiptail "${scsbm_menu_items[@]}" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
return
fi
if [ -z "$selDiskItem" ]; then
showMessageBox "ERROR" "You did not make a selection, please try again."
else
break
fi
done
selDisk=$(echo "$selDiskItem" | cut -d" " -f1)
if [ -z "$selDisk" ]; then
showMessageBox "ERROR" "No partition was selected, returning..."
return
fi
sudo mount /dev/$selDisk $HSHQ_BACKUP_DIR > /dev/null
if [ $? -ne 0 ]; then
showMessageBox "ERROR" "There was an error mounting this partition, returning..."
return
fi
newPart=/dev/$selDisk
newPartID=$(sudo blkid -s UUID -o value $newPart)
sudo cp /etc/fstab /etc/fstab.old
echo "# Backup Drive" | sudo tee -a /etc/fstab > /dev/null 2>&1
if [ -z "$newPartID" ]; then
echo "$newPart $HSHQ_BACKUP_DIR ext4 defaults,nofail,x-systemd.device-timeout=15 0 0" | sudo tee -a /etc/fstab > /dev/null 2>&1
else
echo "UUID=$newPartID $HSHQ_BACKUP_DIR ext4 defaults,nofail,x-systemd.device-timeout=15 0 0" | sudo tee -a /etc/fstab > /dev/null 2>&1
fi
sudo systemctl daemon-reload > /dev/null 2>&1
sudo findmnt --verify | grep "Success, no errors or warnings detected" > /dev/null 2>&1
if [ $? -ne 0 ]; then
showMessageBox "ERROR" "There was a problem adding the requisite entry to /etc/fstab. Your backup disk will not be auto-mounted after a reboot."
sudo mv /etc/fstab.old /etc/fstab
else
sudo rm -f /etc/fstab.old
fi
}
function initScreen()
{
# This function may appear to be overly complex and
# contrived, but the main purpose is to inject the
# super-user (sudo) password into the running screen
# with as minimal of a security footprint as possible.
# There's also some race conditions to overcome, hence
# the added complexity. This method avoids anything
# (long-running) from showing up in the process list
# (ps auxwwe).
screenName="$1"
screenCmd="$2"
set +e
dirtest1=/tmp/sctest1
isScreenSuccess=false
curScreenAttempt=0
maxScreenAttempt=5
screen -XS $screenName quit > /dev/null 2>&1
# The screen utility has a problem with stuffing
# special characters in strings, specifically
# in this case when trying to inject the sudo
# password. Thus, the easiest way to handle this
# is to create a temporary "safe" key, output
# the encrypted password to file using the key
# and transmit the key to the screen session.
sudoPWKey=$(pwgen -c -n 32 1)
echo -n "$USER_SUDO_PW" | openssl enc -e -aes256 -pbkdf2 -pass pass:$sudoPWKey > $ENC_PW_FILE
while [ $curScreenAttempt -lt $maxScreenAttempt ]
do
((curScreenAttempt++))
rm -fr $dirtest1
screen -L -Logfile $HSHQ_LOG_FILE -dmS $screenName
screen -S $screenName -X stuff "stty -echo;mkdir $dirtest1\n"
curCheckCount=0
while ! [ -d $dirtest1 ] && [ $curCheckCount -lt 5 ]
do
((curCheckCount++))
sleep 1
done
if ! [ -d $dirtest1 ]; then
screen -XS $screenName quit > /dev/null 2>&1
continue
fi
screen -S $screenName -X stuff "$screenCmd\n"
curCheckCount=0
while ! [ -d $TMP_PW_CHECKDIR ] && [ $curCheckCount -lt 15 ]
do
((curCheckCount++))
sleep 1
done
screen -S $screenName -X stuff "$sudoPWKey\n"
curCheckCount=0
while [ -d $TMP_PW_CHECKDIR ] && [ $curCheckCount -lt 15 ]
do
((curCheckCount++))
sleep 1
done
rm -fr $dirtest1
if [ -d $TMP_PW_CHECKDIR ]; then
rm -fr $TMP_PW_CHECKDIR
echo "The sudo password was not correctly transmitted to the screen session, exiting..."
exit 5
fi
rm -fr $TMP_PW_CHECKDIR
isScreenSuccess=true
break
done
if ! [ "$isScreenSuccess" = "true" ]; then
return 1
fi
}
function performInstallVariantsPre()
{
return
}
function performInstallVariantsPost()
{
return
}
# Stacks Functions
function showStacksMenu()
{
set +e
if ! [ "$IS_INSTALLED" = "true" ]; then
showMessageBox "System Not Installed" "You must perform a system installation first before using this utility."
exit
fi
svcsmenu=$(cat << EOF
$(getLogo)
EOF
)
menures=$(whiptail --title "Select an option" --menu "$svcsmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT \
"1" "Install Service(s) From List" \
"2" "Install All Available Services" \
"3" "Update Service(s) From List" \
"4" "Update All Available Services" \
"5" "Remove Service(s) From List" \
"6" "Exit" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ] || [ $menures -eq 6 ]; then
return
fi
set +e
getUpdateAssets
if [ $? -ne 0 ]; then
showMessageBox "ERROR" "ERROR: Could not update image assets, returning..."
return
fi
case $menures in
0)
return 0 ;;
1)
installStacksFromList
set +e
return 1 ;;
2)
installAllAvailableStacks true
set +e
return 1 ;;
3)
performStackUpdatesFromList
set +e
return 1 ;;
4)
performAllAvailableStackUpdates true
set +e
return 1 ;;
5)
deleteStacksFromList
set +e
return 1 ;;
6)
return 0 ;;
esac
}
function installStacksFromList()
{
set +e
setSystemState $SS_INSTALLING
sortedStackList=$(sortCSVList $HSHQ_OPTIONAL_STACKS)
stackListArr=($(echo $sortedStackList | tr "," "\n"))
menu_items=""
for curStack in "${stackListArr[@]}"
do
if ! [ -d $HSHQ_STACKS_DIR/$curStack ]; then
menu_items=${menu_items}"$curStack | OFF "
fi
done
if [ -z "$menu_items" ]; then
showMessageBox "No Available Services" "There are no available services to install."
return
fi
selsvcsmenu=$(cat << EOF
$(getLogo)
Select the services that you wish to install:
EOF
)
sel_svcs=($(whiptail --title "Select Services" --checklist "$selsvcsmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT $menu_items 3>&1 1>&2 2>&3))
if [ -z "$sel_svcs" ]; then
showMessageBox "Empty Selection" "You have not selected anything, returning to main menu..."
return 0
fi
#showYesNoMessageBox "Perform Integration?" "Do you want to automatically integrate these services, i.e. within Caddy, Heimdall, UptimeKuma, etc.?"
is_integrate=true
#if [ $? -eq 0 ]; then
# is_integrate=true
#else
# is_integrate=false
#fi
showYesNoMessageBox "Confirm Installation" "This selected service(s) will be installed, Continue?"
if [ $? -ne 0 ]; then
return
fi
setSudoTimeoutInstall
for cur_svc in "${sel_svcs[@]}"
do
installStackByName ${cur_svc//\"} $is_integrate
done
removeSudoTimeoutInstall
outputStackListsScriptServer
setSystemState $SS_RUNNING
}
function installListOfServices()
{
echo "Installing list of services, Start time: $(date '+%Y-%m-%d %H:%M:%S')"
setSystemState $SS_INSTALLING
stackListArr=($(echo "$1" | tr "," "\n"))
setSudoTimeoutInstall
for curStack in "${stackListArr[@]}"
do
installStackByName $curStack true
done
sudo systemctl daemon-reload
sudo systemctl restart ntpsec
docker container restart authelia > /dev/null 2>&1
removeSudoTimeoutInstall
outputStackListsScriptServer
setSystemState $SS_RUNNING
echo "Installing list of services, End time: $(date '+%Y-%m-%d %H:%M:%S')"
}
function installAllAvailableStacks()
{
is_msgbox_prompt="$1"
set +e
echo "Installing all services, Start time: $(date '+%Y-%m-%d %H:%M:%S')"
setSystemState $SS_INSTALLING
echo "Pulling common images..."
pullCommonImages
stackListArr=($(echo $HSHQ_OPTIONAL_STACKS | tr "," "\n"))
sel_svcs=()
is_list_empty=true
for curStack in "${stackListArr[@]}"
do
if ! [ -d $HSHQ_STACKS_DIR/$curStack ] && ! [ "$(isServiceDisabled $curStack)" = "true" ]; then
sel_svcs+=($curStack)
is_list_empty=false
fi
done
if [ "$is_list_empty" = "true" ]; then
if [ "$is_msgbox_prompt" = "true" ]; then
showMessageBox "No Available Services" "There are no available services to install."
fi
return
fi
is_integrate=true
if [ "$is_msgbox_prompt" = "true" ]; then
#showYesNoMessageBox "Perform Integration?" "Do you want to automatically integrate these services, i.e. within Caddy, Heimdall, UptimeKuma, etc.?"
#if [ $? -eq 0 ]; then
# is_integrate=true
#else
# is_integrate=false
#fi
showYesNoMessageBox "Confirm Installation" "All available service(s) will be installed, Continue?"
if [ $? -ne 0 ]; then
return
fi
fi
set -e
setSudoTimeoutInstall
for cur_svc in "${sel_svcs[@]}"
do
installStackByName $cur_svc $is_integrate
done
sudo systemctl restart ntpsec
docker container restart authelia > /dev/null 2>&1
removeSudoTimeoutInstall
outputStackListsScriptServer
setSystemState $SS_RUNNING
echo "Installing all services, End time: $(date '+%Y-%m-%d %H:%M:%S')"
}
function getStacksToUpdate()
{
set +e
stacks_to_update_list=""
# Special case for portainer compose
check_stack_firstline=$(sudo sed -n 1p $HSHQ_STACKS_DIR/portainer/docker-compose.yml)
if [ "$(checkStackHSHQManaged $check_stack_firstline)" = "true" ]; then
check_stack_name=$(getStackNameFromComposeLine "$check_stack_firstline")
if [ $? -eq 0 ]; then
check_stack_version=$(getVersionFromComposeLine "$check_stack_firstline")
if [ $? -eq 0 ]; then
hshq_stack_ver=$(getScriptStackVersionNumber $(getScriptStackVersion $check_stack_name))
if [ $? -eq 0 ]; then
if [ $check_stack_version -lt $hshq_stack_ver ]; then
stacks_to_update_list=${stacks_to_update_list}"${check_stack_name},"
fi
fi
fi
fi
fi
dirList=($(sudo ls $HSHQ_STACKS_DIR/portainer/compose/))
for curDir in "${dirList[@]}"
do
check_stack_firstline=$(sudo sed -n 1p $HSHQ_STACKS_DIR/portainer/compose/$curDir/docker-compose.yml)
if ! [ "$(checkStackHSHQManaged $check_stack_firstline)" = "true" ]; then
continue
fi
check_stack_name=$(getStackNameFromComposeLine "$check_stack_firstline")
if [ $? -ne 0 ]; then continue; fi
check_stack_version=$(getVersionFromComposeLine "$check_stack_firstline")
if [ $? -ne 0 ] || [ -z "$check_stack_version" ]; then continue; fi
hshq_stack_ver=$(getScriptStackVersionNumber $(getScriptStackVersion $check_stack_name))
if [ $? -ne 0 ] || [ -z "$hshq_stack_ver" ]; then continue; fi
if [ $check_stack_version -lt $hshq_stack_ver ]; then
stacks_to_update_list=${stacks_to_update_list}"${check_stack_name},"
fi
if [ $? -ne 0 ]; then continue; fi
done
if ! [ -z "$stacks_to_update_list" ]; then
stacks_to_update_list=${stacks_to_update_list%?}
fi
sortCSVList "$stacks_to_update_list"
}
function performStackUpdatesFromList()
{
set +e
this_stack_list=$(getStacksToUpdate)
stackListArr=($(echo $this_stack_list | tr "," "\n"))
menu_items=""
for curStack in "${stackListArr[@]}"
do
if [ -z "$curStack" ]; then
continue
fi
menu_items=${menu_items}"$curStack | OFF "
done
if [ -z "$menu_items" ]; then
showMessageBox "No Available Updates" "All services are updated."
return
fi
selsvcsmenu=$(cat << EOF
$(getLogo)
Select the services that you wish to update:
EOF
)
sel_svcs=($(whiptail --title "Select Services" --checklist "$selsvcsmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT $menu_items 3>&1 1>&2 2>&3))
if [ -z "$sel_svcs" ]; then
showMessageBox "Empty Selection" "You have not selected anything, returning to main menu..."
return 0
fi
showYesNoMessageBox "Confirm Updates" "This selected service(s) will be updated, Continue?"
if [ $? -ne 0 ]; then
return
fi
update_list=""
for cur_svc in "${sel_svcs[@]}"
do
update_list="${update_list}${cur_svc//\"},"
done
updateListOfStacks "$update_list"
}
function performAllAvailableStackUpdates()
{
is_msgbox_prompt="$1"
stacksToUpdate="$(getStacksToUpdate)"
if [ -z "$stacksToUpdate" ]; then
if [ "$is_msgbox_prompt" = "true" ]; then
showMessageBox "No Available Updates" "All services are updated."
else
echo "All services are updated."
fi
return
fi
if [ "$is_msgbox_prompt" = "true" ]; then
showYesNoMessageBox "Confirm Updates" "All available stacks will be updated, Continue?"
if [ $? -ne 0 ]; then
return
fi
fi
updateListOfStacks "$stacksToUpdate"
}
function updateListOfStacks()
{
echo "Updating list of services, Start time: $(date '+%Y-%m-%d %H:%M:%S')"
stackListArr=($(echo "$1" | tr "," "\n"))
stacks_need_update_list="$(getStacksToUpdate)"
setSudoTimeoutInstall
report_start_time=$(date)
full_update_report=""
full_update_report="${full_update_report} Services that updated successfully: \n"
full_update_report="${full_update_report}--------------------------------------------------"
upgrade_error_report="\n\n"
upgrade_error_report="${upgrade_error_report} Services that encountered errors: \n"
upgrade_error_report="${upgrade_error_report}--------------------------------------------------"
set +e
for cur_svc in "${stackListArr[@]}"
do
if [ -z "$cur_svc" ]; then continue; fi
unset is_upgrade_error
is_upgrade_error=""
echo -e "\n\nUpdating ${cur_svc}..."
if ! [ "$(isItemInCSVList $cur_svc $stacks_need_update_list)" = "true" ]; then
continue
fi
performUpdateStackByName "$cur_svc"
if [ "$is_upgrade_error" = "true" ]; then
upgrade_error_report="${upgrade_error_report}\n$perform_update_report\n"
else
full_update_report="${full_update_report}\n$perform_update_report"
fi
done
full_update_report="${full_update_report}\n${upgrade_error_report}"
removeSudoTimeoutInstall
report_end_time=$(date)
full_update_report_header=""
full_update_report_header="${full_update_report_header}==================================================\n"
full_update_report_header="${full_update_report_header} Services Upgrade Report \n"
full_update_report_header="${full_update_report_header}==================================================\n"
full_update_report_header="${full_update_report_header}Start Time: $report_start_time\n"
full_update_report_header="${full_update_report_header} End Time: $report_end_time\n\n"
sendEmail -s "Services Upgrade Report" -b "${full_update_report_header}$full_update_report" -f "$(getAdminEmailName) <$EMAIL_SMTP_EMAIL_ADDRESS>" -t $EMAIL_ADMIN_EMAIL_ADDRESS
echo "Sending update results to email manager ($EMAIL_ADMIN_EMAIL_ADDRESS). Refer to this for further details."
outputStackListsScriptServer
echo "Updating list of services, End time: $(date '+%Y-%m-%d %H:%M:%S')"
}
function deleteStacksFromList()
{
sortedStackList=$(sortCSVList $HSHQ_OPTIONAL_STACKS)
stackListArr=($(echo $sortedStackList | tr "," "\n"))
menu_items=""
for curStack in "${stackListArr[@]}"
do
if [ -d $HSHQ_STACKS_DIR/$curStack ]; then
menu_items=${menu_items}"$curStack | OFF "
fi
done
if [ -z "$menu_items" ]; then
showMessageBox "No Available Services" "There are no available services to remove."
return
fi
selsvcsmenu=$(cat << EOF
$(getLogo)
Select the services that you wish to remove:
EOF
)
sel_svcs=($(whiptail --title "Select Services" --checklist "$selsvcsmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT $menu_items 3>&1 1>&2 2>&3))
if [ -z "$sel_svcs" ]; then
showMessageBox "Empty Selection" "You have not selected anything, returning to main menu..."
return 0
fi
showYesNoMessageBox "Confirm Installation" "This selected service(s) will be removed, Continue?"
if [ $? -ne 0 ]; then
return
fi
del_stack_list=""
for svc in "${sel_svcs[@]}"
do
del_stack_list="${del_stack_list}${svc//\"},"
cur_svc=${svc//\"}
done
ENABLE_STACK_DELETE=true
deleteListOfStacks false "$del_stack_list"
ENABLE_STACK_DELETE=false
}
function deleteListOfStacks()
{
is_force_delete_stack="$1"
stackListArr=($(echo "$2" | tr "," "\n"))
setSudoTimeoutInstall
docker container stop uptimekuma >/dev/null
docker container stop heimdall >/dev/null
for cur_stack in "${stackListArr[@]}"
do
checkDeleteStackAndDirectory $cur_stack $cur_stack true "$is_force_delete_stack"
for curSVC in "${SVCS_ARR[@]}"
do
cur_stack_name=$(echo $curSVC | cut -d"=" -f2 | cut -d"," -f1)
if [ "${cur_stack_name//\"}" = "$cur_stack" ]; then
user_type=$(echo $curSVC | cut -d"=" -f2 | cut -d"," -f4)
subdom=$(echo $curSVC | cut -d"=" -f2 | cut -d"," -f6)
deleteSvcAll "${user_type//\"}" "https://${subdom//\"}.$HOMESERVER_DOMAIN" false
fi
done
done
docker container start uptimekuma >/dev/null
docker container start heimdall >/dev/null
removeSudoTimeoutInstall
restartAllCaddyContainers
outputStackListsScriptServer
}
function clearRedisTempDataByStackName()
{
stackName="$1"
if ! [ -d $HSHQ_NONBACKUP_DIR/$stackName/redis ]; then
echo "ERROR: Could not find corresponding redis directory"
return
fi
stackStatus=$(getStackStatusByName "$stackName")
if ! [ "$stackStatus" = "1" ]; then
echo "WARNING: This stack is not currently running"
sudo rm -fr $HSHQ_NONBACKUP_DIR/$stackName/redis/*
return
fi
echo "Stopping $stackName..."
startStopStack "$stackName" stop
echo "Clearing redis data..."
sudo rm -fr $HSHQ_NONBACKUP_DIR/$stackName/redis/*
echo "Starting $stackName..."
startStopStack "$stackName" start
echo "Complete!"
}
# VPN Setup
function setupVPNConnection()
{
if [[ "$(isProgramInstalled wg)" = "false" ]]; then
echo "Installing WireGuard utils, please wait..."
performAptInstall wireguard-tools > /dev/null 2>&1
fi
if [ "$PRIMARY_VPN_SETUP_TYPE" = "none" ]; then
vpnmenu=$(cat << EOF
$(getLogo)
EOF
)
menures=$(whiptail --title "Select an option for your PRIMARY VPN connection" --menu "$vpnmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT \
"1" "Host a VPN" \
"2" "Setup Later" 3>&1 1>&2 2>&3)
case $menures in
1)
PRIMARY_VPN_SETUP_TYPE=host
setupHostedVPN
if [ $? -ne 0 ]; then
return 1
fi
;;
2)
PRIMARY_VPN_SETUP_TYPE=none
updatePlaintextRootConfigVar PRIMARY_VPN_SETUP_TYPE $PRIMARY_VPN_SETUP_TYPE
# Determine if this is a private IP, and if so, let the user know about networking.
showMsg="Since you will set up a RelayServer later, you will need to manually change the DNS server on any client devices to the IP address of this HomeServer ($HOMESERVER_HOST_PRIMARY_INTERFACE_IP), in order to access your services. "
if [ "$(checkIsIPPrivate $HOMESERVER_HOST_PRIMARY_INTERFACE_IP)" = "true" ]; then
showMsg=$showMsg"The client devices must be on the same local network (connected to the same router) as well."
fi
showMessageBox "DNS Server" "$showMsg"
;;
esac
fi
}
function setupJoinPrimaryVPN()
{
vpnmenu=$(cat << EOF
$(getLogo)
EOF
)
menures=$(whiptail --title "Select an option" --menu "$vpnmenu" $MENU_HEIGHT $MENU_WIDTH $MENU_INT_HEIGHT \
"1" "Generate Join Application" \
"2" "Submit Join Configuration" \
"3" "Exit" 3>&1 1>&2 2>&3)
case $menures in
1)
applyHomeServerPrimaryVPNConfig
releaseLock hshqopen "setupJoinPrimaryVPN" false
return 1 ;;
2)
createNetworkJoin false
if [ $? -ne 0 ]; then
return 1
fi
PRIMARY_VPN_SETUP_TYPE=join
updatePlaintextRootConfigVar PRIMARY_VPN_SETUP_TYPE $PRIMARY_VPN_SETUP_TYPE ;;
3)
return 1 ;;
esac
}
function initRelayServerCredentials()
{
RELAYSERVER_WG_SV_IP=$(sipcalc $PRIMARY_VPN_SUBNET | grep "^Usable range" | rev | cut -d" " -f1 | cut -d"." -f2- | rev).$(($(sipcalc $PRIMARY_VPN_SUBNET | grep "^Usable range" | rev | cut -d" " -f1 | cut -d"." -f1 | rev)))
updateConfigVar RELAYSERVER_WG_SV_IP $RELAYSERVER_WG_SV_IP
if [ -z "$RELAYSERVER_WG_SV_PRIVATEKEY" ]; then
RELAYSERVER_WG_SV_PRIVATEKEY=$(wg genkey)
updateConfigVar RELAYSERVER_WG_SV_PRIVATEKEY $RELAYSERVER_WG_SV_PRIVATEKEY
RELAYSERVER_WG_SV_PUBLICKEY=$(echo $RELAYSERVER_WG_SV_PRIVATEKEY | wg pubkey)
updateConfigVar RELAYSERVER_WG_SV_PUBLICKEY $RELAYSERVER_WG_SV_PUBLICKEY
fi
RELAYSERVER_WG_SV_PRESHAREDKEY=$(wg genpsk)
updateConfigVar RELAYSERVER_WG_SV_PRESHAREDKEY $RELAYSERVER_WG_SV_PRESHAREDKEY
RELAYSERVER_WG_SV_CLIENTDNS_IP=$(sipcalc $PRIMARY_VPN_SUBNET | grep "^Usable range" | rev | cut -d" " -f1 | cut -d"." -f2- | rev).$(($(sipcalc $PRIMARY_VPN_SUBNET | grep "^Usable range" | rev | cut -d" " -f1 | cut -d"." -f1 | rev)-1))
updateConfigVar RELAYSERVER_WG_SV_CLIENTDNS_IP $RELAYSERVER_WG_SV_CLIENTDNS_IP
RELAYSERVER_WG_SV_CLIENTDNS_PRIVATEKEY=$(wg genkey)
updateConfigVar RELAYSERVER_WG_SV_CLIENTDNS_PRIVATEKEY $RELAYSERVER_WG_SV_CLIENTDNS_PRIVATEKEY
RELAYSERVER_WG_SV_CLIENTDNS_PRESHAREDKEY=$(wg genpsk)
updateConfigVar RELAYSERVER_WG_SV_CLIENTDNS_PRESHAREDKEY $RELAYSERVER_WG_SV_CLIENTDNS_PRESHAREDKEY
RELAYSERVER_WG_SV_CLIENTDNS_PUBLICKEY=$(echo $RELAYSERVER_WG_SV_CLIENTDNS_PRIVATEKEY | wg pubkey)
RELAYSERVER_WG_HS_IP=$(sipcalc $PRIMARY_VPN_SUBNET | grep "^Usable range" | rev | cut -d" " -f1 | cut -d"." -f2- | rev).$(($(sipcalc $PRIMARY_VPN_SUBNET | grep "^Usable range" | rev | cut -d" " -f1 | cut -d"." -f1 | rev)-2))
updateConfigVar RELAYSERVER_WG_HS_IP $RELAYSERVER_WG_HS_IP
RELAYSERVER_WG_HS_PRIVATEKEY=$(wg genkey)
updateConfigVar RELAYSERVER_WG_HS_PRIVATEKEY $RELAYSERVER_WG_HS_PRIVATEKEY
RELAYSERVER_WG_HS_PRESHAREDKEY=$(wg genpsk)
updateConfigVar RELAYSERVER_WG_HS_PRESHAREDKEY $RELAYSERVER_WG_HS_PRESHAREDKEY
RELAYSERVER_WG_HS_PUBLICKEY=$(echo $RELAYSERVER_WG_HS_PRIVATEKEY | wg pubkey)
RELAYSERVER_WG_HS_CLIENTDNS_IP=$(sipcalc $PRIMARY_VPN_SUBNET | grep "^Usable range" | rev | cut -d" " -f1 | cut -d"." -f2- | rev).$(($(sipcalc $PRIMARY_VPN_SUBNET | grep "^Usable range" | rev | cut -d" " -f1 | cut -d"." -f1 | rev)-3))
updateConfigVar RELAYSERVER_WG_HS_CLIENTDNS_IP $RELAYSERVER_WG_HS_CLIENTDNS_IP
RELAYSERVER_WG_HS_CLIENTDNS_PRIVATEKEY=$(wg genkey)
updateConfigVar RELAYSERVER_WG_HS_CLIENTDNS_PRIVATEKEY $RELAYSERVER_WG_HS_CLIENTDNS_PRIVATEKEY
RELAYSERVER_WG_HS_CLIENTDNS_PRESHAREDKEY=$(wg genpsk)
updateConfigVar RELAYSERVER_WG_HS_CLIENTDNS_PRESHAREDKEY $RELAYSERVER_WG_HS_CLIENTDNS_PRESHAREDKEY
RELAYSERVER_WG_HS_CLIENTDNS_PUBLICKEY=$(echo $RELAYSERVER_WG_HS_CLIENTDNS_PRIVATEKEY | wg pubkey)
RELAYSERVER_WG_INTERNET_HS_IP=$(getRandomWireGuardIP)
RELAYSERVER_WG_INTERNET_HS_PRIVATEKEY=$(wg genkey)
updateConfigVar RELAYSERVER_WG_INTERNET_HS_PRIVATEKEY $RELAYSERVER_WG_INTERNET_HS_PRIVATEKEY
RELAYSERVER_WG_INTERNET_HS_PRESHAREDKEY=$(wg genpsk)
updateConfigVar RELAYSERVER_WG_INTERNET_HS_PRESHAREDKEY $RELAYSERVER_WG_INTERNET_HS_PRESHAREDKEY
RELAYSERVER_WG_INTERNET_HS_PUBLICKEY=$(echo $RELAYSERVER_WG_INTERNET_HS_PRIVATEKEY | wg pubkey)
RELAYSERVER_WG_USER_IP=$(getRandomWireGuardIP)
RELAYSERVER_WG_USER_PRIVATEKEY=$(wg genkey)
RELAYSERVER_WG_USER_PRESHAREDKEY=$(wg genpsk)
RELAYSERVER_WG_USER_PUBLICKEY=$(echo $RELAYSERVER_WG_USER_PRIVATEKEY | wg pubkey)
SMTP_RELAY_HOST="[$SUB_POSTFIX.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN]:$MAILU_PORT_5"
updateConfigVar SMTP_RELAY_HOST $SMTP_RELAY_HOST
SMTP_RELAY_USERNAME=$HOMESERVER_DOMAIN
updateConfigVar SMTP_RELAY_USERNAME $SMTP_RELAY_USERNAME
SMTP_RELAY_PASSWORD=$(pwgen -c -n 32 1)
updateConfigVar SMTP_RELAY_PASSWORD $SMTP_RELAY_PASSWORD
}
function outputHostedVPNConfigs()
{
sudo rm -f $HSHQ_WIREGUARD_DIR/vpn/${RELAYSERVER_WG_VPN_NETNAME}.conf
sudo tee $HSHQ_WIREGUARD_DIR/vpn/${RELAYSERVER_WG_VPN_NETNAME}.conf >/dev/null <<EOFCF
[Interface]
PrivateKey = $RELAYSERVER_WG_HS_PRIVATEKEY
Address = ${RELAYSERVER_WG_HS_IP}/32
MTU = $RELAYSERVER_CLIENT_DEFAULT_MTU
Table = off
PostUp = ip route add 10.0.0.0/8 dev %i table 1100; ip route add $PRIMARY_VPN_SUBNET dev %i; ip rule add fwmark 1100 table 1100 pref 20000; iptables -t mangle -A PREROUTING -i %i -j CONNMARK --set-mark 1100; iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark; iptables -t mangle -A OUTPUT -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff
PreDown = iptables -t mangle -D OUTPUT -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff; iptables -t mangle -D PREROUTING -j CONNMARK --restore-mark; iptables -t mangle -D PREROUTING -i %i -j CONNMARK --set-mark 1100; ip rule del fwmark 1100 table 1100 pref 20000; ip route del $PRIMARY_VPN_SUBNET dev %i; ip route del 10.0.0.0/8 dev %i table 1100
[Peer]
PublicKey = $RELAYSERVER_WG_SV_PUBLICKEY
PresharedKey = $RELAYSERVER_WG_HS_PRESHAREDKEY
AllowedIPs = 10.0.0.0/8
Endpoint = $RELAYSERVER_SUB_WG.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN:$RELAYSERVER_WG_PORT
PersistentKeepalive = $RELAYSERVER_PERSISTENT_KEEPALIVE
EOFCF
sudo chmod 0400 $HSHQ_WIREGUARD_DIR/vpn/${RELAYSERVER_WG_VPN_NETNAME}.conf
sudo cp $HSHQ_WIREGUARD_DIR/vpn/${RELAYSERVER_WG_VPN_NETNAME}.conf /etc/wireguard/${RELAYSERVER_WG_VPN_NETNAME}.conf
sudo rm -f $HSHQ_WIREGUARD_DIR/internet/${RELAYSERVER_WG_INTERNET_NETNAME}.conf
tableid=$(getNextWGRoutingTableID)
dockerNetworkName="dwg-${RELAYSERVER_WG_INTERNET_NETNAME}"
sudo tee $HSHQ_WIREGUARD_DIR/internet/${RELAYSERVER_WG_INTERNET_NETNAME}.conf >/dev/null <<EOFCF
#NETNAME=$RELAYSERVER_WG_INTERNET_NETNAME
#ROUTING_TABLE_ID=$tableid
#DOCKER_NETWORK_NAME=$dockerNetworkName
#DOCKER_NETWORK_SUBNET=
#CLIENT_ADDRESS=${RELAYSERVER_WG_INTERNET_HS_IP}/32
#MTU=$RELAYSERVER_CLIENT_DEFAULT_MTU
#IS_ENABLED=true
#EXT_DOMAIN=$RELAYSERVER_SUB_WG.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN
[Interface]
PrivateKey = $RELAYSERVER_WG_INTERNET_HS_PRIVATEKEY
[Peer]
PublicKey = $RELAYSERVER_WG_SV_PUBLICKEY
PresharedKey = $RELAYSERVER_WG_INTERNET_HS_PRESHAREDKEY
AllowedIPs = $(getAllowedPublicIPs)
Endpoint = $RELAYSERVER_SUB_WG.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN:$RELAYSERVER_WG_PORT
PersistentKeepalive = $RELAYSERVER_PERSISTENT_KEEPALIVE
EOFCF
sudo chmod 0400 $HSHQ_WIREGUARD_DIR/internet/${RELAYSERVER_WG_INTERNET_NETNAME}.conf
sudo rm -f $HSHQ_WIREGUARD_DIR/users/${RELAYSERVER_WG_VPN_NETNAME}-user1.conf
sudo tee $HSHQ_WIREGUARD_DIR/users/${RELAYSERVER_WG_VPN_NETNAME}-user1.conf >/dev/null <<EOFCF
[Interface]
PrivateKey = $RELAYSERVER_WG_USER_PRIVATEKEY
Address = ${RELAYSERVER_WG_USER_IP}/32
MTU = $RELAYSERVER_CLIENT_DEFAULT_MTU
# DNS Servers (Use only one at a time)
# Home Network DNS Server
DNS = $RELAYSERVER_WG_HS_CLIENTDNS_IP
# VPN DNS Server
#DNS = $RELAYSERVER_WG_SV_CLIENTDNS_IP
[Peer]
PublicKey = $RELAYSERVER_WG_SV_PUBLICKEY
PresharedKey = $RELAYSERVER_WG_USER_PRESHAREDKEY
AllowedIPs = $(getAllowedPublicIPs $PRIMARY_VPN_SUBNET)
Endpoint = $RELAYSERVER_SUB_WG.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN:$RELAYSERVER_WG_PORT
PersistentKeepalive = $RELAYSERVER_PERSISTENT_KEEPALIVE
EOFCF
sudo chmod 0400 $HSHQ_WIREGUARD_DIR/users/${RELAYSERVER_WG_VPN_NETNAME}-user1.conf
sudo rm -f $HSHQ_WIREGUARD_DIR/users/clientdns-user1.conf
sudo tee $HSHQ_WIREGUARD_DIR/users/clientdns-user1.conf >/dev/null <<EOFCF
[Interface]
PrivateKey = $RELAYSERVER_WG_HS_CLIENTDNS_PRIVATEKEY
Address = ${RELAYSERVER_WG_HS_CLIENTDNS_IP}/32
MTU = $RELAYSERVER_CLIENT_DEFAULT_MTU
[Peer]
PublicKey = $RELAYSERVER_WG_SV_PUBLICKEY
PresharedKey = $RELAYSERVER_WG_HS_CLIENTDNS_PRESHAREDKEY
AllowedIPs = 10.0.0.0/8
Endpoint = $RELAYSERVER_SUB_WG.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN:$RELAYSERVER_WG_PORT
PersistentKeepalive = $RELAYSERVER_PERSISTENT_KEEPALIVE
EOFCF
sudo chmod 0400 $HSHQ_WIREGUARD_DIR/users/clientdns-user1.conf
}
function insertSQLHostedVPN()
{
curdt=$(getCurrentDate)
sudo sqlite3 $HSHQ_DB "insert into connections(Name,EmailAddress,ConnectionType,NetworkType,PublicKey,PresharedKey,IPAddress,IsInternet,InterfaceName,EndpointHostname,LastUpdated) values('WireGuardServer','$EMAIL_ADMIN_EMAIL_ADDRESS','wgserver','relayserver','$RELAYSERVER_WG_SV_PUBLICKEY','$RELAYSERVER_WG_SV_PRESHAREDKEY','$RELAYSERVER_WG_SV_IP',false,'$RELAYSERVER_WG_INTERFACE_NAME','$RELAYSERVER_SUB_WG.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN','$curdt');"
sudo sqlite3 $HSHQ_DB "insert into connections(Name,EmailAddress,ConnectionType,NetworkType,PublicKey,PresharedKey,IPAddress,IsInternet,InterfaceName,EndpointHostname,LastUpdated) values('RelayServerClientDNS','$EMAIL_ADMIN_EMAIL_ADDRESS','clientdns','relayserver','$RELAYSERVER_WG_SV_CLIENTDNS_PUBLICKEY','$RELAYSERVER_WG_SV_CLIENTDNS_PRESHAREDKEY','$RELAYSERVER_WG_SV_CLIENTDNS_IP',false,'wg0','$RELAYSERVER_SUB_WG.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN','$curdt');"
db_id=$(sudo sqlite3 $HSHQ_DB "insert into connections(Name,EmailAddress,ConnectionType,NetworkType,PublicKey,PresharedKey,IPAddress,IsInternet,InterfaceName,EndpointHostname,LastUpdated,Network_Subnet,IsExposeToNetwork,InputAllowPorts,DockerUserAllowPorts) values('Primary-VPN-${HOMESERVER_DOMAIN}','$EMAIL_ADMIN_EMAIL_ADDRESS','homeserver_vpn','primary','$RELAYSERVER_WG_HS_PUBLICKEY','$RELAYSERVER_WG_HS_PRESHAREDKEY','$RELAYSERVER_WG_HS_IP',false,'$RELAYSERVER_WG_VPN_NETNAME','$RELAYSERVER_SUB_WG.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN','$curdt','$PRIMARY_VPN_SUBNET',true,'$INPUT_PRIMARY_VPN_ALLOW_PORTS_DEFAULT','$DOCKERUSER_PRIMARY_VPN_ALLOW_PORTS_DEFAULT');select last_insert_rowid();")
mail_host_id=$(sudo sqlite3 $HSHQ_DB "insert into mailhosts(MailHost) values('$SUB_POSTFIX.$HOMESERVER_DOMAIN');select last_insert_rowid();")
sudo sqlite3 $HSHQ_DB "PRAGMA foreign_keys=ON;insert into hsvpn_connections(ID,HomeServerName,IsPrimary,DomainName,ExternalPrefix,InternalPrefix,MailHostID,CA_Abbrev,CA_IP,CA_Subdomain,CA_URL,RS_VPN_IP,VPNRoutingTable) values($db_id,'$HOMESERVER_NAME',1,'$HOMESERVER_DOMAIN','$EXT_DOMAIN_PREFIX','$INT_DOMAIN_PREFIX',$mail_host_id,'$HOMESERVER_ABBREV','$RELAYSERVER_WG_HS_IP','$SUB_CADDY.$HOMESERVER_DOMAIN','https://$SUB_CADDY.$HOMESERVER_DOMAIN/acme/$HOMESERVER_ABBREV/directory' ,'$RELAYSERVER_WG_SV_IP',1100);"
sudo sqlite3 $HSHQ_DB "PRAGMA foreign_keys=ON;insert into mailhostmap(MailHostID,Domain,IsFirstDomain) values ($mail_host_id,'$HOMESERVER_DOMAIN',true);"
sudo sqlite3 $HSHQ_DB "insert into connections(Name,EmailAddress,ConnectionType,NetworkType,PublicKey,PresharedKey,IPAddress,IsInternet,InterfaceName,EndpointHostname,LastUpdated) values('clientdns-user1','$EMAIL_ADMIN_EMAIL_ADDRESS','clientdns','primary','$RELAYSERVER_WG_HS_CLIENTDNS_PUBLICKEY','$RELAYSERVER_WG_HS_CLIENTDNS_PRESHAREDKEY','$RELAYSERVER_WG_HS_CLIENTDNS_IP',false,'wg0','$RELAYSERVER_SUB_WG.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN','$curdt');"
sudo sqlite3 $HSHQ_DB "insert into connections(Name,EmailAddress,ConnectionType,NetworkType,PublicKey,PresharedKey,IPAddress,IsInternet,InterfaceName,EndpointHostname,LastUpdated) values('Primary-Internet-${HOMESERVER_DOMAIN}','$EMAIL_ADMIN_EMAIL_ADDRESS','homeserver_internet','primary','$RELAYSERVER_WG_INTERNET_HS_PUBLICKEY','$RELAYSERVER_WG_INTERNET_HS_PRESHAREDKEY','$RELAYSERVER_WG_INTERNET_HS_IP',true,'$RELAYSERVER_WG_INTERNET_NETNAME','$RELAYSERVER_SUB_WG.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN','$curdt');"
sudo sqlite3 $HSHQ_DB "insert into connections(Name,EmailAddress,ConnectionType,NetworkType,PublicKey,PresharedKey,IPAddress,IsInternet,EndpointHostname,LastUpdated) values('User-1','$EMAIL_ADMIN_EMAIL_ADDRESS','user','mynetwork','$RELAYSERVER_WG_USER_PUBLICKEY','$RELAYSERVER_WG_USER_PRESHAREDKEY','$RELAYSERVER_WG_USER_IP',true,'$RELAYSERVER_SUB_WG.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN','$curdt');"
updateDeviceIPMappingFile
}
function updateHeimdallUptimeKumaRelayServer()
{
docker container stop heimdall > /dev/null 2>&1
docker container stop uptimekuma > /dev/null 2>&1
insertEnableSvcHeimdall adguard "${FMLNAME_ADGUARD}" relayserver "https://$SUB_ADGUARD.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" "adguardhome.png" false "$(getHeimdallOrderFromSub $SUB_ADGUARD.$INT_DOMAIN_PREFIX relayserver)"
insertEnableSvcUptimeKuma adguard "${FMLNAME_ADGUARD}-RelayServer" relayserver "https://$SUB_ADGUARD.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" false
insertEnableSvcHeimdall clientdns "${FMLNAME_CLIENTDNS}" relayserver "https://$SUB_CLIENTDNS.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" "dnsmasq.png" false "$(getHeimdallOrderFromSub $SUB_CLIENTDNS.$INT_DOMAIN_PREFIX relayserver)"
checkInsertServiceHeimdall caddy-dns "${FMLNAME_CADDYDNS}" relayserver "https://$SUB_CADDYDNS.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" "dnsmasq.png" false 0 "$(getHeimdallOrderFromSub $SUB_CADDYDNS.$INT_DOMAIN_PREFIX relayserver)"
insertEnableSvcHeimdall portainer "${FMLNAME_PORTAINER}" relayserver "https://$SUB_PORTAINER.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" "portainer.png" false "$(getHeimdallOrderFromSub $SUB_PORTAINER.$INT_DOMAIN_PREFIX relayserver)"
insertEnableSvcUptimeKuma portainer "${FMLNAME_PORTAINER}-RelayServer" relayserver "https://$SUB_PORTAINER.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" false
insertEnableSvcHeimdall rspamd "${FMLNAME_RSPAMD}" relayserver "https://$SUB_RSPAMD.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" "rspamd.png" false "$(getHeimdallOrderFromSub $SUB_RSPAMD.$INT_DOMAIN_PREFIX relayserver)"
insertEnableSvcUptimeKuma rspamd "${FMLNAME_RSPAMD}-RelayServer" relayserver "https://$SUB_RSPAMD.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" false
insertEnableSvcHeimdall syncthing "${FMLNAME_SYNCTHING}" relayserver "https://$SUB_SYNCTHING.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" "syncthing.png" false "$(getHeimdallOrderFromSub $SUB_SYNCTHING.$INT_DOMAIN_PREFIX relayserver)"
insertEnableSvcUptimeKuma syncthing "${FMLNAME_SYNCTHING}-RelayServer" relayserver "https://$SUB_SYNCTHING.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" false
checkInsertServiceHeimdall wgportal "${FMLNAME_WGPORTAL}" relayserver "https://$SUB_WGPORTAL.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" "wgportal.png" false 0 "$(getHeimdallOrderFromSub $SUB_WGPORTAL.$INT_DOMAIN_PREFIX relayserver)"
insertEnableSvcUptimeKuma wgportal "${FMLNAME_WGPORTAL}-RelayServer" relayserver "https://$SUB_WGPORTAL.$INT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" false
checkInsertServiceUptimeKuma filebrowser "${FMLNAME_FILEBROWSER}-RelayServer" relayserver "https://$SUB_FILEBROWSER.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" false 0
checkInsertServiceHeimdall filebrowser "${FMLNAME_FILEBROWSER}" relayserver "https://$SUB_FILEBROWSER.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" "filebrowser.png" false 0 "$(getHeimdallOrderFromSub $SUB_FILEBROWSER.$EXT_DOMAIN_PREFIX relayserver)"
docker container start heimdall > /dev/null 2>&1
docker container start uptimekuma > /dev/null 2>&1
}
function prepSvcsHostedVPN()
{
set +e
echo "Updating Portainer and Jitsi..."
updateSvcsIPChanges
set +e
echo "Updating IP tables..."
checkUpdateAllIPTables prepSvcsHostedVPN
}
function webTransferHostedVPN()
{
# These variables should already be set by Script-server
# rs_cur_username
# rs_external_ip
# rs_cur_password
# rs_new_password
# rs_cur_ssh_port
# rs_new_ssh_port
set +e
# Clear out any old hosts
ssh-keygen -f "$HOME/.ssh/known_hosts" -R "[$RELAYSERVER_SUB_RELAYSERVER.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN]:$RELAYSERVER_SSH_PORT" > /dev/null 2>&1
ssh-keygen -f "$HOME/.ssh/known_hosts" -R "[$RELAYSERVER_SERVER_IP]:$RELAYSERVER_SSH_PORT" > /dev/null 2>&1
ssh-keygen -f "$HOME/.ssh/known_hosts" -R "[$rs_external_ip]:$rs_cur_ssh_port" > /dev/null 2>&1
ssh-keygen -f "$HOME/.ssh/known_hosts" -R "[$rs_external_ip]:$rs_new_ssh_port" > /dev/null 2>&1
if ! [ "$rs_cur_username" = "root" ] && ! [ "$rs_cur_username" = "$RELAYSERVER_REMOTE_USERNAME" ]; then
echo "ERROR: The username on the new RelayServer must match the existing username ($RELAYSERVER_REMOTE_USERNAME)"
return 2
fi
rs_new_username="$RELAYSERVER_REMOTE_USERNAME"
if ! [ "$rs_cur_username" = "root" ]; then
rs_new_password="$rs_cur_password"
fi
# Login, upload check script, log back in and run check script
# 1. Login
echo "Logging into RelayServer..."
perfRemoteAction -m ssh -p $rs_cur_ssh_port -s "$rs_cur_password" -o "-T -o ConnectTimeout=5 -o 'StrictHostKeyChecking accept-new'" -u "$rs_cur_username" -h "$rs_external_ip" -c "echo hello >/dev/null" -f
is_err=$?
if [ $is_err -ne 0 ]; then
echo "ERROR: There was a problem logging in to the RelayServer, returning..."
return 2
fi
# 2. Upload check script
echo "Uploading validation script..."
outputRelayServerValidationScript
outputRelayServerInstallSetupScript
perfRemoteAction -m scp -p $rs_cur_ssh_port -s "$rs_cur_password" -a $HOME/$RS_INSTALL_VALIDATION_SCRIPT_NAME -u $rs_cur_username -h $rs_external_ip -c ":~" -f
is_err=$?
rm -f $HOME/$RS_INSTALL_VALIDATION_SCRIPT_NAME
if [ $is_err -ne 0 ]; then
echo "ERROR: There was an problem uploading the validation script to the RelayServer, returning..."
return 2
fi
perfRemoteAction -m scp -p $rs_cur_ssh_port -s "$rs_cur_password" -a $HSHQ_RELAYSERVER_DIR/scripts/$RS_INSTALL_SETUP_SCRIPT_NAME -u $rs_cur_username -h $rs_external_ip -c ":~/$RS_INSTALL_VALIDATION_LIB_SCRIPT_NAME" -f
is_err=$?
rm -f $HSHQ_RELAYSERVER_DIR/scripts/$RS_INSTALL_SETUP_SCRIPT_NAME
if [ $is_err -ne 0 ]; then
echo "ERROR: There was an problem uploading the lib script to the RelayServer, returning..."
return 2
fi
# 3. Log back in and run check script
echo "Performing pre-installation checks, please wait..."
perfRemoteAction -m ssh -p $rs_cur_ssh_port -s "$rs_cur_password" -o "-T -o ConnectTimeout=10 -o 'StrictHostKeyChecking accept-new'" -u $rs_cur_username -h $rs_external_ip -c "bash ~/$RS_INSTALL_VALIDATION_SCRIPT_NAME -s" -f -i "$rs_new_password"
is_err=$?
if [ $is_err -ne 0 ]; then
echo "ERROR: There was a problem with the RelayServer, see above."
return 2
fi
echo "Testing RelayServer login with pub/priv key..."
perfRemoteAction -m ssh -p $rs_cur_ssh_port -o "-T -o ConnectTimeout=10 -o 'StrictHostKeyChecking accept-new'" -u $RELAYSERVER_REMOTE_USERNAME -h $rs_external_ip -c "echo \"Successfully logged in to RelayServer with key! \"" -f
is_err=$?
if [ $is_err -ne 0 ]; then
echo "ERROR: Could not login to RelayServer with pub/priv key."
return 2
fi
setSudoTimeoutInstall
setSystemState $SS_TRANSFERRING
# Pause syncthing RelayServer
curl -s -H "X-API-Key: $SYNCTHING_API_KEY" -X PATCH -d "{\"paused\": true}" -k https://127.0.0.1:$SYNCTHING_LOCAL_WEB_PORT/rest/config/devices/$RELAYSERVER_SYNCTHING_DEVICE_ID
echo "Compressing RelayServer data..."
sudo tar cvzf $HOME/rsbackup.tar.gz -C $HSHQ_RELAYSERVER_DIR/ ./backup >/dev/null
echo "Checking if RelayServer is ready..."
countRSPreRetries=0
maxRSPreRetries=20
while true;
do
perfRemoteAction -m ssh -p $rs_cur_ssh_port -o "-T -o ConnectTimeout=10 -o 'StrictHostKeyChecking accept-new'" -u $rs_new_username -h $rs_external_ip -c "sleep 1;if [ -f ~/$RELAYSERVER_NOT_READY_FILE ]; then exit 1; fi" -r 1 -b 1
if [ $? -eq 0 ]; then
break
fi
if [ $countRSPreRetries -ge $maxRSPreRetries ]; then
echo "Max retries reached. Something has likely gone wrong with the RelayServer pre-installation script, exiting..."
return 2
fi
((countRSPreRetries++))
echo "($countRSPreRetries of $maxRSPreRetries) RelayServer not ready for installation, sleeping 30 seconds then will retry..."
sleep 30
done
removeHomeNetIP ${RELAYSERVER_SERVER_IP}/32 false
RELAYSERVER_SERVER_IP="$rs_external_ip"
RELAYSERVER_CURRENT_SSH_PORT="$rs_cur_ssh_port"
RELAYSERVER_SSH_PORT="$rs_new_ssh_port"
outputRelayServerInstallSetupScript
outputRelayServerInstallTransferScript
perfRemoteAction -m scp -p $RELAYSERVER_CURRENT_SSH_PORT -s "$rs_new_password" -a $HSHQ_RELAYSERVER_DIR/scripts/$RS_INSTALL_SETUP_SCRIPT_NAME -u $rs_new_username -h $rs_external_ip -c ":~/$RS_INSTALL_SETUP_SCRIPT_NAME" -f
is_err=$?
rm -f $HSHQ_RELAYSERVER_DIR/scripts/$RS_INSTALL_SETUP_SCRIPT_NAME
if [ $is_err -ne 0 ]; then
echo "ERROR: There was an problem uploading the setup script to the RelayServer, returning..."
return 2
fi
perfRemoteAction -m scp -p $RELAYSERVER_CURRENT_SSH_PORT -s "$rs_new_password" -a $HSHQ_RELAYSERVER_DIR/scripts/$RS_INSTALL_TRANSFER_SCRIPT_NAME -u $rs_new_username -h $rs_external_ip -c ":~/$RS_INSTALL_TRANSFER_SCRIPT_NAME" -f
is_err=$?
if [ $is_err -ne 0 ]; then
echo "ERROR: There was an problem uploading the transfer script to the RelayServer, returning..."
return 2
fi
perfRemoteAction -m scp -p $RELAYSERVER_CURRENT_SSH_PORT -a $HOME/rsbackup.tar.gz -u $rs_new_username -h $rs_external_ip -c ":/home/$RELAYSERVER_REMOTE_USERNAME" -f
sudo rm -f $HOME/rsbackup.tar.gz
echo "Initializing transfer process..."
perfRemoteAction -m ssh -p $RELAYSERVER_CURRENT_SSH_PORT -s "$rs_new_password" -o "-tt -o ConnectTimeout=10 -o 'StrictHostKeyChecking accept-new'" -u $rs_new_username -h $rs_external_ip -c "bash ~/$RS_INSTALL_TRANSFER_SCRIPT_NAME -i" -f -i "$rs_new_password" -r 1 -b 1
is_err=$?
if [ $is_err -ne 0 ]; then
echo "There was a problem initializing the transfer, see above."
return $is_err
fi
echo "Transfer Initialization complete! Rebooting.."
perfRemoteAction -m ssh -p $RELAYSERVER_SSH_PORT -s "$rs_new_password" -o "-tt -o ConnectTimeout=10 -o 'StrictHostKeyChecking accept-new'" -u $rs_new_username -h $rs_external_ip -c "sudo reboot" -f -i "$rs_new_password"
echo
echo
echo "============================================================"
echo " RelayServer is rebooting... "
echo " You can now modify your DNS Records to "
echo " point to the new RelayServer's IP Address "
echo "============================================================"
echo
echo
sleep 30
countRSPreRetries=0
maxRSPreRetries=20
while true;
do
perfRemoteAction -m ssh -p $RELAYSERVER_SSH_PORT -o "-T -o ConnectTimeout=10 -o 'StrictHostKeyChecking accept-new'" -u $rs_new_username -h $rs_external_ip -c "echo hello >/dev/null" -r 1 -b 1
if [ $? -eq 0 ]; then
break
fi
if [ $countRSPreRetries -ge $maxRSPreRetries ]; then
echo "Max retries reached. Something has likely gone wrong with the RelayServer transfer init script."
read -r -p "Enter cancel to exit or any other key to retry: " raRetry
if [ "$raRetry" = "cancel" ]; then
return 2
fi
fi
((countRSPreRetries++))
echo "($countRSPreRetries of $maxRSPreRetries) RelayServer not ready for transfer, sleeping 15 seconds then will retry..."
sleep 15
done
echo "RelayServer has rebooted, finishing transfer..."
perfRemoteAction -m ssh -p $RELAYSERVER_SSH_PORT -s "$rs_new_password" -o "-tt -o ConnectTimeout=10 -o 'StrictHostKeyChecking accept-new'" -u $rs_new_username -h $rs_external_ip -c "bash ~/$RS_INSTALL_TRANSFER_SCRIPT_NAME -s" -f -i "$rs_new_password" -r 1 -b 1
is_err=$?
if [ $is_err -ne 0 ]; then
echo "There was a problem performing the transfer, see above."
return $is_err
fi
echo "The RelayServer is fully prepped for transfer. If you haven't already updated your DNS records, ensure to do that now. Both A records should point to $rs_external_ip. Please also review the above logs to ensure there were no errors. If everything appears normal, then enter continue. If there are any errors, then enter cancel to halt the process."
while true;
do
read -r -p "Enter continue or cancel: " raRetry
if [ "$raRetry" = "cancel" ]; then
return 2
elif [ "$raRetry" = "continue" ]; then
break
else
echo "Invalid input."
fi
done
updateConfigVar RELAYSERVER_SERVER_IP $RELAYSERVER_SERVER_IP
updateConfigVar RELAYSERVER_CURRENT_SSH_PORT $RELAYSERVER_CURRENT_SSH_PORT
updateConfigVar RELAYSERVER_SSH_PORT $RELAYSERVER_SSH_PORT
addHomeNetIP ${RELAYSERVER_SERVER_IP}/32 true
addDomainAdguardHS "*.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" "$RELAYSERVER_SERVER_IP"
# Restart all vpn connections
echo "Restarting all wg connections..."
db_id=$(sqlite3 $HSHQ_DB "select ID from connections where ConnectionType='homeserver_vpn' and NetworkType='primary';")
ifaceName=$(sqlite3 $HSHQ_DB "select InterfaceName from connections where ID=$db_id;")
sudo systemctl restart wg-quick@$ifaceName
db_id=$(sqlite3 $HSHQ_DB "select ID from connections where ConnectionType='homeserver_internet' and NetworkType='primary';")
ifaceName=$(sqlite3 $HSHQ_DB "select InterfaceName from connections where ID=$db_id;")
sudo $HSHQ_WIREGUARD_DIR/scripts/wgDockInternet.sh $HSHQ_WIREGUARD_DIR/internet/${ifaceName}.conf restart
clientdns_arr=($(docker ps -a --filter name=clientdns.*wireguard --format "{{.Names}}"))
for curCDNS in "${clientdns_arr[@]}"
do
curStackName=$(echo "$curCDNS" | rev | cut -d"-" -f2- | rev)
if ! [ -d "$HSHQ_STACKS_DIR/$curStackName" ] || ! [ -f "$HSHQ_STACKS_DIR/$curStackName/${curStackName}.conf" ]; then
continue
fi
startStopStack "$curStackName" stop
startStopStack "$curStackName" start
done
updateEndpointIPs
jsonbody="{\"paused\": false}"
curl -s -H "X-API-Key: $SYNCTHING_API_KEY" -X PATCH -d "$jsonbody" -k https://127.0.0.1:$SYNCTHING_LOCAL_WEB_PORT/rest/config/devices/$RELAYSERVER_SYNCTHING_DEVICE_ID
docker container restart syncthing
perfRemoteAction -m ssh -p $RELAYSERVER_SSH_PORT -o "-T -o 'StrictHostKeyChecking accept-new'" -u $RELAYSERVER_REMOTE_USERNAME -h $RELAYSERVER_SUB_RELAYSERVER.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN -c "echo \"Successful! IP Address is: \$(curl --silent https://api.ipify.org)\"" -f
notifyMyNetworkTransferRelayServer
removeSudoTimeoutInstall
setSystemState $SS_RUNNING
}
function webSetupHostedVPN()
{
set +e
# These variables should already be set by Script-server
# rs_name
# rs_cur_username
# rs_cur_password
# rs_cur_ssh_port
# rs_external_ip
# rs_ledomains
# rs_new_username
# rs_new_password
# rs_new_ssh_port
# rs_primary_vpn_subnet
case "$PRIMARY_VPN_SETUP_TYPE" in
"host")
echo "ERROR: You are already hosting a VPN, please remove the existing one first, returning..."
return
;;
"join")
echo "ERROR: You have already joined an existing primary VPN. You must remove this first, returning..."
return
;;
"none"|"manual")
PRIMARY_VPN_SETUP_TYPE=none
updatePlaintextRootConfigVar PRIMARY_VPN_SETUP_TYPE "$PRIMARY_VPN_SETUP_TYPE"
;;
*)
echo "ERROR: Unknown VPN option, returning..."
return
;;
esac
pullImage $IMG_WIREGUARD
if [ $? -ne 0 ]; then
echo "ERROR: There was a problem pulling the WireGuard docker image..."
return
fi
pullImage $IMG_DNSMASQ
if [ $? -ne 0 ]; then
echo "ERROR: There was a problem pulling the DNSMasq docker image..."
return
fi
RELAYSERVER_WG_VPN_NETNAME="vpn-"${HOMESERVER_ABBREV:0:6}
curNum=1
while [ "$(checkInterfaceNameExists $RELAYSERVER_WG_VPN_NETNAME)" = "true" ] && [ $curNum -lt 100 ]
do
RELAYSERVER_WG_VPN_NETNAME="vpn-${HOMESERVER_ABBREV:0:5}${curNum}"
((curNum++))
done
if [ "$(checkInterfaceNameExists $RELAYSERVER_WG_VPN_NETNAME)" = "true" ]; then
echo "ERROR: You have 99 other connections that start with ${HOMESERVER_ABBREV:0:5}?!? Returning..."
return
fi
RELAYSERVER_WG_INTERNET_NETNAME="ext-${HOMESERVER_ABBREV:0:6}"
curNum=1
while [ "$(checkInterfaceNameExists $RELAYSERVER_WG_INTERNET_NETNAME)" = "true" ] && [ $curNum -lt 100 ]
do
RELAYSERVER_WG_INTERNET_NETNAME="ext-${HOMESERVER_ABBREV:0:5}${curNum}"
((curNum++))
done
if [ "$(checkInterfaceNameExists $RELAYSERVER_WG_INTERNET_NETNAME)" = "true" ]; then
echo "ERROR: You have 99 other connections that start with ${HOMESERVER_ABBREV:0:5}?!? Returning..."
return
fi
RELAYSERVER_EXT_EMAIL_HOSTNAME=$SUB_POSTFIX.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN
if [ $(checkValidStringUpperLowerNumbers "$rs_name" "[:space:],.-") = "false" ]; then
echo "ERROR: The selected RelayServer name contains invalid characters, returning..."
return
fi
RELAYSERVER_NAME="$rs_name"
if ! [ -z "$rs_ledomains" ] && [ $(checkValidString "$rs_ledomains" ",.-") = "false" ]; then
echo "ERROR: The selected LetsEncrypt subdomains list contains invalid characters, returning..."
return
fi
if [ "$rs_cur_username" = "root" ]; then
if [ "$(checkValidUsername $rs_new_username)" = "false" ]; then
echo "ERROR: The selected username is invalid. It must consists of a-z and/or 0-9. No special characters, the length must be between 4-32 characters, and it must begin with a letter, returning..."
return
fi
if [ "$(checkValidPassword "$rs_new_password" 16)" = "false" ]; then
echo "ERROR: The password is invalid or is too weak($rs_new_password). It must contain at least 16 characters and consist of uppercase letters, lowercase letters, and numbers. It can only contain the following symbols: ~!@#%^&*()-_+=<>?.,"
return
fi
USER_RELAY_SUDO_PW="$rs_new_password"
else
rs_new_username="$rs_cur_username"
USER_RELAY_SUDO_PW="$rs_cur_password"
fi
RELAYSERVER_LE_CERT_DOMAINS="$rs_ledomains"
RELAYSERVER_REMOTE_USERNAME="$rs_new_username"
RELAYSERVER_SERVER_IP="$rs_external_ip"
RELAYSERVER_CURRENT_SSH_PORT="$rs_cur_ssh_port"
if [ "$(checkValidIPAddress $rs_primary_vpn_subnet)" = "false" ]; then
echo "ERROR: Invalid RelayServer IP address, returning..."
return
fi
if ! [ "$(echo "$rs_primary_vpn_subnet" | cut -d"." -f1)" = "10" ]; then
echo "ERROR: VPN subnet must be in the 10.0.0.0/8 range, returning..."
return
fi
is_intersect="$(isNetworkIntersectOurNetworks ${rs_primary_vpn_subnet}/24 false true)"
if ! [ -z "$is_intersect" ]; then
echo "ERROR: VPN subnet collision - $is_intersect, returning..."
return
fi
PRIMARY_VPN_SUBNET="${rs_primary_vpn_subnet}/24"
PRIMARY_VPN_SETUP_TYPE=host
setupPortForwardingDB
chkSSHPort="$(checkPortForwardingIntersect tcp $rs_new_ssh_port $rs_new_ssh_port)"
if ! [ -z "$chkSSHPort" ]; then
sudo sqlite3 $HSHQ_DB "drop table portforwarding;"
echo "ERROR: $chkSSHPort, returning..."
return
fi
RELAYSERVER_SSH_PORT="$rs_new_ssh_port"
RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT=""
tmp_port=$PORTAINER_LOCAL_HTTPS_PORT
while [ -z "$RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT" ]
do
chkSSHPort="$(checkPortForwardingIntersect tcp $tmp_port $tmp_port)"
if ! [ -z "$chkSSHPort" ]; then
tmp_port=$((9000 + $RANDOM % 999))
else
RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT=$tmp_port
break
fi
done
# Clear out any old hosts
ssh-keygen -f "$HOME/.ssh/known_hosts" -R "[$RELAYSERVER_SUB_RELAYSERVER.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN]:$RELAYSERVER_SSH_PORT" > /dev/null 2>&1
ssh-keygen -f "$HOME/.ssh/known_hosts" -R "[$RELAYSERVER_SERVER_IP]:$RELAYSERVER_SSH_PORT" > /dev/null 2>&1
if [ -z "$RELAYSERVER_SSH_PRIVATE_KEY_FILENAME" ]; then
echo "Generating keys, please wait..."
RELAYSERVER_SSH_PRIVATE_KEY_FILENAME=$HOMESERVER_ABBREV".key"
updateConfigVar RELAYSERVER_SSH_PRIVATE_KEY_FILENAME $RELAYSERVER_SSH_PRIVATE_KEY_FILENAME
rm -f $HSHQ_CONFIG_DIR/$RELAYSERVER_SSH_PRIVATE_KEY_FILENAME
rm -f $HSHQ_CONFIG_DIR/${RELAYSERVER_SSH_PRIVATE_KEY_FILENAME}.pub
RELAYSERVER_SSH_PRIVATE_KEY_PHRASE=$(pwgen -c -n 64 1)
updateConfigVar RELAYSERVER_SSH_PRIVATE_KEY_PHRASE $RELAYSERVER_SSH_PRIVATE_KEY_PHRASE
ssh-keygen -f $HSHQ_CONFIG_DIR/$RELAYSERVER_SSH_PRIVATE_KEY_FILENAME -N $RELAYSERVER_SSH_PRIVATE_KEY_PHRASE
chmod 0400 $HSHQ_CONFIG_DIR/$RELAYSERVER_SSH_PRIVATE_KEY_FILENAME
chmod 0400 $HSHQ_CONFIG_DIR/${RELAYSERVER_SSH_PRIVATE_KEY_FILENAME}.pub
fi
# Login, upload check script, log back in and run check script
# 1. Login
echo "Logging into RelayServer..."
perfRemoteAction -m ssh -p $RELAYSERVER_CURRENT_SSH_PORT -s "$rs_cur_password" -o "-T -o ConnectTimeout=5 -o 'StrictHostKeyChecking accept-new'" -u "$rs_cur_username" -h "$RELAYSERVER_SERVER_IP" -c "echo hello >/dev/null" -f
is_err=$?
if [ $is_err -ne 0 ]; then
echo "ERROR: There was a problem logging in to the RelayServer, returning..."
return
fi
# 2. Upload check script
echo "Uploading validation script..."
outputRelayServerValidationScript
outputRelayServerInstallSetupScript
perfRemoteAction -m scp -p $RELAYSERVER_CURRENT_SSH_PORT -s "$rs_cur_password" -a $HOME/$RS_INSTALL_VALIDATION_SCRIPT_NAME -u $rs_cur_username -h $RELAYSERVER_SERVER_IP -c ":~" -f
is_err=$?
rm -f $HOME/$RS_INSTALL_VALIDATION_SCRIPT_NAME
if [ $is_err -ne 0 ]; then
echo "ERROR: There was an problem uploading the validation script to the RelayServer, returning..."
return
fi
perfRemoteAction -m scp -p $RELAYSERVER_CURRENT_SSH_PORT -s "$rs_cur_password" -a $HSHQ_RELAYSERVER_DIR/scripts/$RS_INSTALL_SETUP_SCRIPT_NAME -u $rs_cur_username -h $RELAYSERVER_SERVER_IP -c ":~/$RS_INSTALL_VALIDATION_LIB_SCRIPT_NAME" -f
is_err=$?
rm -f $HSHQ_RELAYSERVER_DIR/scripts/$RS_INSTALL_SETUP_SCRIPT_NAME
if [ $is_err -ne 0 ]; then
echo "ERROR: There was an problem uploading the setup script to the RelayServer, returning..."
return
fi
rm -f $HOME/$RS_INSTALL_VALIDATION_SCRIPT_NAME
# 3. Log back in and run check script
if ! [ "$rs_cur_username" = "root" ]; then
rs_new_password="$rs_cur_password"
fi
echo "Performing pre-installation checks, please wait..."
perfRemoteAction -m ssh -p $RELAYSERVER_CURRENT_SSH_PORT -s "$rs_cur_password" -o "-T -o ConnectTimeout=10 -o 'StrictHostKeyChecking accept-new'" -u $rs_cur_username -h $RELAYSERVER_SERVER_IP -c "bash ~/$RS_INSTALL_VALIDATION_SCRIPT_NAME -s" -f -i "$rs_new_password"
is_err=$?
if [ $is_err -ne 0 ]; then
echo "ERROR: There was a problem with the RelayServer, see above."
return
fi
echo "Testing RelayServer login with pub/priv key..."
perfRemoteAction -m ssh -p $RELAYSERVER_CURRENT_SSH_PORT -o "-T -o ConnectTimeout=10 -o 'StrictHostKeyChecking accept-new'" -u $RELAYSERVER_REMOTE_USERNAME -h $RELAYSERVER_SERVER_IP -c "echo \"Successfully logged in to RelayServer with key! \"" -f
is_err=$?
if [ $is_err -ne 0 ]; then
echo "ERROR: Could not login to RelayServer with pub/priv key."
return
fi
echo "All is good, assigning values..."
RELAYSERVER_HSHQ_BASE_DIR=/home/$RELAYSERVER_REMOTE_USERNAME/hshq
RELAYSERVER_HSHQ_DATA_DIR=$RELAYSERVER_HSHQ_BASE_DIR/data
RELAYSERVER_HSHQ_NONBACKUP_DIR=$RELAYSERVER_HSHQ_BASE_DIR/nonbackup
RELAYSERVER_HSHQ_SCRIPTS_DIR=$RELAYSERVER_HSHQ_DATA_DIR/scripts
RELAYSERVER_HSHQ_SECRETS_DIR=$RELAYSERVER_HSHQ_DATA_DIR/secrets
RELAYSERVER_HSHQ_STACKS_DIR=$RELAYSERVER_HSHQ_DATA_DIR/stacks
RELAYSERVER_HSHQ_SSL_DIR=$RELAYSERVER_HSHQ_DATA_DIR/ssl
resetRelayServerData
updatePlaintextRootConfigVar PRIMARY_VPN_SETUP_TYPE "$PRIMARY_VPN_SETUP_TYPE"
updateConfigVar RELAYSERVER_SSH_PORT $RELAYSERVER_SSH_PORT
# SSH (do both protocols, even though only need TCP)
sudo sqlite3 $HSHQ_DB "PRAGMA foreign_keys=ON;insert into portforwarding(PFType, Name, ExtStart, ExtEnd, IntStart, IntEnd, Protocol, InternalHost, IPAddress, LastUpdated) values('System', 'RS-SSH', $RELAYSERVER_SSH_PORT, $RELAYSERVER_SSH_PORT, 0, 0, 'both','','','$curdt');"
updateConfigVar RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT $RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT
# Portainer (do both protocols, even though only need TCP)
sudo sqlite3 $HSHQ_DB "PRAGMA foreign_keys=ON;insert into portforwarding(PFType, Name, ExtStart, ExtEnd, IntStart, IntEnd, Protocol, InternalHost, IPAddress, LastUpdated) values('System', 'RS-Portainer', $RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT, $RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT, 0, 0, 'both','','','$curdt');"
updateConfigVar RELAYSERVER_WG_VPN_NETNAME "$RELAYSERVER_WG_VPN_NETNAME"
updateConfigVar RELAYSERVER_WG_INTERNET_NETNAME "$RELAYSERVER_WG_INTERNET_NETNAME"
updateConfigVar RELAYSERVER_EXT_EMAIL_HOSTNAME $RELAYSERVER_EXT_EMAIL_HOSTNAME
updateConfigVar RELAYSERVER_NAME "$RELAYSERVER_NAME"
updateConfigVar RELAYSERVER_LE_CERT_DOMAINS $RELAYSERVER_LE_CERT_DOMAINS
updateConfigVar RELAYSERVER_REMOTE_USERNAME $RELAYSERVER_REMOTE_USERNAME
updateConfigVar RELAYSERVER_HSHQ_BASE_DIR $RELAYSERVER_HSHQ_BASE_DIR
updateConfigVar RELAYSERVER_HSHQ_DATA_DIR $RELAYSERVER_HSHQ_DATA_DIR
updateConfigVar RELAYSERVER_HSHQ_NONBACKUP_DIR $RELAYSERVER_HSHQ_NONBACKUP_DIR
updateConfigVar RELAYSERVER_HSHQ_SCRIPTS_DIR $RELAYSERVER_HSHQ_SCRIPTS_DIR
updateConfigVar RELAYSERVER_HSHQ_SECRETS_DIR $RELAYSERVER_HSHQ_SECRETS_DIR
updateConfigVar RELAYSERVER_HSHQ_STACKS_DIR $RELAYSERVER_HSHQ_STACKS_DIR
updateConfigVar RELAYSERVER_HSHQ_SSL_DIR $RELAYSERVER_HSHQ_SSL_DIR
updateConfigVar RELAYSERVER_SERVER_IP $RELAYSERVER_SERVER_IP
updateConfigVar RELAYSERVER_CURRENT_SSH_PORT $RELAYSERVER_CURRENT_SSH_PORT
updatePlaintextRootConfigVar PRIMARY_VPN_SUBNET $PRIMARY_VPN_SUBNET
leCertsArr=($(echo $RELAYSERVER_LE_CERT_DOMAINS | tr "," "\n"))
sudo sqlite3 $HSHQ_DB "PRAGMA foreign_keys=ON;delete from lecertdomains;"
for leCert in "${leCertsArr[@]}"
do
sudo sqlite3 $HSHQ_DB "insert or ignore into lecertdomains(Domain,BaseDomain) values('$leCert','$HOMESERVER_DOMAIN');"
curBaseDomain=$(getBaseDomain $leCert)
curSubDomain=$(getSubDomain $leCert)
if [ "$curBaseDomain" = "$HOMESERVER_DOMAIN" ]; then
for curSVC in "${SVCS_ARR[@]}"
do
var_base=$(echo $curSVC | cut -d"=" -f1 | cut -d"_" -f 2-)
subdom=$(echo $curSVC | cut -d"=" -f2 | cut -d"," -f6)
subdom="${subdom//\"}"
if [ "$subdom" = "$curSubDomain" ]; then
var_name="SVCD_"$var_base
var_value=$(getConfigVar $var_name)
var_valueArr=($(echo $var_value | tr "," "\n"))
var_valueArr[6]=le
printf -v new_value '%s,' "${var_valueArr[@]}"
new_value="$(echo "${new_value%,}")"
updateConfigVar $var_name $new_value
fi
done
fi
done
initRelayServerCredentials
outputHostedVPNConfigs
insertSQLHostedVPN
# This upload must take place as soon as possible after the
# validation check, as the RelayServer might be installing
# docker and reboooting (required for debian 12 atm)
# See "Performing pre-installation checks" comment above.
echo "Generating RelayServer install scripts..."
outputRelayServerInstallSetupScript
outputRelayServerInstallFreshScript
addDomainAdguardHS "*.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN" "$RELAYSERVER_SERVER_IP"
perfRemoteAction -m scp -p $RELAYSERVER_CURRENT_SSH_PORT -a $HSHQ_RELAYSERVER_DIR/scripts/$RS_INSTALL_SETUP_SCRIPT_NAME -u $RELAYSERVER_REMOTE_USERNAME -h $RELAYSERVER_SERVER_IP -c ":/home/$RELAYSERVER_REMOTE_USERNAME" -f
perfRemoteAction -m scp -p $RELAYSERVER_CURRENT_SSH_PORT -a $HSHQ_RELAYSERVER_DIR/scripts/$RS_INSTALL_FRESH_SCRIPT_NAME -u $RELAYSERVER_REMOTE_USERNAME -h $RELAYSERVER_SERVER_IP -c ":/home/$RELAYSERVER_REMOTE_USERNAME" -f
perfRemoteAction -m ssh -p $RELAYSERVER_CURRENT_SSH_PORT -o "-T -o ConnectTimeout=10" -u $RELAYSERVER_REMOTE_USERNAME -h $RELAYSERVER_SERVER_IP -c "touch ~/$RELAYSERVER_SCRIPTS_UPLOADED_FILE" -f
rm -f $HSHQ_RELAYSERVER_DIR/scripts/$RS_INSTALL_SETUP_SCRIPT_NAME
rm -f $HSHQ_RELAYSERVER_DIR/scripts/$RS_INSTALL_FRESH_SCRIPT_NAME
set +e
sendDNSRecordsEmail $HOMESERVER_DOMAIN
sleep 2
prepSvcsHostedVPN
set +e
startStopStack mailu stop
sleep 1
echo "Generating mailu certs..."
generateCert mail "$SMTP_HOSTNAME,$SUB_POSTFIX.$HOMESERVER_DOMAIN"
echo "Setting mailu relay creds..."
updateMailuStackRelayHost
# Start Install
set +e
echo "Starting installation on RelayServer..."
countRSPreRetries=0
maxRSPreRetries=20
while true;
do
perfRemoteAction -m ssh -p $RELAYSERVER_CURRENT_SSH_PORT -o "-T -o ConnectTimeout=10 -o 'StrictHostKeyChecking accept-new'" -u $RELAYSERVER_REMOTE_USERNAME -h $RELAYSERVER_SERVER_IP -c "sleep 1;if [ -f ~/$RELAYSERVER_NOT_READY_FILE ]; then exit 1; fi" -r 1 -b 1
if [ $? -eq 0 ]; then
break
fi
if [ $countRSPreRetries -ge $maxRSPreRetries ]; then
echo "Max retries reached. Something has likely gone wrong with the RelayServer pre-installation script, exiting..."
exit 2
fi
((countRSPreRetries++))
echo "($countRSPreRetries of $maxRSPreRetries) RelayServer not ready for installation, sleeping 30 seconds then will retry..."
sleep 30
done
perfRemoteAction -m ssh -p $RELAYSERVER_CURRENT_SSH_PORT -o "-T -o ConnectTimeout=10" -u $RELAYSERVER_REMOTE_USERNAME -h $RELAYSERVER_SERVER_IP -c "bash $RS_INSTALL_FRESH_SCRIPT_NAME -s" -i "$USER_RELAY_SUDO_PW" -f
perfRemoteAction -m ssh -p $RELAYSERVER_CURRENT_SSH_PORT -o "-t -o ConnectTimeout=10" -u $RELAYSERVER_REMOTE_USERNAME -h $RELAYSERVER_SERVER_IP -c "export TERM=linux; screen -r hshqInstall" -r 1 -b 1
echo "RelayServer is rebooting..."
echo "Sleeping 10 seconds, then will attempt to connect..."
sleep 10
set +e
connectPrimaryInternet
set +e
connectPrimaryVPN true
set +e
updateEndpointIPs
set +e
rs_wg_user_conf=$(sudo cat $HSHQ_WIREGUARD_DIR/users/${RELAYSERVER_WG_VPN_NETNAME}-user1.conf)
wgconfig=""
wgconfig="$wgconfig""============= User WireGuard Configuration ============\n"
wgconfig="$wgconfig""""""$rs_wg_user_conf""""\n"
wgconfig="$wgconfig""=======================================================\n"
if [ "$IS_DESKTOP_ENV" = "true" ]; then
wgconfigfile="$HOME/Desktop/User1_WireGuard.conf"
else
wgconfigfile="$HOME/User1_WireGuard.conf"
fi
rm -f $wgconfigfile
echo -e "$wgconfig" > $wgconfigfile
}
function setupHostedVPN()
{
echo "Checking for required utils, please wait..."
if [[ "$(isProgramInstalled dig)" = "false" ]]; then
echo "Installing dig, please wait..."
performAptInstall dnsutils > /dev/null 2>&1
fi
if [[ "$(isProgramInstalled sshpass)" = "false" ]]; then
echo "Installing sshpass, please wait..."
performAptInstall sshpass > /dev/null 2>&1
fi
if [[ "$(isProgramInstalled sipcalc)" = "false" ]]; then
echo "Installing sipcalc, please wait..."
performAptInstall sipcalc > /dev/null 2>&1
fi
resetRelayServerData
if [ -z "$RELAYSERVER_WG_VPN_NETNAME" ]; then
RELAYSERVER_WG_VPN_NETNAME="vpn-"${HOMESERVER_ABBREV:0:6}
curNum=1
while [ "$(checkInterfaceNameExists $RELAYSERVER_WG_VPN_NETNAME)" = "true" ] && [ $curNum -lt 100 ]
do
RELAYSERVER_WG_VPN_NETNAME="vpn-${HOMESERVER_ABBREV:0:5}${curNum}"
((curNum++))
done
if [ "$(checkInterfaceNameExists $RELAYSERVER_WG_VPN_NETNAME)" = "true" ]; then
showMessageBox "Too Many Connections" "You have 99 other connections that start with ${HOMESERVER_ABBREV:0:5}?!?"
return 1
fi
updateConfigVar RELAYSERVER_WG_VPN_NETNAME "$RELAYSERVER_WG_VPN_NETNAME"
fi
if [ -z "$RELAYSERVER_WG_INTERNET_NETNAME" ]; then
RELAYSERVER_WG_INTERNET_NETNAME="ext-${HOMESERVER_ABBREV:0:6}"
curNum=1
while [ "$(checkInterfaceNameExists $RELAYSERVER_WG_INTERNET_NETNAME)" = "true" ] && [ $curNum -lt 100 ]
do
RELAYSERVER_WG_INTERNET_NETNAME="ext-${HOMESERVER_ABBREV:0:5}${curNum}"
((curNum++))
done
if [ "$(checkInterfaceNameExists $RELAYSERVER_WG_INTERNET_NETNAME)" = "true" ]; then
showMessageBox "Too Many Connections" "You have 99 other connections that start with ${HOMESERVER_ABBREV:0:5}?!?"
return 1
fi
updateConfigVar RELAYSERVER_WG_INTERNET_NETNAME "$RELAYSERVER_WG_INTERNET_NETNAME"
fi
if [ -z "$IS_ACCEPT_DEFAULTS" ]; then
set +e
showYesNoMessageBox "Accept Defaults?" "Do you wish to use defaults where applicable?"
mbres=$?
if [ $mbres -eq 0 ]; then
IS_ACCEPT_DEFAULTS=yes
else
IS_ACCEPT_DEFAULTS=no
fi
set -e
fi
set +e
sudo sqlite3 $HSHQ_DB "drop table portforwarding;" > /dev/null 2>&1
set -e
setupPortForwardingDB
RELAYSERVER_EXT_EMAIL_HOSTNAME=$SUB_POSTFIX.$EXT_DOMAIN_PREFIX.$HOMESERVER_DOMAIN
updateConfigVar RELAYSERVER_EXT_EMAIL_HOSTNAME $RELAYSERVER_EXT_EMAIL_HOSTNAME
RELAYSERVER_SSH_PORT=""
while [ -z "$RELAYSERVER_SSH_PORT" ]
do
RELAYSERVER_SSH_PORT=$(promptUserInputMenu "$SSH_PORT" "Enter NEW RelayServer SSH Port" "Enter your desired SSH Port for your RelayServer. It is highly advised to change your default SSH port (22). Bots will constantly probe port 22:")
if [ -z "$RELAYSERVER_SSH_PORT" ]; then
showMessageBox "SSH Port Empty" "The SSH port cannot be empty"
elif [ "$(checkValidNumber $RELAYSERVER_SSH_PORT)" = "false" ]; then
showMessageBox "Invalid Character(s)" "The port contains invalid character(s). It must be a number."
RELAYSERVER_SSH_PORT=""
elif [ $RELAYSERVER_SSH_PORT -lt 1024 ] || [ $RELAYSERVER_SSH_PORT -gt 65536 ]; then
showMessageBox "Invalid Character(s)" "The port number is invalid, it must be between 1024-65536."
RELAYSERVER_SSH_PORT=""
fi
if ! [ -z "$RELAYSERVER_SSH_PORT" ]; then
chkSSHPort="$(checkPortForwardingIntersect tcp $RELAYSERVER_SSH_PORT $RELAYSERVER_SSH_PORT)"
if ! [ -z "$chkSSHPort" ]; then
showMessageBox "ERROR" "Error with port: $chkSSHPort"
RELAYSERVER_SSH_PORT=""
else
updateConfigVar RELAYSERVER_SSH_PORT $RELAYSERVER_SSH_PORT
fi
fi
done
# SSH (do both protocols, even though only need TCP)
sudo sqlite3 $HSHQ_DB "PRAGMA foreign_keys=ON;insert into portforwarding(PFType, Name, ExtStart, ExtEnd, IntStart, IntEnd, Protocol, InternalHost, IPAddress, LastUpdated) values('System', 'RS-SSH', $RELAYSERVER_SSH_PORT, $RELAYSERVER_SSH_PORT, 0, 0, 'both','','','$curdt');"
RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT=""
while [ -z "$RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT" ]
do
tmp_port=$((9000 + $RANDOM % 999))
while [ "$tmp_port" = "$RELAYSERVER_SSH_PORT" ]
do
tmp_port=$((9000 + $RANDOM % 999))
done
if [ "$IS_ACCEPT_DEFAULTS" = "yes" ]; then
RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT="$tmp_port"
else
RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT=$(promptUserInputMenu "$tmp_port" "Enter RelayServer Portainer Port" "Enter a port to use for Portainer on the RelayServer. A random port between 9000-9999 has been generated for you.")
fi
if [ -z "$RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT" ]; then
showMessageBox "Port Empty" "The port cannot be empty"
elif [ "$(checkValidNumber $RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT)" = "false" ]; then
showMessageBox "Invalid Character(s)" "The port contains invalid character(s). It must be a number."
RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT=""
elif [ $RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT -lt 1024 ] || [ $RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT -gt 65536 ]; then
showMessageBox "Invalid Character(s)" "The port number is invalid, it must be between 1024-65536."
RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT=""
fi
if ! [ -z "$RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT" ]; then
chkSSHPort="$(checkPortForwardingIntersect tcp $RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT $RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT)"
if ! [ -z "$chkSSHPort" ]; then
showMessageBox "ERROR" "Error with port: $chkSSHPort"
RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT=""
else
updateConfigVar RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT $RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT
fi
fi
done
# Portainer (do both protocols, even though only need TCP)
sudo sqlite3 $HSHQ_DB "PRAGMA foreign_keys=ON;insert into portforwarding(PFType, Name, ExtStart, ExtEnd, IntStart, IntEnd, Protocol, InternalHost, IPAddress, LastUpdated) values('System', 'RS-Portainer', $RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT, $RELAYSERVER_PORTAINER_LOCAL_HTTPS_PORT, 0, 0, 'both','','','$curdt');"
while [ -z "$RELAYSERVER_NAME" ]
do
if [ "$IS_ACCEPT_DEFAULTS" = "yes" ]; then
RELAYSERVER_NAME="$HOMESERVER_NAME RelayServer"
else
RELAYSERVER_NAME=$(promptUserInputMenu "$HOMESERVER_NAME RelayServer" "Enter RelayServer Name" "Enter the RelayServer Name:")
fi
if [ -z "$RELAYSERVER_NAME" ]; then
showMessageBox "RelayServer Name Empty" "The RelayServer Name cannot be empty"
elif [ $(checkValidStringUpperLowerNumbers "$RELAYSERVER_NAME" "[:space:],.-") = "false" ]; then
showMessageBox "Invalid Character(s)" "The RelayServer name contains invalid character(s). It must consist of upper/lowercase letters, numbers, spaces, commas, periods, or hyphens."
RELAYSERVER_NAME=""
else
updateConfigVar RELAYSERVER_NAME "$RELAYSERVER_NAME"
fi
done
while [ "$RELAYSERVER_LE_CERT_DOMAINS" = "unset" ]
do
lecert_def=$(getLetsEncryptCertsDefault)
if [ "$IS_ACCEPT_DEFAULTS" = "yes" ]; then
RELAYSERVER_LE_CERT_DOMAINS="$lecert_def"
else
RELAYSERVER_LE_CERT_DOMAINS=$(promptUserInputMenu "$lecert_def" "Enter LE Cert Subdomains" "Enter the subdomains for which the certificates will be managed by LetsEncrypt (comma-separated):")
if ! [ -z "$RELAYSERVER_LE_CERT_DOMAINS" ] && [ $(checkValidString "$RELAYSERVER_LE_CERT_DOMAINS" ",.-") = "false" ]; then
showMessageBox "Invalid Character(s)" "The domain list contains invalid character(s). It must consist of a-z (lowercase), 0-9, -, and/or ."
RELAYSERVER_LE_CERT_DOMAINS=unset
fi
fi
done
updateConfigVar RELAYSERVER_LE_CERT_DOMAINS $RELAYSERVER_LE_CERT_DOMAINS
leCertsArr=($(echo $RELAYSERVER_LE_CERT_DOMAINS | tr "," "\n"))
isReload=false
sudo sqlite3 $HSHQ_DB "PRAGMA foreign_keys=ON;delete from lecertdomains;"
for leCert in "${leCertsArr[@]}"
do
sudo sqlite3 $HSHQ_DB "insert or ignore into lecertdomains(Domain,BaseDomain) values('$leCert','$HOMESERVER_DOMAIN');"
curBaseDomain=$(getBaseDomain $leCert)
curSubDomain=$(getSubDomain $leCert)
if [ "$curBaseDomain" = "$HOMESERVER_DOMAIN" ]; then
for curSVC in "${SVCS_ARR[@]}"
do
var_base=$(echo $curSVC | cut -d"=" -f1 | cut -d"_" -f 2-)
subdom=$(echo $curSVC | cut -d"=" -f2 | cut -d"," -f6)
subdom="${subdom//\"}"
if [ "$subdom" = "$curSubDomain" ]; then
var_name="SVCD_"$var_base
var_value=$(getConfigVar $var_name)
var_valueArr=($(echo $var_value | tr "," "\n"))
var_valueArr[6]=le
printf -v new_value '%s,' "${var_valueArr[@]}"
new_value="$(echo "${new_value%,}")"
updateConfigVar $var_name $new_value
isReload=true
fi
done
fi
done
if [ "$isReload" = "true" ]; then
loadSvcVars
fi
echo "Initializing RelayServer service credentials..."
num_tries=1
max_tries=100
PRIMARY_VPN_SUBNET=""
while [ -z "$PRIMARY_VPN_SUBNET" ] && [ $num_tries -lt $max_tries ]
do
((num_tries++))
if [ "$IS_ACCEPT_DEFAULTS" = "yes" ]; then
PRIMARY_VPN_SUBNET=10.$(( $RANDOM % 256 )).$(( $RANDOM % 256 )).0/24
else
PRIMARY_VPN_SUBNET=$(promptUserInputMenu "10.$(( $RANDOM % 256 )).$(( $RANDOM % 256 )).0/24" "Enter Subnet" "Enter the VPN Hosting Subnet (in CIDR): ")
fi
if [ -z "$PRIMARY_VPN_SUBNET" ] || [ "$(checkValidIPAddress $PRIMARY_VPN_SUBNET)" = "false" ]; then
showMessageBox "Invalid Subnet" "The VPN Subnet is invalid."
PRIMARY_VPN_SUBNET=""
continue
fi
is_intersect="$(isNetworkIntersectOurNetworks $PRIMARY_VPN_SUBNET false true)"
if ! [ -z "$is_intersect" ]; then
if ! [ "$IS_ACCEPT_DEFAULTS" = "yes" ]; then
showMessageBox "Network Collision" "Network Collision: $is_intersect"
fi
PRIMARY_VPN_SUBNET=""
continue
fi
set +e
firstOct=$(echo "$PRIMARY_VPN_SUBNET" | cut -d"." -f1)
if ! [ "$firstOct" = "10" ]; then
showYesNoMessageBox "Confirm" "This subnet is not in the 10.0.0.0/8 range. It is highly recommended that you stay within this range. Are you sure you know what you are doing?"
if [ $? -ne 0 ]; then
PRIMARY_VPN_SUBNET=""
continue
fi
fi
vpnCIDR=$(echo "$PRIMARY_VPN_SUBNET" | cut -d"/" -f2-)
if ! [ "$vpnCIDR" = "24" ]; then
showYesNoMessageBox "The VPN subnet must be of size /24."
PRIMARY_VPN_SUBNET=""
continue
fi
set -e
updatePlaintextRootConfigVar PRIMARY_VPN_SUBNET $PRIMARY_VPN_SUBNET
done
if [ -z "$PRIMARY_VPN_SUBNET" ]; then
# We tried...
showMessageBox "ERROR" "Could not allocate VPN network subnet, returning..."
return 1
fi
initRelayServerCredentials
outputHostedVPNConfigs
insertSQLHostedVPN
echo "Generating RelayServer install scripts..."
outputRelayServerInstallSetupScript
outputRelayServerInstallFreshScript
set +e
uploadVPNInstallScripts
if [ $? -ne 0 ]; then
sudo sqlite3 $HSHQ_DB "PRAGMA foreign_keys=ON;delete from lecertdomains;"
return 1
fi
if [ "$IS_INSTALLED" = "true" ]; then
echo "Preparing mailu, please wait..."
startStopStack mailu stop
sleep 5
echo "Generating mailu certs..."
generateCert mail "$SMTP_HOSTNAME,$SUB_POSTFIX.$HOMESERVER_DOMAIN"
startStopStack mailu start
prepSvcsHostedVPN
fi
updatePlaintextRootConfigVar PRIMARY_VPN_SETUP_TYPE $PRIMARY_VPN_SETUP_TYPE
}
function outputRelayServerValidationScript()
{
# This function assumes rs_cur_username and rs_new_username have been defined before calling it.
# It also assumes that the caller will send the current/new user's password immediately via
# standard input.
if [ -z "$rs_cur_username" ] || [ -z "$rs_new_username" ]; then
# Fatal
echo "RelayServer usernames undefined for script, exiting..."
exit 5
fi
tee $HOME/$RS_INSTALL_VALIDATION_SCRIPT_NAME >/dev/null <<EOFRS
#!/bin/bash
curUsername=$rs_cur_username
newUsername=$rs_new_username
pubkey="$(cat $HSHQ_CONFIG_DIR/${RELAYSERVER_SSH_PRIVATE_KEY_FILENAME}.pub)"
function main()
{
while getopts ':ips' opt; do
case "\$opt" in
s)
startValidation ;;
p)
getSuper ;;
i)
performDebian12PreInstall ;;
?|h)
echo "Usage: \$(basename \$0)"
exit 1 ;;
esac
done
shift "\$((\$OPTIND -1))"
}
function startValidation()
{
set +e
which sudo > /dev/null 2>&1
if [ \$? -ne 0 ]; then
if [ "\$curUsername" = "root" ]; then
DEBIAN_FRONTEND=noninteractive apt update > /dev/null 2>&1
dpkg --configure -a > /dev/null 2>&1
DEBIAN_FRONTEND=noninteractive apt install -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' sudo > /dev/null 2>&1
else
echo "-----------------------------------------------------------------"
echo " ERROR: Sudo is not installed. Please either log in with"
echo " the root account or install it manually."
echo "-----------------------------------------------------------------"
exit 2
fi
fi
if [ "\$curUsername" = "root" ]; then
read -r -s -p "" USER_RELAY_SUDO_PW
id "\$newUsername" > /dev/null 2>&1
if [ \$? -eq 0 ]; then
echo "-----------------------------------------------------------------"
echo " ERROR: User (\$newUsername) already exists."
echo "-----------------------------------------------------------------"
removeMyself
exit 1
fi
checkForHSHQ
checkForRunningDockerContainers
checkForBoundPorts
sudo useradd -m -G sudo -s /bin/bash "\$newUsername" > /dev/null 2>&1
echo "\$newUsername:\$USER_RELAY_SUDO_PW" | sudo chpasswd > /dev/null 2>&1
else
read -r -s -p "" USER_RELAY_SUDO_PW
echo "\$USER_RELAY_SUDO_PW" | sudo -S -v -p "" > /dev/null 2>&1
if [ \$? -ne 0 ]; then
echo "-----------------------------------------------------------------"
echo " ERROR: The sudo password for \$curUsername is incorrect."
echo "-----------------------------------------------------------------"
removeMyself
exit 2
fi
sudo bash -c "\$(declare -f checkForValidOSVersion); checkForValidOSVersion \$curUsername" 2> /dev/null
if [ \$? -ne 0 ]; then
removeMyself
exit 2
fi
sudo bash -c "\$(declare -f checkForHSHQ); checkForHSHQ" 2> /dev/null
if [ \$? -ne 0 ]; then
removeMyself
exit 2
fi
sudo bash -c "\$(declare -f checkForRunningDockerContainers); checkForRunningDockerContainers" 2> /dev/null
if [ \$? -ne 0 ]; then
removeMyself
exit 2
fi
sudo bash -c "\$(declare -f checkBoundPort; declare -f checkForBoundPorts); checkForBoundPorts" 2> /dev/null
if [ \$? -ne 0 ]; then
removeMyself
exit 2
fi
fi
getent group docker >/dev/null || sudo groupadd docker > /dev/null 2>&1
sudo usermod -aG docker \$newUsername > /dev/null 2>&1
mkdir -p /home/\$newUsername/.ssh
chmod 775 /home/\$newUsername/.ssh
pubk=\$(echo "\$pubkey" | cut -d " " -f2)
grep "\$pubk" /home/\$newUsername/.ssh/authorized_keys > /dev/null 2>&1
if [ \$? -ne 0 ]; then
echo "\$pubkey" >> /home/\$newUsername/.ssh/authorized_keys
fi
chown -R \$newUsername:\$newUsername /home/\$newUsername/.ssh
rm -f /home/\$newUsername/$RS_INSTALL_SETUP_SCRIPT_NAME
rm -f /home/\$newUsername/$RS_INSTALL_FRESH_SCRIPT_NAME
checkPerformPreInstall
}
function checkForValidOSVersion()
{
source /home/\$1/$RS_INSTALL_VALIDATION_LIB_SCRIPT_NAME lib
checkSupportedHostOS
rtVal=\$?
if [ \$rtVal -ne 0 ]; then
exit \$rtVal
fi
}
function checkForHSHQ()
{
findhshq="\$(find /home -maxdepth 3 -type d -name hshq 2>/dev/null | head -n 1)"
if ! [ -z "\$findhshq" ]; then
echo "------------------------------------------------------------------------------------"
echo " ERROR: An hshq directory already exists: \$findhshq. You must log into the"
echo " RelayServer and run the nuke.sh script (bash nuke.sh) to clear everything out."
echo "------------------------------------------------------------------------------------"
exit 5
fi
}
function checkForRunningDockerContainers()
{
is_containers=\$(docker ps -q 2>/dev/null | head -n 1)
if ! [ -z "\$is_containers" ]; then
echo "------------------------------------------------------------------------------------"
echo " ERROR: There are currently running docker containers,"
echo " the server must be clear of all activity."
echo "------------------------------------------------------------------------------------"
exit 6
fi
}
function checkForBoundPorts()
{
checkBoundPort $MAILU_PORT_1
checkBoundPort $MAILU_PORT_5
checkBoundPort $CADDY_HTTP_PORT
checkBoundPort $CADDY_HTTPS_PORT
checkBoundPort $SYNCTHING_SYNC_PORT
checkBoundPort $SYNCTHING_DISC_PORT
checkBoundPort $SYNCTHING_LOCAL_WEB_PORT
checkBoundPort $RELAYSERVER_WG_PORTAL_PORT
checkBoundPort $RELAYSERVER_WG_PORT
}
function checkBoundPort()
{
chkPort=\$1
set +e
sudo netstat -tulpn 2>/dev/null | grep :\$chkPort > /dev/null 2>&1
if [ \$? -eq 0 ]; then
echo "------------------------------------------------------------------------------------"
echo " A needed port is bound by another service: \$chkPort"
echo " The server must be clear of all services on the following port lists:"
echo " External - $MAILU_PORT_1, $CADDY_HTTP_PORT, $CADDY_HTTPS_PORT, $RELAYSERVER_WG_PORT"
echo " Internal - $MAILU_PORT_5, $SYNCTHING_SYNC_PORT, $SYNCTHING_DISC_PORT, $SYNCTHING_LOCAL_WEB_PORT, $RELAYSERVER_WG_PORTAL_PORT"
echo "------------------------------------------------------------------------------------"
exit 7
fi
}
function removeMyself()
{
rm -f ~/$RS_INSTALL_VALIDATION_LIB_SCRIPT_NAME
rm -f \$0
}
function checkPerformPreInstall()
{
sudo DEBIAN_FRONTEND=noninteractive apt update > /dev/null 2>&1
sudo dpkg --configure -a > /dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt remove --purge -y needrestart > /dev/null 2>&1
which screen > /dev/null 2>&1
if [ \$? -ne 0 ]; then
DEBIAN_FRONTEND=noninteractive sudo apt install -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' screen > /dev/null 2>&1
fi
which pwgen > /dev/null 2>&1
if [ \$? -ne 0 ]; then
DEBIAN_FRONTEND=noninteractive sudo apt install -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' pwgen > /dev/null 2>&1
fi
which netstat > /dev/null 2>&1
if [ \$? -ne 0 ]; then
DEBIAN_FRONTEND=noninteractive sudo apt install -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' net-tools > /dev/null 2>&1
fi
docker ps > /dev/null 2>&1
if [ \$? -ne 0 ]; then
source ~/$RS_INSTALL_VALIDATION_LIB_SCRIPT_NAME lib
if [ "\$DISTRO_ID" = "debian" ] && [[ "\$DISTRO_VERSION" =~ ^12. ]]; then
echo "Installing docker on RelayServer and rebooting it..."
# Must install docker and reboot
touch /home/\$newUsername/$RELAYSERVER_NOT_READY_FILE
scName=hshqPreInstall
initScreen "\$scName"
screen -S "\$scName" -X stuff "bash \$0 -i\n"
return
fi
fi
removeMyself
}
function performDebian12PreInstall()
{
sudo DEBIAN_FRONTEND=noninteractive apt update > /dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt upgrade -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' > /dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt dist-upgrade -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' > /dev/null 2>&1
source ~/$RS_INSTALL_VALIDATION_LIB_SCRIPT_NAME lib
installDocker
echo "Checking for uploaded scripts..."
# Need to ensure other files have been uploading before rebooting.
maxCount=120
curCount=0
while [ \$curCount -lt \$maxCount ]
do
if [ -f /home/\$newUsername/$RELAYSERVER_SCRIPTS_UPLOADED_FILE ]; then
break
fi
sleep 5
((curCount++))
done
echo "Scripts found, rebooting..."
rm -f /home/\$newUsername/$RELAYSERVER_SCRIPTS_UPLOADED_FILE
removeMyself
rm -f /home/\$newUsername/$RELAYSERVER_NOT_READY_FILE
reboot
}
function getSuper()
{
set +e
stty -echo
mkdir -p $TMP_PW_CHECKDIR
read -r -s -p "" sudoPWKey
USER_RELAY_SUDO_PW="\$(openssl enc -d -aes256 -pbkdf2 -in $ENC_PW_FILE -pass pass:\$sudoPWKey)"
rm -f $ENC_PW_FILE
echo "\$USER_RELAY_SUDO_PW" | sudo -S -v -p "" > /dev/null 2>&1
if [ \$? -ne 0 ]; then
exit 6
fi
rm -fr $TMP_PW_CHECKDIR
clear
stty echo
}
function initScreen()
{
set +e
screenName="\$1"
dirtest1=/tmp/scPre1
isScreenSuccess=false
curScreenAttempt=0
maxScreenAttempt=5
screen -XS \$screenName quit > /dev/null 2>&1
sudoPWKey=\$(pwgen -c -n 32 1)
echo -n "\$USER_RELAY_SUDO_PW" | openssl enc -e -aes256 -pbkdf2 -pass pass:\$sudoPWKey > $ENC_PW_FILE
while [ \$curScreenAttempt -lt \$maxScreenAttempt ]
do
((curScreenAttempt++))
rm -fr \$dirtest1
rm -fr $TMP_PW_CHECKDIR
screen -dmS \$screenName
screen -S \$screenName -X stuff "stty -echo;mkdir \$dirtest1\n"
curCheckCount=0
while ! [ -d \$dirtest1 ] && [ \$curCheckCount -lt 5 ]
do
((curCheckCount++))
sleep 1
done
if ! [ -d \$dirtest1 ]; then
screen -XS \$screenName quit > /dev/null 2>&1
continue
fi
screen -S \$screenName -X stuff "bash \$0 -p\n"
curCheckCount=0
while ! [ -d $TMP_PW_CHECKDIR ] && [ \$curCheckCount -lt 15 ]
do
((curCheckCount++))
sleep 1
done
screen -S \$screenName -X stuff "\$sudoPWKey\n"
curCheckCount=0
while [ -d $TMP_PW_CHECKDIR ] && [ \$curCheckCount -lt 15 ]
do
((curCheckCount++))
sleep 1
done
rm -fr \$dirtest1
if [ -d $TMP_PW_CHECKDIR ]; then
rm -fr $TMP_PW_CHECKDIR
echo "The sudo password was not correctly transmitted to the screen session, exiting..."
exit 5
fi
rm -fr $TMP_PW_CHECKDIR
isScreenSuccess=true
break
done
USER_RELAY_SUDO_PW=""
if ! [ "\$isScreenSuccess" = "true" ]; then
return 1
fi
}
main "\$@"
EOFRS
chmod 600 $HOME/$RS_INSTALL_VALIDATION_SCRIPT_NAME
}
function outputRelayServerInstallSetupScript()
{
rm -f $HSHQ_RELAYSERVER_DIR/scripts/$RS_INSTALL_SETUP_SCRIPT_NAME
cat <<EOFRS > $HSHQ_RELAYSERVER_DIR/scripts/$RS_INSTALL_SETUP_SCRIPT_NAME
#!/bin/bash
set +e
function init()
{
USERNAME=\$(id -u -n)
RELAYSERVER_HSHQ_BASE_DIR=\$HOME/hshq
RELAYSERVER_HSHQ_DATA_DIR=\$RELAYSERVER_HSHQ_BASE_DIR/data
RELAYSERVER_HSHQ_NONBACKUP_DIR=\$RELAYSERVER_HSHQ_BASE_DIR/nonbackup
RELAYSERVER_HSHQ_SCRIPTS_DIR=\$RELAYSERVER_HSHQ_DATA_DIR/scripts
NET_EXTERNAL_SUBNET=$RELAYSERVER_NET_EXTERNAL_SUBNET
NET_EXTERNAL_SUBNET_PREFIX=$RELAYSERVER_NET_EXTERNAL_SUBNET_PREFIX
NET_WEBPROXY_SUBNET=$RELAYSERVER_NET_WEBPROXY_SUBNET
NET_WEBPROXY_SUBNET_PREFIX=$RELAYSERVER_NET_WEBPROXY_SUBNET_PREFIX
MAIL_RELAY_SUBNET=$RELAYSERVER_MAIL_RELAY_SUBNET
MAIL_RELAY_SUBNET_PREFIX=$RELAYSERVER_MAIL_RELAY_SUBNET_PREFIX
CLIENT_DNS_SUBNET=$RELAYSERVER_CLIENT_DNS_SUBNET
CLIENT_DNS_SUBNET_PREFIX=$RELAYSERVER_CLIENT_DNS_SUBNET_PREFIX
loadVersionVars
IS_RUST_UTILS=false
if [ "\$DISTRO_ID" = "ubuntu" ] && [[ "\$DISTRO_VERSION" =~ ^26\.04. ]]; then
IS_RUST_UTILS=true
fi
}
function setupMain()
{
init
echo "Running setup script..."
rm -f ~/$RELAYSERVER_SCRIPTS_UPLOADED_FILE
outputNukeScript
new_hostname="RelayServer-$(echo $HOMESERVER_DOMAIN | sed 's/\./-/g')"
if [ -z "\$(cat /etc/hosts | grep \$new_hostname)" ]; then
echo "127.0.1.1 \$new_hostname" | sudo tee -a /etc/hosts
fi
sudo hostnamectl set-hostname \$new_hostname
rm -f \$HOME/dead.letter
# Ensure we can login during the installation process,
# since the firewall will block the connection if we
# haven't logged in before the firewall goes up.
cur_ssh_port=\$(sudo grep ^Port /etc/ssh/sshd_config)
if [ \$? -ne 0 ]; then
cur_ssh_port=22
else
cur_ssh_port=\$(sudo grep ^Port /etc/ssh/sshd_config | xargs | cut -d" " -f2)
fi
if [[ "\$(isProgramInstalled iptables)" = "false" ]]; then
sudo DEBIAN_FRONTEND=noninteractive apt update > /dev/null 2>&1
performAptInstall iptables > /dev/null 2>&1
fi
sudo iptables -C INPUT -p tcp -m tcp --dport \$cur_ssh_port -j ACCEPT > /dev/null 2>&1 || sudo iptables -A INPUT -p tcp -m tcp --dport \$cur_ssh_port -j ACCEPT
# Change SSH port on host
sudo sed -i "s|^#*Port .*\$|Port $RELAYSERVER_SSH_PORT|g" /etc/ssh/sshd_config
sudo sed -i "s|^Port .*\$|Port $RELAYSERVER_SSH_PORT|g" /etc/ssh/sshd_config
# Some other SSH settings
sudo sed -i "s|^ClientAliveInterval .*\$|ClientAliveInterval 15|g" /etc/ssh/sshd_config
sudo sed -i "s|^ClientAliveCountMax .*\$|ClientAliveCountMax 3|g" /etc/ssh/sshd_config
sudo sed -i "s|^PermitEmptyPasswords .*\$|PermitEmptyPasswords no|g" /etc/ssh/sshd_config
sudo sed -i "s|^#*ClientAliveInterval .*\$|ClientAliveInterval 15|g" /etc/ssh/sshd_config
sudo sed -i "s|^#*ClientAliveCountMax .*\$|ClientAliveCountMax 3|g" /etc/ssh/sshd_config
sudo sed -i "s|^#*PermitEmptyPasswords .*\$|PermitEmptyPasswords no|g" /etc/ssh/sshd_config
# Update sudoers file
sudo sed -i '/\/userasroot/d' /etc/sudoers >/dev/null
echo "\$USERNAME ALL=(ALL) NOPASSWD: \$RELAYSERVER_HSHQ_SCRIPTS_DIR/userasroot/*.sh" | sudo tee -a /etc/sudoers >/dev/null
sudo sed -i '/timestamp_timeout/d' /etc/sudoers >/dev/null
echo "Defaults timestamp_timeout=$SUDO_NORMAL_TIMEOUT" | sudo tee -a /etc/sudoers >/dev/null
sudo sed -i '/logfile=/d' /etc/sudoers >/dev/null
if [ "\$IS_RUST_UTILS" = "true" ]; then
echo "authpriv.* /var/log/sudo.log" | sudo tee /etc/rsyslog.d/50-sudo.conf >/dev/null
sudo systemctl restart rsyslog
else
echo "Defaults logfile=/var/log/sudo.log" | sudo tee -a /etc/sudoers >/dev/null
fi
sudo sed -i '/includedir/d' /etc/sudoers >/dev/null
echo "@includedir /etc/sudoers.d" | sudo tee -a /etc/sudoers >/dev/null
# Set timezone
sudo timedatectl set-timezone "$TZ"
# Create swap file
createSwapfile
installDependencies
createDockerNetworks
}
function createSwapfile()
{
sudo grep swapfile /etc/fstab > /dev/null 2>&1
if [ \$? -ne 0 ] && ! [ -f /swapfile ]; then
sudo fallocate -l $RELAYSERVER_SWAP_SIZE /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo "# Swap file" | sudo tee -a /etc/fstab > /dev/null 2>&1
echo "/swapfile none swap sw 0 0" | sudo tee -a /etc/fstab > /dev/null 2>&1
fi
}
function updateSysctl()
{
set +e
sudo tee /etc/sysctl.d/88-hshq.conf >/dev/null <<EOFSC
kernel.panic = 10
fs.file-max = 10000000
fs.nr_open = 10000000
fs.inotify.max_user_instances = 8192
fs.inotify.max_user_watches = 524288
net.core.rmem_max = 4194304
net.core.wmem_max = 4194304
net.core.somaxconn = 65536
net.mptcp.enabled = 0
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_loose = 0
net.netfilter.nf_conntrack_tcp_timeout_established = 1800
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 10
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 20
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 20
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 20
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 20
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 10
net.ipv4.route.flush = 1
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_rfc1337 = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5
net.ipv4.tcp_timestamps = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.ip_forward = 1
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
EOFSC
sudo sysctl --system > /dev/null 2>&1
}
function updateMOTD()
{
cat <<EOFMD > \$HOME/88-hshq
#!/bin/bash
echo
echo " #===============================================================#"
echo " # ▀█ █ ▄▄ ▄▄ ▄▄ ▄▄▄ █▀▀▀█ ▄▄▄ ▄▄▄ ▄ ▄ ▄▄▄ ▄▄▄ █ █ █▀▀█ #"
echo " # █▀▀█ █ █ █ █ █ ▄▄ ▀▀▀▄▄ ▄▄ ▄▄▄▀ ▀▄ ▄▀ ▄▄ ▄▄▄▀ █▀▀█ █ █ #"
echo " # █ █ ▀▄▄▀ █ █ ▄▄▄ █▄▄▄█ ▄▄▄ ▄▄▄▄ ▀█▀ ▄▄▄ ▄▄▄▄ █ █ █▄▄█▄ #"
echo " #===============================================================#"
echo
echo " Linux OS: \\\$(lsb_release -d | cut -d":" -f2 | cut -d"(" -f1 | xargs)"
printf " Memory Usage: %.1f%% of \\\$(free -h | awk '/Mem:/{print \\\$2}')\n" \\\$((10000 - \\\$((10**4 * \\\$(grep "MemAvailable" /proc/meminfo | xargs | cut -d" " -f2) / \\\$(grep "MemTotal" /proc/meminfo | xargs | cut -d" " -f2)))))e-2
totSwap=\\\$(grep "SwapTotal" /proc/meminfo | xargs | cut -d" " -f2)
if [ \\\$totSwap -gt 0 ]; then
printf " Swap Usage: %.1f%% of \\\$(free -h | awk '/Swap:/{print \\\$2}')\n" \\\$((10000 - \\\$((10**4 * \\\$(grep "SwapFree" /proc/meminfo | xargs | cut -d" " -f2) / \\\$totSwap))))e-2
fi
echo
echo " Disks: "
echo " -----------------------------------------------------------------"
echo " Filesystem Size Used Avail Use% Mounted on"
echo " -----------------------------------------------------------------"
df -hP | grep "^/dev" | awk '{printf " %-21s %-7s %-7s %-7s %-7s %-8s\n", \\\$1, \\\$2, \\\$3, \\\$4, \\\$5, \\\$6}'
echo
# Let's only show zombie count if greater than 10
zombie_count=\\\$(ps aux | grep "defunct" | wc -l)
if [ \\\$zombie_count -gt 10 ]; then
echo " => There are \\\$zombie_count zombie processes."
echo
fi
EOFMD
chmod 755 \$HOME/88-hshq
sudo chown root:root \$HOME/88-hshq
sudo chmod -x /etc/update-motd.d/*
sudo mv \$HOME/88-hshq /etc/update-motd.d/
if [ -f /etc/motd ]; then
sudo mv /etc/motd /etc/motd.old
fi
}
function performSuggestedSecUpdates()
{
disa_curE=\${-//[^e]/}
set +e
grep "enabled=1" /etc/default/apport > /dev/null 2>&1
if [ \$? -eq 0 ]; then
sudo sed -i "s/enabled=1/enabled=0/g" /etc/default/apport
sudo systemctl stop apport
sudo systemctl --now disable apport
sudo systemctl daemon-reload
fi
echo "Authorized uses only." | sudo tee /etc/issue > /dev/null 2>&1
echo "Authorized uses only." | sudo tee /etc/issue.net > /dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt remove --purge -y telnet > /dev/null 2>&1
# Assume the caller will restart sshd - don't want to risk breaking the installation process
sudo sed -i "s/^#MaxAuthTries.*/MaxAuthTries 4/" /etc/ssh/sshd_config
sudo sed -i "s/^#Banner none.*/Banner \/etc\/issue.net/" /etc/ssh/sshd_config
sudo sed -i "s/^#MaxStartups.*/MaxStartups 10:30:60/" /etc/ssh/sshd_config
sudo sed -i "s/^#LoginGraceTime.*/LoginGraceTime 60/" /etc/ssh/sshd_config
sudo sed -i "s/^#AllowTcpForwarding yes.*/AllowTcpForwarding no/" /etc/ssh/sshd_config
sudo chown root:root /etc/crontab
sudo chmod og-rwx /etc/crontab
sudo chown root:root /etc/cron.hourly/
sudo chmod og-rwx /etc/cron.hourly/
sudo chown root:root /etc/cron.daily/
sudo chmod og-rwx /etc/cron.daily/
sudo chown root:root /etc/cron.weekly/
sudo chmod og-rwx /etc/cron.weekly/
sudo chown root:root /etc/cron.monthly/
sudo chmod og-rwx /etc/cron.monthly/
sudo chown root:root /etc/cron.d/
sudo chmod og-rwx /etc/cron.d/
if ! [ -z \$disa_curE ]; then
set -e
fi
}
function performAptInstall()
{
sudo DEBIAN_FRONTEND=noninteractive apt install -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' \$1
}
function installDependencies()
{
UTILS_LIST="$RELAYSERVER_UTILS_LIST"
APT_REMOVE_LIST="$APT_REMOVE_LIST"
echo "Updating host..."
sudo DEBIAN_FRONTEND=noninteractive apt update > /dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt remove --purge -y needrestart > /dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt upgrade -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' > /dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt autoremove -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' > /dev/null 2>&1
sudo DEBIAN_FRONTEND=noninteractive apt remove --purge -y needrestart > /dev/null 2>&1
echo "Host update complete!"
performAptInstall ssmtp > /dev/null 2>&1
sudo tee /etc/ssmtp/ssmtp.conf >/dev/null <<EOFSM
root=$EMAIL_ADMIN_EMAIL_ADDRESS
mailhub=${SUB_POSTFIX}.${HOMESERVER_DOMAIN}:$MAILU_PORT_5
hostname=\$(cat /etc/hostname)
TLS_CA_FILE=/etc/ssl/certs/ca-certificates.crt
UseSTARTTLS=yes
FromLineOverride=no
AuthUser=$EMAIL_SMTP_EMAIL_ADDRESS
AuthPass=$EMAIL_SMTP_PASSWORD
EOFSM
sudo tee /etc/ssmtp/revaliases >/dev/null <<EOFSM
root:$EMAIL_SMTP_EMAIL_ADDRESS
\$USERNAME:$EMAIL_SMTP_EMAIL_ADDRESS
EOFSM
performAptInstall mailutils > /dev/null 2>&1
getent group mailsenders >/dev/null || sudo groupadd mailsenders
sudo usermod -aG mailsenders \$USERNAME
sudo chown root:mailsenders /usr/bin/mail.mailutils
sudo chmod 750 /usr/bin/mail.mailutils
set +e
# Install Rsyslog
sudo systemctl status rsyslog > /dev/null 2>&1
if [ \$? -ne 0 ]; then
echo "Installing rsyslog, please wait..."
performAptInstall rsyslog > /dev/null 2>&1
sudo systemctl enable rsyslog
sudo systemctl start rsyslog
fi
for util in \$UTILS_LIST; do
if [[ "\$(isProgramInstalled \$util)" = "false" ]]; then
lib_name=\$(echo \$util | cut -d"|" -f2)
echo "Installing \$lib_name, please wait..."
performAptInstall \$lib_name > /dev/null 2>&1
fi
done
updateSysctl
updateMOTD
performSuggestedSecUpdates
installDocker
if sudo test -f /etc/logrotate.d/rsyslog; then
# Set max size of syslog to 2G
grep maxsize /etc/logrotate.d/rsyslog > /dev/null 2>&1
if [ \$? -ne 0 ]; then
sudo sed -i "/weekly\$/a\ maxsize 2G" /etc/logrotate.d/rsyslog
fi
fi
# Move logrotate into hourly
if sudo test -f /etc/cron.daily/logrotate; then
sudo mv /etc/cron.daily/logrotate /etc/cron.hourly
fi
# Set conditions for docker container logs
sudo tee /etc/logrotate.d/docker >/dev/null <<EOFLR
/var/log/docker/*.log {
weekly
maxsize 100M
rotate 4
missingok
notifempty
}
EOFLR
sudo systemctl restart logrotate
for rem_util in \$APT_REMOVE_LIST; do
sudo DEBIAN_FRONTEND=noninteractive apt remove --purge -y \$rem_util
done
sudo DEBIAN_FRONTEND=noninteractive apt autoremove -y
}
function loadVersionVars()
{
DISTRO_ID=\$(cat /etc/*-release | grep "^ID=" | cut -d"=" -f2 | xargs | tr '[:upper:]' '[:lower:]')
DISTRO_NAME=\$(cat /etc/*-release | grep "^NAME=" | cut -d"=" -f2 | xargs)
DISTRO_VERSION=\$(cat /etc/*-release | grep "^VERSION=" | cut -d"=" -f2 | xargs)
DISTRO_CODENAME=\$(cat /etc/*-release 2> /dev/null | grep "^VERSION_CODENAME=" | cut -d"=" -f2 | xargs)
}
function checkSupportedHostOS()
{
if [ "\$DISTRO_ID" = "ubuntu" ] && [[ "\$DISTRO_VERSION" =~ ^22\.04. ]]; then
return
fi
if [ "\$DISTRO_ID" = "ubuntu" ] && [[ "\$DISTRO_VERSION" =~ ^24\.04. ]]; then
return
fi
if [ "\$DISTRO_ID" = "ubuntu" ] && [[ "\$DISTRO_VERSION" =~ ^26\.04. ]]; then
return
fi
if [ "\$DISTRO_ID" = "debian" ] && [[ "\$DISTRO_VERSION" =~ ^12. ]]; then
return
fi
if [ "\$DISTRO_ID" = "linuxmint" ] && [[ "\$DISTRO_VERSION" =~ ^22. ]]; then
return
fi
echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
echo "@ Unsupported Host Operating System @"
echo "@ @"
echo "@ This installation only supports the following Linux distribution(s): @"
echo "@ @"
echo "@ - Ubuntu 22.04 (Jammy Jellyfish) @"
echo "@ - Ubuntu 24.04 (Noble Numbat) @"
echo "@ - Debian 12 (Bookworm) @"
echo "@ - Mint 22 (Wilma) @"
echo "@ @"
echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
return 2
}
function outputDockerSettings()
{
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json >/dev/null <<EOFRL
{
"default-address-pools":
[
{"base":"$DOCKER_NETWORK_RESERVED_RANGE","size":$DOCKER_NETWORK_SIZE}
],
"log-driver": "syslog",
"log-opts": {
"syslog-address": "unixgram:///dev/log",
"tag": "docker/{{.Name}}"
},
"ipv6": false,
"registry-mirrors": ["https://mirror.gcr.io"],
"min-api-version": "1.24"
}
EOFRL
set +e
grep -q DockerDaemonLogFileName /etc/rsyslog.d/docker-logs.conf > /dev/null 2>&1
if [ \$? -ne 0 ]; then
sudo tee /etc/rsyslog.d/docker-logs.conf >/dev/null <<EOFRL
\\\$FileCreateMode 0644
\\\$template DockerDaemonLogFileName,"/var/log/docker/docker.log"
\\\$template DockerContainerLogFileName,"/var/log/docker/%SYSLOGTAG:R,ERE,1,FIELD:docker/(.*)\[--end:secpath-replace%.log"
if \\\$programname == 'dockerd' then {
?DockerDaemonLogFileName
stop
}
if \\\$programname == 'containerd' then {
?DockerDaemonLogFileName
stop
}
if \\\$programname == 'docker' then {
if \\\$syslogtag contains 'docker/' then {
?DockerContainerLogFileName
stop
}
}
\\\$FileCreateMode 0600
EOFRL
fi
}
function installDocker()
{
set +e
outputDockerSettings
util="docker|docker"
if [[ "\$(isProgramInstalled \$util)" = "true" ]]; then
sudo systemctl restart rsyslog
sudo systemctl restart docker
return 0
fi
echo "Installing docker, please wait..."
performAptInstall apt-transport-https > /dev/null 2>&1
performAptInstall ca-certificates > /dev/null 2>&1
performAptInstall curl > /dev/null 2>&1
performAptInstall gnupg > /dev/null 2>&1
performAptInstall gnupg2 > /dev/null 2>&1
performAptInstall software-properties-common > /dev/null 2>&1
performAptInstall lsb-release > /dev/null 2>&1
case "\$DISTRO_ID" in
"ubuntu")
if [[ "\$DISTRO_VERSION" =~ ^22\.04. ]]; then
installDockerUbuntu2204
elif [[ "\$DISTRO_VERSION" =~ ^24\.04. ]]; then
installDockerUbuntu2404
elif [[ "\$DISTRO_VERSION" =~ ^26\.04. ]]; then
installDockerUbuntu2604
else
echo "Linux version not found when installing Docker, exiting..."
exit 5
fi
;;
"debian")
if [[ "\$DISTRO_VERSION" =~ ^12. ]]; then
installDockerDebian12
else
echo "Linux version not found when installing Docker, exiting..."
exit 5
fi
;;
"linuxmint")
if [[ "\$DISTRO_VERSION" =~ ^22. ]]; then
installDockerUbuntu2404
elif [[ "\$DISTRO_VERSION" =~ ^6. ]]; then
installDockerDebian12
else
echo "Linux version not found when installing Docker, exiting..."
exit 5
fi
;;
*)
;;
esac
# See https://www.portainer.io/blog/portainer-and-docker-26
sudo apt-mark hold docker-ce
sudo apt-mark hold docker-ce-cli
sudo systemctl restart rsyslog
sudo systemctl restart docker
sudo usermod -aG docker \$USERNAME
}
function installDockerUbuntu2204()
{
# Install Docker (https://docs.docker.com/engine/install/ubuntu/)
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg --yes
echo "deb [arch=\$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo DEBIAN_FRONTEND=noninteractive apt update > /dev/null 2>&1
echo "Installing docker..."
sudo DEBIAN_FRONTEND=noninteractive apt install -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' docker-ce=$DOCKER_VERSION_UBUNTU_2204 docker-ce-cli=$DOCKER_VERSION_UBUNTU_2204 containerd.io docker-buildx-plugin docker-compose docker-compose-plugin > /dev/null 2>&1
}
function installDockerUbuntu2404()
{
# Install Docker (https://docs.docker.com/engine/install/ubuntu/)
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \$(. /etc/os-release && echo "\$DISTRO_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo DEBIAN_FRONTEND=noninteractive apt update > /dev/null 2>&1
echo "Installing docker..."
sudo DEBIAN_FRONTEND=noninteractive apt install -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' docker-ce=$DOCKER_VERSION_UBUNTU_2404 docker-ce-cli=$DOCKER_VERSION_UBUNTU_2404 containerd.io docker-buildx-plugin docker-compose docker-compose-plugin > /dev/null 2>&1
}
function installDockerUbuntu2604()
{
# Install Docker (https://docs.docker.com/engine/install/ubuntu/)
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \$(. /etc/os-release && echo "\$DISTRO_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo DEBIAN_FRONTEND=noninteractive apt update > /dev/null 2>&1
echo "Installing docker..."
sudo DEBIAN_FRONTEND=noninteractive apt install -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' docker-ce=$DOCKER_VERSION_UBUNTU_2604 docker-ce-cli=$DOCKER_VERSION_UBUNTU_2604 containerd.io docker-buildx-plugin docker-compose-plugin > /dev/null 2>&1
}
function installDockerDebian12()
{
# Install Docker (https://docs.docker.com/engine/install/debian/)
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "\$DISTRO_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo DEBIAN_FRONTEND=noninteractive apt update > /dev/null 2>&1
echo "Installing docker..."
sudo DEBIAN_FRONTEND=noninteractive apt install -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' docker-ce=$DOCKER_VERSION_DEBIAN_12 docker-ce-cli=$DOCKER_VERSION_DEBIAN_12 containerd.io docker-buildx-plugin docker-compose docker-compose-plugin > /dev/null 2>&1
}
function outputNukeScript()
{
cat <<EOFNS > \$HOME/$NUKE_SCRIPT_NAME
HSHQ_BASE_DIR=\$RELAYSERVER_HSHQ_BASE_DIR
HSHQ_SCRIPTS_DIR=\$RELAYSERVER_HSHQ_SCRIPTS_DIR
set +e
function removeWGInterfaceQuick()
{
config_filename=\\\$1
sudo systemctl stop wg-quick@\\\${config_filename}.service
sudo systemctl disable wg-quick@\\\${config_filename}.service
sudo systemctl daemon-reload
sudo rm -f /etc/wireguard/\\\${config_filename}.conf
}
function main()
{
echo
echo
echo
echo
echo "============================================================"
echo "This script will entirely remove HSHQ and all artifacts."
echo "It will permanently delete ALL data"
echo "It will not delete any previously downloaded docker images."
echo "In order to remove them as well, type 'docker image prune -f'"
echo "Please type 'nuclear' in order to continue."
echo "============================================================"
echo
echo
read -r -p "Type 'nuclear' (no quotes): " isnuke
if ! [ "\\\$isnuke" = "nuclear" ]; then
echo "String does not match, exiting..."
exit 1
fi
sudo -k
sudo crontab -r
sudo doc