keyhelp panel on debian 13

keyhelp panel debian 13

#!/bin/bash

# _ __ _ _ _ (R)
# | |/ /___ _ _| |_| |___| |___
# | | / -_) |_/ | _ | -_) | _ \
# |_|\_\___|\__, |_| |_|___|_| _/
# |___/ |_|


## Usage: install_keyhelp.sh [options]
##
## Options:
## -h, --help Show this help
## -v, --version Print version info
## --preferred-protocol Preferred IP protocol for connections
## [Values: "none", "ipv4", "ipv6" | Default "ipv4"]
## --debug Get KeyHelp from debug release channel - don't do this ;)
##
## Try "php /home/keyhelp/www/keyhelp/install/install.php --help" for more options.
## Any unknown arguments will be passed to the main installer.

#=======================================================================================================================
# Globals
#=======================================================================================================================

# Setting PATH, important for OS >= Debian 10, when user switching to root with "su root" instead "su -"
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

ARGV=$@
PASS_ARGV=""
INSTALLER_VERSION=1
DEBUG=false
NON_INTERACTIVE=false
PREFERRED_PROTOCOL="ipv4"
USERNAME="keyhelp"
HOME_DIR="/home/keyhelp/"
INSTALL_DIR="$HOME_DIR/www/keyhelp/"
INSTALL_FILE=$(readlink -m "$INSTALL_DIR/install/install.php"); # remove unnecessary slashes

#=======================================================================================================================
# Functions | Common
#=======================================================================================================================

# Prints a given error message and exit.
#
# param string The error message.
# return void
function die()
{
echo -en "\033[1;31mERROR:\033[0m "
echo -e "$*" >&2
exit 1
}

# Prints a given message and highlight it as warning.
#
# param string The message.
# return void
function warn()
{
echo -en "\033[1;33mWARNING:\033[0m "
echo "$*" >&2
}

# Prints a given message and highlight it.
#
# param string The message
# return void
function print_headline()
{
echo -e "\033[1;32m$*\033[0m"
}

#=======================================================================================================================
# Functions | Update default values
#=======================================================================================================================

# Update the default value of PREFERRED_PROTOCOL, in case of IPv6 only machines.
function update_preferred_protocol()
{
if [[ ! `hostname -I` =~ "." ]]; then
PREFERRED_PROTOCOL="none"
fi
}
#=======================================================================================================================
# Functions | Input / Arguments
#=======================================================================================================================

# Parses command line arguments and store them in global variables.
#
# return void
function parse_args()
{
while [[ $# > 0 ]]
do
key="$1"
case $key in
-h|--help)
echo "$(grep '^##' "${BASH_SOURCE[0]}" | cut -c 4-)"
echo ""
exit 0
;;
-v|--version)
echo "Version: $INSTALLER_VERSION"
echo "Copyright (C) `date +'%Y'` Keyweb AG"
exit 0
;;
--debug)
DEBUG=true
;;
--non-interactive)
NON_INTERACTIVE=true
# -> Pass to installer.
PASS_ARGV="$PASS_ARGV $key"
;;
--preferred-protocol)
value="$2"
PREFERRED_PROTOCOL=`sanitize_preferred_protocol $value`
shift
# -> Pass to installer.
PASS_ARGV="$PASS_ARGV $key $PREFERRED_PROTOCOL"
;;
--preferred-protocol=*)
value="${key#*=}"
PREFERRED_PROTOCOL=`sanitize_preferred_protocol $value`
# -> Pass to installer.
PASS_ARGV="$PASS_ARGV $key"
;;
*)
# Unknown argument
# -> Pass to installer.
PASS_ARGV="$PASS_ARGV $key"
;;
esac
shift
done
}

# Sanitizes the input of the --preferred-protocol parameter.
#
# param string The protocol ("ipv4", "ipv6", "none").
function sanitize_preferred_protocol()
{
protocol=`echo $1 | awk '{print tolower($0)}'`
if [ "$protocol" == "ipv4" ] || [ "$protocol" == "ipv6" ] || [ "$protocol" == "none" ]; then
echo $protocol
else
echo "ipv4"
fi
}

#=======================================================================================================================
# Functions | Initial checks
#=======================================================================================================================

# Checks if current user has sudo privileges.
# Terminates if it fails.
#
# return void
function check_root()
{
if [ `id -u` -ne 0 ]; then
die "You need super-user privileges to install KeyHelp"
fi
}

