commit 29b2a63b6b69dc2a4918d7fef531daf8b85ac9e8 Author: itiligent <94789708+itiligent@users.noreply.github.com> Date: Sun Apr 16 20:22:00 2023 +1000 v1.5.0.0 v1.5.0.0 diff --git a/1-setup.sh b/1-setup.sh new file mode 100644 index 0000000..3b06e30 --- /dev/null +++ b/1-setup.sh @@ -0,0 +1,546 @@ +#!/bin/bash +###################################################################################################################### +# Guacamole appliance setup script +# For Ubuntu / Debian / Raspian +# David Harrop +# April 2023 +####################################################################################################################### + +# To install latest snapshot: +# wget https://raw.githubusercontent.com/itiligent/Guacamole-Setup/main/1-setup.sh && chmod +x 1-setup.sh && ./1-setup.sh + +# If something isn't working? # tail -f /var/log/syslog /var/log/tomcat*/*.out /var/log/mysql/*.log + +# This whole install routine could be collated into one huge script, but it is far easer to manage and maintan by +# breaking up the different stages of the install into at least 4 separate scripts as follows... +# 1-setup.sh is a central script that manages all inputs, options and sequences other included 'install' scripts. +# 2-install-guacamole is the main guts of the whole build. This script downloads and builds Guacamole from source. +# 3-install-nginx.sh automatically installs and configues Nginx to work as an http port 80 front end to Gaucamole +# 4a-install-self-signed-nginx.sh sets up the new Nginx/Guacamole front end with self signed SSL certificates. +# 4b-install-ssl-letsencrypt-nginx.sh sets up Nginx with public SSL certificates from LetsEncrypt. + + +####################################################################################################################### +# Initial enviromment setup ########################################################################################### +####################################################################################################################### + +clear + +# Prepare text output colours +GREY='\033[0;37m' +DGREY='\033[0;90m' +GREYB='\033[1;37m' +RED='\033[0;31m' +LRED='\033[0;91m' +GREEN='\033[0;32m' +LGREEN='\033[0;92m' +YELLOW='\033[0;33m' +LYELLOW='\033[0;93m' +BLUE='\033[0;34m' +LBLUE='\033[0;94m' +CYAN='\033[0;36m' +LCYAN='\033[0;96m' +MAGENTA='\033[0;35m' +LMAGENTA='\033[0;95m' +NC='\033[0m' #No Colour + +#Setup download and temp directory paths +USER_HOME_DIR=$(eval echo ~${SUDO_USER}) +DOWNLOAD_DIR=$USER_HOME_DIR/guac-setup +DB_BACKUP_DIR=$USER_HOME_DIR/mysqlbackups/ +TMP_DIR=$DOWNLOAD_DIR/tmp +source /etc/os-release +OS_FLAVOUR=$ID +OS_VERSION=$VERSION +JPEGTURBO="" +LIBPNG="" + +# Announce the script we're running +echo +echo -e "${GREYB}Itiligent Jump Server Appliance Setup." +echo -e " ${LGREEN}Powered by Guacamole" +echo + +# Setup directory locations +mkdir -p $DOWNLOAD_DIR +mkdir -p $DB_BACKUP_DIR +mkdir -p $TMP_DIR + +# Now prompt for sudo and set dir permissions so both sudo and non sudo functions can access tmp setup files +sudo chmod -R 770 $TMP_DIR +sudo chown -R $SUDO_USER:root $TMP_DIR + +# Github download branch +GITHUB="https://raw.githubusercontent.com/itiligent/Guacamole-Setup/main/" + +#Version of Guacamole to install +GUAC_VERSION="1.5.0" + +# Set preferred Apache CDN download link +GUAC_SOURCE_LINK="http://apache.org/dyn/closer.cgi?action=download&filename=guacamole/${GUAC_VERSION}" + +# MySQL Connector/J version +MYSQLJCON="8.0.30" + +# Apache Tomcat version. You will need to check the correct version for your particular distro. +TOMCAT_VERSION="tomcat9" + +# Install log Location +LOG_LOCATION="${DOWNLOAD_DIR}/guacamole_${GUAC_VERSION}_setup.log" + +# Guacamole default install URL +GUAC_URL=http://localhost:8080/guacamole/ + +# Non interactive silent setup options - add true/false or specific values +SERVER_NAME="" # Preferred server hostname +INSTALL_MYSQL="" # Install locally true/false +SECURE_MYSQL="" # Apply mysql secure configurarion tool +MYSQL_HOST="" # leave blank for localhost default, only specify for remote servers +MYSQL_PORT="" # If blank default is 3306 +GUAC_DB="" # If blank default is guacamole_db +GUAC_USER="" # if blank default is guacamole_user +GUAC_PWD="" # Should not be blank as this may break some aspects of install +MYSQL_ROOT_PWD="" # Should not be blank as this may break some aspects of install +INSTALL_TOTP="" # TOTP MFA extension +INSTALL_DUO="" # DUO MFA extension (cant be installed simultaneously with TOTP) +INSTALL_LDAP="" # Active Directory extension +INSTALL_NGINX="" # Install and configure Guacamole behind Nginx reverse proxy (http port 80 only) +PROXY_SITE="" # Local DNS name for reverse proxy and self signed ssl certificates +SELF_SIGNED="" # Add self signed SSL support to Nginx (Let's Encrypt not available) +INSTALL_LETS_ENCRYPT="" # Add Lets Encrypt public SSL support for Nginx (self signed SSL certs not available) +LE_DNS_NAME="" # Public DNS name to bind with Lets Encrypt certificates +LE_EMAIL="webmaster@itiligent.com" # Webmaster/admin email for Lets Encrypt +EMAIL_DOMAIN="itiligent.com" # Email relay domain for backup notifications +ALERT_EMAIL="alerts@itiligent.com" # Email address for backup notifications +BACKUP_RETAIN_DAYS="30" # How long to keep backups for +CERT_COUNTRY="AU" # 2 coutry charater code only, must not be blank +CERT_STATE="Victoria" # Optional to change, must not be blank +CERT_LOCATION="Melbourne" # Optional to change, must not be blank +CERT_ORG="Itiligent" # Optional to change, must not be blank +CERT_OU="I.T." # Optional to change, must not be blank +CERT_DAYS="3650" # Number of days until self signed certificate expiry + +# We need to try and grab a default value for the local FQDN. Domain search suffix is used in this case becausue +# this is the simplest common default resolv.conf value available between recent Debian and Ubuntu flavours. YMMV. +DOMAIN_SEARCH_SUFFIX=$(grep search /etc/resolv.conf | grep -v "#" | sed 's/'search[[:space:]]'//') +DEFAULT_FQDN=$HOSTNAME.$DOMAIN_SEARCH_SUFFIX + +# Finally we check to prevent reinstalling over the current from possibly older/previous versions of build files. +if [ "$( find . -maxdepth 2 \( -name 'guacamole-*' -o -name 'mysql-connector-java-*' \) )" != "" ]; then + echo -e "${RED}Possible previous temp files detected in current build path. Please review and remove old 'guacamole-*' & 'mysql-connector-java-*' files before proceeding.${GREY}" 1>&2 +exit 1 +fi + + +####################################################################################################################### +# Download github setup scripts ####################################################################################### +####################################################################################################################### + +# Download config scripts and setup items from github +cd $DOWNLOAD_DIR +echo +echo -e "${GREY}Downloading setup files...${DGREY}" +wget -q --show-progress ${GITHUB}2-install-guacamole.sh -O 2-install-guacamole.sh +wget -q --show-progress ${GITHUB}3-install-nginx.sh -O 3-install-nginx.sh +wget -q --show-progress ${GITHUB}4a-install-ssl-self-signed-nginx.sh -O 4a-install-ssl-self-signed-nginx.sh +wget -q --show-progress ${GITHUB}4b-install-ssl-letsencrypt-nginx.sh -O 4b-install-ssl-letsencrypt-nginx.sh + +# Grab Guacamole auth extension config scripts +wget -q --show-progress ${GITHUB}add-auth-duo.sh -O add-auth-duo.sh +wget -q --show-progress ${GITHUB}add-auth-ldap.sh -O add-auth-ldap.sh +wget -q --show-progress ${GITHUB}add-auth-totp.sh -O add-auth-totp.sh + + +# Grab backup and security hardening scripts +wget -q --show-progress ${GITHUB}backup-guac.sh -O backup-guac.sh +wget -q --show-progress ${GITHUB}add-ssl-guac-gaucd.sh -O add-ssl-guac-gaucd.sh +wget -q --show-progress ${GITHUB}add-fail2ban.sh -O add-fail2ban.sh + +# Grab a (customisable) branding extension +wget -q --show-progress ${GITHUB}branding.jar -O branding.jar +chmod +x *.sh + + +####################################################################################################################### +# Begin install menu prompts ########################################################################################## +####################################################################################################################### + +# We need a default hostname avaiable to apply even if we do not want to change the hostname. This approach allows the +# user to simply hit enter at the prompt without this creating a blank entry into the /etc/hosts file. +# Hostnames and matching DNS entries are essential for implementing SSL succesfully. +echo +echo +if [[ -z ${SERVER_NAME} ]]; then + echo -e "${LYELLOW}Update Linux system HOSTNAME [Enter to keep: ${HOSTNAME}]${LGREEN}" + read -p " Enter new HOSTNAME : " SERVER_NAME + if [[ "${SERVER_NAME}" = "" ]]; then + SERVER_NAME=$HOSTNAME + echo + sudo hostnamectl set-hostname $SERVER_NAME &>> ${LOG_LOCATION} + sudo sed -i '/127.0.1.1/d' /etc/hosts &>> ${LOG_LOCATION} + echo '127.0.1.1 '${SERVER_NAME}'' | sudo tee -a /etc/hosts &>> ${LOG_LOCATION} + sudo systemctl restart systemd-hostnamed &>> ${LOG_LOCATION} + else + echo + sudo hostnamectl set-hostname $SERVER_NAME &>> ${LOG_LOCATION} + sudo sed -i '/127.0.1.1/d' /etc/hosts &>> ${LOG_LOCATION} + echo '127.0.1.1 '${SERVER_NAME}'' | sudo tee -a /etc/hosts &>> ${LOG_LOCATION} + sudo systemctl restart systemd-hostnamed &>> ${LOG_LOCATION} + fi +fi + +clear +echo +# For convenience & sanity check, diplay status of preset script options at start of install +echo -e "${GREY}Enabled non-interactive presets are listed below, blank entries will prompt." +echo -e "(Ctrl+C to exit & edit 1-setup.sh before continuning.)" +echo -e "${DGREY}Server host name\t= ${GREY}${SERVER_NAME}" +echo -e "${DGREY}Install MYSQL locally\t= ${GREY}${INSTALL_MYSQL}" +echo -e "${DGREY}MySQL secure install\t= ${GREY}${SECURE_MYSQL}" +echo -e "${DGREY}MySQL hostname/IP\t= ${GREY}${MYSQL_HOST}" +echo -e "${DGREY}MySQL port\t\t= ${GREY}${MYSQL_PORT}" +echo -e "${DGREY}Guacamole db name\t= ${GREY}${GUAC_DB}" +echo -e "${DGREY}Guacamole db user name\t= ${GREY}${GUAC_USER}" +echo -e "${DGREY}Guacamole user pwd\t= ${GREY}${GUAC_PWD}" +echo -e "${DGREY}MySQL root pwd\t\t= ${GREY}${MYSQL_ROOT_PWD}" +echo -e "${DGREY}Install TOTP\t\t= ${GREY}${INSTALL_TOTP}" +echo -e "${DGREY}Install DUO\t\t= ${GREY}${INSTALL_DUO}" +echo -e "${DGREY}Install LDAP\t\t= ${GREY}${INSTALL_LDAP}${GREY}" +echo -e "${DGREY}Install Nginx rev proxy\t= ${GREY}${INSTALL_NGINX}${GREY}" +echo -e "${DGREY}Proxy local DNS name\t= ${GREY}${PROXY_SITE}" +echo -e "${DGREY}Add self signed SSL\t= ${GREY}${SELF_SIGNED}${GREY}" +echo -e "${DGREY}Add Let's Encrypt SSL\t= ${GREY}${INSTALL_LETS_ENCRYPT}${GREY}" +echo -e "${DGREY}Let's Encrypt pub FQDN\t= ${GREY}${LE_DNS_NAME}${GREY}" +echo -e "${DGREY}Let's Encrypt email\t= ${GREY}${LE_EMAIL}${GREY}" +echo -e "${DGREY}SMTP relay domain\t= ${GREY}${EMAIL_DOMAIN}${GREY}" +echo -e "${DGREY}Alerts email address\t= ${GREY}${ALERT_EMAIL}${GREY}" +echo -e "${DGREY}Days to keep backups\t= ${GREY}${BACKUP_RETAIN_DAYS}${GREY}" +echo -e "${DGREY}Self signed cert days\t= ${DGREY}${CERT_DAYS}${GREY}" +echo -e "${DGREY}Self signed country\t= ${DGREY}${CERT_COUNTRY}${GREY}" +echo -e "${DGREY}Self signed state\t= ${DGREY}${CERT_STATE}${GREY}" +echo -e "${DGREY}Self signed location\t= ${DGREY}${CERT_LOCATION}${GREY}" +echo -e "${DGREY}Self signed ORG\t\t= ${DGREY}${CERT_ORG}${GREY}" +echo -e "${DGREY}Self signed OU\t\t= ${DGREY}${CERT_OU}${GREY}" + +# Prompt the user to see if they would like to install MySQL, default of yes +if [[ -z ${INSTALL_MYSQL} ]]; then + echo + echo -e -n "${LGREEN}SQL: Install MySQL? (for a remote MySQL Server select 'n') (y/n) [default y]: ${GREY}" + read PROMPT + if [[ ${PROMPT} =~ ^[Nn]$ ]]; then + INSTALL_MYSQL=false + else + INSTALL_MYSQL=true + fi +fi + +# Prompt the user to see if they would like to apply the Mysql secure installation +if [ -z ${SECURE_MYSQL} ] && [ "${INSTALL_MYSQL}" = true ]; then + echo -e -n "${GREY}SQL: Apply MySQL secure installation settings to LOCAL db? (y/n) [default y]: ${GREY}" + read PROMPT + if [[ ${PROMPT} =~ ^[Nn]$ ]]; then + SECURE_MYSQL=false + else + SECURE_MYSQL=true + fi +fi + +# Prompt the user to see if they would like to apply the Mysql secure installation +if [ -z ${SECURE_MYSQL} ] && [ "${INSTALL_MYSQL}" = false ]; then + echo -e -n "${GREY}SQL: Apply MySQL secure installation settings to REMOTE db? (y/n) [default n]: ${GREY}" + read PROMPT + if [[ ${PROMPT} =~ ^[Yy]$ ]]; then + SECURE_MYSQL=true + else + SECURE_MYSQL=false + fi +fi + +if [ "${INSTALL_MYSQL}" = false ]; then + # We need to get some additional MYSQL values + [ -z "${MYSQL_HOST}" ] \ + && read -p "SQL: Enter MySQL server hostname or IP: " MYSQL_HOST + [ -z "${MYSQL_PORT}" ] \ + && read -p "SQL: Enter MySQL server port [3306]: " MYSQL_PORT + [ -z "${GUAC_DB}" ] \ + && read -p "SQL: Enter Guacamole database name [guacamole_db]: " GUAC_DB + [ -z "${GUAC_USER}" ] \ + && read -p "SQL: Enter Guacamole user name [guacamole_user]: " GUAC_USER +fi + +# Checking if a mysql host given, if not set a default +if [ -z "${MYSQL_HOST}" ]; then + MYSQL_HOST="localhost" +fi + +# Checking if a mysql port given, if not set a default +if [ -z "${MYSQL_PORT}" ]; then + MYSQL_PORT="3306" +fi + +# Checking if a database name given, if not set a default +if [ -z "${GUAC_DB}" ]; then + GUAC_DB="guacamole_db" +fi + +# Checking if a mysql user given, if not set a default +if [ -z "${GUAC_USER}" ]; then + GUAC_USER="guacamole_user" +fi + +# Get MySQL root password, confirm correct password entry and prevent blank passwords +if [ -z "${MYSQL_ROOT_PWD}" ]; then + while true; do + read -s -p "SQL: Enter ${MYSQL_HOST}'s MySQL root password: " MYSQL_ROOT_PWD + echo + read -s -p "SQL: Confirm ${MYSQL_HOST}'s MySQL root password: " PROMPT2 + echo + [ "${MYSQL_ROOT_PWD}" = "${PROMPT2}" ] && [ "${MYSQL_ROOT_PWD}" != "" ] && [ "${PROMPT2}" != "" ] && break + echo -e "${RED}Passwords don't match or can't be null. Please try again.${GREY}" 1>&2 + done +fi + +# Get Guacamole User password, confirm correct password entry and prevent blank passwords +if [ -z "${GUAC_PWD}" ]; then + while true; do + read -s -p "SQL: Enter ${MYSQL_HOST}'s MySQL ${GUAC_USER} password: " GUAC_PWD + echo + read -s -p "SQL: Confirm ${MYSQL_HOST}'s MySQL ${GUAC_USER} password: " PROMPT2 + echo + [ "${GUAC_PWD}" = "${PROMPT2}" ] && [ "${GUAC_PWD}" != "" ] && [ "${PROMPT2}" != "" ] && break + echo -e "${RED}Passwords don't match or can't be null. Please try again.${GREY}" 1>&2 + done +fi + +# Prompt the user if they would like to install TOTP MFA, default of no +if [[ -z "${INSTALL_TOTP}" ]] && [[ "${INSTALL_DUO}" != true ]]; then + echo -e -n "${GREY}GUAC MFA: Install TOTP? (choose 'n' if you want Duo) (y/n)? [default n]: " + read PROMPT + if [[ ${PROMPT} =~ ^[Yy]$ ]]; then + INSTALL_TOTP=true + INSTALL_DUO=false + else + INSTALL_TOTP=false + fi +fi + +# Prompt the user if they would like to install Duo MFA, default of no +if [[ -z "${INSTALL_DUO}" ]] && [[ "${INSTALL_TOTP}" != true ]]; then + echo -e -n "${GREY}GUAC MFA: Install Duo? (y/n) [default n]: " + read PROMPT + if [[ ${PROMPT} =~ ^[Yy]$ ]]; then + INSTALL_DUO=true + INSTALL_TOTP=false + else + INSTALL_DUO=false + fi +fi + +# We can't install TOTP and Duo at the same time, option not supported by Guacamole... +if [[ "${INSTALL_TOTP}" = true ]] && [[ "${INSTALL_DUO}" = true ]]; then + echo -e "${RED}GUAC MFA: TOTP and Duo cannot be installed at the same time.${GREY}" 1>&2 + exit 1 +fi + +# Prompt the user if they would like to install Duo MFA, default of no +if [[ -z "${INSTALL_LDAP}" ]]; then + echo -e -n "${GREY}GUAC AUTH: Install LDAP? (y/n) [default n]: " + read PROMPT + if [[ ${PROMPT} =~ ^[Yy]$ ]]; then + INSTALL_LDAP=true + else + INSTALL_LDAP=false + fi +fi + +# Prompt for Guacamole front end reverse proxy option +if [[ -z ${INSTALL_NGINX} ]]; then + echo -e -n "${LGREEN}ADD REVERSE PROXY?: Protect Gucamole behind Nginx reverse proxy (y/n)? [default y]: ${GREY}" + read PROMPT + if [[ ${PROMPT} =~ ^[Nn]$ ]]; then + INSTALL_NGINX=false + else + INSTALL_NGINX=true + fi +fi + +# We must assign a DNS name for the new proxy site +if [[ -z ${PROXY_SITE} ]] && [[ "${INSTALL_NGINX}" = true ]]; then + while true; do + read -p "REVERSE PROXY NAME?: Enter proxy local DNS name? [Enter to use ${DEFAULT_FQDN}]: " PROXY_SITE + [ "${PROXY_SITE}" = "" ] || [ "${PROXY_SITE}" != "" ] && break + # rather than allow any default, alternately force user to enter an explicit name instead + # [ "${PROXY_SITE}" != "" ] && break + # echo -e "${RED}You must enter a proxy site DNS name. Please try again.${GREY}" 1>&2 + done +fi + +# If no proxy site dns name is given, lets assume a default FQDN +if [ -z "${PROXY_SITE}" ]; then +PROXY_SITE="${DEFAULT_FQDN}" +fi + +# Prompt for self signed SSL reverse proxy option +if [[ -z ${SELF_SIGNED} ]] && [[ "${INSTALL_NGINX}" = true ]]; then + # Prompt the user to see if they would like to install self signed SSL support for Nginx, default of no + echo -e -n "${GREY}PROXY SSL?: Add self signed SSL support to Nginx? (y/n)? (choose 'n' for Let's Encrypt)[default n]: " + read PROMPT + if [[ ${PROMPT} =~ ^[Yy]$ ]]; then + SELF_SIGNED=true + else + SELF_SIGNED=false + fi +fi + +# Prompt to assign the self sign SSL certficate a custom expiry date, uncomment to force a manual entry +#if [ "${SELF_SIGNED}" = true ]; then +# read - p "PROXY: Enter number of days till SSL certificate expires [default 3650]: " CERT_DAYS +#fi + +# If no self sign SSL certificate expiry given, lets assume a generous 10 year default certificate expiry +if [ -z "${CERT_DAYS}" ]; then + CERT_DAYS="3650" +fi + +# Prompt for Let's Encrypt SSL reverse proxy configuration option +if [[ -z ${INSTALL_LETS_ENCRYPT} ]] && [[ "${INSTALL_NGINX}" = true ]] && [[ "${SELF_SIGNED}" = "false" ]]; then + echo -e -n "${GREY}SSL PROXY: Add Let's Encrypt SSL support to Nginx reverse proxy (y/n) [default n]: ${GREY}" + read PROMPT + if [[ ${PROMPT} =~ ^[Yy]$ ]]; then + INSTALL_LETS_ENCRYPT=true + else + INSTALL_LETS_ENCRYPT=false + fi +fi + +# Prompt for Let's Encrypt public dns name +if [[ -z ${LE_DNS_NAME} ]] && [[ "${INSTALL_LETS_ENCRYPT}" = true ]]; then + while true; do + read -p "Enter the FQDN for your public proxy site : " LE_DNS_NAME + [ "${LE_DNS_NAME}" != "" ] && break + echo -e "${RED}You must enter a public DNS name. Please try again.${GREY}" 1>&2 + done +fi + +# Prompt for Let's Encrypt admin email +if [[ -z ${LE_EMAIL} ]] && [[ "${INSTALL_LETS_ENCRYPT}" = true ]]; then + while true; do + read -p "Enter the email address for Let's Encrypt notifications : " LE_EMAIL + [ "${LE_EMAIL}" != "" ] && break + echo -e "${RED}You must enter an email address. Please try again.${GREY}" 1>&2 + done +fi + + +####################################################################################################################### +# Start global setup actions ######################################################################################### +####################################################################################################################### + +# Ubuntu and Debian each require different dependency packages. Below works ok from Ubuntu 18.04 / Debian 10 and above. +echo -e "${GREY}Checking linux distro specific dependencies..." +if [[ $OS_FLAVOUR == "ubuntu" ]] || [[ $OS_FLAVOUR == "ubuntu"* ]]; then # potentially expand out distro choices here + JPEGTURBO="libjpeg-turbo8-dev" + LIBPNG="libpng-dev" + sudo add-apt-repository -y universe &>> ${LOG_LOCATION} + elif [[ $OS_FLAVOUR == "debian" ]] || [[ $OS_FLAVOUR == "raspbian" ]] ; then # expand distro choices here if req + JPEGTURBO="libjpeg62-turbo-dev" + LIBPNG="libpng-dev" +fi +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 +else + echo -e "${LGREEN}OK${GREY}" +fi + +# Because the below scripts may be run manually after install, we need to sync them +# with global variables or setup prompt options. +sed -i "s|MYSQL_HOST=|MYSQL_HOST='${MYSQL_HOST}'|g" $DOWNLOAD_DIR/backup-guac.sh +sed -i "s|MYSQL_PORT=|MYSQL_PORT='${MYSQL_PORT}'|g" $DOWNLOAD_DIR/backup-guac.sh +sed -i "s|GUAC_USER=|GUAC_USER='${GUAC_USER}'|g" $DOWNLOAD_DIR/backup-guac.sh +sed -i "s|GUAC_PWD=|GUAC_PWD='${GUAC_PWD}'|g" $DOWNLOAD_DIR/backup-guac.sh +sed -i "s|GUAC_DB=|GUAC_DB='${GUAC_DB}'|g" $DOWNLOAD_DIR/backup-guac.sh +sed -i "s|DB_BACKUP_DIR=|DB_BACKUP_DIR='${DB_BACKUP_DIR}'|g" $DOWNLOAD_DIR/backup-guac.sh +sed -i "s|ALERT_EMAIL=|ALERT_EMAIL='${ALERT_EMAIL}'|g" $DOWNLOAD_DIR/backup-guac.sh +sed -i "s|BACKUP_RETAIN_DAYS=|BACKUP_RETAIN_DAYS='${BACKUP_RETAIN_DAYS}'|g" $DOWNLOAD_DIR/backup-guac.sh +sed -i "s|GUAC_VERSION=|GUAC_VERSION='${GUAC_VERSION}'|g" $DOWNLOAD_DIR/add-auth-duo.sh +sed -i "s|GUAC_VERSION=|GUAC_VERSION='${GUAC_VERSION}'|g" $DOWNLOAD_DIR/add-auth-ldap.sh +sed -i "s|GUAC_VERSION=|GUAC_VERSION='${GUAC_VERSION}'|g" $DOWNLOAD_DIR/add-auth-totp.sh +sed -i "s|TOMCAT_VERSION=|TOMCAT_VERSION='${TOMCAT_VERSION}'|g" $DOWNLOAD_DIR/add-auth-duo.sh +sed -i "s|TOMCAT_VERSION=|TOMCAT_VERSION='${TOMCAT_VERSION}'|g" $DOWNLOAD_DIR/add-auth-ldap.sh +sed -i "s|TOMCAT_VERSION=|TOMCAT_VERSION='${TOMCAT_VERSION}'|g" $DOWNLOAD_DIR/add-auth-totp.sh +sed -i "s|TOMCAT_VERSION=|TOMCAT_VERSION='${TOMCAT_VERSION}'|g" $DOWNLOAD_DIR/add-fail2ban.sh +# As web links may contain "&" characters, GUAC_SOURCE_LINK is fixed to parse "&" with sed correctly +FIXED_LINK=$(sed 's/\&/\\&/g' <<< $GUAC_SOURCE_LINK) +sed -i "s|GUAC_SOURCE_LINK=|GUAC_SOURCE_LINK='${FIXED_LINK}'|g" $DOWNLOAD_DIR/add-auth-duo.sh +sed -i "s|GUAC_SOURCE_LINK=|GUAC_SOURCE_LINK='${FIXED_LINK}'|g" $DOWNLOAD_DIR/add-auth-ldap.sh +sed -i "s|GUAC_SOURCE_LINK=|GUAC_SOURCE_LINK='${FIXED_LINK}'|g" $DOWNLOAD_DIR/add-auth-totp.sh + +# Pass the relevant variable selections to child install scripts below +# (This is a more robust method than export, which is unreliable in this instance) +COLOUR_VAR="GREY=$GREY DGREY=$DGREY GREYB=$GREYB RED=$RED LRED=$LRED GREEN=$GREEN LGREEN=$LGREEN YELLOW=$YELLOW LYELLOW=$LYELLOW BLUE=$BLUE LBLUE=$LBLUECYAN=$CYAN LCYAN=$LCYAN MAGENTA=$MAGENTA LMAGENTA=$LMAGENTA NC=$NC" +GUAC_VAR="JPEGTURBO=$JPEGTURBO LIBPNG=$LIBPNG GUAC_VERSION=$GUAC_VERSION MYSQLJCON=$MYSQLJCON GUAC_SOURCE_LINK=$GUAC_SOURCE_LINK TOMCAT_VERSION=$TOMCAT_VERSION LOG_LOCATION=$LOG_LOCATION INSTALL_MYSQL=$INSTALL_MYSQL SECURE_MYSQL=$SECURE_MYSQL MYSQL_HOST=$MYSQL_HOST MYSQL_PORT=$MYSQL_PORT GUAC_DB=$GUAC_DB GUAC_USER=$GUAC_USER GUAC_PWD=$GUAC_PWD MYSQL_ROOT_PWD=$MYSQL_ROOT_PWD INSTALL_TOTP=$INSTALL_TOTP INSTALL_DUO=$INSTALL_DUO INSTALL_LDAP=$INSTALL_LDAP EMAIL_DOMAIN=$EMAIL_DOMAIN" +NGINX_VAR="TOMCAT_VERSION=$TOMCAT_VERSION LOG_LOCATION=$LOG_LOCATION GUAC_URL=$GUAC_URL PROXY_SITE=$PROXY_SITE" +SELF_SIGN_VAR="DOWNLOAD_DIR=$DOWNLOAD_DIR TMP_DIR=$TMP_DIR TOMCAT_VERSION=$TOMCAT_VERSION LOG_LOCATION=$LOG_LOCATION GUAC_URL=$GUAC_URL PROXY_SITE=$PROXY_SITE CERT_COUNTRY=$CERT_COUNTRY CERT_STATE=$CERT_STATE CERT_LOCATION=$CERT_LOCATION CERT_ORG=$CERT_ORG CERT_OU=$CERT_OU" +LE_VAR="DOWNLOAD_DIR=$DOWNLOAD_DIR TOMCAT_VERSION=$TOMCAT_VERSION LOG_LOCATION=$LOG_LOCATION PROXY_SITE=$PROXY_SITE GUAC_URL=$GUAC_URL LE_DNS_NAME=$LE_DNS_NAME LE_EMAIL=$LE_EMAIL" + +# Run the Guacamole install script +sudo $GUAC_VAR $COLOUR_VAR ./2-install-guacamole.sh +if [ $? -ne 0 ]; then + echo -e "${RED}2-install-guacamole.sh FAILED. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}Guacamole installation complete\n- Visit: http://${PROXY_SITE}:8080/guacamole\n- Default login (user/pass): guacadmin/guacadmin\n${LYELLOW}***Be sure to change the password***.${GREY}" +fi + +# Duo Settings reminder that if due is slected you cant login to Guacamole at all until this extension is fully configured +if [ $INSTALL_DUO == "true" ]; then + echo -e "${YELLOW}Reminder: Duo requires extra account specific config before you can log in to Guacamole." + echo -e "See https://guacamole.apache.org/doc/${GUAC_VERSION}/gug/duo-auth.html" +fi + +# Add a Guacamole database backup (mon-fri 12:00am) into cron +crontab -l > cron_1 > /dev/null 2>&1 + # Remove existing entry to allow multiple runs + sed -i '/# backup guacamole/d' cron_1 + # Create the job + echo "0 0 * * 1-5 ${DOWNLOAD_DIR}/backup-guac.sh # backup guacamole" >> cron_1 +# Overwrite the cron settings and cleanup +crontab cron_1 > /dev/null 2>&1 +rm cron_1 + + +####################################################################################################################### +# Start optional setup actions ###################################################################################### +####################################################################################################################### + +### Install Nginx reverse proxy front end to Guacamole if option is selected +if [ "${INSTALL_NGINX}" = true ]; then + sudo $NGINX_VAR $COLOUR_VAR ./3-install-nginx.sh | tee -a ${LOG_LOCATION} + echo -e "${LGREEN}Nginx installation complete\n- Site changed to : http://${PROXY_SITE}\n- Default login (user/pass): guacadmin/guacadmin\n${LYELLOW}***Be sure to change the password***.${GREY}" + fi + + +### Apply self signed SSL certificates to Nginx reverse proxy if option is selected +if [[ "${INSTALL_NGINX}" = true ]] && [[ "${SELF_SIGNED}" = true ]]; then + sudo -E $SELF_SIGN_VAR $COLOUR_VAR ./4a-install-ssl-self-signed-nginx.sh ${PROXY_SITE} ${CERT_DAYS} | tee -a ${LOG_LOCATION} +echo -e "${LGREEN}Self signed certificates successfully created and configured for Nginx \n- Site changed to : ${LYELLOW}https:${LGREEN}//${PROXY_SITE}\n- Default login (user/pass): guacadmin/guacadmin\n${LYELLOW}***Be sure to change the password***.${GREY}" +fi + + +### Apply Let's Encrypt SSL certificates to Nginx reverse proxy if option is selected +if [[ "${INSTALL_NGINX}" = true ]] && [[ "${INSTALL_LETS_ENCRYPT}" = true ]]; then + sudo -E $LE_VAR $COLOUR_VAR ./4b-install-ssl-letsencrypt-nginx.sh | tee -a ${LOG_LOCATION} +echo -e "${LGREEN}Let's Encrypt SSL successfully configured for Nginx \n- Site changed to : ${LYELLOW}https:${LGREEN}//${LE_DNS_NAME}\n- Default login (user/pass): guacadmin/guacadmin\n${LYELLOW}***Be sure to change the password***.${GREY}" +fi + + +# Final tidy up +mv $USER_HOME_DIR/1-setup.sh $DOWNLOAD_DIR +sudo rm -R $TMP_DIR + + +# Done +echo +printf "${LGREEN}Guacamole ${GUAC_VERSION} install complete! \n${NC}" +echo -e ${NC} diff --git a/2-install-guacamole.sh b/2-install-guacamole.sh new file mode 100644 index 0000000..8cca399 --- /dev/null +++ b/2-install-guacamole.sh @@ -0,0 +1,474 @@ +#!/bin/bash +####################################################################################################################### +# Guacamole main build script +# For Ubuntu / Debian / Raspian +# David Harrop +# April 2023 +# Special thanks to MysticRyuujin for much of the guac install outline here +# pls see https://github.com/MysticRyuujin/guac-install for more +####################################################################################################################### + +clear + +# Pre-seed MySQL install values +if [ "${INSTALL_MYSQL}" = true ]; then +debconf-set-selections <<< "mysql-server mysql-server/root_password password ${MYSQL_ROOT_PWD}" +debconf-set-selections <<< "mysql-server mysql-server/root_password_again password ${MYSQL_ROOT_PWD}" +fi + +# Checking if (any kind of) mysql-client or compatible command installed. This is useful for existing mariadb server +if [ "${INSTALL_MYSQL}" = true ]; then + MYSQL="default-mysql-server default-mysql-client mysql-common" + elif [ -x "$( command -v mysql )" ]; then + MYSQL="" + else + MYSQL="default-mysql-client" +fi + +# Don't do annoying prompts during apt installs +echo +echo -e "${GREY}Updating base Linux OS..." +export DEBIAN_FRONTEND=noninteractive &>> ${LOG_LOCATION} +sudo apt-get update &>> ${LOG_LOCATION} +sudo apt-get upgrade -y &>> ${LOG_LOCATION} +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" +fi + +# Install Guacamole build dependencies. +echo +echo -e "${GREY}Installing dependencies required for building Guacamole, this might take a few minutes..." +apt-get -y install ${JPEGTURBO} ${LIBPNG} ufw htop pwgen wget crudini build-essential libcairo2-dev libtool-bin uuid-dev libavcodec-dev libavformat-dev libavutil-dev \ +libswscale-dev freerdp2-dev libpango1.0-dev libssh2-1-dev libtelnet-dev libvncserver-dev libwebsockets-dev libpulse-dev libssl-dev \ +libvorbis-dev libwebp-dev ghostscript \ +${MYSQL} ${TOMCAT_VERSION} &>> ${LOG_LOCATION} +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" +fi + +# Setup email relay +echo +echo -e "${GREY}Setting up SMTP for backup alerts (requires SMTP relay be permitted from this server's IP address)..." +echo "postfix postfix/mailname string ${EMAIL_DOMAIN} | debconf-set-selections" &>> ${LOG_LOCATION} +DEBIAN_FRONTEND="noninteractive" apt-get install postfix -y &>> ${LOG_LOCATION} +apt-get install mailutils -y &>> ${LOG_LOCATION} +sed -i 's/inet_interfaces = all/inet_interfaces = loopback-only/g' /etc/postfix/main.cf &>> ${LOG_LOCATION} +service postfix restart &>> ${LOG_LOCATION} +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" +fi + +# Download Guacamole Server +echo +echo -e "${GREY}Downloading Guacamole source files..." +wget -q --show-progress -O guacamole-server-${GUAC_VERSION}.tar.gz ${GUAC_SOURCE_LINK}/source/guacamole-server-${GUAC_VERSION}.tar.gz +if [ $? -ne 0 ]; then + echo -e "${RED}Failed to download guacamole-server-${GUAC_VERSION}.tar.gz" 1>&2 + echo -e "${GUAC_SOURCE_LINK}/source/guacamole-server-${GUAC_VERSION}.tar.gz${GREY}" + exit 1 + else + tar -xzf guacamole-server-${GUAC_VERSION}.tar.gz +fi +echo -e "${LGREEN}Downloaded guacamole-server-${GUAC_VERSION}.tar.gz${GREY}" + +# Download Guacamole Client +wget -q --show-progress -O guacamole-${GUAC_VERSION}.war ${GUAC_SOURCE_LINK}/binary/guacamole-${GUAC_VERSION}.war +if [ $? -ne 0 ]; then + echo -e "${RED}Failed to download guacamole-${GUAC_VERSION}.war" 1>&2 + echo -e "${GUAC_SOURCE_LINK}/binary/guacamole-${GUAC_VERSION}.war${GREY}" + exit 1 +fi +echo -e "${LGREEN}Downloaded guacamole-${GUAC_VERSION}.war${GREY}" + +# Download Guacamole authentication extensions +wget -q --show-progress -O guacamole-auth-jdbc-${GUAC_VERSION}.tar.gz ${GUAC_SOURCE_LINK}/binary/guacamole-auth-jdbc-${GUAC_VERSION}.tar.gz +if [ $? -ne 0 ]; then + echo -e "${RED}Failed to download guacamole-auth-jdbc-${GUAC_VERSION}.tar.gz" 1>&2 + echo -e "${GUAC_SOURCE_LINK}/binary/guacamole-auth-jdbc-${GUAC_VERSION}.tar.gz" + exit 1 + else + tar -xzf guacamole-auth-jdbc-${GUAC_VERSION}.tar.gz +fi +echo -e "${LGREEN}Downloaded guacamole-auth-jdbc-${GUAC_VERSION}.tar.gz${GREY}" + +# Download TOTP extension +if [ "${INSTALL_TOTP}" = true ]; then + wget -q --show-progress -O guacamole-auth-totp-${GUAC_VERSION}.tar.gz ${GUAC_SOURCE_LINK}/binary/guacamole-auth-totp-${GUAC_VERSION}.tar.gz + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to download guacamole-auth-totp-${GUAC_VERSION}.tar.gz" 1>&2 + echo -e "${GUAC_SOURCE_LINK}/binary/guacamole-auth-totp-${GUAC_VERSION}.tar.gz" + exit 1 + else + tar -xzf guacamole-auth-totp-${GUAC_VERSION}.tar.gz + fi +echo -e "${LGREEN}Downloaded guacamole-auth-totp-${GUAC_VERSION}.tar.gz${GREY}" +fi + +# Download DUO extension +if [ "${INSTALL_DUO}" = true ]; then + wget -q --show-progress -O guacamole-auth-duo-${GUAC_VERSION}.tar.gz ${GUAC_SOURCE_LINK}/binary/guacamole-auth-duo-${GUAC_VERSION}.tar.gz + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to download guacamole-auth-duo-${GUAC_VERSION}.tar.gz" 1>&2 + echo -e "${GUAC_SOURCE_LINK}/binary/guacamole-auth-duo-${GUAC_VERSION}.tar.gz" + exit 1 + else + tar -xzf guacamole-auth-duo-${GUAC_VERSION}.tar.gz + fi +echo -e "${LGREEN}Downloaded guacamole-auth-duo-${GUAC_VERSION}.tar.gz${GREY}" +fi + +# Download LDAP extension +if [ "${INSTALL_LDAP}" = true ]; then + wget -q --show-progress -O guacamole-auth-ldap-${GUAC_VERSION}.tar.gz ${GUAC_SOURCE_LINK}/binary/guacamole-auth-ldap-${GUAC_VERSION}.tar.gz + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to download guacamole-auth-ldap-${GUAC_VERSION}.tar.gz" 1>&2 + echo -e "${GUAC_SOURCE_LINK}/binary/guacamole-auth-ldap-${GUAC_VERSION}.tar.gz" + exit 1 + else + tar -xzf guacamole-auth-ldap-${GUAC_VERSION}.tar.gz + fi +echo -e "${LGREEN}Downloaded guacamole-auth-ldap-${GUAC_VERSION}.tar.gz${GREY}" +fi + +# Download MySQL connector/j +wget -q --show-progress -O mysql-connector-java-${MYSQLJCON}.tar.gz https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-${MYSQLJCON}.tar.gz +if [ $? -ne 0 ]; then + echo -e "${RED}Failed to download mysql-connector-java-${MYSQLJCON}.tar.gz" 1>&2 + echo -e "https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-${MYSQLJCON}}.tar.gz${GREY}" + exit 1 + else + tar -xzf mysql-connector-java-${MYSQLJCON}.tar.gz +fi +echo -e "${LGREEN}Downloaded mysql-connector-java-${MYSQLJCON}.tar.gz${GREY}" + +echo +echo -e "${LGREEN}Source download complete.${GREY}" + +# Option to pause script here as we might want to make final tweaks to source code just before compiling +#echo -e "${LYELLOW}" +#read -t 15 -p $'Script paused for (optional) tweaking of source before building. Enter to Continue... (Script will auto resume after 15 sec.)\n' +#echo -e "${GREY}" + +# Make Guacamole directories +rm -rf /etc/guacamole/lib/ +rm -rf /etc/guacamole/extensions/ +mkdir -p /etc/guacamole/lib/ +mkdir -p /etc/guacamole/extensions/ + +# Fix for #196 see https://github.com/MysticRyuujin/guac-install/issues/196 +mkdir -p /usr/sbin/.config/freerdp +chown daemon:daemon /usr/sbin/.config/freerdp + +# Fix for #197 see https://github.com/MysticRyuujin/guac-install/issues/197 +mkdir -p /var/guacamole +chown daemon:daemon /var/guacamole + +# Make and install guacd (Guacamole-Server) +cd guacamole-server-${GUAC_VERSION}/ +echo +echo -e "${GREY}Compiling Guacamole-Server from source with with GCC $( gcc --version | head -n1 | grep -oP '\)\K.*' | awk '{print $1}' ), this might take a few minutes...${GREY}" + +# Fix for warnings see #222 https://github.com/MysticRyuujin/guac-install/issues/222 +export CFLAGS="-Wno-error" + +# Configure Guacamole Server source +./configure --with-systemd-dir=/etc/systemd/system &>> ${LOG_LOCATION} +if [ $? -ne 0 ]; then + echo "Failed to configure guacamole-server" + echo "Trying again with --enable-allow-freerdp-snapshots" + ./configure --with-systemd-dir=/etc/systemd/system --enable-allow-freerdp-snapshots + if [ $? -ne 0 ]; then + echo "Failed to configure guacamole-server - again" + exit + fi + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +echo -e "${GREY}Running Make and building the Guacamole-Server application..." +make &>> ${LOG_LOCATION} +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +echo -e "${GREY}Installing Guacamole-Server..." +make install &>> ${LOG_LOCATION} +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi +ldconfig + +# Move files to correct install locations (guacamole-client & Guacamole authentication extensions) +cd .. +mv -f guacamole-${GUAC_VERSION}.war /etc/guacamole/guacamole.war +mv -f guacamole-auth-jdbc-${GUAC_VERSION}/mysql/guacamole-auth-jdbc-mysql-${GUAC_VERSION}.jar /etc/guacamole/extensions/ + +# Create a symbolic link for Tomcat +ln -sf /etc/guacamole/guacamole.war /var/lib/${TOMCAT_VERSION}/webapps/ + +# Move MySQL connector/j files +echo -e "${GREY}Moving mysql-connector-java-${MYSQLJCON}.jar (/etc/guacamole/lib/mysql-connector-java.jar)..." +mv -f mysql-connector-java-${MYSQLJCON}/mysql-connector-java-${MYSQLJCON}.jar /etc/guacamole/lib/mysql-connector-java.jar +echo + +# Move TOTP files +if [ "${INSTALL_TOTP}" = true ]; then + echo -e "${GREY}Moving guacamole-auth-totp-${GUAC_VERSION}.jar (/etc/guacamole/extensions/)..." + mv -f guacamole-auth-totp-${GUAC_VERSION}/guacamole-auth-totp-${GUAC_VERSION}.jar /etc/guacamole/extensions/ + echo +fi + +# Move Duo files +if [ "${INSTALL_DUO}" = true ]; then + echo -e "${GREY}Moving guacamole-auth-duo-${GUAC_VERSION}.jar (/etc/guacamole/extensions/)..." + mv -f guacamole-auth-duo-${GUAC_VERSION}/guacamole-auth-duo-${GUAC_VERSION}.jar /etc/guacamole/extensions/ + echo +fi + +# Move LDAP files +if [ "${INSTALL_LDAP}" = true ]; then + echo -e "${GREY}Moving guacamole-auth-ldap-${GUAC_VERSION}.jar (/etc/guacamole/extensions/)..." + mv -f guacamole-auth-ldap-${GUAC_VERSION}/guacamole-auth-ldap-${GUAC_VERSION}.jar /etc/guacamole/extensions/ + echo +fi + +# Configure guacamole.properties file +rm -f /etc/guacamole/guacamole.properties +touch /etc/guacamole/guacamole.properties +echo "mysql-hostname: ${MYSQL_HOST}" >> /etc/guacamole/guacamole.properties +echo "mysql-port: ${MYSQL_PORT}" >> /etc/guacamole/guacamole.properties +echo "mysql-database: ${GUAC_DB}" >> /etc/guacamole/guacamole.properties +echo "mysql-username: ${GUAC_USER}" >> /etc/guacamole/guacamole.properties +echo "mysql-password: ${GUAC_PWD}" >> /etc/guacamole/guacamole.properties + +# Output Duo configuration settings into guacamole.properties +if [ "${INSTALL_DUO}" = true ]; then + echo "duo-api-hostname: " >> /etc/guacamole/guacamole.properties + echo "duo-integration-key: " >> /etc/guacamole/guacamole.properties + echo "duo-secret-key: " >> /etc/guacamole/guacamole.properties + echo "duo-application-key: " >> /etc/guacamole/guacamole.properties + echo -e "${YELLOW}Duo is installed, it will need to be configured via guacamole.properties${GREY}" +fi + +echo -e "${GREY}Applying branded Guacamole login page and favicons." +# For details on how to brand Guacamole, see https://github.com/Zer0CoolX/guacamole-customize-loginscreen-extension +sudo mv branding.jar /etc/guacamole/extensions +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Restart Tomcat +echo -e "${GREY}Restarting Tomcat service & enable at boot..." +service ${TOMCAT_VERSION} restart +if [ $? -ne 0 ]; then + echo -e "${RED}Failed${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" +fi +# Set Tomcat to start at boot +systemctl enable ${TOMCAT_VERSION} +echo + +# Set MySQL password +export MYSQL_PWD=${MYSQL_ROOT_PWD} + +# Restart MySQL service +if [ "${INSTALL_MYSQL}" = true ]; then + echo -e "${GREY}Restarting MySQL service & enable at boot..." + service mysql restart +fi +if [ $? -ne 0 ]; then + echo -e "${RED}Failed${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Set MySQl to start at boot +systemctl enable mysql + +# Default locations of MySQL config files +for x in /etc/mysql/mariadb.conf.d/50-server.cnf \ + /etc/mysql/mysql.conf.d/mysqld.cnf \ + /etc/mysql/my.cnf \ + ; do + # Check the path exists +if [ -e "${x}" ]; then + # Does it have the necessary section? + if grep -q '^\[mysqld\]$' "${x}"; then + mysqlconfig="${x}" + break + fi +fi +done + +if [ -z "${mysqlconfig}" ]; then + echo -e "${GREY}Couldn't detect MySQL config file - you may need to manually enter timezone settings" + else + # Is there already a value? + if grep -q "^default_time_zone[[:space:]]?=" "${mysqlconfig}"; then + echo -e "MySQL database timezone already defined in ${mysqlconfig}" + else + timezone="$( cat /etc/timezone )" + if [ -z "${timezone}" ]; then + echo -e "Couldn't find system timezone, using UTC$" + timezone="UTC" +fi + echo -e "Setting MySQL database timezone as ${timezone}${GREY}" + # Fix for https://issues.apache.org/jira/browse/GUACAMOLE-760 + mysql_tzinfo_to_sql /usr/share/zoneinfo 2>/dev/null | mysql -u root -D mysql -h ${MYSQL_HOST} -P ${MYSQL_PORT} + crudini --set ${mysqlconfig} mysqld default_time_zone "${timezone}" + # Restart to apply + service mysql restart +fi +fi +if [ $? -ne 0 ]; then + echo -e "${RED}Failed${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Create ${GUAC_DB} and grant ${GUAC_USER} permissions to it +# SQL code +GUAC_USERHost="localhost" +if [[ "${MYSQL_HOST}" != "localhost" ]]; then + GUAC_USERHost="%" + echo -e "${YELLOW}MySQL Guacamole user is set to accept login from any host, please change this for security reasons if possible.${GREY}" +fi + +# Check for ${GUAC_DB} already being there +echo -e "${GREY}Checking MySQL for existing database (${GUAC_DB})" +SQLCODE=" +SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='${GUAC_DB}';" + +# Execute SQL code +MYSQL_RESULT=$( echo ${SQLCODE} | mysql -u root -D information_schema -h ${MYSQL_HOST} -P ${MYSQL_PORT} ) +if [[ $MYSQL_RESULT != "" ]]; then + echo -e "${RED}It appears there is already a MySQL database (${GUAC_DB}) on ${MYSQL_HOST}${GREY}" 1>&2 + echo -e "${RED}Try: mysql -e 'DROP DATABASE ${GUAC_DB}'${GREY}" 1>&2 + #exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Check for ${GUAC_USER} already being there +echo -e "${GREY}Checking MySQL for existing user (${GUAC_USER})" +SQLCODE=" +SELECT COUNT(*) FROM mysql.user WHERE user = '${GUAC_USER}';" + +# Execute SQL code +MYSQL_RESULT=$( echo ${SQLCODE} | mysql -u root -D mysql -h ${MYSQL_HOST} -P ${MYSQL_PORT} | grep '0' ) +if [[ $MYSQL_RESULT == "" ]]; then + echo -e "${RED}It appears there is already a MySQL user (${GUAC_USER}) on ${MYSQL_HOST}${GREY}" 1>&2 + echo -e "${RED}Try: mysql -e \"DROP USER '${GUAC_USER}'@'${GUAC_USERHost}'; FLUSH PRIVILEGES;\"${GREY}" 1>&2 + #exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Create database & user, then set permissions +SQLCODE=" +DROP DATABASE IF EXISTS ${GUAC_DB}; +CREATE DATABASE IF NOT EXISTS ${GUAC_DB}; +CREATE USER IF NOT EXISTS '${GUAC_USER}'@'${GUAC_USERHost}' IDENTIFIED BY \"${GUAC_PWD}\"; +GRANT SELECT,INSERT,UPDATE,DELETE ON ${GUAC_DB}.* TO '${GUAC_USER}'@'${GUAC_USERHost}'; +FLUSH PRIVILEGES;" + +# Execute SQL code +echo ${SQLCODE} | mysql -u root -D mysql -h ${MYSQL_HOST} -P ${MYSQL_PORT} + +# Add Guacamole schema to newly created database +echo -e "${GREY}Adding database tables..." +cat guacamole-auth-jdbc-${GUAC_VERSION}/mysql/schema/*.sql | mysql -u root -D ${GUAC_DB} -h ${MYSQL_HOST} -P ${MYSQL_PORT} +if [ $? -ne 0 ]; then + echo -e "${RED}Failed${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Create guacd.conf. This is later changed to 127.0.0.1 during Nginx reverse proxy install. +echo -e "${GREY}Binding guacd to 0.0.0.0 port 4822..." +cat > /etc/guacamole/guacd.conf <<- "EOF" +[server] +bind_host = 0.0.0.0 +bind_port = 4822 +EOF +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Ensure guacd is started +echo -e "${GREY}Starting guacd service & enable at boot..." +systemctl enable guacd +service guacd stop 2>/dev/null +service guacd start +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Cleanup +echo -e "${GREY}Cleanup install files...${GREY}" +rm -rf guacamole-* +rm -rf mysql-connector-java-* +unset MYSQL_PWD +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Apply Secure MySQL installation settings +if [ "${SECURE_MYSQL}" = true ]; then +echo -e "${GREY}Applying mysql_secure_installation settings...${GREY}" +printf "${MYSQL_ROOT_PWD}\n n\n n\n y\n y\n y\n y\n y\n" | mysql_secure_installation -u root --password="${MYSQL_ROOT_PWD}" &>> ${LOG_LOCATION} +fi +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Done +echo -e ${NC} diff --git a/3-install-nginx.sh b/3-install-nginx.sh new file mode 100644 index 0000000..9954d79 --- /dev/null +++ b/3-install-nginx.sh @@ -0,0 +1,117 @@ +#!/bin/bash +####################################################################################################################### +# Add Nginx reverse proxy fromt end to default Guacamole install +# For Ubuntu / Debian / Raspian +# 3 of 4 +# David Harrop +# August 2023 +####################################################################################################################### + +# Install Nginx +sudo apt-get install nginx -y &>> ${LOG_LOCATION} + +# Configure /etc/nginx/sites-available/(local dns site name) +cat >/etc/nginx/sites-available/$PROXY_SITE <&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Symlink from sites-available to sites-enabled +ln -s /etc/nginx/sites-available/$PROXY_SITE /etc/nginx/sites-enabled/ + +# Make sure default Nginx site is unlinked +unlink /etc/nginx/sites-enabled/default + +# Do mandatory Nginx tweaks for logging actual client IPs through a proxy IP of 127.0.0.1 - DO NOT CHANGE COMMAND FORMATING! +sudo sed -i '/pattern="%h %l %u %t "%r" %s %b"/a \ \n ' /etc/$TOMCAT_VERSION/server.xml +echo -e "${GREY}Configuring Apache Tomcat's internal proxy valve to support proxy client IP4 & IPv6 address passthough for correct logging and ACL support...${GREY}" +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Allow large file transfers through Nginx +sudo sed -i '/client_max_body_size/d' /etc/nginx/nginx.conf # remove this line if it already exists to prevent duplicates +sudo sed -i "/Basic Settings/a \ client_max_body_size 100000000M;" /etc/nginx/nginx.conf # Add the larger file transfer size +echo -e "${GREY}Boosting Nginx's 'maximum body size' parameter to support file transfers > 100 TB through the proxy...${GREY}" +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Bind guacd to localhost and force all Guacamole connections via reverse proxy +echo -e "${GREY}Binding guacd to 127.0.0.1 port 4822..." +cat > /etc/guacamole/guacd.conf <<- "EOF" +[server] +bind_host = 127.0.0.1 +bind_port = 4822 +EOF +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 +else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Update general ufw rules so force traffic via reverse proxy. Only Nginx and SSH will be available over the network. +echo -e "${GREY}Updating firewall rules to allow only SSH and tcp 80/443..." +sudo ufw default allow outgoing &>> ${LOG_LOCATION} +sudo ufw default deny incoming &>> ${LOG_LOCATION} +sudo ufw delete allow 8080/tcp &>> ${LOG_LOCATION} +sudo ufw allow OpenSSH &>> ${LOG_LOCATION} +sudo ufw allow 80/tcp &>> ${LOG_LOCATION} +sudo ufw allow 443/tcp &>> ${LOG_LOCATION} +echo "y" | sudo ufw enable &>> ${LOG_LOCATION} +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Reload everything +echo -e "${GREY}Restaring Guacamole & Ngnix..." +sudo systemctl restart $TOMCAT_VERSION &>> ${LOG_LOCATION} +sudo systemctl restart guacd &>> ${LOG_LOCATION} +sudo systemctl restart nginx &>> ${LOG_LOCATION} +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Done +echo -e ${NC} diff --git a/4a-install-ssl-self-signed-nginx.sh b/4a-install-ssl-self-signed-nginx.sh new file mode 100644 index 0000000..4a55c3f --- /dev/null +++ b/4a-install-ssl-self-signed-nginx.sh @@ -0,0 +1,219 @@ +#!/bin/bash +####################################################################################################################### +# Add self signed SSL certificates to Guacamole with Nginx reverse proxy +# For Ubuntu / Debian / Raspian +# 4a of 4 +# David Harrop +# April 2023 +####################################################################################################################### + +# To run manually and regenerate certificates, this script must be run in the current user enviroment [-E switch] +# to provide certifacate outputs correctly. Runing just as sudo will save certs to sudo's home path +# sudo -E ./4a-install-ssl-self-signed-nginx.sh [your-dns-name.local] [3650] + +# Hack to assist with displaying "$" symbols and " ' quotes in a (cut/pasteable) bash screen output format for Nginx configs +SHOWASTEXT1='$mypwd' +SHOWASTEXT2='"Cert:\LocalMachine\Root"' + +# Discover all IPv4 interfaces addresses to bind to new SSL certficates +echo + echo -e "${GREY}Discovering the default route interface and DNS names to bind with the new SSL certificate..." + # Dump interface info and copy this output to a temp file + DUMP_IPS=$(ip -o addr show up primary scope global | while read -r num dev fam addr rest; do echo ${addr%/*}; done) + echo $DUMP_IPS > $TMP_DIR/dump_ips.txt + + # Filter out anything but numerical characters, then add output to a temporary list + grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}" $TMP_DIR/dump_ips.txt > $TMP_DIR/ip_list.txt + + # Separate each row in the temporary ip_list.txt file and further split each single row into a separate new temp file for each individual IP address found + sed -n '1p' $TMP_DIR/ip_list.txt > $TMP_DIR/1st_ip.txt + #sed -n '2p' $TMP_DIR/ip_list.txt > $TMP_DIR/2nd_ip.txt # uncomment for 2nd interface + #sed -n '3p' $TMP_DIR/ip_list.txt > $TMP_DIR/3rd_ip.txt # uncomment for 3rd interface etc + + # Assign each individual IP address temp file a discreet variable for use in the certificate parameters setup + IP1=$(cat $TMP_DIR/1st_ip.txt) + #IP2=$(cat $TMP_DIR/2nd_ip.txt) # uncomment for 2nd interface + #IP3=$(cat $TMP_DIR/3rd_ip.txt) # uncomment for 3rd interface etc +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${GREEN}OK${GREY}" + echo +fi + +echo +echo -e "${GREY}New self signed SSL certificate attributes are shown below...${GREY}" +echo -e "${DGREY}" + +# Display the new SSL cert parameters. Prompt for change if required +cat <&2 + exit 1 + else + echo -e "${GREEN}OK${GREY}" + echo +fi + +# Backup the current Nginx config before update +cp /etc/nginx/sites-enabled/${PROXY_SITE} $DOWNLOAD_DIR/${PROXY_SITE}-nginx.bak +echo -e "${GREY}Backing up previous Nginx proxy to $DOWNLOAD_DIR/$PROXY_SITE-nginx.bak" +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${GREEN}OK${GREY}" + echo +fi + +# Update Nginx config to accept the new certificates +cat > /etc/nginx/sites-available/$PROXY_SITE < /dev/null +server { + #listen 80 default_server; + root /var/www/html; + index index.html index.htm index.nginx-debian.html; + server_name $PROXY_SITE; + location / { + proxy_pass $GUAC_URL; + proxy_buffering off; + proxy_http_version 1.1; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection \$http_connection; + access_log off; + } + listen 443 ssl; + ssl_certificate /etc/nginx/ssl/cert/$SSLNAME.crt; + ssl_certificate_key /etc/nginx/ssl/private/$SSLNAME.key; + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; +} +server { + return 301 https://\$host\$request_uri; + listen 80 default_server; + root /var/www/html; + index index.html index.htm index.nginx-debian.html; + server_name $PROXY_SITE; + location / { + proxy_pass $GUAC_URL; + proxy_buffering off; + proxy_http_version 1.1; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection \$http_connection; + access_log off; + } +} +EOL + +echo -e "${GREY}Configuring Nginx proxy to use self signed SSL certificates and setting up automatic HTTP to HTTPS redirect...${GREY}" +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${GREEN}OK${GREY}" + echo +fi + +printf "${GREY}+------------------------------------------------------------------------------------------------------------- +${GREEN}+ WINDOWS CLIENT SELF SIGNED SSL BROWSER CONFIG - SAVE THIS BEFORE CONTINUING!${GREY} ++ ++ 1. In ${DOWNLOAD_DIR} is a Windows friendly version of the new certificate ${LYELLOW}$SSLNAME.pfx${GREY} ++ 2. Copy this .pfx file to a location accessible by Windows. ++ 3. Import the PFX file into your Windows client with the below Powershell commands (as administrator): +\n" +echo -e "${SHOWASTEXT1} = ConvertTo-SecureString -String "1234" -Force -AsPlainText" +echo -e "Import-pfxCertificate -FilePath $SSLNAME.pfx -Password "${SHOWASTEXT1}" -CertStoreLocation "${SHOWASTEXT2}"" +echo -e "(Clear your browser cache and restart your browser to test.)" +printf "${GREY}+------------------------------------------------------------------------------------------------------------- +${GREEN}+ LINUX CLIENT SELF SIGNED SSL BROWSER CONFIG - SAVE THIS BEFORE CONTINUING!${GREY} ++ ++ 1. In In ${DOWNLOAD_DIR} is also the Linux native OpenSSL certificate ${LYELLOW}$SSLNAME.crt${GREY} ++ 2. Copy this file to a location accessible by Linux. ++ 3. Import the CRT file into your Linux client certificate store with the below command (as sudo): +\n" +echo -e "certutil -d sql:$HOME/.pki/nssdb -A -t "CT,C,c" -n $SSLNAME -i $SSLNAME.crt" +echo -e "(If certutil is not installed, run apt-get install libnss3-tools)" +printf "+-------------------------------------------------------------------------------------------------------------\n" +echo +echo -e "${LYELLOW}The above SSL browser config instructions are also saved in ${LGREEN}$LOG_LOCATION${GREY}" +echo + +# Reload everything +echo -e "${GREY}Restaring Guacamole & Ngnix..." +sudo systemctl restart $TOMCAT_VERSION +sudo systemctl restart guacd +sudo systemctl restart nginx +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 +else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Done +echo -e ${NC} diff --git a/4b-install-ssl-letsencrypt-nginx.sh b/4b-install-ssl-letsencrypt-nginx.sh new file mode 100644 index 0000000..318b74a --- /dev/null +++ b/4b-install-ssl-letsencrypt-nginx.sh @@ -0,0 +1,158 @@ +#!/bin/bash +####################################################################################################################### +# Add Let's Encrypt SSL Certificates to Guacamole with Nginx reverse proxy +# For Ubuntu / Debian / Raspian +# 4b of 4 +# David Harrop +# April 2023 +####################################################################################################################### + +GREY='\033[0;37m' +RED='\033[0;31m' +LGREEN='\033[0;92m' +NC='\033[0m' #No Colour + +# Announce which script you're running +echo -e "${GREY}" +echo -e "Let's Encrypt SSL configuration for Nginx.." + +############################################################################ +# If running this script standalone un-comment entire below section as we need +# the correct $PROXY_SITE, LE_DNS_NAME and LE_EMAIL values to reconfigure Nginx + +#TOMCAT_VERSION="tomcat9" +#LOG_LOCATION=$(eval echo ~${SUDO_USER})/lets-encrypt-inst.log +#DOWNLOAD_DIR=$(eval echo ~${SUDO_USER}) +#GUAC_URL=http://localhost:8080/guacamole/ +#for file in "/etc/nginx/sites-enabled"/* +#do +#PROXY_SITE="${file##*/}" +#done +#echo +#echo -e "${GREY}Discovering exising proxy sites to configure with SSL...${GREY}" +#if [ $? -ne 0 ]; then +# echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 +# exit 1 +# else +# echo -e "${LGREEN}OK${GREY}" +#fi + +# Get domain name for new Let's encrypt certificate +#while true +#do +#echo -e "${LGREEN}" +#read -p "Enter the public FQDN for your proxy site: " LE_DNS_NAME +#echo +# [ "${LE_DNS_NAME}" != "" ] && break +#done + +# Get admin email for Let's encrypt certificate notifications +#while true +#do +#echo -e "${LGREEN}" +#read -p "Enter the email address for Let's Encrypt notifications : " LE_EMAIL +#echo +# [ "${LE_EMAIL}" != "" ] && break +#done + +#echo -e "${GREY}" + +############################################################################ + +# Install nginx +apt-get update +apt-get install nginx certbot python3-certbot-nginx -y &>> ${LOG_LOCATION} + +# Backup the current Nginx config + cp /etc/nginx/sites-enabled/${PROXY_SITE} $DOWNLOAD_DIR/${PROXY_SITE}-nginx.bak + echo + echo -e "${GREY}Backing up previous Nginx proxy to $DOWNLOAD_DIR/$PROXY_SITE-nginx.bak" +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 +else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Configure Nginx to accept the new certificates +cat > /etc/nginx/sites-available/$PROXY_SITE <&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Bounce Nginx to reload the new Nginx config so certbot config can start continue +systemctl restart nginx + +# Run certbot to create and associate certificates with currenly public IP (must have tcp 80 and 443 open to work) +certbot --nginx -n -d $LE_DNS_NAME --email $LE_EMAIL --agree-tos --redirect --hsts +echo -e +echo -e "${GREY}Let's Encrypt successfully installed. Check for errors above (DNS & firewall are usual culprits).${GREY}" +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Select a random daily time to schedule a daily check for Let's Encrypt certificates due to expire in next 30 days. +# If are any due to expire within a 30 day window, Certbot will attempt to renew automatically renew. +echo -e "${GREY}Scheduling automatic certificate renewals for certificates with < 30 days till expiry.)${GREY}" +#Dump out the current crontab +crontab -l > cron_1 &>> ${LOG_LOCATION} +# Remove any previosly added certbot renewal entries +sed -i '/# certbot renew/d' cron_1 +# Randomly choose a daily update schedule and append this to the cron schedule +HOUR=$(shuf -i 0-23 -n 1) +MINUTE=$(shuf -i 0-59 -n 1) +echo "${MINUTE} ${HOUR} * * * /usr/bin/certbot renew --quiet --pre-hook 'service nginx stop' --post-hook 'service nginx start'" >> cron_1 +# Overwrite old cron settings and cleanup +crontab cron_1 &>> ${LOG_LOCATION} +rm cron_1 +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Reload everything once again +echo -e "${GREY}Restaring Guacamole & Ngnix..." +sudo systemctl restart $TOMCAT_VERSION +sudo systemctl restart guacd +sudo systemctl restart nginx +if [ $? -ne 0 ]; then + echo -e "${RED}Failed. See ${LOG_LOCATION}${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}OK${GREY}" + echo +fi + +# Done +echo -e ${NC} diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ef6b89 --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +# Guacamole 1.5.0 RDP jump server appliance with MFA, Active Directory integration & Nginx SSL reverse proxy + +## Automatic build, install & config script: + + wget https://raw.githubusercontent.com/itiligent/Guacamole-Setup/main/1-setup.sh && chmod +x 1-setup.sh && ./1-setup.sh + + +## Prerequisites: + + Ubuntu / Debian / Raspian + Min 8GB RAM, 40GB HDD + Public or private DNS entries matching the default physical interface IP address. (needed for SSL) + Incoming access on tcp 22, 80 & 443 + + +### All install variables can be set from the first setup script. i.e. Guacamole, Tomcat & MySQL connector versions etc. Follow on screen prompts to install Guacamole, Nginx & SSL. + +### Scripted setup options are: + +### 1. Install default Guacamole with either a local MySQL database or with a remote MySQL instance + + a. Add Guacamole MFA and Auth extensions (DUO, TOTP, LDAP) + b. Add MySQL mysql_secure_installation settings + +### 2. Optionally add a reverse proxy front end to Guacamole of either: + + a) None: Skip Nginx and keep the default Guacamole front end e.g. http://hostname:8080/guacamole + b) Install Nginx with NO SSL (http 80) e.g. http://hostname.local + c) Install Nginx with SELF SIGNED SSL certificates e.g. https://hostname.local + - includes client certificates for Windows & Linux browsers with final SSL client setup instructions. + d) Install Nginx with LET'S ENCRYPT certificates e.g. https://public.site.com + +### 3. After installation, optional hardening scripts are included for : + a. Adding a fail2ban lockdown policy for Guacamole + b. Encryption of internal traffic between the Gaucamole client and Guacd deamon with SSL + To do list: Create hardening scripts for Nginx & MFA for shell access) + +### Items downloaded with the setup command above are setup are placed in the $DOWNLOAD_DIR/guacamole-setup dir as follows + 1. 1-setup.sh - the parent install script itself + 2. 2-install-guacamole.sh - Guacamole install script (inspired by https://github.com/MysticRyuujin/guac-install) + 3. 3-install-nginx.sh - Installs Nginx and auto configures as a front end for Guacamole (optional) + 4. 4a-install-ssl-self-signed-nginx.sh - Configures self signed ssl certs for Nginx (optional) + 5. 4b-install-ssl-letsencrypt-nginx.sh - Installs and configures Let's Encrypt with Guacamole and Nginx (optional) + 6. add-auth-duo.sh - Adds the Duo MFA extensions if not selected at install (optional) + 7. add-auth-ldap.sh - Adds the LDAP Active Directory extension and guides the specific LDAP setup requirements (optional) + 8. add-auth-totp.sh - Adds the TOTP MFA extension if not selected at install (optional) + 9. add-ssl-guac-gaucd.sh - A hardening script to wrap an extra ssl layer between the guacd server and the Guacamole client (optional) + 10. add-fail2ban.sh - Adds and configures fail2ban to secure Guacamole against brute force attacks + 11. backup-guacamole.sh - A simple Guacamole backup script + 12. branding.jar - An extension to customise the Guacomole login screen (optional) + see: https://github.com/Zer0CoolX/guacamole-customize-loginscreen-extension + +Special acknowledgement to MysticRyuujin @ https://github.com/MysticRyuujin/guac-install and Zer0CoolX @ https://github.com/Zer0CoolX/guacamole-customize-loginscreen-extension whos repos were a helpful source of ideas in assembling this project. diff --git a/add-auth-duo.sh b/add-auth-duo.sh new file mode 100644 index 0000000..d6db814 --- /dev/null +++ b/add-auth-duo.sh @@ -0,0 +1,67 @@ +#!/bin/bash +####################################################################################################################### +# Add Duo (MFA) support to Guacamole +# For Ubuntu / Debian / Raspian +# David Harrop +# April 2023 +####################################################################################################################### + +# Prepare text output colours +GREY='\033[0;37m' +DGREY='\033[0;90m' +GREYB='\033[1;37m' +RED='\033[0;31m' +LRED='\033[0;91m' +GREEN='\033[0;32m' +LGREEN='\033[0;92m' +YELLOW='\033[0;33m' +LYELLOW='\033[0;93m' +BLUE='\033[0;34m' +LBLUE='\033[0;94m' +CYAN='\033[0;36m' +LCYAN='\033[0;96m' +MAGENTA='\033[0;35m' +LMAGENTA='\033[0;95m' +NC='\033[0m' #No Colour + +clear + +if ! [ $( id -u ) = 0 ]; then + echo + echo -e "${LGREEN}Please run this script as sudo or root${NC}" 1>&2 + exit 1 +fi + +GUAC_VERSION= +TOMCAT_VERSION= +GUAC_SOURCE_LINK= +echo +wget -q --show-progress -O guacamole-auth-duo-${GUAC_VERSION}.tar.gz ${GUAC_SOURCE_LINK}/binary/guacamole-auth-duo-${GUAC_VERSION}.tar.gz +tar -xzf guacamole-auth-duo-${GUAC_VERSION}.tar.gz +echo +mv -f guacamole-auth-duo-${GUAC_VERSION}/guacamole-auth-duo-${GUAC_VERSION}.jar /etc/guacamole/extensions/ +chmod 664 /etc/guacamole/extensions/guacamole-auth-duo-${GUAC_VERSION}.jar +echo "duo-integration-key: " >> /etc/guacamole/guacamole.properties +echo "duo-secret-key: " >> /etc/guacamole/guacamole.properties +echo "duo-api-hostname: " >> /etc/guacamole/guacamole.properties +echo "duo-application-key: " >> /etc/guacamole/guacamole.properties + +systemctl restart ${TOMCAT_VERSION} +sudo systemctl restart guacd + +echo -e "${LYELLOW}You must now set up your online Duo account with a new 'Web SDK' application." +echo +echo "Next you must copy the API settings from your Duo account into /etc/guacamole/guacamole.properties in the EXACT below format." +echo -e "Be VERY careful to avoid extra trailing spaces or other line feed characters when pasting!${GREY}" +echo +echo "duo-integration-key: ??????????" +echo "duo-api-hostname: ??????????" +echo "duo-secret-key: ??????????" +echo "duo-application-key: (this is locally created - run 'pwgen 40 1' to manually generate this 40 char random value)" +echo +echo "Once this change is complete, restart Guacamole with sudo systemctl restart tomcat9" + +rm -rf guacamole-* + +echo +echo -e ${NC} diff --git a/add-auth-ldap.sh b/add-auth-ldap.sh new file mode 100644 index 0000000..d6ec619 --- /dev/null +++ b/add-auth-ldap.sh @@ -0,0 +1,80 @@ +#!/bin/bash +####################################################################################################################### +# Add Active Directory integration with Guacamole +# For Ubuntu / Debian / Raspian +# David Harrop +# April 2023 +####################################################################################################################### + +# Prepare text output colours +GREY='\033[0;37m' +DGREY='\033[0;90m' +GREYB='\033[1;37m' +RED='\033[0;31m' +LRED='\033[0;91m' +GREEN='\033[0;32m' +LGREEN='\033[0;92m' +YELLOW='\033[0;33m' +LYELLOW='\033[0;93m' +BLUE='\033[0;34m' +LBLUE='\033[0;94m' +CYAN='\033[0;36m' +LCYAN='\033[0;96m' +MAGENTA='\033[0;35m' +LMAGENTA='\033[0;95m' +NC='\033[0m' #No Colour + +clear + +# Check if user is root or sudo + +if ! [ $( id -u ) = 0 ]; then + echo + echo -e "${LGREEN}Please run this script as sudo or root${NC}" 1>&2 + exit 1 +fi + +GUAC_VERSION= +TOMCAT_VERSION= +GUAC_SOURCE_LINK= +echo +echo -e "${LYELLOW}Have you updated this script to reflect your Active Directory settings?${NC}" + +read -p "Do you want to proceed? (yes/no) " yn +echo +case $yn in + y ) echo Beginning LDAP auth config...;; + n ) echo exiting...; + exit;; + * ) echo invalid response; + exit 1;; +esac + +echo +wget -q --show-progress -O guacamole-auth-ldap-${GUAC_VERSION}.tar.gz ${GUAC_SOURCE_LINK}/binary/guacamole-auth-ldap-${GUAC_VERSION}.tar.gz +tar -xzf guacamole-auth-ldap-${GUAC_VERSION}.tar.gz +echo +echo Adding the below config to /etc/guacamole/guacamole.properties +cat <&2 + exit 1 +fi + +GUAC_VERSION= +TOMCAT_VERSION= +GUAC_SOURCE_LINK= +echo +wget -q --show-progress -O guacamole-auth-totp-${GUAC_VERSION}.tar.gz ${GUAC_SOURCE_LINK}/binary/guacamole-auth-totp-${GUAC_VERSION}.tar.gz +tar -xzf guacamole-auth-totp-${GUAC_VERSION}.tar.gz +mv -f guacamole-auth-totp-${GUAC_VERSION}/guacamole-auth-totp-${GUAC_VERSION}.jar /etc/guacamole/extensions/ +chmod 664 /etc/guacamole/extensions/guacamole-auth-totp-${GUAC_VERSION}.jar +systemctl restart ${TOMCAT_VERSION} +systemctl restart guacd + +rm -rf guacamole-* + +echo +echo "Done!" +echo -e ${NC} diff --git a/add-fail2ban.sh b/add-fail2ban.sh new file mode 100644 index 0000000..20cb31a --- /dev/null +++ b/add-fail2ban.sh @@ -0,0 +1,276 @@ +#!/bin/bash +####################################################################################################################### +# Add fail2ban restrictions to Guacamole +# For Ubuntu / Debian / Raspian +# David Harrop +# April 2023 +####################################################################################################################### + +# Prepare text output colours +GREY='\033[0;37m' +DGREY='\033[0;90m' +GREYB='\033[1;37m' +RED='\033[0;31m' +LRED='\033[0;91m' +GREEN='\033[0;32m' +LGREEN='\033[0;92m' +YELLOW='\033[0;33m' +LYELLOW='\033[0;93m' +BLUE='\033[0;34m' +LBLUE='\033[0;94m' +CYAN='\033[0;36m' +LCYAN='\033[0;96m' +MAGENTA='\033[0;35m' +LMAGENTA='\033[0;95m' +NC='\033[0m' #No Colour + +clear + +if ! [ $( id -u ) = 0 ]; then + echo + echo -e "${LGREEN}Please run this script as sudo or root${NC}" 1>&2 + exit 1 +fi + +# Initialise variables +FAIL2BAN_BASE="" +FAIL2BAN_GUAC="" +FAIL2BAN_NGINX="" +FAIL2BAN_SSH="" +TOMCAT_VERSION= + +#Clean up from any previous runs +rm -f /tmp/fail2ban.conf +rm -f /tmp/ip_list.txt +rm -f /tmp/netaddr.txt +rm -f /tmp/fail2ban.update + + +####################################################################################################################### +# Start setup prompts ################################################################################################# +####################################################################################################################### + +# Prompt to install fail2ban base app, default of yes +if [[ -z ${FAIL2BAN_BASE} ]]; then + echo + echo -e -n "${LGREEN}Install Fail2ban? [default y]: ${GREY}" + read PROMPT + if [[ ${PROMPT} =~ ^[Nn]$ ]]; then + FAIL2BAN_BASE=false + else + FAIL2BAN_BASE=true + fi +fi + +# Prompt to install Guacamole fail2ban config defaults, default of no +if [[ -z ${FAIL2BAN_GUAC} ]] && [[ "${FAIL2BAN_BASE}" = true ]]; then + echo -e -n "${GREY}POLICY: Apply Guacamole fail2ban security policy? (y/n) [default n]:${GREY}" + read PROMPT + if [[ ${PROMPT} =~ ^[Yy]$ ]]; then + FAIL2BAN_GUAC=true + else + FAIL2BAN_GUAC=false + fi +fi + +# Prompt to install Nginx fail2ban config defaults , default of no +if [[ -z ${FAIL2BAN_NGINX} ]] && [[ "${FAIL2BAN_BASE}" = true ]]; then + echo -e -n "${GREY}POLICY: Apply Nginx fail2ban security policy? (y/n) [default n]:${GREY}" + read PROMPT + if [[ ${PROMPT} =~ ^[Yy]$ ]]; then + FAIL2BAN_NGINX=true + else + FAIL2BAN_NGINX=false + fi +fi + +# Prompt to install SSH fail2ban config defaults , default of no +if [[ -z ${FAIL2BAN_SSH} ]] && [[ "${FAIL2BAN_BASE}" = true ]]; then + echo -e -n "${GREY}POLICY: Apply SSH fail2ban security policy? (y/n) [default n]:${GREY}" + read PROMPT + if [[ ${PROMPT} =~ ^[Yy]$ ]]; then + FAIL2BAN_SSH=true + else + FAIL2BAN_SSH=false + fi +fi + + +####################################################################################################################### +# Fail2ban base setup ################################################################################################# +####################################################################################################################### + +# Install base fail2ban base application (no policy defined yet) +if [ "${FAIL2BAN_BASE}" = true ]; then + +#Update and install fail2ban (and john for management of config file updates) +sudo apt-get update > /dev/null 2>&1 +sudo apt-get install fail2ban john -y > /dev/null 2>&1 + +# Create the basic jail.local template +cat > /tmp/fail2ban.conf < /tmp/ip_list.txt + +# Loop the list of discovered ips and extract the subnet ID addresses for each interface +FILE=/tmp/ip_list.txt +LINES=$(cat $FILE) +for LINE in $LINES + +do + +tonum() { + if [[ $LINE =~ ([[:digit:]]+)\.([[:digit:]]+)\.([[:digit:]]+)\.([[:digit:]]+) ]]; then + addr=$(( (${BASH_REMATCH[1]} << 24) + (${BASH_REMATCH[2]} << 16) + (${BASH_REMATCH[3]} << 8) + ${BASH_REMATCH[4]} )) + eval "$2=\$addr" + fi +} +toaddr() { + b1=$(( ($1 & 0xFF000000) >> 24)) + b2=$(( ($1 & 0xFF0000) >> 16)) + b3=$(( ($1 & 0xFF00) >> 8)) + b4=$(( $1 & 0xFF )) + eval "$2=\$b1.\$b2.\$b3.\$b4" +} + +if [[ $LINE =~ ^([0-9\.]+)/([0-9]+)$ ]]; then + # CIDR notation + IPADDR=${BASH_REMATCH[1]} + NETMASKLEN=${BASH_REMATCH[2]} + PREFIX=$NETMASKLEN + zeros=$((32-NETMASKLEN)) + NETMASKNUM=0 + for (( i=0; i<$zeros; i++ )); do + NETMASKNUM=$(( (NETMASKNUM << 1) ^ 1 )) + done + NETMASKNUM=$((NETMASKNUM ^ 0xFFFFFFFF)) + toaddr $NETMASKNUM NETMASK +else + IPADDR=${1:-192.168.1.1} + NETMASK=${2:-255.255.255.0} +fi + +tonum $IPADDR IPADDRNUM +tonum $NETMASK NETMASKNUM + +# The logic to calculate network and broadcast +INVNETMASKNUM=$(( 0xFFFFFFFF ^ NETMASKNUM )) +NETWORKNUM=$(( IPADDRNUM & NETMASKNUM )) +BROADCASTNUM=$(( INVNETMASKNUM | NETWORKNUM )) + +toaddr $NETWORKNUM NETWORK +toaddr $BROADCASTNUM BROADCAST + +# Reverse engineer the subnet ID from the calcualted IP address and subnet prefix +IFS=. read -r i1 i2 i3 i4 <<< "$IPADDR" +IFS=. read -r m1 m2 m3 m4 <<< "$NETMASK" + +# Lay out the subnet ID address as a variable +printf -v NETADDR "%d.%d.%d.%d" "$((i1 & m1))" "$((i2 & m2))" "$((i3 & m3))" "$((i4 & m4))" + +#Dump out the calcualted subnet IDs to a file +echo $NETADDR"/"$NETMASKLEN | tr '\n' ' ' | cat >> /tmp/netaddr.txt + +done + +fi + +if [ "${FAIL2BAN_BASE}" = true ]; then +# Now the above loop is done, append the single loopback address to all the discovered the subnet IDs in a single line +sed -i 's/^/127.0.0.1\/24 /' /tmp/netaddr.txt + +# Finally assemble the entire syntaxt of the ignoreip whitelist for insertion into the base fail2ban config +SED_IGNORE=$(echo "ignoreip = ") +SED_NETADDR=$(cat /tmp/netaddr.txt) +sed -i "s|ignoreip \=|${SED_IGNORE}${SED_NETADDR}|g" /tmp/fail2ban.conf + +# Move the new base fail2ban config to the jail.local file +touch /etc/fail2ban/jail.local + +# Apply thhe base config, keeping any pre-existing settings +sudo bash -c 'cat /tmp/fail2ban.conf /etc/fail2ban/jail.local | unique /tmp/fail2ban.update ; cat /tmp/fail2ban.update > /etc/fail2ban/jail.local' + +# Clean up +rm -f /tmp/fail2ban.conf +rm -f /tmp/ip_list.txt +rm -f /tmp/netaddr.txt +rm -f /tmp/fail2ban.update + +# bounce the service to relaod the new config +sudo systemctl restart fail2ban + +# Done +echo +echo -e "${LGREEN}Fail2ban installed.${GREY}" +echo + +else +echo -e "${LGREEN}Fail2ban setup cancelled.${GREY}" + +fi + + +####################################################################################################################### +# Fail2ban optional setup items ####################################################################################### +####################################################################################################################### + +# Create the Guacamole jail.local policy template +cat > /tmp/fail2ban.conf < /etc/fail2ban/jail.local' + + # Backup the defualt Fail2ban Guacamole filter + cp /etc/fail2ban/filter.d/guacamole.conf /etc/fail2ban/filter.d/guacamole.conf.bak + + # Remove the default log search regex + sudo bash -c 'sed -e "/Authentication attempt from/ s/^#*/#/" -i /etc/fail2ban/filter.d/guacamole.conf' + + # Create a new log search regex specific for tomcat logs (as a variable due to complexity of characters for sed syntax) + REGEX='failregex = ^.*WARN o\.a\.g\.r\.auth\.AuthenticationService - Authentication attempt from for user "[^"]*" failed\.$' + #Insert the new regex + sed -i -e "/Authentication attempt from/a ${REGEX}" /etc/fail2ban/filter.d/guacamole.conf + +# Bounce the service to relaod the new config +sudo systemctl restart fail2ban + +# Clean up +rm -f /tmp/fail2ban.conf +rm -f /tmp/ip_list.txt +rm -f /tmp/netaddr.txt +rm -f /tmp/fail2ban.update + +# Done +echo -e "${LGREEN}Guacamole security policy applied${GREY}\n-${SED_NETADDR}are whitelisted from all IP bans.\n- To alter the whitelist edit /etc/fail2ban/jail.local & sudo systemctl restart fail2ban" +echo + + +############## Start Fail2ban NGINX security policy option ############### +if [ "${FAIL2BAN_NGINX}" = true ]; then +echo -e "${LGREEN}Nginx Fail2ban policy not implemented yet.${GREY}" +echo +fi + +############### Start Fail2ban SSH security policy option ################ +if [ "${FAIL2BAN_SSH}" = true ]; then +echo -e "${LGREEN}SSH Fail2ban policy not implemented yet..${GREY}" +echo +fi + +#Done +echo -e ${NC} \ No newline at end of file diff --git a/add-ssl-guac-gaucd.sh b/add-ssl-guac-gaucd.sh new file mode 100644 index 0000000..e7f4d08 --- /dev/null +++ b/add-ssl-guac-gaucd.sh @@ -0,0 +1,96 @@ +#!/bin/bash +####################################################################################################################### +# Harden Guacd <-> Guac client traffic in SSL wrapper +# For Ubuntu / Debian / Raspian +# David Harrop +# April 2023 +####################################################################################################################### + +# Prepare text output colours +GREY='\033[0;37m' +DGREY='\033[0;90m' +GREYB='\033[1;37m' +RED='\033[0;31m' +LRED='\033[0;91m' +GREEN='\033[0;32m' +LGREEN='\033[0;92m' +YELLOW='\033[0;33m' +LYELLOW='\033[0;93m' +BLUE='\033[0;34m' +LBLUE='\033[0;94m' +CYAN='\033[0;36m' +LCYAN='\033[0;96m' +MAGENTA='\033[0;35m' +LMAGENTA='\033[0;95m' +NC='\033[0m' #No Colour + +clear + +if ! [ $( id -u ) = 0 ]; then + echo + echo -e "${LGREEN}Please run this script as sudo or root${NC}" 1>&2 + exit 1 +fi + +# Create the special directory for guacd ssl certfifacte and key. +sudo mkdir /etc/guacamole/ssl +echo +cat < \ +${DB_BACKUP_DIR}${GUAC_DB}-${TODAY}.sql +SQLFILE=${DB_BACKUP_DIR}${GUAC_DB}-${TODAY}.sql +if [ $? -ne 0 ]; then + echo -e "${RED}Backup failed.${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}Backup completed ok.${GREY}" + echo +fi +gzip -f ${SQLFILE} +# Error check and email alerts +if [ $? -ne 0 ]; then + echo -e "${RED}Backup failed.${GREY}" 1>&2 + exit 1 + else + echo -e "${LGREEN}${GUAC_DB} backup was successfully copied to ${DB_BACKUP_DIR}" + #mailx -s "Guacamomle Database Backup Success" ${ALERT_EMAIL} + echo "${GUAC_DB} backup was successfully copied to $DB_BACKUP_DIR}" | mailx -s "Guacamole backup " ${ALERT_EMAIL} +fi + +echo -e ${NC} diff --git a/branding.jar b/branding.jar new file mode 100644 index 0000000..09861b0 Binary files /dev/null and b/branding.jar differ diff --git a/useful-commands.txt b/useful-commands.txt new file mode 100644 index 0000000..07c2573 --- /dev/null +++ b/useful-commands.txt @@ -0,0 +1,39 @@ +kill Nginx!! (slow loris load testing) + +https://ourcodeworld.com/articles/read/949/how-to-perform-a-dos-attack-slow-http-with-slowhttptest-test-your-server-slowloris-protection-in-kali-linux + +slowhttptest -c 10000 -H -g -o ./output_file -i 3 -r 500 -t GET -u http://jumpbox.domain.com -x 24 -p 2 + + +## UAudit Guacamole Connections and User access.## + +Query current connection profile allocations + +mysql -u root -p guacamole_db + +select + guacamole_entity.name, + guacamole_connection.connection_name, + guacamole_connection_permission.permission +from + guacamole_connection + left join guacamole_connection_permission on guacamole_connection_permission.connection_id = guacamole_connection.connection_id + left join guacamole_entity on guacamole_entity.entity_id = guacamole_connection_permission.entity_id +where + guacamole_connection_permission.permission = 'READ' + and guacamole_entity.name != 'guacadmin'; + +Quit to exit + + +## Reset TOTP configuration for a user account +## This is likely not needed in Gucamole 1.40 as the gui provides an option to reset. Kept for reference. + +mysql -u root -p + +use guacamol_db; + +SELECT user_id FROM guacamole_user INNER JOIN guacamole_entity ON guacamole_entity.entity_id = guacamole_user.entity_id WHERE guacamole_entity.name = 'guacadmin'; + +UPDATE guacamole_user_attribute SET attribute_value='false' WHERE attribute_name = 'guac-totp-key-confirmed' and user_id = '1'; +quit;