commit 0e912c8ea8ca5d7b411c46fbfd4c186272f653d9 Author: Christoph Date: Sat Mar 15 10:55:15 2025 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..319bff8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ + +# - common +*.log +*.swp +conf/*.conf + +crontab-* + +temporary-login-credentials.txt diff --git a/add-cron-for-checking-cert.sh b/add-cron-for-checking-cert.sh new file mode 100755 index 0000000..f163673 --- /dev/null +++ b/add-cron-for-checking-cert.sh @@ -0,0 +1,228 @@ +#!/usr/bin/env bash + +script_name="$(basename $(realpath $0))" +working_dir="$(dirname $(realpath $0))" + +#conf_file="${working_dir}/conf/${script_name%%.*}.conf" +conf_file="${working_dir}/conf/keycloak.conf" + +LOCK_DIR="/tmp/$(basename $0).$$.LOCK" +log_file="${LOCK_DIR}/${script_name%%.*}.log" + +backup_date="$(date +%Y-%m-%d-%H%M)" + +crontab_backup_file="${script_dir}/crontab-root-${backup_date}" + + +# ---------- +# Base Function(s) +# ---------- + +clean_up() { + + if [[ -f "$crontab_backup_file" ]]; then + echononl "Reenable previously saved crontab from '$(basename "${crontab_backup_file}")'.." + crontab $_backup_crontab_file > $log_file 2>&1 + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + error "$(cat $log_file)" + fi + fi + + # Perform program exit housekeeping + rm -rf "$LOCK_DIR" + blank_line + exit $1 +} + +echononl(){ + if $terminal ; then + echo X\\c > /tmp/shprompt$$ + if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then + echo -e -n " $*\\c" 1>&2 + else + echo -e -n " $*" 1>&2 + fi + rm /tmp/shprompt$$ + fi +} + +fatal(){ + if $terminal ; then + echo "" + echo -e " [ \033[31m\033[1mFatal\033[m ] $*" + echo "" + echo -e " \033[1mScript is canceled\033[m.." + echo "" + else + echo "" + echo -e " [ Fatal ] $*" + echo "" + echo -e " Script is canceled.." + echo "" + fi + if [[ -f "$crontab_backup_file" ]]; then + echononl "Reenable previously saved crontab from '$(basename "${crontab_backup_file}")'.." + crontab $_backup_crontab_file > $log_file 2>&1 + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + error "$(cat $log_file)" + fi + fi + + rm -rf $LOCK_DIR + exit 1 +} + +error (){ + echo "" + if $terminal ; then + echo -e " [ \033[31m\033[1mError\033[m ] $*" + else + echo " [ Error ] $*" + fi + echo "" +} + +warn (){ + echo "" + if $terminal ; then + echo -e " [ \033[33m\033[1mWarning\033[m ] $*" + else + echo " [ Error ] $*" + fi + echo "" +} + +info (){ + if $terminal ; then + echo "" + if $terminal ; then + echo -e " [ \033[32m\033[1mInfo\033[m ] $*" + else + echo " [ Info ] $*" + fi + echo "" + fi +} + + +echo_ok() { + if $terminal ; then + echo -e "\033[85G[ \033[32mok\033[m ]" + fi +} +echo_failed(){ + if $terminal ; then + echo -e "\033[85G[ \033[1;31mfailed\033[m ]" + fi +} +echo_skipped() { + if $terminal ; then + echo -e "\033[85G[ \033[33m\033[1mskipped\033[m ]" + fi +} +echo_wait(){ + if $terminal ; then + echo -en "\033[85G[ \033[5m\033[1m..\033[m ]" + fi +} + +trim() { + local var="$*" + var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters + var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters + echo -n "$var" +} + +blank_line() { + if $terminal ; then + echo "" + fi +} + +detect_os () { + + if $(which lsb_release > /dev/null 2>&1) ; then + + DIST="$(lsb_release -i | awk '{print tolower($3)}')" + DIST_VERSION="$(lsb_release -r | awk '{print tolower($2)}')" + DIST_CODENAME="$(lsb_release -c | awk '{print tolower($2)}')" + + if [[ "$DIST" = "debian" ]]; then + if $(echo "$DIST_VERSION" | grep -q '\.') ; then + DIST_VERSION=$(echo "$DIST_VERSION" | cut --delimiter='.' -f1) + fi + fi + + elif [[ -e "/etc/os-release" ]]; then + + . /etc/os-release + + DIST=$ID + DIST_VERSION=${VERSION_ID} + + fi + + # remove whitespace from DIST and DIST_VERSION + DIST="${DIST// /}" + DIST_VERSION="${DIST_VERSION// /}" + +} + +# ---------- +# - Jobhandling +# ---------- + +# - Run 'clean_up' for signals SIGHUP SIGINT SIGTERM +# - +trap clean_up SIGHUP SIGINT SIGTERM + +# - Create lock directory '$LOCK_DIR" +# +mkdir "$LOCK_DIR" + +blank_line + +echononl "Add a cronjob for checking cert.." +if [[ -f "/var/spool/cron/crontabs/root" ]] ; then + if ! grep -i -E "/root/bin/monitoring/check_cert_for_keycloak.sh" /var/spool/cron/crontabs/root > /dev/null 2>&1; then + installation_failed=false + crontab -l > /tmp/tmp_crontab 2> $log_file + if [[ "$?" -ne 0 ]] ; then + installation_failed=true + fi + + cat << EOF >> /tmp/tmp_crontab 2>> $log_file + +# Check if cert for Keycloak service is up-to-date +# +51 05 * * * /root/bin/monitoring/check_cert_for_keycloak.sh +EOF + if [[ "$?" -ne 0 ]] ; then + installation_failed=true + fi + crontab /tmp/tmp_crontab > /dev/null 2>> $log_file + if [[ "$?" -ne 0 ]] ; then + installation_failed=true + fi + rm /tmp/tmp_crontab > /dev/null 2>> $log_file + if [[ "$?" -ne 0 ]] ; then + installation_failed=true + fi + if ! $installation_failed ; then + echo_ok + else + echo_failed + error "Adding cronjob for checking cert failed!" + fi + else + echo_skipped + fi +fi + +clean_up 0 diff --git a/conf/keycloak.conf.sample b/conf/keycloak.conf.sample new file mode 100644 index 0000000..738dddd --- /dev/null +++ b/conf/keycloak.conf.sample @@ -0,0 +1,108 @@ +#-------------------------------------- +# Settings for Keycloak Install scripts +#-------------------------------------- + +# FQHN_HOSTNAME +# +# The full qualified histname under which bbb service +# is available +# +# Defaults to full qualified hostname of the system +# +#FQHN_HOSTNAME="" + + +# KEYCLOAK_USER +# +# The user under which Keycloak service is running. +# +# Defaults to: KEYCLOAK_USER="keycloak" +# +#KEYCLOAK_USER="" + + +# KEYCLOAK_GROUP +# +# The group of the keycloak user. +# +# +#KEYCLOAK_GROUP="" + + +# KEYCLOAK_BASE_INSTALL_PATH +# +# Base directora in which the keycloak installation lives. +# +# Defaults to: KEYCLOAK_BASE_INSTALL_PATH="/opt" +# +#KEYCLOAK_BASE_INSTALL_PATH="/opt" + + +# DB_TYPE +# +# Type of Keycloak database +# +# Possible values are 'pgsql' (PostgeSQL) or 'mysql' (MySQL) +# +# Defaults to POSTFIX_DB_TYPE="pgsql" +# +# DB_TYPE="pgsql" + + +# MYSQL_CREDENTIAL_ARGS +# +# Giving password on command line is insecure an sind mysql 5.5 +# you will get a warning doing so. +# +# Reading username/password fro file ist also possible, using MySQL/MariaDB +# commandline parameter '--defaults-file'. +# +# Since Version 5.6, that method is considered as insecure. +# To avoid giving the password on command line, we use an +# encrypted option file +# +# Create (encrypted) option file: +# $ mysql_config_editor set --login-path=local --socket=/var/run/mysqld/mysqld.sock --user=backup --password +# $ Password: +# +# Use of option file: +# $ mysql --login-path=local ... +# +# Example +# MYSQL_CREDENTIAL_ARGS="--login-path=local" +# MYSQL_CREDENTIAL_ARGS="--defaults-file=/etc/mysql/debian.cnf" (Debian default) +# MYSQL_CREDENTIAL_ARGS="--defaults-file=/usr/local/mysql/sys-maint.cnf" +# +# # MariaDB 10.4.x +# MYSQL_CREDENTIAL_ARGS="-u root -S /tmp/mysql.sock" +# +# Defaults to MYSQL_CREDENTIAL_ARGS="-u root -S /tmp/mysql.sock" +# +#MYSQL_CREDENTIAL_ARGS="--login-path=local" + + +# DB_NAME +# +# Database Name of Mattemost's Database +# +# Defaults to: DB_NAME="keycloak" +# +#DB_NAME="keycloak" + +# DB_USER +# +# Database USER of Mattemost's Database +# +# Defaults to: DB_USER="keycloak" +# +#DB_USER="keycloak" + + +# DB_PASS +# +# Database Password used for Mattemost's Database +# +# Defaults to a random created one. +# +#DB_PASS="" + diff --git a/install-keycloak.sh b/install-keycloak.sh new file mode 100755 index 0000000..a152782 --- /dev/null +++ b/install-keycloak.sh @@ -0,0 +1,1903 @@ +#!/usr/bin/env bash + +script_name="$(basename $(realpath $0))" +working_dir="$(dirname $(realpath $0))" + +#conf_file="${working_dir}/conf/${script_name%%.*}.conf" +conf_file="${working_dir}/conf/keycloak.conf" + +LOCK_DIR="/tmp/$(basename $0).$$.LOCK" +log_file="${LOCK_DIR}/${script_name%%.*}.log" + +backup_date="$(date +%Y-%m-%d-%H%M)" + +crontab_backup_file="${working_dir}/crontab-root-${backup_date}" + + +# ---------- +# Base Function(s) +# ---------- + +clean_up() { + + # Perform program exit housekeeping + rm -rf "$LOCK_DIR" + blank_line + exit $1 +} + +echononl(){ + if $terminal ; then + echo X\\c > /tmp/shprompt$$ + if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then + echo -e -n " $*\\c" 1>&2 + else + echo -e -n " $*" 1>&2 + fi + rm /tmp/shprompt$$ + fi +} + +fatal(){ + if $terminal ; then + echo "" + echo -e " [ \033[31m\033[1mFatal\033[m ] $*" + echo "" + echo -e " \033[1mScript is canceled\033[m.." + echo "" + else + echo "" + echo -e " [ Fatal ] $*" + echo "" + echo -e " Script is canceled.." + echo "" + fi + if [[ -f "${crontab_backup_file}" ]]; then + echononl "Reenable previously saved crontab from '$(basename "${crontab_backup_file}")'.." + crontab ${crontab_backup_file} > /dev/null 2>&1 + if [[ $? -eq 0 ]]; then + echo_ok + rm -f ${crontab_backup_file} > /dev/null 2>&11 + else + echo_failed + error "$(cat $log_file)" + fi + fi + + rm -rf $LOCK_DIR + exit 1 +} + +error (){ + echo "" + if $terminal ; then + echo -e " [ \033[31m\033[1mError\033[m ] $*" + else + echo " [ Error ] $*" + fi + echo "" +} + +warn (){ + echo "" + if $terminal ; then + echo -e " [ \033[33m\033[1mWarning\033[m ] $*" + else + echo " [ Error ] $*" + fi + echo "" +} + +info (){ + if $terminal ; then + echo "" + if $terminal ; then + echo -e " [ \033[32m\033[1mInfo\033[m ] $*" + else + echo " [ Info ] $*" + fi + echo "" + fi +} + + +echo_ok() { + if $terminal ; then + echo -e "\033[85G[ \033[32mok\033[m ]" + fi +} +echo_failed(){ + if $terminal ; then + echo -e "\033[85G[ \033[1;31mfailed\033[m ]" + fi +} +echo_skipped() { + if $terminal ; then + echo -e "\033[85G[ \033[33m\033[1mskipped\033[m ]" + fi +} +echo_wait(){ + if $terminal ; then + echo -en "\033[85G[ \033[5m\033[1m..\033[m ]" + fi +} + +trim() { + local var="$*" + var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters + var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters + echo -n "$var" +} + +blank_line() { + if $terminal ; then + echo "" + fi +} + +detect_os () { + + if $(which lsb_release > /dev/null 2>&1) ; then + + DIST="$(lsb_release -i | awk '{print tolower($3)}')" + DIST_VERSION="$(lsb_release -r | awk '{print tolower($2)}')" + DIST_CODENAME="$(lsb_release -c | awk '{print tolower($2)}')" + + if [[ "$DIST" = "debian" ]]; then + if $(echo "$DIST_VERSION" | grep -q '\.') ; then + DIST_VERSION=$(echo "$DIST_VERSION" | cut --delimiter='.' -f1) + fi + fi + + elif [[ -e "/etc/os-release" ]]; then + + . /etc/os-release + + DIST=$ID + DIST_VERSION=${VERSION_ID} + + fi + + # remove whitespace from DIST and DIST_VERSION + DIST="${DIST// /}" + DIST_VERSION="${DIST_VERSION// /}" + +} + +# Funktion zur Generierung eines zufälligen Zeichens aus einer gegebenen Zeichenmenge +random_char() { + local chars="$1" + echo -n "${chars:RANDOM%${#chars}:1}" +} + +# Funktion zur Generierung eines zufälligen Strings mit den angegebenen Anforderungen +generate_random_string() { + local length="$1" + + # Überprüfen, ob die Länge größer als 8 ist + if [[ "$length" -le 8 ]]; then + echo "Fehler: Die Länge muss größer als 8 Zeichen sein." + return 1 + fi + + # Zeichenmengen + local lower="abcdefghijklmnopqrstuvwxyz" + local upper="ABCDEFGHIJKLMNOPQRSTUVWXYZ" + local digits="0123456789" + #local special="!@#$%^&*()_+-=[]{}|;:,.<>?/" + local special="_+--//...." + + # Generiere mindestens ein Zeichen aus jeder Kategorie + local random_string=$(random_char "$lower") + random_string+=$(random_char "$upper") + random_string+=$(random_char "$digits") + random_string+=$(random_char "$special") + random_string+=$(random_char "$special") + + # Fülle den Rest der Zeichenkette mit zufälligen Zeichen aus allen Kategorien + local all_chars="$lower$upper$digits$special" + for (( i=${#random_string}; i ${log_file} 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi +else + echo_skipped + warn "No configuration file '$conf_file' present.\n + Loading default values.." +fi + +[[ -n "${FQHN_HOSTNAME}" ]] && DEFAULT_FQHN_HOSTNAME="${FQHN_HOSTNAME}" + +if [[ -n "$DB_TYPE" ]] ; then + if [[ "${DB_TYPE,,}" = "postgres" ]] || [[ "${DB_TYPE,,}" = "postgresql" ]] || [[ "${DB_TYPE,,}" = "pgsql" ]] || [[ "${DB_TYPE,,}" = "psql" ]] ; then + + DEFAULT_DB_TYPE=pgsql + + elif [[ "${DB_TYPE,,}" = "mysql" ]] ; then + + DEFAULT_DB_TYPE=mysql + + else + fatal "Wrong or empty Database Type (DB_TYPE) - must be 'mysql' or 'pgsql'." + fi +fi + +[[ -n "${DB_NAME}" ]] && DEFAULT_DB_NAME="${DB_NAME}" +[[ -n "${DB_USER}" ]] && DEFAULT_DB_NAME="${DB_USER}" +[[ -n "${DB_PASS}" ]] && DEFAULT_DB_PASS="${DB_PASS}" + +[[ -n "${KEYCLOAK_USER}" ]] && DEFAULT_KEYCLOAK_USER="${KEYCLOAK_USER}" +if [[ -n "${KEYCLOAK_GROUP}" ]]; then + DEFAULT_KEYCLOAK_GROUP="${KEYCLOAK_GROUP}" +else + DEFAULT_KEYCLOAK_GROUP="$DEFAULT_KEYCLOAK_USER" +fi + +[[ -n "${KEYCLOAK_BASE_INSTALL_PATH}" ]] && DEFAULT_KEYCLOAK_BASE_INSTALL_PATH="${KEYCLOAK_BASE_INSTALL_PATH}" + +echo -e "\033[32m--\033[m" +echo "" +echo "Version Number of Keycloak Server to install" +echo "" +echo " see: https://keycloak.org/downloads" +echo "" +echo "" +KEYCLOAK_VERSION= +while [ "X$KEYCLOAK_VERSION" = "X" ] +do + echononl "KEYCLOAK Server Version: " + read KEYCLOAK_VERSION + if [ "X$KEYCLOAK_VERSION" = "X" ]; then + echo -e "\n\t\033[33m\033[1mA Version number is required!\033[m\n" + fi +done +DOWNLOAD_ARCHIVE="keycloak-${KEYCLOAK_VERSION}.tar.gz" +DOWNLOAD_URL="https://github.com/keycloak/keycloak/releases/download/${KEYCLOAK_VERSION}/${DOWNLOAD_ARCHIVE}" + + +KEYCLOAK_BASE_INSTALL_PATH= +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Specify the base directory in which keycloak is to be installed." +echo "" +while [[ "X${KEYCLOAK_BASE_INSTALL_PATH}" = "X" ]]; do + echononl "Base directory for keycloak installation [${DEFAULT_KEYCLOAK_BASE_INSTALL_PATH}]: " + read KEYCLOAK_BASE_INSTALL_PATH + if [[ "X${KEYCLOAK_BASE_INSTALL_PATH}" = "X" ]]; then + KEYCLOAK_BASE_INSTALL_PATH="${DEFAULT_KEYCLOAK_BASE_INSTALL_PATH}" + fi + if [[ ! -d "${KEYCLOAK_BASE_INSTALL_PATH}" ]] ; then + echo -e "\n\tGiven directory \033[33m\033[1m${KEYCLOAK_BASE_INSTALL_PATH}\033[m does not exist!.\n" + KEYCLOAK_BASE_INSTALL_PATH="" + fi +done +KEYCLOAK_INSTALL_DIR="${KEYCLOAK_BASE_INSTALL_PATH}/keycloak-${KEYCLOAK_VERSION}" + +if [[ -h "${KEYCLOAK_BASE_INSTALL_PATH}/keycloak" ]] ; then + OLD_INSTALL_DIR="$(realpath "${KEYCLOAK_BASE_INSTALL_PATH}/keycloak")" + OLD_VERSION="$(basename "${OLD_INSTALL_DIR}" | sed "s/^keycloak-//")" +fi + +echo "" +echo "--" +echo "" +echo "Enter user and group for Keycloak Service." +echo "" +KEYCLOAK_USER= +while [ "X${KEYCLOAK_USER}" = "X" ] +do + echononl "KEYCLOAK user [${DEFAULT_KEYCLOAK_USER}]: " + read KEYCLOAK_USER + if [ "X${KEYCLOAK_USER}" = "X" ]; then + KEYCLOAK_USER=$DEFAULT_KEYCLOAK_USER + fi +done +KEYCLOAK_GROUP= +while [ "X${KEYCLOAK_GROUP}" = "X" ] +do + echononl "KEYCLOAK group [$DEFAULT_KEYCLOAK_GROUP]: " + read KEYCLOAK_GROUP + if [ "X${KEYCLOAK_GROUP}" = "X" ]; then + KEYCLOAK_GROUP=$DEFAULT_KEYCLOAK_GROUP + fi +done + + +DB_TYPE="" +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Choose Database Type" +echo "" +if [[ "$DEFAULT_DB_TYPE" = "mysql" ]]; then + echo -e "\033[3G\033[37m\033[1m[1] MySQL\033[m" +else + echo -e "\033[3G[1] MySQL" +fi +if [[ "$DEFAULT_DB_TYPE" = "pgsql" ]] ; then + echo -e "\033[3G[2] \033[37m\033[1mPostgeSQL\033[m" +else + echo -e "\033[3G[2] PostgeSQL" +fi +echo "" +echo "Type a number or press to choose highlighted value" +echo "" +echononl "Eingabe: " +while [ "$DB_TYPE" != "mysql" -a "$DB_TYPE" != "pgsql" ]; do + read OPTION + case $OPTION in + 1) + DB_TYPE="mysql" + ;; + 2) + DB_TYPE="pgsql" + ;; + '') DB_TYPE=$DEFAULT_DB_TYPE + ;; + *) + echo "" + echo -e "\033[3GFalsche Eingabe ! [ 1 = MySQL ; 2 = PostgreSQL ] or type " + echo "" + echononl "Eingabe: " + ;; + esac +done + + +DB_NAME= +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Enter Database Name used by Keycloak Service" +echo "" +if [[ -n "$DEFAULT_DB_NAME" ]]; then + while [[ "X${DB_NAME}" = "X" ]]; do + echononl "Database Name [${DEFAULT_DB_NAME}]: " + read DB_NAME + if [[ "X${DB_NAME}" = "X" ]]; then + DB_NAME=$DEFAULT_DB_NAME + fi + done +else + while [[ "X${DB_NAME}" = "X" ]]; do + echononl "Database Name: " + read DB_NAME + if [[ "X${DB_NAME}" = "X" ]]; then + echo -e "\n\t\033[33m\033[1m Database Name is reqired\033[m\n" + fi + done +fi + +DB_USER= +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Enter Database User used by Keycloak Service" +echo "" +if [[ -n "$DEFAULT_DB_USER" ]]; then + while [[ "X${DB_USER}" = "X" ]]; do + echononl "Database User [${DEFAULT_DB_USER}]: " + read DB_USER + if [[ "X${DB_USER}" = "X" ]]; then + DB_USER=$DEFAULT_DB_USER + fi + done +else + while [[ "X${DB_USER}" = "X" ]]; do + echononl "Database User: " + read DB_USER + if [[ "X${DB_USER}" = "X" ]]; then + echo -e "\n\t\033[33m\033[1m Database User is reqired\033[m\n" + fi + done +fi + +DB_PASS= +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Enter Database Password used by Keycloak Service" +echo "" +if [[ -n "$DEFAULT_DB_PASS" ]]; then + while [[ "X${DB_PASS}" = "X" ]]; do + echononl "Database Password [${DEFAULT_DB_PASS}]: " + read DB_PASS + if [[ "X${DB_PASS}" = "X" ]]; then + DB_PASS=$DEFAULT_DB_PASS + fi + done +else + while [[ "X${DB_PASS}" = "X" ]]; do + echononl "Database Password: " + read DB_PASS + if [[ "X${DB_PASS}" = "X" ]]; then + echo -e "\n\t\033[33m\033[1m Database Password is reqired\033[m\n" + fi + done +fi + + +if [[ "$DB_TYPE" = "mysql" ]] ; then + + [[ -n "${MYSQL_CREDENTIAL_ARGS}" ]] && DEFAULT_MYSQL_CREDENTIAL_ARGS="${MYSQL_CREDENTIAL_ARGS}" + + # Test database connection + # + if ! $(mysql ${DEFAULT_MYSQL_CREDENTIAL_ARGS} -N -s -e 'quit' > /dev/null 2>&1) ; then + + echo "" + echo -e "\033[32m--\033[m" + echo "" + echo "Insert Database Credentials.." + echo "" + + echo -e " A string containing credentials to establish a database connection. This character string + can consist of username and password, but also of login information stored in the file system + or the database itself. + + Example: + + \033[33m-u root -p''\033[m + \033[33m--login-path=local\033[m + \033[33m--login-path=mysql-5.7\033[m + \033[33m--defaults-file=/usr/local/mysql/sys-maint.cnf\033[m + \033[33m-u root -S /run/mysqld/mysqld.sock\033[m" + + echo "" + + echononl "Database Credentials: " + read MYSQL_CREDENTIAL_ARGS + while [ "X${MYSQL_CREDENTIAL_ARGS}" = "X" ] ; do + echo -e "\n\t\033[33m\033[1mEingabe erforderlich.\033[m\n" + echononl "Database Credentials: " + read MYSQL_CREDENTIAL_ARGS + done + + else + + MYSQL_CREDENTIAL_ARGS="${DEFAULT_MYSQL_CREDENTIAL_ARGS}" + + fi + + if ! $(mysql ${MYSQL_CREDENTIAL_ARGS} -N -s -e 'quit' > /dev/null 2>&1) ; then + fatal "Connection to MySQL Service failed.!" + fi + +fi + + +FQHN_HOSTNAME= +echo "" +echo -e "\033[32m--\033[m" +echo "" +echo "Insert full qualified hostname for Keycloak Service" +echo "" +if [[ -n "${DEFAULT_FQHN_HOSTNAME}" ]]; then + while [[ "X${FQHN_HOSTNAME}" = "X" ]]; do + echononl "Full qualified hostname [${DEFAULT_FQHN_HOSTNAME}]: " + read FQHN_HOSTNAME + if [[ "X${FQHN_HOSTNAME}" = "X" ]]; then + FQHN_HOSTNAME=$DEFAULT_FQHN_HOSTNAME + fi + if [[ ! ${FQHN_HOSTNAME} =~ \. ]]; then + echo -e "\n\tGiven Host \033[33m\033[1m${FQHN_HOSTNAME}\033[m seems not to be a full qualified hostname.\n" + FQHN_HOSTNAME="" + fi + done +else + while [[ "X${FQHN_HOSTNAME}" = "X" ]]; do + echononl "Full qualified hostname: " + read FQHN_HOSTNAME + if [[ "X${FQHN_HOSTNAME}" = "X" ]]; then + echo -e "\n\t\033[33m\033[1mFull qualified hostname is reqired\033[m\n" + fi + if [[ ! ${FQHN_HOSTNAME} =~ \. ]]; then + echo -e "\n\tGiven Host \033[33m\033[1m${FQHN_HOSTNAME}\033[m seems not to be a full qualified hostname.\n" + FQHN_HOSTNAME="" + fi + done +fi +HOSTNAME="${FQHN_HOSTNAME%%.*}" + + +echo "" +echo "" +echo -e "\t\033[32mStart install script for Keycloak Service with the following parameters\033[m" +echo "" +echo -e "\t(New) Keycloak Server Version...: \033[33m\033[1m${KEYCLOAK_VERSION}\033[m" +echo "" +if [[ -n "${OLD_VERSION}" ]] ; then + echo -e "\tCurrent (old) Keycloak Version..: ${OLD_VERSION}" +else + echo -e "\tCurrent (old) Keycloak Version..: \033[33mkeycloak is currently NOT installed\033[m" +fi +echo "" +echo -e "\tFull qualified Hostname.........: ${FQHN_HOSTNAME}" +echo -e "\tHostname........................: ${HOSTNAME}" +echo "" +echo -e "\tKeycloak user...................: ${KEYCLOAK_USER}" +echo -e "\tKeycloak group..................: ${KEYCLOAK_GROUP}" +echo "" +echo -e "\tKeycloak base install dir.......: ${KEYCLOAK_BASE_INSTALL_PATH}" +echo -e "\tKeycloak install dir............: ${KEYCLOAK_INSTALL_DIR}" +echo "" +echo -e "\tDownload archive................: ${DOWNLOAD_ARCHIVE}" +echo -e "\tDownload URL....................: ${DOWNLOAD_URL}" +echo "" +if [[ "${DB_TYPE}" = "pgsql" ]] ; then + echo -e "\tDatabase Type...................: PostgreSQL" +else + echo -e "\tDatabase Type...................: MySQL" +fi +echo "" +if [[ "${DB_TYPE}" = "mysql" ]]; then + echo -e "\tMYSQL_CREDENTIAL_ARGS...........: ${MYSQL_CREDENTIAL_ARGS}" + echo "" +fi +echo -e "\tDatabase Name...................: ${DB_NAME}" +echo -e "\tDatabase User...................: ${DB_USER}" +echo -e "\tDatabase Password...............: ${DB_PASS}" +echo "" +echononl "einverstanden (yes/no): " +read OK +OK=${OK,,} +while [ "X$OK" != "Xyes" -a "X$OK" != "Xno" ]; do + echononl "Wrong entry! [yes/no]: " + read OK + OK=${OK,,} +done +[ $OK = "yes" ] || fatal Repeat with other settings.. + +cat < $conf_file +#-------------------------------------- +# Settings for Keycloak Install scripts +#-------------------------------------- + +# FQHN_HOSTNAME +# +# The full qualified histname under which bbb service +# is available +# +# Defaults to full qualified hostname of the system +# +FQHN_HOSTNAME="${FQHN_HOSTNAME}" + + +# KEYCLOAK_USER +# +# The user under which Keycloak service is running. +# +# Defaults to: KEYCLOAK_USER="keycloak" +# +#KEYCLOAK_USER="" +KEYCLOAK_USER="${KEYCLOAK_USER}" + + +# KEYCLOAK_GROUP +# +# The group of the keycloak user. +# +# +#KEYCLOAK_GROUP="" +KEYCLOAK_GROUP="${KEYCLOAK_GROUP}" + + +# KEYCLOAK_BASE_INSTALL_PATH +# +# Base directora in which the keycloak installation lives. +# +# Defaults to: KEYCLOAK_BASE_INSTALL_PATH="/opt" +# +#KEYCLOAK_BASE_INSTALL_PATH="/opt" +KEYCLOAK_BASE_INSTALL_PATH="${KEYCLOAK_BASE_INSTALL_PATH}" + + +# DB_TYPE +# +# Type of Keycloak database +# +# Possible values are 'pgsql' (PostgeSQL) or 'mysql' (MySQL) +# +# Defaults to POSTFIX_DB_TYPE="pgsql" +# +# DB_TYPE="pgsql" +DB_TYPE="${DB_TYPE}" + + +# MYSQL_CREDENTIAL_ARGS +# +# Giving password on command line is insecure an sind mysql 5.5 +# you will get a warning doing so. +# +# Reading username/password fro file ist also possible, using MySQL/MariaDB +# commandline parameter '--defaults-file'. +# +# Since Version 5.6, that method is considered as insecure. +# To avoid giving the password on command line, we use an +# encrypted option file +# +# Create (encrypted) option file: +# $ mysql_config_editor set --login-path=local --socket=/var/run/mysqld/mysqld.sock --user=backup --password +# $ Password: +# +# Use of option file: +# $ mysql --login-path=local ... +# +# Example +# MYSQL_CREDENTIAL_ARGS="--login-path=local" +# MYSQL_CREDENTIAL_ARGS="--defaults-file=/etc/mysql/debian.cnf" (Debian default) +# MYSQL_CREDENTIAL_ARGS="--defaults-file=/usr/local/mysql/sys-maint.cnf" +# +# # MariaDB 10.4.x +# MYSQL_CREDENTIAL_ARGS="-u root -S /tmp/mysql.sock" +# +# Defaults to MYSQL_CREDENTIAL_ARGS="-u root -S /tmp/mysql.sock" +# +#MYSQL_CREDENTIAL_ARGS="--login-path=local" +EOF +if [[ "${DB_TYPE}" = "mysql" ]]; then + cat <> ${conf_file} +MYSQL_CREDENTIAL_ARGS="${MYSQL_CREDENTIAL_ARGS}" +EOF +fi + +cat <> ${conf_file} + + +# DB_NAME +# +# Database Name of Mattemost's Database +# +# Defaults to: DB_NAME="keycloak" +# +#DB_NAME="keycloak" +DB_NAME="${DB_NAME}" + +# DB_USER +# +# Database USER of Mattemost's Database +# +# Defaults to: DB_USER="keycloak" +# +#DB_USER="keycloak" +DB_USER="${DB_USER}" + + +# DB_PASS +# +# Database Password used for Mattemost's Database +# +# Defaults to a random created one. +# +#DB_PASS="" +DB_PASS="${DB_PASS}" + +EOF + + +echo +echo -e "\033[37m\033[1mSome checks....\033[m" +echo + +_failed=false +echononl "Check if Nginx Webservice is installed.." +if $(dpkg -s nginx-extras > "$log_file" 2>&1) ; then + nginx_installed=true +elif $(dpkg -s nginx-full > "$log_file" 2>&1) ; then + nginx_installed=true +else + nginx_installed=false +fi +if $nginx_installed ; then + echo -e "\033[85G[ \033[32mYES\033[m ]" +else + echo -e "\033[85G[ \033[1;31mNOT installed\033[m ]" + + echo "" + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + blank_line +fi + + +_failed=false +echononl "Check if OpenJDK is installed.." +if $(dpkg -s default-jdk > "$log_file" 2>&1) ; then + java_jdk_installed=true +elif $(dpkg -s default-jre > "$log_file" 2>&1) ; then + java_installed=true +else + java_installed=false +fi +if $java_installed ; then + echo -e "\033[85G[ \033[32mYES\033[m ]" +else + echo -e "\033[85G[ \033[1;31mNOT installed\033[m ]" + + echo "" + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + blank_line +fi + + +_failed=false +if [[ "${DB_TYPE}" = "mysql" ]]; then + echononl "Check if MySQL Database Service is installed.." + if $(dpkg -s mysql-server > "$log_file" 2>&1) ; then + database_service_installed=true + else + database_service_installed=false + fi + if ${database_service_installed} ; then + echo -e "\033[85G[ \033[32mYES\033[m ]" + else + echo -e "\033[85G[ \033[1;31mNOT installed\033[m ]" + + echo "" + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + blank_line + fi +else + echononl "Check if PostgreSQL Database Service is installed.." + if $(dpkg -s postgresql > "$log_file" 2>&1) ; then + database_service_installed=true + else + database_service_installed=false + fi + if ${database_service_installed} ; then + echo -e "\033[85G[ \033[32mYES\033[m ]" + else + echo -e "\033[85G[ \033[1;31mNOT installed\033[m ]" + + echo "" + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + blank_line + fi +fi + + + +_failed=false +echononl "Check if certificate for '${FQHN_HOSTNAME}' is present.." +if [[ -d "/var/lib/dehydrated/certs/${FQHN_HOSTNAME}" ]] ; then + if [[ -h "/var/lib/dehydrated/certs/${FQHN_HOSTNAME}/fullchain.pem" ]]; then + cert_present=true + else + cert_present=false + fi +else + cert_present=false +fi +if $cert_present ; then + echo -e "\033[85G[ \033[32mYES\033[m ]" +else + echo -e "\033[85G[ \033[1;31mNOT present\033[m ]" +fi + +if ! ${nginx_installed} || ! ${database_service_installed} || ! ${java_installed} ; then + + fatal "Prerequisites are a correct installation of the NGINX Web Service as well + as a correct installation of the ${DB_TYPE} database service. Also Java + is needed (recommended: OpenJDK 17 or 21) + + It's also highly recommended to have a valid certificate for your + FQHN Hostname '${FQHN_HOSTNAME}'." + +elif ! ${cert_present} ; then + + warn "It is highly recommended to have a valid certificate for your FQHN Hostname '${FQHN_HOSTNAME}'." + + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')" + while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + +fi + + +echo +echo -e "\033[37m\033[1mSome pre-installation stuff..\033[m" +echo + +echononl "Backup crontab" +crontab -u root -l > ${crontab_backup_file} 2> $log_file +if [[ "$?" = "0" ]]; then + echo_ok +else + echo_failed + error "$(cat $log_file)" +fi + +echononl "Disable crontab for user root" +crontab -r -u root > ${log_file} 2>&1 +if [[ "$?" = "0" ]]; then + echo_ok +else + echo_failed + error "$(cat $tmp_err_msg)" +fi + + +blank_line + +echononl "Stop Keycloak Service.." +if $(systemctl is-active --quiet service keycloak.service) ; then + systemctl stop keycloak.service > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi +else + echo_skipped +fi + +blank_line + +if [[ "${DB_TYPE}" = "mysql" ]] ; then + + #echononl "Check if Database '${DB_NAME}' already exists.." + #if $(mysql ${MYSQL_CREDENTIAL_ARGS} -e "use ${DB_NAME}" 2> /dev/null) ; then + # database_exists=true + #else + # database_exists=false + #fi + + echononl "Create MySQL Database User '${DB_USER}' with Password '${DB_PASS}'.." + if [[ "$(mysql ${MYSQL_CREDENTIAL_ARGS} -N -s -e \ + "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '${DB_USER}')" 2>/dev/null)" = 1 ]]; then + echo_skipped + else + mysql ${MYSQL_CREDENTIAL_ARGS} -N -s -e \ + "CREATE USER '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}'" > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi + fi + + echononl "Create MySQL Database '${DB_NAME}'.." + if [[ "$(mysql ${MYSQL_CREDENTIAL_ARGS} -N -s -e \ + "SHOW DATABASES LIKE '${DB_NAME}'" 2>/dev/null)" = "${DB_NAME}" ]]; then + + echo_skipped + else + mysql ${MYSQL_CREDENTIAL_ARGS} -N -s -e "CREATE DATABASE ${DB_NAME}" > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi + fi + + echononl "Grant access privileges to the user '${DB_USER}'’." + mysql ${MYSQL_CREDENTIAL_ARGS} -N -s -e \ + "GRANT ALL PRIVILEGES ON ${DB_NAME}.* to '${DB_USER}'@'localhost';" > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi + + echononl "FLUSH PRIVILEGES to dadabase engine .." + mysql ${MYSQL_CREDENTIAL_ARGS} -N -s -e \ + "FLUSH PRIVILEGES" > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi + +else + + # Check if PostgreSQL database '$DB_NAME' exists .. + # + count=$(su - postgres -c "psql -q -A -t -l" | grep -c -e "^$DB_NAME") + if [[ $count -eq 0 ]];then + database_exists=false + else + database_exists=true + fi + +# sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME};" > $log_file 2>&1 +# sudo -u postgres psql -c "CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASS}';" > $log_file 2>&1 +# sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} to ${DB_USER};" > $log_file 2>&1 +# sudo -u postgres psql -c "ALTER DATABASE ${DB_NAME} OWNER TO ${DB_USER};" > $log_file 2>&1 +# sudo -u postgres psql -c "GRANT USAGE, CREATE ON SCHEMA PUBLIC TO ${DB_USER};" > $log_file 2>&1 + + echononl "Create PostgreSQL database '${DB_NAME}'.." + if ${database_exists} ; then + echo_skipped + + + echononl "Backup existing database '${DB_NAME}'" + sudo -u postgres pg_dump -U postgres -d ${DB_NAME} \ + -f /tmp/${DB_NAME}-${OLD_VERSION}-${backup_date}.sql > $log_file 2>&1 + if [[ $? -ne 0 ]] ; then + echo_failed + error "$(cat "$log_file")" + + echo "" + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + + else + echo_ok + fi + + + echononl "Move database backup file to folder '${KEYCLOAK_BASE_INSTALL_PATH}'.." + mv "/tmp/${DB_NAME}-${OLD_VERSION}-${backup_date}.sql" "${KEYCLOAK_BASE_INSTALL_PATH}" > $log_file 2>&1 + if [[ $? -ne 0 ]] ; then + echo_failed + error "$(cat "$log_file")" + + echo "" + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + + else + echo_ok + fi + + + echononl "Empty Database '${DB_NAME}'" + + # Get a list of all tables in the database + # + db_tables=$(sudo -u postgres psql -U postgres -d ${DB_NAME} -t \ + -c "SELECT tablename FROM pg_tables WHERE schemaname = 'public';") > $log_file 2>&1 + + # Delete each table + # + : > $log_file + _failed=false + error_message="Errors deleting tables from database ${DB_NAME}:" + for _table in ${db_tables} ; do + sudo -u postgres psql -U postgres -d ${DB_NAME} -c "DROP TABLE IF EXISTS $_table CASCADE;" >> $log_file 2>&1 + if [[ $? -ne 0 ]] ; then + error_message+="\n Unable to remove table ${_table} from database ${DB_NAME}" + _failed=true + fi + done + if ${_failed} ; then + echo_failed + error "$(echo -e "${error_message}"i)" + + echo "" + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + else + echo_ok + fi + + + # Also delete all sequences, if available + # + : > $log_file + _failed=false + error_message="Errors deleting sequences from database ${DB_NAME}:" + db_sequences=$(sudo -u postgres psql -U postgres -d ${DB_NAME} -t \ + -c "SELECT sequence_name FROM information_schema.sequences WHERE sequence_schema = 'public';") > $log_file 2>&1 + + for _sequence in ${db_sequences}; do + sudo -u postgres psql -U postgres -d ${DB_NAME} \ + -c "DROP SEQUENCE IF EXISTS ${_sequence} CASCADE;" >> $log_file 2>&1 + if [[ $? -ne 0 ]] ; then + error_message+="\n Unable to remove sequence ${_sequence} from database ${DB_NAME}" + _failed=true + fi + done + + else + sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME};" > $log_file 2>&1 + + if [[ $? -ne 0 ]] ; then + echo_failed + error "$(cat "$log_file")" + + echo "" + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + + else + echo_ok + fi + + fi + + + declare -i _response + _response="$(sudo -u postgres psql postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='${DB_USER}'" 2>/dev/null)" + + echononl "Create PostgreSQL database user ${DB_USER}.." + + if [[ ${_response} -eq 1 ]]; then + echo_skipped + + + echononl "Reset the Password for database user '${DB_USER}'.." + sudo -u postgres psql -c "ALTER USER ${DB_USER} WITH PASSWORD '${DB_PASS}';" > $log_file 2>&1 + + if [[ $? -ne 0 ]] ; then + echo_failed + error "$(cat "$log_file")" + + echo "" + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + + else + echo_ok + fi + + else + + sudo -u postgres psql -c "CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASS}';" > $log_file 2>&1 + + if [[ $? -ne 0 ]] ; then + echo_failed + error "$(cat "$log_file")" + + echo "" + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + + else + echo_ok + fi + fi + + + echononl "Grant the user access to the Keycloak database.." + + sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} to ${DB_USER};" > $log_file 2>&1 + + if [[ $? -ne 0 ]] ; then + echo_failed + error "$(cat "$log_file")" + + echo "" + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + + else + echo_ok + fi + + + echononl "Change the owner of database '${DB_NAME}' to '${DB_USER}'.." + + sudo -u postgres psql -c "ALTER DATABASE ${DB_NAME} OWNER TO ${DB_USER};" > $log_file 2>&1 + + if [[ $? -ne 0 ]] ; then + echo_failed + error "$(cat "$log_file")" + + echo "" + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + + else + echo_ok + fi + + echononl "Grant access to objects contained in the specified schema.." + + sudo -u postgres psql -c "GRANT USAGE, CREATE ON SCHEMA PUBLIC TO ${DB_USER};" > $log_file 2>&1 + + if [[ $? -ne 0 ]] ; then + echo_failed + error "$(cat "$log_file")" + + echo "" + echononl "\033[1mcontinue anyway\033[m [yes/no]: " + read OK + while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do + echononl "Wrong entry! - repeat [yes/nno]: " + read OK + done + [[ $OK = "yes" ]] || fatal "Abbruch durch User" + + else + echo_ok + fi + +fi + + + +echo +echo -e "\033[37m\033[1mInstalling Keycloak Server..\033[m" +echo + +echononl "Create the Keycloak (system) group.." +if cat /etc/group | grep -e "^${KEYCLOAK_GROUP}:" > /dev/null 2>&1 ; then + echo_skipped +else + groupadd -r ${KEYCLOAK_GROUP} > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi +fi + +echononl "Create the Keycloak (system) user.." +if id -u ${KEYCLOAK_USER} > /dev/null 2>&1; then + echo_skipped +else + useradd -r -M -d /opt/Keycloak -s /bin/false -g ${KEYCLOAK_GROUP} ${KEYCLOAK_USER} > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi +fi + + +echononl "Download the latest version (${KEYCLOAK_VERSION}) of the Keycloak Server.." +if [[ ! -f "${working_dir}/${DOWNLOAD_ARCHIVE}" ]]; then + wget -O "${working_dir}/${DOWNLOAD_ARCHIVE}" "${DOWNLOAD_URL}" > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi +else + echo_skipped +fi + + +echo +echo -e "\033[37m\033[1mInstalling Keycloak Service..\033[m" +echo + + +echononl "Create the Keycloak (system) group.." +if cat /etc/group | grep -e "^${KEYCLOAK_GROUP}:" > /dev/null 2>&1 ; then + echo_skipped +else + groupadd -r ${KEYCLOAK_GROUP} > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi +fi + +echononl "Create the Keycloak (system) user.." +KEYCLOAK_HOME="${KEYCLOAK_BASE_INSTALL_PATH}/keycloak" +if id -u ${KEYCLOAK_USER} > /dev/null 2>&1; then + echo_skipped +else + useradd -r -M -d ${KEYCLOAK_HOME} -s /bin/false -g ${KEYCLOAK_GROUP} ${KEYCLOAK_USER} > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi +fi + + +blank_line + +echononl "Backup Keycloak Installation directory.." +if [[ -n "${OLD_INSTALL_DIR}" ]]; then + cp -a "${OLD_INSTALL_DIR}" "${OLD_INSTALL_DIR}.${backup_date}" > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi +else + echo_skipped +fi + +echononl "Remove Symlink '${KEYCLOAK_BASE_INSTALL_PATH}/keycloak' .." +rm "${KEYCLOAK_BASE_INSTALL_PATH}/keycloak" > "$log_file" 2>&1 +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + + +blank_line + +echononl "Extract the Keycloak Service files.." +tar -C "${KEYCLOAK_BASE_INSTALL_PATH}" -xvzf "${working_dir}/${DOWNLOAD_ARCHIVE}" > "$log_file" 2>&1 +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + + +echononl "Set ownbership of installation directory '${KEYCLOAK_INSTALL_DIR}'.." +chown -R ${KEYCLOAK_USER}:${KEYCLOAK_GROUP} "${KEYCLOAK_INSTALL_DIR}" > "$log_file" 2>&1 +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + +echononl "Give write permissions to the keycloak group.." +chmod -R g+w "${KEYCLOAK_INSTALL_DIR}" > "$log_file" 2>&1 +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + + +blank_line + +echononl "Symlink keycloak -> keycloak-${KEYCLOAK_VERSION} .." +ln -s "keycloak-${KEYCLOAK_VERSION}" "${KEYCLOAK_BASE_INSTALL_PATH}/keycloak" > "$log_file" 2>&1 +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + +echononl "Backup original configuration file.." +mv "${KEYCLOAK_BASE_INSTALL_PATH}/keycloak/conf/keycloak.conf" \ + "${KEYCLOAK_BASE_INSTALL_PATH}/keycloak/conf/keycloak.conf.ORIG" +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + + +_certs_installed=false +echononl "Run script 'check_cert_for_keycloak.sh'.." +if [[ -x "/root/bin/monitoring/check_cert_for_keycloak.sh" ]] ; then + /root/bin/monitoring/check_cert_for_keycloak.sh > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + cat "$log_file" + else + echo_ok + _certs_installed=true + fi +else + warn "Certificate/Key for ${FQHN_HOSTNAME} cannot be provided." +fi + + +echononl "Add a cronjob for checking cert.." +if [[ -f "$crontab_backup_file" ]]; then + if ! grep -iq -E "/root/bin/monitoring/check_cert_for_keycloak.sh" "$crontab_backup_file" > /dev/null 2>&1; then + cat <> $crontab_backup_file + +# Check if cert for Keycloak service is up-to-date +# +51 05 * * * /root/bin/monitoring/check_cert_for_keycloak.sh +EOF + if [[ "$?" -ne 0 ]] ; then + echo_failed + else + echo_ok + fi + + else + echo_skipped + fi + +elif [[ -f "/var/spool/cron/crontabs/root" ]] ; then + + if ! grep -i -E "/root/bin/monitoring/check_cert_for_keycloak.sh" /var/spool/cron/crontabs/root > /dev/null 2>&1; then + installation_failed=false + crontab -l > /tmp/tmp_crontab 2> $log_file + if [[ "$?" -ne 0 ]] ; then + installation_failed=true + fi + + cat << EOF >> /tmp/tmp_crontab 2>> $log_file + +# Check if cert for Keycloak service is up-to-date +# +51 05 * * * /root/bin/monitoring/check_cert_for_keycloak.sh +EOF + if [[ "$?" -ne 0 ]] ; then + installation_failed=true + fi + crontab /tmp/tmp_crontab > /dev/null 2>> $log_file + if [[ "$?" -ne 0 ]] ; then + installation_failed=true + fi + rm /tmp/tmp_crontab > /dev/null 2>> $log_file + if [[ "$?" -ne 0 ]] ; then + installation_failed=true + fi + if ! $installation_failed ; then + echo_ok + else + echo_failed + error "Adding cronjob for checking cert failed!" + fi + else + echo_skipped + fi +fi + + +echononl "Create new configuration .." +if [[ "${DB_TYPE}" = "pgsql" ]]; then + _db_type="postgres" + _db_url="jdbc:postgresql://localhost/${DB_NAME}" +else + _db_type="mysql" + _db_url="jdbc:mysql://localhost/${DB_NAME}?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8" +fi +cat < "${KEYCLOAK_BASE_INSTALL_PATH}/keycloak/conf/keycloak.conf" 2> "$log_file" +# --------------------------------------------- +# Configuration for KEYCLOAK Service +# located: /opt/keycloak/conf/keycloak.conf +# --------------------------------------------- + +# Database +db=${_db_type} +db-url=${_db_url} +db-username=${DB_USER} +db-password=${DB_PASS} + +# Hostname v2 +hostname=${FQHN_HOSTNAME} +hostname-strict=true + +# HTTP(S) +http-enabled=true +http-host=127.0.0.1 +http-port=8080 +EOF +if ${_certs_installed} ; then + cat <> "${KEYCLOAK_BASE_INSTALL_PATH}/keycloak/conf/keycloak.conf" 2> "$log_file" +# HTTPS aktivieren +#https-port=8443 +#https-certificate-file=/opt/keycloak/certs/${FQHN_HOSTNAME}.crt +#https-certificate-key-file=/opt/keycloak/certs/${FQHN_HOSTNAME}.key +EOF +else + cat <> "${KEYCLOAK_BASE_INSTALL_PATH}/keycloak/conf/keycloak.conf" 2> "$log_file" +# HTTPS aktivieren +#https-port=8443 +#https-certificate-file=/opt/keycloak/certs/${FQHN_HOSTNAME}.crt +#https-certificate-key-file=/opt/keycloak/certs/${FQHN_HOSTNAME}.key +EOF +fi +cat <> "${KEYCLOAK_BASE_INSTALL_PATH}/keycloak/conf/keycloak.conf" 2> "$log_file" + +# Proxy +proxy-headers=xforwarded + +# Admin CLI aktivieren +#admin-cli-enabled=true + +EOF +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + + + +echo +echo -e "\033[37m\033[1mSetup Keycloak to use systemd for starting and stopping..\033[m" +echo + +echononl "Create a systemd unit file.." +if [[ "${DB_TYPE}" = "pgsql" ]]; then + cat < /etc/systemd/system/keycloak.service 2>"$log_file" +[Unit] +Description=Keycloak Server +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +User=${KEYCLOAK_USER} +Group=${KEYCLOAK_GROUP} +ExecStart=${KEYCLOAK_BASE_INSTALL_PATH}/keycloak/bin/kc.sh start --optimized +#Environment="JAVA_OPTS=-Xms512m -Xmx1024m" +Restart=always +LimitNOFILE=102400 + +[Install] +WantedBy=multi-user.target +EOF + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi +else + cat < /etc/systemd/system/keycloak.service 2>"$log_file" +[Unit] +Description=Keycloak Server +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +User=${KEYCLOAK_USER} +Group=${KEYCLOAK_GROUP} +ExecStart=${KEYCLOAK_BASE_INSTALL_PATH}/keycloak/bin/kc.sh start --optimized +Environment="JAVA_OPTS=-Xms512m -Xmx1024m" +Restart=always +LimitNOFILE=102400 + +[Install] +WantedBy=multi-user.target +EOF + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi +fi + + +echononl "Make systemd load the new unit.." +systemctl daemon-reload > "$log_file" 2>&1 +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + +echononl "Set Keycloak Service to start on machine start up.." +systemctl enable keycloak.service > "$log_file" 2>&1 +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + + +blank_line + +echononl "Genereate temporary admin user.." +export ADMIN_PASS="$(generate_random_string "16")" +/opt/keycloak/bin/kc.sh bootstrap-admin \ + user --username temp-admin \ + --password:env ADMIN_PASS \ + --no-prompt > "$log_file" 2>&1 +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + +#echononl "\033[1mMaybe you want create an initial admin user. continue iby typig\033[m [yes/no]: " +#read OK +#while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do +# echononl "Wrong entry! - repeat [yes/nno]: " +# read OK +#done +#[[ $OK = "yes" ]] || fatal "Abbruch durch User" + +blank_line +echononl "Creates a new and optimized server image.." +echo "${KEYCLOAK_BASE_INSTALL_PATH}/keycloak/bin/kc.sh " +${KEYCLOAK_BASE_INSTALL_PATH}/keycloak/bin/kc.sh build > "$log_file" 2>&1 + +#blank_line +#echononl "\033[1mcontinue anyway\033[m [yes/no]: " +#read OK +#while [[ "${OK,,}" != "yes" ]] && [[ "${OK,,}" != "no" ]] ; do +# echononl "Wrong entry! - repeat [yes/nno]: " +# read OK +#done +#[[ $OK = "yes" ]] || fatal "Abbruch durch User" +blank_line + + +blank_line +echononl "Start Keycloak Service" +systemctl start keycloak.service > "$log_file" 2>&1 +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + + +echo "" +echo -e "\033[37m\033[1mConfiguring NGINX with SSL and HTTP/2..\033[m" +echo "" + +echononl "Backup existing NGINX configuration.." +if [[ -f "/etc/nginx/sites-available/${FQHN_HOSTNAME}.conf" ]] ; then + cp -a "/etc/nginx/sites-available/${FQHN_HOSTNAME}.conf" \ + "/etc/nginx/sites-available/${FQHN_HOSTNAME}.conf.${backup_date}" > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi +else + echo_skipped +fi + +echononl "NGINX virtual host configuration for '${FQHN_HOSTNAME}'.." +cat < "/etc/nginx/sites-available/${FQHN_HOSTNAME}.conf" 2> "$log_file" +# -- ${FQHN_HOSTNAME} -- + +upstream kc_backend { + server 127.0.0.1:8443; + keepalive 32; +} + +server { + listen 80; + listen [::]:80; + server_name ${FQHN_HOSTNAME}; + + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + + server_name ${FQHN_HOSTNAME}; + + # Include location directive for Let's Encrypt ACME Challenge + # + # Needed for (automated) updating certificate + # + include snippets/letsencrypt-acme-challenge.conf; + + ssl_certificate /var/lib/dehydrated/certs/${FQHN_HOSTNAME}/fullchain.pem; + ssl_certificate_key /var/lib/dehydrated/certs/${FQHN_HOSTNAME}/privkey.pem; + ssl_trusted_certificate /var/lib/dehydrated/certs/${FQHN_HOSTNAME}/chain.pem; + + # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits + # + # To generate a dhparam.pem file, run in a terminal + # openssl dhparam -dsaparam -out /etc/nginx/ssl/dhparam.pem 2048 + # + ssl_dhparam /etc/nginx/ssl/dhparam.pem; + + # Enable TLS versions (TLSv1.3 is required upcoming HTTP/3 QUIC). + ssl_protocols TLSv1.2 TLSv1.3; + + # Enable TLSv1.3's 0-RTT. Use \$ssl_early_data when reverse proxying to + # prevent replay attacks. + # + # @see: https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_early_data + ssl_early_data on; + + # ECDHE better than DHE (faster) ECDHE & DHE GCM better than CBC (attacks on AES) + # Everything better than SHA1 (deprecated) + # + ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; + ssl_prefer_server_ciphers on; + + # Eable session resumption to improve https performance + ssl_session_cache shared:SSL:50m; + ssl_session_timeout 10m; + ssl_session_tickets off; + + # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) + # + add_header Strict-Transport-Security max-age=15768000; + + # OCSP Stapling --- + # fetch OCSP records from URL in ssl_certificate and cache them + ssl_stapling on; + ssl_stapling_verify on; + + #add_header X-Early-Data \$tls1_3_early_data; + + location / { + proxy_pass http://localhost:8080; + #proxy_pass https://localhost:8443; + #proxy_pass https://kc_backend; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + #proxy_set_header X-Forwarded-Host \$server_name; + proxy_set_header X-Forwarded-Proto https; + #proxy_set_header X-Forwarded-Proto \$scheme; + } +} + +EOF +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + +echononl "Enable created configuration.." +if [[ ! -h "/etc/nginx/sites-enabled/${FQHN_HOSTNAME}.conf" ]]; then + ln -s "../sites-available/${FQHN_HOSTNAME}.conf" \ + "/etc/nginx/sites-enabled/${FQHN_HOSTNAME}.conf" > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" + else + echo_ok + fi +else + echo_skipped +fi + +echononl "Restart NGINX Service.." +systemctl restart nginx > "$log_file" 2>&1 +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + + +echo +echo -e "\033[37m\033[1mSome post-installation stuff..\033[m" +echo + +_cron_reenabled=false +echononl "Reenable previously saved crontab from '$(basename "${crontab_backup_file}")'.." +if [[ -f "${crontab_backup_file}" ]] ; then + crontab ${crontab_backup_file} > $log_file 2>&1 + if [[ $? -eq 0 ]]; then + echo_ok + _cron_reenabled=true + else + echo_failed + error "$(cat $log_file)" + fi +else + echo_skipped +fi + +echononl "Remove previously saved crontab file '$(basename "${crontab_backup_file}")'.." +if ${_cron_reenabled} ; then + rm "${crontab_backup_file}" > $log_file 2>&1 + if [[ $? -eq 0 ]]; then + echo_ok + else + echo_failed + error "$(cat $log_file)" + fi +else + echo_skipped +fi + + +blank_line + +echononl "Save login credentials into file 'temporary-login-credentials.txt'.." +cat < "${working_dir}/temporary-login-credentials.txt" 2> "$log_file" + + Login into new Keycloak Service: + + URL: https://${FQHN_HOSTNAME} + USER: temp-admin + PASSSWORD: ${ADMIN_PASS} + +EOF +if [[ $? -ne 0 ]]; then + echo_failed + error "$(cat "$log_file")" +else + echo_ok +fi + +info "Login into new Keycloak Service: + + URL: https://${FQHN_HOSTNAME} + + USER: temp-admin + PASSSWORD: ${ADMIN_PASS} + + see also: ${working_dir}/temporary-login-credentials.txt +" + +clean_up 0 + diff --git a/keycloak-26.1.3.tar.gz b/keycloak-26.1.3.tar.gz new file mode 100644 index 0000000..32c3292 Binary files /dev/null and b/keycloak-26.1.3.tar.gz differ