# Checks if a given OS string is supported by KeyHelp.
# Terminates if it fails.
#
# return void
function check_supported_os()
{
local os=`get_os`

# Beta version
#if [ "$os" = "Ubuntu_24.04" ] && [ "$NON_INTERACTIVE" = false ]; then
# echo
# warn "The support for Ubuntu 24.04 is currently in closed BETA state. It should only be used for testing purpose."
# warn "You will not be able to proceed if you are not a BETA tester."
# echo
# echo "[CRTL] + [C] to cancel | [ENTER] to continue"
# read
#fi

if [ "$os" != 'Ubuntu_20.04' ] &&
[ "$os" != 'Ubuntu_22.04' ] &&
[ "$os" != 'Ubuntu_24.04' ] &&
[ "$os" != 'Debian_11' ] &&
[ "$os" != 'Debian_12' ] &&
[ "$os" != 'Debian_13' ]; then

die "Unsupported OS"
fi
}

# Checks if the system is running with supported architectures.
# Terminates if it fails.
#
# return void
function check_supported_architecture()
{
if [ `uname -m` = 'x86_64' ]; then
return
elif [ `uname -m` = 'aarch64' ] && [ `dpkg --print-architecture` = 'arm64' ]; then
return
fi

die "Unsupported system architecture"
}

# Checks if dpkg/apt is running.
#
# return void
function check_dpkg()
{
declare -a lock_paths=("/var/lib/dpkg/lock" "/var/lib/dpkg/lock-frontend" "/var/lib/apt/lists/lock")

for path in ${lock_paths[@]}; do
lsof $path >/dev/null 2>&1

if [ $? = 0 ]; then
die "The package management system (dpkg/apt) is being locked by another process.\nPlease try again later or release the lock (not recommended)."
fi
done
}

#=======================================================================================================================
# Functions | Receive system information
#=======================================================================================================================

# Returns the current OS name and version.
#
# return string Format eg. "Ubuntu_24.04", "Debian_11"
function get_os()
{
local distro
local version

if [ `uname -s` = 'Linux' ] && [ -e '/etc/debian_version' ]; then
if [ -e '/etc/lsb-release' ]; then
# Mostly Ubuntu, but also Debian can have it too.
. /etc/lsb-release
distro="$DISTRIB_ID"
version="$DISTRIB_RELEASE"
else
distro="Debian"
version=`head -n 1 /etc/debian_version`
version=`echo $version | grep -o "^[0-9]\+"`
fi
else
echo 'false'
fi

echo ${distro}_$version
}


# Reads the main version number of installed PHP.
#
# return string Main version number (eg 7.0, 7.4)
function get_php_version()
{
local php=`php -v | grep -oP "^PHP\s[0-9]\.[0-9]*" | cut -d" " -f2`

if [ -z "$php" ]; then
die "PHP version check failed"
fi

echo $php
}

#=======================================================================================================================
# Functions | System setup
#=======================================================================================================================

# Creates the 'keyhelp' system user.
#
# return void
function create_keyhelp_user()
{
local shell="/bin/false"

if ! id -u $USERNAME >/dev/null 2>&1
then
useradd --home-dir $HOME_DIR -M --shell $shell $USERNAME
else
usermod --home $HOME_DIR --shell $shell $USERNAME
fi

mkdir -p $HOME_DIR
}

# Installs ca-certificates bundle to prevent failing wget calls (otherwise use "no-check-certificate").
#
# return void
function install_ca_certificates()
{
apt-get -y -qq install ca-certificates
}

