keycloak/install-keycloak.sh
2025-03-18 16:09:18 +01:00

2034 lines
51 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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<length; i++ )); do
random_string+=$(random_char "$all_chars")
done
# Mische die Zeichenkette, um die Reihenfolge der Zeichen zufällig zu machen
random_string=$(echo "$random_string" | fold -w1 | shuf | tr -d '\n')
# Ausgabe des generierten Strings
echo "$random_string"
}
# ----------
# - Jobhandling
# ----------
# - Run 'clean_up' for signals SIGHUP SIGINT SIGTERM
# -
trap clean_up SIGHUP SIGINT SIGTERM
# - Create lock directory '$LOCK_DIR"
#
mkdir "$LOCK_DIR"
# ----------
# - Some checks ..
# ----------
# - Running in a terminal?
# -
if [[ -t 1 ]] ; then
terminal=true
else
fatal "Script must run in a terminal."
fi
# ==========
# - Begin Main Script
# ==========
# ----------
# - Headline
# ----------
if $terminal ; then
echo ""
echo -e "\033[1m----------\033[m"
echo -e "\033[32m\033[1mRunning script \033[m\033[1m$script_name\033[32m .. \033[m"
echo -e "\033[1m----------\033[m"
fi
# ----------
# Read Configurations from $conf_file
# ----------
# - Give your default values here
# -
DEFAULT_FQHN_HOSTNAME="$(hostname -f)"
DEFAULT_KEYCLOAK_USER="keycloak"
DEFAULT_DB_NAME="keycloak"
DEFAULT_DB_USER="keycloak"
DEFAULT_KEYCLOAK_BASE_INSTALL_PATH="/opt"
DEFAULT_DB_TYPE="pgsql"
DEFAULT_MYSQL_CREDENTIAL_ARGS="-u root -S /run/mysqld/mysqld.sock"
# generate random password
regexp_digit="([23456789].*){2}"
regexp_special_char="([-_%+].*){2}"
regexp_not_alowed="([0ODl18B])"
LENGTH=16
while [ 1 ] ; do
DEFAULT_DB_PASS="$(head -c 300 /dev/urandom | tr -cd 'a-zA-Z1-9\-_%' | head -c ${LENGTH})"
# - Check Password
# -
if [[ "$DEFAULT_DB_PASS" =~ $regexp_not_alowed ]] ; then
continue
fi
if [[ ! "$DEFAULT_DB_PASS" =~ $regexp_special_char ]] ; then
continue
fi
if [[ ! "$DEFAULT_DB_PASS" =~ $regexp_digit ]] ; then
continue
fi
break
done
blank_line
echononl "Read configuration '$(basename "${conf_file}")' .."
if [[ -f "$conf_file" ]]; then
source "$conf_file" > ${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 <RETURN> 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 <RETURN>"
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'<password>'\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 <<EOF > $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 <<EOF >> ${conf_file}
MYSQL_CREDENTIAL_ARGS="${MYSQL_CREDENTIAL_ARGS}"
EOF
fi
cat <<EOF >> ${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
cron_root_is_empty=false
echononl "Backup crontab.."
if [[ $(crontab -u root -l 2> /dev/null | wc -c) -gt 0 ]] ; then
crontab -u root -l > ${crontab_backup_file} 2> $log_file
if [[ "$?" = "0" ]]; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
cron_root_is_empty=true
echo_skipped
fi
echononl "Disable crontab for user root"
if ! ${cron_root_is_empty} ; then
crontab -r -u root > ${log_file} 2>&1
if [[ "$?" = "0" ]]; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
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
blank_line
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
blank_line
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 "Backup Keycloak Installation directory.."
if [[ -n "${OLD_INSTALL_DIR}" ]]; then
mv "${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' .."
if [[ -h "${KEYCLOAK_BASE_INSTALL_PATH}/keycloak" ]] ; then
rm "${KEYCLOAK_BASE_INSTALL_PATH}/keycloak" > "$log_file" 2>&1
if [[ $? -ne 0 ]]; then
echo_failed
error "$(cat "$log_file")"
else
echo_ok
fi
else
echo_skipped
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
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
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 <<EOF > "${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 <<EOF >> "${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 <<EOF >> "${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 <<EOF >> "${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 <<EOF > /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 <<EOF > /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
_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 <<EOF >> $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
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
blank_line
echononl "Creates a new and optimized server image.."
${KEYCLOAK_BASE_INSTALL_PATH}/keycloak/bin/kc.sh build > "$log_file" 2>&1
if [[ $? -ne 0 ]]; then
echo_failed
error "$(cat "$log_file")"
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
blank_line
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 "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 <<EOF > "/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
blank_line
echononl "Wait until the Keycloak service has started completely."
declare -i index=0
declare -i _max_secs_waiting=20
keycloak_service_started=false
while true ; do
# Try to establish a connection to localhost:8080
#
if $(curl -s -o /dev/null -I http://localhost:8080) ; then
echo_ok
keycloak_service_started=true
break
fi
if [[ ${index} -gt ${_max_secs_waiting} ]]; then
echo_failed
error "Could not connect to loacalhost on port 8080 after about 20 seconds!"
break
fi
(( index++ ))
sleep 1
done
echononl "Save configuration to file 'current-configuration.txt'."
${KEYCLOAK_BASE_INSTALL_PATH}/keycloak/bin/kc.sh show-config > "${working_dir}/current-configuration.txt" 2> "$log_file"
if [[ $? -ne 0 ]]; then
echo_failed
error "$(cat "$log_file")"
else
echo_ok
fi
blank_line
_admin_user_created=true
echononl "Login as temporary admin user .."
if ${keycloak_service_started} ; then
export KC_CLI_PASSWORD=${ADMIN_PASS}
/opt/keycloak/bin/kcadm.sh config credentials \
--server http://localhost:8080 \
--realm master \
--user temp-admin > "$log_file" 2>&1
if [[ $? -eq 0 ]]; then
echo_ok
else
echo_failed
_admin_user_created=false
error "$(cat $log_file)"
fi
else
echo_skipped
fi
echononl "Create permanent user 'admin'.."
if ${_admin_user_created} ; then
/opt/keycloak/bin/kcadm.sh create users \
-r master \
-s username=admin \
-s enabled=true \
-o --fields id,username > "$log_file" 2>&1
if [[ $? -eq 0 ]]; then
echo_ok
else
echo_failed
_admin_user_created=false
error "$(cat $log_file)"
fi
else
echo_skipped
fi
echononl "Set password for user 'admin'.."
if ${_admin_user_created} ; then
NEW_ADMIN_PASS="$(generate_random_string "16")"
/opt/keycloak/bin/kcadm.sh set-password --username admin --new-password ${NEW_ADMIN_PASS}
if [[ $? -eq 0 ]]; then
echo_ok
else
echo_failed
_admin_user_created=false
error "$(cat $log_file)"
fi
else
echo_skipped
fi
roles="admin create-realm uma_authorization offline_access"
for _role in ${roles} ; do
echononl "Add Role '${_role}' to user 'admin'.."
if ${_admin_user_created} ; then
if ${keycloak_service_started} ; then
/opt/keycloak/bin/kcadm.sh add-roles --uusername admin --rolename ${_role}
if [[ $? -eq 0 ]]; then
echo_ok
else
echo_failed
_admin_user_created=false
error "$(cat $log_file)"
fi
else
echo_skipped
fi
else
echo_skipped
fi
done
blank_line
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 credentials for 'temp-admin' into file 'temporary-login-credentials.txt'.."
cat <<EOF > "${working_dir}/login-credentials-temp-admin.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 as temporary admin user:
URL: https://${FQHN_HOSTNAME}
USER: temp-admin
PASSSWORD: ${ADMIN_PASS}
see also: ${working_dir}/login-credentials-temp-admin.txt
"
if ${_admin_user_created} ; then
echononl "Save credentials for permanent admin into file 'login-credentials-admin.txt'.."
cat <<EOF > "${working_dir}/login-credentials-admin.txt" 2> "$log_file"
Login into new Keycloak Service:
URL: https://${FQHN_HOSTNAME}
USER: admin
PASSSWORD: ${NEW_ADMIN_PASS}
EOF
if [[ $? -ne 0 ]]; then
echo_failed
error "$(cat "$log_file")"
else
echo_ok
info "Login into new Keycloak Service as permanent admin user:
URL: https://${FQHN_HOSTNAME}
USER: admin
PASSSWORD: ${NEW_ADMIN_PASS}
see also: ${working_dir}/login-credentials-admin.txt
"
fi
else
rm -r "${working_dir}/login-credentials-admin.txt" > /dev/null 2>&1
fi
clean_up 0