# Installs PHP/-packages, needed for launching the KeyHelp installation routine.
#
# return void
function install_php()
{
rm -rf /var/lib/apt/lists/*
apt-get -qq update

case `get_os` in
"Ubuntu_20.04")
apt-get install -y -qq php php7.4-mysql php-intl php-mbstring php7.4-readline
phpenmod mbstring
;;
"Ubuntu_22.04"|"Ubuntu_24.04"|"Debian_11"|"Debian_12"|"Debian_13")
apt-get install -y -qq php php-mysql php-intl php-mbstring php-readline
phpenmod mbstring
;;
*)
die "Unsupported OS"
;;
esac

apt-get -y autoremove
apt-get clean
}

# Installs ionCube and activate it.
#
# return void
function install_ioncube()
{
local archive_path="/tmp/ioncube.tar.gz"
local ioncube_ini_path="/etc/php/`get_php_version`/mods-available/ioncube.ini"

declare -a locations=("https://install.keyhelp.de/files/tools/ioncube")

# Download
case `dpkg --print-architecture` in
"amd64")
local download_file="ioncube_loaders_lin_x86-64.tar.gz"
;;
"arm64")
local download_file="ioncube_loaders_lin_aarch64.tar.gz"
;;
*)
die "Unsupported system architecture"
;;
esac

for location in "${locations[@]}"
do
local download_url="$location/$download_file"
wget --prefer-family="$PREFERRED_PROTOCOL" --quiet --show-progress --no-check-certificate --output-document $archive_path $download_url
local exit_code=$?

if [ $exit_code -eq 0 ]; then
break
else
warn "Primary download location is not available, trying alternative URL..."
fi
done

# Extract
local extract_to="/usr/local"
mkdir -p $extract_to
tar -xzf $archive_path -C $extract_to

local so_file="$extract_to/ioncube/ioncube_loader_lin_`get_php_version`.so"
chown --recursive root:root $(dirname $so_file)

# Setup ioncube.ini.
echo "; configuration by KeyHelp" > $ioncube_ini_path
echo "; priority=01" >> $ioncube_ini_path
echo "zend_extension=$so_file" >> $ioncube_ini_path

# Enable ioncube.ini
# First perform dismod, in case the installer is called multiple times.
phpdismod ioncube
phpenmod ioncube
}

# Downloads KeyHelp, extract content to INSTALL_DIR
#
# return void
function download_keyhelp()
{
#
# !!! wget with --no-check-certificate
# Debian doesn't know Let's Encrypt CA?
#

if [ "$DEBUG" = 'true' ]; then
local release_channel='&release_channel=debug';
else
local release_channel='';
fi

local url="https://install.keyhelp.de/get_version.php?encode=plain&php_version=`get_php_version`$release_channel"

declare -A release_info
release_info[error]=true
release_info[error_msg]='Invalid response. / Server not reachable.'

# Contact server and load release info.
while read line
do
local key=`echo $line | awk -F= '{print $1}'`
local value=`echo $line | awk -F= '{print $2}'`

if [ -n "$key" ]; then
release_info[$key]=$value
fi
done < <(wget --prefer-family="$PREFERRED_PROTOCOL" --quiet --no-check-certificate -O- $url)

if [ ${release_info[error]} == true ]; then
die "An error occurred: ${release_info[error_msg]}"
fi

# Download
local archive_path="/tmp/keyhelp.tar.gz"
wget --prefer-family="$PREFERRED_PROTOCOL" --quiet --show-progress --no-check-certificate --output-document $archive_path ${release_info[download]}

# Check checksum.
if [ -n ${release_info[sha1]} ]; then

local sha1_is=`sha1sum /tmp/keyhelp.tar.gz | cut -d ' ' -f 1`

if [ "${release_info[sha1]}" != "$sha1_is" ]; then
die 'Checksum check failed - please try again / contact support!'
fi

else
warn 'Skipping checksum check, sha1 not defined.'
fi

# Extract.
mkdir -p $INSTALL_DIR
rm -rf $INSTALL_DIR/*
tar -xzf $archive_path -C $INSTALL_DIR
chmod 0755 $INSTALL_DIR # after un-tar, chmod will be 0775

}

# Updates the folder permissions for the KeyHelp home directory.
#
# return void
function update_folder_permissions()
{
chown --recursive $USERNAME:$USERNAME $HOME_DIR

# If this script was called accidently with an existing KeyHelp installation and www-data already exists.
if id -u 'www-data' >/dev/null 2>&1; then
chown $USERNAME:www-data $HOME_DIR
fi
}


#=======================================================================================================================
# Program
#=======================================================================================================================

parse_args $ARGV

print_headline "You are about to install KeyHelp.";

print_headline "Running system checks...";
check_root
check_supported_os
check_supported_architecture
check_dpkg
update_preferred_protocol

print_headline "Installing certificates..."
install_ca_certificates

print_headline "Installing PHP..."
install_php

print_headline "Installing KeyHelp..."
create_keyhelp_user
download_keyhelp
update_folder_permissions

print_headline "Installing ionCube..."
install_ioncube

print_headline "Run $INSTALL_FILE --installer-version $INSTALLER_VERSION $PASS_ARGV" | sed 's/\(admin-password[= ]\)[^ ]*\( .*\)/\1\*\*\*\*\*\*\2/'
php $INSTALL_FILE --installer-version $INSTALLER_VERSION $PASS_ARGV