#!/bin/bash # The most recent program version. _VERSION="2.1.1" _V="2.1" # The program full name PROGRAM_NAME="Docker Deployment" # make sure whiptail is installed command -v whiptail >/dev/null 2>&1 || { echo >&2 "ERROR: ${PROGRAM_NAME} v${_VERSION} script require whiptail." exit 1 } # make sure docker is installed command -v docker >/dev/null 2>&1 || { echo >&2 "ERROR: ${PROGRAM_NAME} v${_VERSION} script require docker." exit 1 } # make sure docker-compose is installed command -v docker-compose >/dev/null 2>&1 || { echo >&2 "ERROR: ${PROGRAM_NAME} v${_VERSION} script require docker-compose." exit 1 } # just clear the screen clear #####################################################################################################################VDM ######################################## The main method function main() { # we check if we have a type and a task # we use the __TRuST__ convention AS "PUBLIC METHODS" to avoid wrong calls, and hide other functions if [ ${#VDM_CONTAINER_TYPE} -ge 1 ] && [ ${#VDM_TASK} -ge 1 ] && isFunc "${VDM_CONTAINER_TYPE}__TRuST__${VDM_TASK}"; then "${VDM_CONTAINER_TYPE}__TRuST__${VDM_TASK}" else mainMenu fi } #####################################################################################################################VDM ######################################## SETUP TRAEFIK function traefik__TRuST__setup() { # load this container type globals # shellcheck disable=SC1090 [ -f "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/.env" ] && source "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/.env" # check if we have secure switch set setSecureState # setup letsencrypt stuff if $VDM_SECURE; then VDM_REMOVE_SECURE='' VDM_HTTP_SCHEME="https" # get the email if not set while [ ${#VDM_SECURE_EMAIL} -le 1 ]; do # get the value VDM_SECURE_EMAIL=$(getInput 'Enter email for the Letsencrypt setup.' '' 'Enter Valid Email') # keep asking [ ${#VDM_SECURE_EMAIL} -ge 1 ] || { showError "You must enter an email for the Letsencrypt setup." } done else VDM_REMOVE_SECURE="#" VDM_HTTP_SCHEME="http" fi # set the main domain if not set setMainDomain ########################## ### export all we need # global export VDM_CONTAINER_TYPE export VDM_REPO_PATH export VDM_PROJECT_PATH # container export VDM_REMOVE_SECURE export VDM_HTTP_SCHEME export VDM_SECURE_EMAIL ## create the directory if it does not yet already exist # shellcheck disable=SC2174 mkdir -p -m 700 "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}" ## place this docker composer file in its place traefikContainer >"${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/docker-compose.yml" ## set permissions chmod 600 "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/docker-compose.yml" # saved the file showNotice "Saved ${VDM_CONTAINER_TYPE}:docker-compose.yml file.\nSetup of this container is complete!" # ask if we should continue to enable if (whiptail --yesno "Would you also like to enable this ${VDM_CONTAINER_TYPE^} container" --defaultno --title "Enable Container" 8 112); then enableContainer "${VDM_CONTAINER_TYPE}" fi ########################## ### unset all no longer needed # container unset VDM_REMOVE_SECURE unset VDM_HTTP_SCHEME unset VDM_SECURE_EMAIL # return a success return 0 } # return the Traefik Container setup yml function traefikContainer() { # we build the yml file cat <"${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/docker-compose.yml" ## set permissions chmod 600 "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/docker-compose.yml" # saved the file showNotice "Saved ${VDM_CONTAINER_TYPE}:docker-compose.yml file.\nSetup of this container is complete!" # ask if we should continue to enable if (whiptail --yesno "Would you also like to enable this ${VDM_CONTAINER_TYPE^} container" --defaultno --title "Enable Container" 8 112); then enableContainer "${VDM_CONTAINER_TYPE}" fi ########################## ### unset all no longer needed # container unset VDM_SUBDOMAIN unset VDM_REMOVE_SECURE unset VDM_ENTRY_POINT # return a success return 0 } # return the Portainer Container setup yml function portainerContainer() { # we build the yml file cat <>"${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/.env" # get the database name needed while [ ${#vdm_database_name} -le 1 ]; do # get the value vdm_database_name=$(getInput "Enter Database Name\n[Text with no spaces that is only underscore and alphabetical]" \ "vdm_io" 'Enter Database Name') # keep asking if empty or does exist [ ${#vdm_database_name} -ge 1 ] || { showError "You must enter a database name!" } done # get the database user name needed while [ ${#vdm_database_user} -le 1 ]; do # get the value vdm_database_user=$(getInput "Enter Database Username\n[Text with no spaces that is only underscore and alphabetical]" \ "vdm_user" 'Enter Database Username') # keep asking if empty or does exist [ ${#vdm_database_user} -ge 1 ] || { showError "You must enter a database username!" } done # get the database user password needed while [ ${#vdm_database_pass} -le 1 ]; do # get the value vdm_database_pass=$(getPassword "Enter Database User Password" \ "$(getRandomPass 20)" 'Enter Database User Password') # keep asking if empty or does exist [ ${#vdm_database_pass} -ge 1 ] || { showError "You must enter a database user password!" } done # get the database root password while [ ${#vdm_database_rootpass} -le 1 ]; do # get the value vdm_database_rootpass=$(getPassword "Enter Database Root Password" \ "$(getRandomPass 40)" 'Enter Database Root Password') # keep asking if empty or does exist [ ${#vdm_database_rootpass} -ge 1 ] || { showError "You must enter a database root password!" } done } # add the projects path setContainerEnvVariable "VDM_PROJECT_PATH=\"${VDM_PROJECT_PATH}\"" # add this value if not set variable [ ${#vdm_database_name} -ge 1 ] && setContainerEnvVariable "VDM_${VDM_ENV_KEY^^}_DB=\"${vdm_database_name}\"" # add this value if not set variable [ ${#vdm_database_user} -ge 1 ] && setContainerEnvVariable "VDM_${VDM_ENV_KEY^^}_DB_USER=\"${vdm_database_user}\"" # add this value if not set variable [ ${#vdm_database_pass} -ge 1 ] && setContainerEnvVariable "VDM_${VDM_ENV_KEY^^}_DB_PASS=\"${vdm_database_pass}\"" # add this value if not set variable [ ${#vdm_database_rootpass} -ge 1 ] && setContainerEnvVariable "VDM_${VDM_ENV_KEY^^}_DB_ROOT=\"${vdm_database_rootpass}\"" ########################## ### export all we need # global export VDM_CONTAINER_TYPE export VDM_REPO_PATH export VDM_PROJECT_PATH # container export VDM_JV export VDM_REMOVE_SECURE export VDM_ENTRY_POINT export VDM_HTTP_SCHEME # container lower export vdm_database_name export vdm_database_user export vdm_database_pass export vdm_database_rootpass # set host file if needed updateHostFile # also set the database domain updateHostFile "${VDM_SUBDOMAIN}db" # create the directory if it does not yet already exist # shellcheck disable=SC2174 mkdir -p -m 700 "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/available/${VDM_SUBDOMAIN}.${VDM_DOMAIN}" # place this docker composer file in its place joomlaContainer >"${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/available/${VDM_SUBDOMAIN}.${VDM_DOMAIN}/docker-compose.yml" # set permissions chmod 600 "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/available/${VDM_SUBDOMAIN}.${VDM_DOMAIN}/docker-compose.yml" # saved the file showNotice "Saved ${VDM_CONTAINER_TYPE}:docker-compose.yml file.\nSetup of this container is complete!" # ask if we should continue to enable if (whiptail --yesno "Would you also like to enable this ${VDM_CONTAINER_TYPE^} container" --defaultno --title "Enable Container" 8 112); then # we set the container details export VDM_CONTAINER="${VDM_SUBDOMAIN}.${VDM_DOMAIN}" # then we enable it enableContainer "${VDM_CONTAINER_TYPE}" fi # display the configurations showJoomlaConfigDetails ########################## ### unset all no longer needed # container unset VDM_SUBDOMAIN unset VDM_JV unset VDM_KEY unset VDM_ENV_KEY unset VDM_REMOVE_SECURE unset VDM_ENTRY_POINT unset VDM_HTTP_SCHEME # container lower unset vdm_database_name unset vdm_database_user unset vdm_database_pass unset vdm_database_rootpass # return a success return 0 } # return the Joomla Container setup yml function joomlaContainer() { # we build the yml file cat <"${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/available/${VDM_USER_NAME}.${VDM_DOMAIN}/docker-compose.yml" # set permissions chmod 600 "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/available/${VDM_USER_NAME}.${VDM_DOMAIN}/docker-compose.yml" # saved the file showNotice "Saved ${VDM_CONTAINER_TYPE}:docker-compose.yml file.\nSetup of this container is complete!" # ask if we should continue to enable if (whiptail --yesno "Would you also like to enable this ${VDM_CONTAINER_TYPE^} container" --defaultno --title "Enable Container" 8 112); then # we set the container details export VDM_CONTAINER="${VDM_USER_NAME}.${VDM_DOMAIN}" # then we enable it enableContainer "${VDM_CONTAINER_TYPE}" fi ########################## ### unset all no longer needed # container unset VDM_PORT unset VDM_USER_NAME unset VDM_KEY unset VDM_ENV_KEY unset VDM_PUBLIC_KEY_GLOBAL_DIR unset VDM_PUBLIC_KEY_U_DIR unset VDM_PROJECT_U_DIR # return a success return 0 } # return the Openssh Container setup yml function opensshContainer() { # get the projects to mount local mount_projects="$1" # we build the yml file # we use 33 as this is the www-data ID cat </dev/null 2>&1; then ### Fix the folder permissions so the active user (1000) can access the files echo "Fixing the folder permissions of ${persistent} Joomla so user:$USER can access them" sudo setfacl -R -m u:"$USER":rwx "${VDM_PROJECT_PATH}/${persistent}/joomla" else echo "[ERROR] Could not fix the permissions of the ${persistent} Joomla so user:$USER can access them." echo "[ERROR] You will need to install: setfacl for this option to work, then run this fix again." fi ### Fix the folder ownership of database folders # id -u systemd-coredump # getent group systemd-coredump | awk -F: '{printf "Group %s with GID=%d\n", $1, $3}' echo "Fixing the folder ownership of ${persistent} database folders to belong to 999:systemd-coredump" # sudo chown -R 999:999 "${VDM_PROJECT_PATH}/${persistent}/db" ### Fix the folder permissions for the database files # echo "Fixing the file and folder permissions for the ${persistent} database files" # # Change the file permissions sudo find "${VDM_PROJECT_PATH}/${persistent}/db" -type f -exec chmod 660 {} \; sudo find "${VDM_PROJECT_PATH}/${persistent}/db" -type d -exec chmod 700 {} \; # show the completion of this permission fix showNotice "Permissions fix completed on (${persistent})." else showError "${VDM_PROJECT_PATH}/${persistent} is not a joomla persistent volume and was skipped." fi done fi fi } # delete persistent volumes function deletePersistentVolumes() { # we first check if we have some volumes if hasDirectories '' "${VDM_PROJECT_PATH}"; then # set some local variables local vdm_delete_volumes local persistent # saved the file showNotice "Only delete persistent volumes of which you have made absolutely sure it's no longer in use!" # get containers to enable vdm_delete_volumes=$(getSelectedDirectories "Select persistent volume\s to delete." \ "${VDM_PROJECT_PATH}" "Select Persistent Volume\s to Delete") # check that we have something, else return to main menu if [ ${#vdm_delete_volumes} -ge 1 ]; then # convert the string to and array IFS=' ' read -r -a vdm_delete_volumes_array <<<"${vdm_delete_volumes[@]}" # loop over the directories to build the for volumes in "${vdm_delete_volumes_array[@]}"; do # remove the " from the string persistent="${volumes//\"/}" # last serious check and then its gone if [ -d "${VDM_PROJECT_PATH}/${persistent}" ] && (whiptail --yesno "Are you absolutely sure you would like to delete this (${persistent}) persistent volume? THIS CAN'T BE UNDONE!\n(we need sudo privileges)" --title "Delete Persistent Volume" 15 112); then # then remove soft link sudo rm -rf "${VDM_PROJECT_PATH}/${persistent}" fi done fi else showError "There are no persistent volumes found in ${VDM_PROJECT_PATH}." fi } # make an update function runUpdate() { # we need sudo permissions if (whiptail --yesno "${USER^}, to update ${PROGRAM_NAME} we need sudo privileges." --title "Give sudo Privileges" 8 112); then # check that we have an access token getAccessToken || return 1 # remove the current version if [ -f /usr/local/bin/docker-deploy ]; then # just backup in case of failure sudo mv /usr/local/bin/docker-deploy /usr/local/bin/docker-deploy.bak fi # some local values local branch # get the target branch to use in our update branch=$(getTargetBranch) # pull the latest version. Master is always the latest if sudo curl --fail -L "https://git.vdm.dev/api/v1/repos/octoleo/docker-deploy/raw/src/docker-deploy?ref=${branch:-master}&access_token=${VDM_ACCESS_TOKEN}" -o /usr/local/bin/docker-deploy 2>/dev/null; then # give success message echo "SUCCESS: Update was successful." # do we have a backup if [ -f /usr/local/bin/docker-deploy.bak ]; then # lets remove it now sudo rm -f /usr/local/bin/docker-deploy.bak fi else # show the error echo >&2 "ERROR: Update failed! Please try again later." # do we have a backup if [ -f /usr/local/bin/docker-deploy.bak ]; then # move backup back sudo mv /usr/local/bin/docker-deploy.bak /usr/local/bin/docker-deploy fi fi # always set the permission again if we have a file if [ -f /usr/local/bin/docker-deploy ]; then # the we make sure its executable sudo chmod +x /usr/local/bin/docker-deploy fi # always exit so the new script can load exit 0 fi } # make an uninstall function runUninstall() { # little notice showNotice "We have three uninstall options: 1) Complete. 2) Just Script. 3) Make your selection." # first safety check if (whiptail --yesno "We are just trying to be careful here, therefore we have a few questions. Please remember there is an auto update option!\n\ Are you absolutely sure you want to continue with the uninstalling process?\n(we need sudo privileges)" --defaultno --title "Uninstalling ${PROGRAM_NAME} v${_VERSION} " 15 112); then # we have four main options if (whiptail --yesno "Okay, So do you want to completely remove everything? This is the most dangerous option! IT CAN'T BE UNDONE!\ We will remove literally everything like this script was never here." --defaultno --title "Complete Uninstall of ${PROGRAM_NAME} v${_VERSION} " 12 112); then if [ -d "${VDM_REPO_PATH}/joomla" ]; then # take down all joomla containers downContainers 'joomla' # now completely remove Joomla configurations sudo rm -fr "${VDM_REPO_PATH}/joomla" fi # Remove all of Openssh Docker stuff if [ -d "${VDM_REPO_PATH}/openssh" ]; then # take down all joomla containers downContainers 'openssh' # now completely remove Joomla configurations sudo rm -fr "${VDM_REPO_PATH}/openssh" fi # Remove all of Portainer Docker stuff if [ -d "${VDM_REPO_PATH}/portainer" ]; then # take down all portainer containers disableContainer 'portainer' # now completely remove Portainer configurations sudo rm -fr "${VDM_REPO_PATH}/portainer" fi # Remove all of Traefik Docker stuff if [ -d "${VDM_REPO_PATH}/traefik" ]; then # take down all traefik containers disableContainer 'traefik' # now completely remove Traefik configurations sudo rm -fr "${VDM_REPO_PATH}/traefik" fi # remove config if found if [ -d "${VDM_SRC_PATH}" ]; then sudo rm -fr "${VDM_SRC_PATH}" fi # remove config if found if [ -d "${VDM_REPO_PATH}" ]; then sudo rm -fr "${VDM_REPO_PATH}" fi elif (whiptail --yesno "Would you like to only remove the actual ${PROGRAM_NAME} v${_V} script but keep all the Docker stuff?" --title "Keep Docker Stuff" 8 112); then # now remove the docker-deploy script if [ -f /usr/local/bin/docker-deploy ]; then sudo rm -f /usr/local/bin/docker-deploy fi # give the final message whiptail --title "${PROGRAM_NAME} v${_VERSION} is UNINSTALLED" --msgbox "\n\ ${PROGRAM_NAME} v${_V} has been uninstalled.\n\n\ We have not touched any of the Docker stuff.\n\ To also remove these you will have to manually pull down the Docker containers (if there are any running) and then remove the following directories.\n\n\ DOCKER: ${VDM_REPO_PATH} \n\ VOLUMES: ${VDM_PROJECT_PATH} \n\ CONFIG: ${VDM_SRC_PATH}" 20 112 # we exit here... where done! exit 0 else # little notice showNotice "This option lets you choose what you keep, so read the questions carefully! IT CAN'T BE UNDONE!" # shellcheck disable=SC1090 # ----------------------------------------------------- MULTI CONTAINERS # Remove all of Joomla Docker stuff if [ -d "${VDM_REPO_PATH}/joomla" ] && (whiptail --yesno "Would you like to completely remove the Joomla Docker yaml configurations in (${VDM_REPO_PATH}/joomla)?" --defaultno --title "Remove Docker Config" 12 112); then # take down all joomla containers downContainers 'joomla' # now completely remove Joomla configurations sudo rm -fr "${VDM_REPO_PATH}/joomla" fi # check if we have possible joomla containers (this will only run if they did not remove it above) if [ -e "${VDM_REPO_PATH}/joomla/enabled" ] && (whiptail --yesno "Would you like to take down Joomla containers?" --defaultno --title "Take Down Containers" 8 112); then # take down all joomla containers downContainers 'joomla' # remove all enabled sudo rm -fr "${VDM_REPO_PATH}/joomla/enabled" fi # Remove all of Openssh Docker stuff if [ -d "${VDM_REPO_PATH}/openssh" ] && (whiptail --yesno "Would you like to completely remove the Openssh Docker yaml configurations in (${VDM_REPO_PATH}/openssh)?" --defaultno --title "Remove Docker Config" 12 112); then # take down all joomla containers downContainers 'openssh' # now completely remove Joomla configurations sudo rm -fr "${VDM_REPO_PATH}/openssh" fi # check if we have possible openssh containers (this will only run if they did not remove it above) if [ -e "${VDM_REPO_PATH}/openssh/enabled" ] && (whiptail --yesno "Would you like to take down Openssh containers?" --defaultno --title "Take Down Containers" 8 112); then # take down all joomla containers downContainers 'openssh' # remove all enabled sudo rm -fr "${VDM_REPO_PATH}/openssh/enabled" fi # ----------------------------------------------------- SINGLE CONTAINER # Remove all of Portainer Docker stuff if [ -d "${VDM_REPO_PATH}/portainer" ] && (whiptail --yesno "Would you like to completely remove the Portainer Docker yaml configurations in (${VDM_REPO_PATH}/portainer)?" --defaultno --title "Remove Docker Config" 12 112); then # take down all portainer containers disableContainer 'portainer' # now completely remove Portainer configurations sudo rm -fr "${VDM_REPO_PATH}/portainer" fi # check if we have possible portainer container (this will only run if they did not remove it above) if [ -f "${VDM_REPO_PATH}/portainer/docker-compose.yml" ] && (whiptail --yesno "Would you like to take down Portainer container?" --defaultno --title "Take Down Container" 8 112); then # take down the container disableContainer 'portainer' fi # Remove all of Traefik Docker stuff if [ -d "${VDM_REPO_PATH}/traefik" ] && (whiptail --yesno "Would you like to completely remove the Traefik Docker yaml configurations in (${VDM_REPO_PATH}/traefik)?" --defaultno --title "Remove Docker Config" 12 112); then # take down all traefik containers disableContainer 'traefik' # now completely remove Traefik configurations sudo rm -fr "${VDM_REPO_PATH}/traefik" fi # check if we have possible traefik container (this will only run if they did not remove it above) if [ -f "${VDM_REPO_PATH}/traefik/docker-compose.yml" ] && (whiptail --yesno "Would you like to take down Traefik container?" --defaultno --title "Take Down Container" 8 112); then # take down the container disableContainer 'traefik' fi # remove config if found if [ -d "${VDM_SRC_PATH}" ] && (whiptail --yesno "Would you like to remove the global docker configurations?" --defaultno --title "Remove Docker Config" 8 112); then sudo rm -fr "${VDM_SRC_PATH}" fi fi # now remove the docker-deploy script if [ -f /usr/local/bin/docker-deploy ]; then sudo rm -f /usr/local/bin/docker-deploy fi # last and final question as this is most dangerous and serious issue. if [ -d "${VDM_PROJECT_PATH}" ] && (whiptail --yesno "Last question, would you like to remove all persistent volumes of your containers in (${VDM_PROJECT_PATH})?" --defaultno --title "Completely DELETE persistent volumes" 12 112); then sudo rm -rf "${VDM_PROJECT_PATH}" fi # give the final message whiptail --title "${PROGRAM_NAME} v${_VERSION} is UNINSTALLED" --msgbox "\n\n\ ${PROGRAM_NAME} v${_V} has been uninstalled." 10 112 # exit the program right now exit 0 fi } # we show the octoleo info quietly octoleoQuietly() { local message # now set the message message=" https://git.vdm.dev/octoleo/docker-deploy Welcome to an Octoleo Program (${PROGRAM_NAME} v$_VERSION) Description With this script we can easily deploy docker containers of Joomla and Openssh. This combination of these tools give rise to a powerful and very secure shared development environment. This program has **command input** options as seen in the menus item called **Show command help**, but these commands are _not the only way_ to set these values. When the values are **omitted** you will be _asked in the terminal_ to manually enter the required values as needed. Furthermore, the use of **env variables** are also heavily used across the script. There are more than one .env file and the script will set those up for you whenever you run a task that make use of env variables. The script will check if those values exist, and if they don't it will ask for them, and store them automatically for future use. That same time the output message to the terminal will show you where the specific .env file can be found. Author Licence Llewellyn van der Merwe GNU GENERAL PUBLIC LICENSE Version 2 --quiet" whiptail --msgbox "${message}" --fb --backtitle " Octoleo" 32 112 } # show the commands help menu function showCommandsHelpMenu() { help=$(showHelp) whiptail --msgbox --scrolltext "${help}" --fb --backtitle " Octoleo" 30 90 } #####################################################################################################################VDM ######################################## MENUS # show Joomla menu function showJoomla() { # menu for dynamic addition local menu_options=() # our counter local i=2 # load the back menu menu_options+=("back" "<-- Return to the main menu.") # setup new container menu_options+=("setup" "Setup new container") # enable existing container hasDirectories 'joomla/available' && menu_options+=("enable" "Enable existing container") && i=$((i + 1)) # disable enabled container hasDirectories 'joomla/enabled' && menu_options+=("disable" "Disable enabled container") && i=$((i + 1)) # take all enabled containers down hasDirectories 'joomla/enabled' && menu_options+=("down" "Take all enabled containers down") && i=$((i + 1)) # pull up all enabled containers hasDirectories 'joomla/enabled' && menu_options+=("up" "Pull up all enabled containers") && i=$((i + 1)) # edit available container hasDirectories 'joomla/available' && menu_options+=("edit" "Edit available container") && i=$((i + 1)) # fix permissions hasDirectories '' "${VDM_PROJECT_PATH}" && menu_options+=("fix" "Fix permissions of folders and files of a container") && i=$((i + 1)) # delete a container hasDirectories 'joomla/available' && menu_options+=("delete" "Delete a container") && i=$((i + 1)) # get the selection CHOICE=$( whiptail --menu "Make your selection" 20 112 $i \ --title "Joomla | ${PROGRAM_NAME} v${_V}" --fb \ --backtitle " Octoleo" --nocancel --notags \ "${menu_options[@]}" 3>&2 2>&1 1>&3 ) case $CHOICE in "setup") setupContainer 'joomla' ;; "edit") editContainer 'joomla' ;; "enable") enableContainer 'joomla' ;; "disable") disableContainer 'joomla' ;; "down") downContainers 'joomla' ;; "up") upContainers 'joomla' ;; "fix") fixContainerPermissions ;; "delete") deleteContainer 'joomla' ;; esac } # show Openssh menu function showOpenssh() { # menu for dynamic addition local menu_options=() # our counter local i=2 # load the back menu menu_options+=("back" "<-- Return to the main menu.") # setup new container menu_options+=("setup" "Setup new container") # enable existing container hasDirectories 'openssh/available' && menu_options+=("enable" "Enable existing container") && i=$((i + 1)) # disable enabled container hasDirectories 'openssh/enabled' && menu_options+=("disable" "Disable enabled container") && i=$((i + 1)) # take all enabled containers down hasDirectories 'openssh/enabled' && menu_options+=("down" "Take all enabled containers down") && i=$((i + 1)) # pull up all enabled containers hasDirectories 'openssh/enabled' && menu_options+=("up" "Pull up all enabled containers") && i=$((i + 1)) # edit available container hasDirectories 'openssh/available' && menu_options+=("edit" "Edit available container") && i=$((i + 1)) # delete a container hasDirectories 'openssh/available' && menu_options+=("delete" "Delete a container") && i=$((i + 1)) # get the selection CHOICE=$( whiptail --menu "Make your selection" 20 112 $i \ --title "Openssh | ${PROGRAM_NAME} v${_V}" --fb \ --backtitle " Octoleo" --nocancel --notags \ "${menu_options[@]}" 3>&2 2>&1 1>&3 ) case $CHOICE in "setup") setupContainer 'openssh' ;; "edit") editContainer 'openssh' ;; "enable") enableContainer 'openssh' ;; "disable") disableContainer 'openssh' ;; "down") downContainers 'openssh' ;; "up") upContainers 'openssh' ;; "delete") deleteContainer 'openssh' ;; esac } # show Traefik menu function showTraefik() { # menu for dynamic addition local menu_options=() # our counter local i=2 # load the back menu menu_options+=("back" "<-- Return to the main menu.") # setup new container menu_options+=("setup" "Setup or rebuild Traefik") # enable existing container [ -f "${VDM_REPO_PATH}/traefik/docker-compose.yml" ] && menu_options+=("enable" "Enable Traefik") && i=$((i + 1)) # disable enabled container [ -f "${VDM_REPO_PATH}/traefik/docker-compose.yml" ] && menu_options+=("disable" "Disable Traefik") && i=$((i + 1)) # edit available container [ -f "${VDM_REPO_PATH}/traefik/docker-compose.yml" ] && menu_options+=("edit" "Edit Traefik") && i=$((i + 1)) # delete traefik container [ -f "${VDM_REPO_PATH}/traefik/docker-compose.yml" ] && menu_options+=("delete" "Delete Traefik") && i=$((i + 1)) # get the selection CHOICE=$( whiptail --menu "Make your selection" 16 112 $i \ --title "Traefik | ${PROGRAM_NAME} v${_V}" --fb \ --backtitle " Octoleo" --nocancel --notags \ "${menu_options[@]}" 3>&2 2>&1 1>&3 ) case $CHOICE in "setup") setupContainer 'traefik' ;; "edit") editContainer 'traefik' ;; "enable") enableContainer 'traefik' ;; "disable") disableContainer 'traefik' ;; "delete") deleteContainer 'traefik' ;; esac } # show Portainer menu function showPortainer() { # menu for dynamic addition local menu_options=() # our counter local i=2 # load the back menu menu_options+=("back" "<-- Return to the main menu.") # setup new container menu_options+=("setup" "Setup or rebuild Portainer") # enable existing container [ -f "${VDM_REPO_PATH}/portainer/docker-compose.yml" ] && menu_options+=("enable" "Enable Portainer") && i=$((i + 1)) # disable enabled container [ -f "${VDM_REPO_PATH}/portainer/docker-compose.yml" ] && menu_options+=("disable" "Disable Portainer") && i=$((i + 1)) # edit available container [ -f "${VDM_REPO_PATH}/portainer/docker-compose.yml" ] && menu_options+=("edit" "Edit Portainer") && i=$((i + 1)) # delete traefik container [ -f "${VDM_REPO_PATH}/portainer/docker-compose.yml" ] && menu_options+=("delete" "Delete Portainer") && i=$((i + 1)) # get the selection CHOICE=$( whiptail --menu "Make your selection" 16 112 $i \ --title "Portainer | ${PROGRAM_NAME} v${_V}" --fb \ --backtitle " Octoleo" --nocancel --notags \ "${menu_options[@]}" 3>&2 2>&1 1>&3 ) case $CHOICE in "setup") setupContainer 'portainer' ;; "edit") editContainer 'portainer' ;; "enable") enableContainer 'portainer' ;; "disable") disableContainer 'portainer' ;; "delete") deleteContainer 'portainer' ;; esac } # show systems help menu function showHelpMenu() { # menu for dynamic addition local menu_options=() # our counter local i=7 # load the back menu menu_options+=("back" "<-- Return to the main menu.") # Show command help menu_options+=("command-help" "Help with commands") # Show folder paths menu_options+=("important-paths" "Important paths") # Edit the global config [ -f "${VDM_SRC_PATH}/.env" ] && menu_options+=("edit-config" "Edit Config") && i=$((i + 1)) # Report an Issue menu_options+=("report-issue" "Report an issue") # Update the whole script menu_options+=("update" "Update ${PROGRAM_NAME,,}") # Uninstall the whole script menu_options+=("uninstall" "Uninstall ${PROGRAM_NAME,,}") # Octoleo details menu_options+=("octoleo" "Octoleo") # get the selection CHOICE=$( whiptail --menu "Make your selection" 16 112 $i \ --title "Help Menu | ${PROGRAM_NAME} v${_V}" --fb \ --backtitle " Octoleo" --nocancel --notags \ "${menu_options[@]}" 3>&2 2>&1 1>&3 ) case $CHOICE in "command-help") showCommandsHelpMenu ;; "important-paths") showImportantPaths ;; "edit-config") editConfigFile ;; "report-issue") showLink "https://git.vdm.dev/octoleo/docker-deploy/issues" "To report an issue go to:" "Report an Issue" ;; "update") runUpdate ;; "uninstall") runUninstall ;; "octoleo") octoleoQuietly ;; esac } # show domains menu function domainsMenu() { # menu for dynamic addition local menu_options=() # our counter local i=2 # load the back menu menu_options+=("back" "<-- Return to the main menu.") # add more domains allowMultiDomains && menu_options+=("add" "Add Domain") && i=$((i + 1)) # remove existing domains [ -f "${VDM_SRC_PATH}/.domains" ] && menu_options+=("delete" "Delete Domain") && i=$((i + 1)) # edit available container allowEditHostFile && menu_options+=("edit" "Edit Host File") && i=$((i + 1)) # get the selection CHOICE=$( whiptail --menu "Make your selection" 16 112 $i \ --title "Domains Menu | ${PROGRAM_NAME} v${_V}" --fb \ --backtitle " Octoleo" --nocancel --notags \ "${menu_options[@]}" 3>&2 2>&1 1>&3 ) case $CHOICE in "add") setMultiDomains ;; "delete") deleteMultiDomains ;; "edit") editHostFile ;; esac } # MAIN MENU function mainMenu() { # menu for dynamic addition local menu_options=() # our counter local i=6 # Joomla containers menu_options+=("joomla" "Joomla Containers") # Openssh containers menu_options+=("openssh" "Openssh Containers") # Traefik container menu_options+=("traefik" "Traefik Container") # Portainer container menu_options+=("portainer" "Portainer Container") # Manage Domains if allowEditHostFile || allowMultiDomains; then menu_options+=("domains" "Manage Domains") i=$((i + 1)) fi # Delete Persistent Volumes hasDirectories '' "${VDM_PROJECT_PATH}" && menu_options+=("delete" "Delete Persistent Volumes") && i=$((i + 1)) # Show help menu_options+=("help" "Main Help Menu") # Octoleo details menu_options+=("quit" "Quit Program") # get the selection while true; do CHOICE=$( whiptail --menu "Make your selection" 20 112 $i \ --title "${PROGRAM_NAME} v${_V}" --fb \ --backtitle " Octoleo" --nocancel --notags \ "${menu_options[@]}" 3>&2 2>&1 1>&3 ) case $CHOICE in "joomla") showJoomla ;; "openssh") showOpenssh ;; "traefik") showTraefik ;; "portainer") showPortainer ;; "domains") domainsMenu ;; "delete") deletePersistentVolumes ;; "help") showHelpMenu ;; "quit") exit ;; esac done } #####################################################################################################################VDM ######################################## GENERAL SHARED FUNCTIONS # show error function showError() { whiptail --title "ERROR | ${PROGRAM_NAME} v${_V}" --msgbox "${1}" "${2:-12}" 112 } # show info function showInfo() { whiptail --title "INFO | ${PROGRAM_NAME} v${_V}" --infobox "${1}" "${2:-12}" 112 } # show notice function showNotice() { whiptail --title "NOTICE | ${PROGRAM_NAME} v${_V}" --msgbox "${1}" "${2:-12}" 112 } # show link function showLink() { whiptail --title "${3:-Open Link} | ${PROGRAM_NAME} v${_V}" --msgbox "${2:-To open the link click here:} ${1}" 8 112 } # show important paths function showImportantPaths() { local message # now set the message message=" ${PROGRAM_NAME} v$_VERSION We have a few important paths that you should know, and use to manually manage your setup. DOCKER: ${VDM_REPO_PATH} SSH: ${VDM_REPO_PATH}/openssh/.ssh VOLUMES: ${VDM_PROJECT_PATH} CONFIG: ${VDM_SRC_PATH} Then we have some key environment variable files that hold very useful and important information. Since we do not store any passwords or other important details in the docker-deploy.yml files they are stored in these environment variable files and its permissions are (600) for security. JOOMLA: ${VDM_REPO_PATH}/joomla/.env OPENSSH: ${VDM_REPO_PATH}/openssh/.env TRAEFIK: ${VDM_REPO_PATH}/traefik/.env PORTAINER: ${VDM_REPO_PATH}/portainer/.env Not all these files or folders may exist, they are created only when needed. " whiptail --msgbox "${message}" --fb --backtitle " Octoleo" 32 112 } # show show the Joomla config details function showJoomlaConfigDetails() { # load the env back to show if set previously # shellcheck disable=SC1090 # [ -f "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/.env" ] && source "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/.env" # set some locals local message # now set the message message=" ${PROGRAM_NAME} v$_VERSION Running the Joomla container for the first time you will need these details: -------------------------------------------------------------------- URL: ${VDM_HTTP_SCHEME}${VDM_SUBDOMAIN}.${VDM_DOMAIN} DATABASE_HOST: mariadb_${VDM_KEY}:3306 -------------------------------------------------------------------- VDM_${VDM_ENV_KEY^^}_DB: ${vdm_database_name} VDM_${VDM_ENV_KEY^^}_DB_USER: ${vdm_database_user} VDM_${VDM_ENV_KEY^^}_DB_PASS: ${vdm_database_pass} VDM_${VDM_ENV_KEY^^}_DB_ROOT: ${vdm_database_rootpass} -------------------------------------------------------------------- These details are securely stored in this environment variable file ${VDM_REPO_PATH}/joomla/.env -------------------------------------------------------------------- Do not remove these details from the file, as it's used to deploy the container. " whiptail --msgbox "${message}" --fb --backtitle " Octoleo" 30 112 } # check if a Directory exist and if it has sub-directories function hasDirectories() { # shellcheck disable=SC2046 # shellcheck disable=SC2012 if [ -d "${2:-$VDM_REPO_PATH}/${1:-}" ] && [ $(ls -A "${2:-$VDM_REPO_PATH}/${1:-}" | wc -l) -ne 0 ]; then return 0 fi return 1 } # we must get a random key function getRandomPass() { # simple basic random # shellcheck disable=SC2046 # shellcheck disable=SC2005 echo $(tr -dc 'A-HJ-NP-Za-km-z2-9' &1 1>&2 2>&3) # return the answer echo "${answer}" } # get a selected directory function getSelectedDirectory() { # the selected folder local answer # set the local path local path="${2:-}" # set local var local folder="${3:-}" # we start the selection array local selected=() # our counter local i=0 # now check if this dir has sub dirs if hasDirectories '' "$path"; then # loop over the directory for dir_ in "${path%/}/"*; do # remove the full path dir_="${dir_/$path/}" # load the selection [ "$folder" = "${dir_#/}" ] && selected+=("${dir_#/}" "${dir_#/}" "ON") || selected+=("${dir_#/}" "${dir_#/}" "OFF") # increment our pointer i=$((i + 1)) done # now make the selection answer=$(whiptail --title "${4:-Make a Selection}" --radiolist \ "${1}" 20 112 $i \ "${selected[@]}" --nocancel --notags 3>&1 1>&2 2>&3) fi # return the answer echo "${answer:-$folder}" } # select multiple directories from one directory function getSelectedDirectories() { # the selected folder local answer # set the local path local path="${2:-}" # we start the selection array local selected=() # our counter local i=0 # now check if this dir has sub dirs if hasDirectories '' "$path"; then # loop over the directory for dir_ in "${path%/}/"*; do # remove the full path dir_="${dir_/$path/}" # load the selection selected+=("${dir_#/}" "${dir_#/}" "OFF") # increment our pointer i=$((i + 1)) done # now make the selection answer=$(whiptail --title "${3:-Make a Selection}" --checklist \ "${1}" 20 112 $i \ "${selected[@]}" --nocancel --notags 3>&1 1>&2 2>&3) # return the answer echo "${answer[@]}" else echo '' fi } # create a yml dashed line function getYMLDashLine() { # get the projects to mount local line="$1" # return line cat <&1 1>&2 2>&3) # return the password echo "${PASSWORD}" } # get and set the access token function getAccessToken() { # we try getting the token twice local t=0 local new_token=false # check that we have an access token, els ask for it while [ ${#VDM_ACCESS_TOKEN} -le 30 ]; do # this is a new token new_token=true # get the value VDM_ACCESS_TOKEN=$(getInput "Enter access token for updates.\n[You can get an access token from https://git.vdm.dev/user/settings/applications]" \ '' 'Access Token') # keep asking if empty if [ ${#VDM_ACCESS_TOKEN} -le 30 ]; then showError "Updates can only be done if you have an access token." # increment our try t=$((t + 1)) # when the user for the second time does not supply the correct value we return if [ $t -eq 2 ]; then return 12 fi fi done # make the token available export VDM_ACCESS_TOKEN # store the token for next time $new_token && setEnvVariable "VDM_ACCESS_TOKEN=\"${VDM_ACCESS_TOKEN}\"" && showNotice "The token has been stored in the environment variable file (${VDM_SRC_PATH}/.env)." # we have success return 0 } # get the target branch to use in update function getTargetBranch() { # now make the selection answer=$(whiptail --title "Select Update Channel" --radiolist --nocancel --notags \ "You can select the update channel you would like to use." 10 80 2 \ "master" "Stable Version" "ON" \ "staging" "Developer Version" "OFF" \ 3>&1 1>&2 2>&3) # return the answer (default master) echo "${answer:-master}" } # get a list of domains locally set function getListDomains() { # get the separator local separator="${1}" # menu for dynamic addition local menu_options=() local domains # get existing domains if [ -f "${VDM_SRC_PATH}/.domains" ] && readarray -t domains <"${VDM_SRC_PATH}/.domains"; then # check that we have values if [ "${#domains[@]}" -gt 0 ]; then # build the menu for domain in "${domains[@]}"; do # load the back menu [ "${#domain}" -ge 1 ] && menu_options+=("${domain}") done # we show a list of options including the option to add another # shellcheck disable=SC2005 echo "$(printf "${separator}%s" "${menu_options[@]}")" fi fi } # to set a global multiple domains file and values function getMultiDomain() { # we set some local stuff local domain_name local domains # menu for dynamic addition local menu_options=() # our counter local i=0 # always add the main domain or (default) first # so we should always have a selection saveMultiDomain "${VDM_DOMAIN}" # now clear the main domain (in case we use getDomain) unset VDM_DOMAIN # get existing domains if [ -f "${VDM_SRC_PATH}/.domains" ] && readarray -t domains <"${VDM_SRC_PATH}/.domains"; then # build the menu if [ "${#domains[@]}" -gt 0 ]; then # loop domains for domain in "${domains[@]}"; do # load the domain if it has a length [ "${#domain}" -ge 1 ] && menu_options+=("${domain}" "${domain}" "OFF") # increment our counter [ "${#domain}" -ge 1 ] && i=$((i + 1)) done # make sure we have domains if [ "${#menu_options[@]}" -gt 0 ]; then # we show a list of options including the option to add another domain_name=$(whiptail --title "Select a Domain" --radiolist --nocancel --notags \ "Select a domain, or none to add another." 30 80 $i \ "${menu_options[@]}" 3>&1 1>&2 2>&3) fi fi fi # check if we have a selection if [ "${domain_name:-none}" = 'none' ]; then # we must let them enter the new domain name getDomain 'Enter a domain name' 'Enter a Domain Name' else # set the new main domain VDM_DOMAIN="${domain_name}" # update the main domain export VDM_DOMAIN fi # add for next time saveMultiDomain "${VDM_DOMAIN}" } # set the VDM domain value function getDomain() { # set some locals local message="Enter main domain name" local title="Enter Main Domain" local tld="${VDM_CONTAINER_TYPE:-vdm}" # get the arg passed if any message="${1:-$message}" title="${2:-$title}" # get the VDM Domain value if not already set while [ ${#VDM_DOMAIN} -le 1 ] || [[ "${VDM_DOMAIN}" != *.* ]]; do # get the value VDM_DOMAIN=$(getInput "${message}\n[must have at least one dot]" \ "${USER:-octoleo}.${tld,,}" "${title}") # keep asking if empty or does exist if [ ${#VDM_DOMAIN} -le 1 ]; then showError "You must enter a domain name!" elif [[ "${VDM_DOMAIN}" != *.* ]]; then showError "You must enter a domain name with at least one dot" fi done # update the main domain export VDM_DOMAIN } # set the main domain function getMainDomain() { # we first check if we have a main domain passed as command argument if $VDM_ARG_DOMAIN; then # if domain passed by arg we just return it getDomain 'Enter a domain name' 'Enter a Domain Name' else # update the main domain with a selection getMultiDomain fi } # set the domain function setDomain() { # allow multi domain setup if allowMultiDomains; then # select a domain name getMainDomain else # set the main domain if not set setMainDomain fi } # set the main domain function setMainDomain() { # set the main domain if not set getDomain # add this value if not set variable setEnvVariable "VDM_DOMAIN=\"${VDM_DOMAIN}\"" } # set the sub domain function setSubDomain() { # make sure it has not been used before, # also make sure its not a port or a ....db while [ ${#VDM_SUBDOMAIN} -le 1 ] || [[ "${VDM_SUBDOMAIN}" =~ [^a-zA-Z] ]] || [ -d "${VDM_REPO_PATH}/${3:-$VDM_CONTAINER_TYPE}/available/${VDM_SUBDOMAIN}.${VDM_DOMAIN}" ] || [ -d "${VDM_REPO_PATH}/${3:-$VDM_CONTAINER_TYPE}/available/${VDM_SUBDOMAIN%db}.${VDM_DOMAIN}" ] || [ "${VDM_SUBDOMAIN}" = "${2}" ]; do # get the value VDM_SUBDOMAIN=$(getInput "Enter sub-domain used for this ${VDM_CONTAINER_TYPE^} container.\n[Text with no spaces that is only alphabetical]" \ "${1}" 'Enter Sub-Domain') # keep asking if empty or does exist if [ ${#VDM_SUBDOMAIN} -ge 1 ] && [ -d "${VDM_REPO_PATH}/${3:-$VDM_CONTAINER_TYPE}/available/${VDM_SUBDOMAIN}.${VDM_DOMAIN}" ] || [ -d "${VDM_REPO_PATH}/${3:-$VDM_CONTAINER_TYPE}/available/${VDM_SUBDOMAIN%db}.${VDM_DOMAIN}" ]; then showError "The sub-domain:${VDM_SUBDOMAIN} is already used. Select another" elif [[ "${VDM_SUBDOMAIN}" =~ [^a-zA-Z] ]]; then showError "You must enter a sub-domain that is only alphabetical!" elif [ ${#VDM_SUBDOMAIN} -le 1 ]; then showError "You must enter a sub-domain!" fi done # make sure it is available export VDM_SUBDOMAIN } # to set a global multiple domains file and values function setMultiDomains() { # set some locals local domain_name local list_domains local question # allow multi domain setup if allowMultiDomains; then # get the list of domains list_domains=$(getListDomains "\n") # the question based on current state [ -f "${VDM_SRC_PATH}/.domains" ] && question="Would you like to add another domain to ${PROGRAM_NAME}?\nThese Domains already set:\n${list_domains}" || question="Would you like to add a domain to ${PROGRAM_NAME}?" # get the key if not set while (whiptail --yesno "${question}" --scrolltext --defaultno --title "Add Domain" 20 112); do # now clear the main domain each time unset VDM_DOMAIN # we must let them enter the new domain name getDomain 'Enter a domain name' 'Enter a Domain Name' # add for next time saveMultiDomain "${VDM_DOMAIN}" # get the list of domains list_domains=$(getListDomains "\n") # update the question question="Would you like to add another domain to ${PROGRAM_NAME}?\nThese Domains already set:\n${list_domains}" done fi } # set the multi domain switch function setMultiDomainSwitch() { # Allow multiple domains if [ "${VDM_MULTI_DOMAIN:-not}" = 'not' ]; then # explain the pros and cons... showNotice "Next you should make the selection of using a single or multiple domain setup.\n\n\ With multiple domain setups you will need to select a main domain each time you setup a new container.\n\ With a single domain setup you setup the main domain once, and then only need setup the subdomains\n\ with each new container." # check the security switch if (whiptail --yesno "Would you like to use a multiple domains setup?" --defaultno --title "Allow Multi Domains" 8 112); then # we set the secure switch VDM_MULTI_DOMAIN=true else VDM_MULTI_DOMAIN=false fi fi # add this value if not set variable setEnvVariable "VDM_MULTI_DOMAIN=${VDM_MULTI_DOMAIN}" # make sure it is available export VDM_MULTI_DOMAIN } # set the unique key function setUniqueKey() { # get the key if not set while [ ${#VDM_KEY} -le 1 ] || [ -d "${VDM_PROJECT_PATH}/${VDM_KEY}" ] || [[ "${VDM_KEY}" =~ [^a-zA-Z] ]]; do # get the value VDM_KEY=$(getInput "${1:-Enter key used for container naming and name of the persistent volume folder.}\n[Text with no spaces that is only alphabetical]" \ '' 'Enter Key') # keep asking if empty or does exist if [ ${#VDM_KEY} -ge 1 ] && [ -d "${VDM_PROJECT_PATH}/${VDM_KEY}" ]; then showError "The key:${VDM_KEY} is already used. Select another" elif [[ "${VDM_KEY}" =~ [^a-zA-Z] ]]; then showError "You must enter a key with no spaces that is only alphabetical!" elif [ ${#VDM_KEY} -le 1 ]; then showError "You must enter a key!" fi done # make sure it is available export VDM_KEY } # set the Environment key function setEnvironmentKey() { # get the env key if not set while [ ${#VDM_ENV_KEY} -le 1 ] || [[ "${VDM_ENV_KEY}" =~ [^a-zA-Z] ]]; do # get the value VDM_ENV_KEY=$(getInput "Enter environment key used to load the container environment variables.\n[Text with no spaces that is only alphabetical UPPERCASE]" \ "${VDM_KEY^^}" 'Enter ENV Key') # keep asking if empty or does exist if [ ${#VDM_ENV_KEY} -le 1 ]; then showError "You must enter an environment key!" elif [[ "${VDM_ENV_KEY}" =~ [^a-zA-Z] ]]; then showError "You must enter an environment key with no spaces that is only alphabetical!" fi done # make sure it is available export VDM_ENV_KEY } # set the update of host files function setUpdateHostFile() { # check if we have secure switch set if [ "${VDM_UPDATE_HOST:-not}" = 'not' ]; then # check the security switch if (whiptail --yesno "Would you like to update your host file with each new domain you add?" --defaultno --title "Update /etc/hosts" 8 112); then # we set the secure switch VDM_UPDATE_HOST=true else VDM_UPDATE_HOST=false fi fi # add this value since its not set variable setEnvVariable "VDM_UPDATE_HOST=${VDM_UPDATE_HOST}" # make sure it is available export VDM_UPDATE_HOST } # set the secure state function setSecureState() { if [ "${VDM_SECURE:-not}" = 'not' ]; then # check the security switch if (whiptail --yesno "Would you like to use Letsencrypt auto setup for your containers [ONLY for server with public static IP]" --defaultno --title "Letsencrypt" 8 112); then # we set the secure switch VDM_SECURE=true else VDM_SECURE=false fi fi # add this value if not set variable setEnvVariable "VDM_SECURE=${VDM_SECURE}" # make sure it is available export VDM_SECURE } # set the networks in place function setNetworks() { # we create the networks docker network inspect traefik_webgateway >/dev/null 2>&1 || docker network create traefik_webgateway docker network inspect openssh_gateway >/dev/null 2>&1 || docker network create openssh_gateway } # to set a global Env Variable function setEnvVariable() { # check if the env file exist if [ -f "${VDM_SRC_PATH}/.env" ]; then grep -q "${1}" "${VDM_SRC_PATH}/.env" || echo "${1}" >>"${VDM_SRC_PATH}/.env" elif (whiptail --yesno "Can we create the ${VDM_SRC_PATH}/.env file" --title "Environment Variable File" 8 112); then # make sure the folder exist mkdir -p "${VDM_SRC_PATH}" # so we creat the file echo "${1}" >"${VDM_SRC_PATH}/.env" # make sure the permissions are secured chmod 600 "${VDM_SRC_PATH}/.env" else showError "The ${VDM_SRC_PATH}/.env file does not exist. So ${1} could not be added!" # we return false return 12 fi # we return true return 0 } # to set a container Env Variable function setContainerEnvVariable() { # check if the env file exist if [ -f "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/.env" ]; then grep -q "${1}" "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/.env" || echo "${1}" >>"${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/.env" elif (whiptail --yesno "Can we create the ${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/.env file" --title "Container Environment Variable File" 8 112); then # make sure the folder exist mkdir -p "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}" # so we creat the file echo "${1}" >"${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/.env" # make sure the permissions are secured chmod 600 "${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/.env" else showError "The ${VDM_REPO_PATH}/${VDM_CONTAINER_TYPE}/.env file does not exist. So ${1} could not be added!" # we return false return 12 fi # we return true return 0 } # take down apache function downApache() { # make sure port 80 is not used by apache command -v apache2 >/dev/null 2>&1 && [[ $(service apache2 status) == *"active (running)"* ]] && { if (whiptail --yesno "Traefik needs port 80/443 and since Apache is also enabled we don't know if is using these ports, so Traefik may not work. Can we disable Apache?\n(we need sudo privileges)" --defaultno --title "Disable Apache" 12 112); then sudo systemctl stop apache2.service sudo systemctl disable apache2.service fi } } # do we allow multiple domains function allowMultiDomains() { # Allow multiple domains setMultiDomainSwitch # allow multi domain setup if $VDM_MULTI_DOMAIN; then # yes we do return 0 fi # now we don't return 12 } # do we allow multiple domains function allowEditHostFile() { # check if we allow host file updates setUpdateHostFile # allow multi domain setup if $VDM_UPDATE_HOST; then # yes we do return 0 fi # now we don't return 12 } # to set a global multiple domain file and values function saveMultiDomain() { # set some locals local domain_name local src_path_domain # set the domain and remove any whitespace domain_name="$(echo -e "${1}" | tr -d '[:space:]')"; src_path_domain="${VDM_SRC_PATH}/.domains"; # check that we have a domain string if [ ${#domain_name} -ge 1 ]; then # check if the file exist if [ -f "${src_path_domain}" ]; then # check if its already set if grep -L "${domain_name}" "${src_path_domain}"; then echo "${domain_name}" >>"${src_path_domain}" fi else # make sure the folder exist mkdir -p "${VDM_SRC_PATH}" # so we creat the file echo "${domain_name}" >"${src_path_domain}" # make sure the permissions are secured chmod 600 "${src_path_domain}" fi fi # we return true return 0 } # delete multiple domains function deleteMultiDomains() { # menu for dynamic addition local menu_options=() local domains # our counter local i=0 # get existing domains if [ -f "${VDM_SRC_PATH}/.domains" ] && readarray -t domains <"${VDM_SRC_PATH}/.domains"; then # check that we have values if [ "${#domains[@]}" -gt 0 ]; then # build the menu for domain in "${domains[@]}"; do # load the back menu menu_options+=("${domain}" "${domain}" "OFF") # increment our pointer i=$((i + 1)) done # now make the selection answer=$(whiptail --title "Delete Domains" --checklist \ "Select the domain/s you would like to delete" 20 112 $i \ "${menu_options[@]}" --nocancel --notags 3>&1 1>&2 2>&3) # check that we have a selection if [ "${#answer}" -gt 0 ]; then # convert the string to and array IFS=' ' read -r -a array_answer <<<"${answer[@]}" # remove the domains for remove in "${array_answer[@]}"; do # set the full path remove_domain="${remove//\"/}" # shellcheck disable=SC2015 grep -vx "${remove_domain}" "${VDM_SRC_PATH}/.domains" >"${VDM_SRC_PATH}/.domains_tmp" && mv "${VDM_SRC_PATH}/.domains_tmp" "${VDM_SRC_PATH}/.domains" || rm "${VDM_SRC_PATH}/.domains_tmp" done fi fi fi } # check if some thing is a function function isFunc() { declare -F "$1" >/dev/null } # update the host file function updateHostFile() { # check if we should add to host file if allowEditHostFile; then # check if already in host file if grep -q "${1:-$VDM_SUBDOMAIN}.${2:-$VDM_DOMAIN}" /etc/hosts; then showNotice "${USER^}, ${1:-$VDM_SUBDOMAIN}.${2:-$VDM_DOMAIN} is already in the /etc/hosts file." elif (whiptail --yesno "${USER^}, to add the ${1:-$VDM_SUBDOMAIN}.${2:-$VDM_DOMAIN} entry to your host file we need sudo privileges." --title "Give sudo Privileges" 8 112); then # add the domain to the host file echo "127.0.0.1 ${1:-$VDM_SUBDOMAIN}.${2:-$VDM_DOMAIN}" | sudo tee -a /etc/hosts >/dev/null # show notice showNotice "${USER^}, ${1:-$VDM_SUBDOMAIN}.${2:-$VDM_DOMAIN} was added to the /etc/hosts file." fi fi } # the manually edit the host file function editHostFile() { # check if we should add to host file if allowEditHostFile; then # if this container is enabled ask if it should be redeployed if (whiptail --yesno "To edit the host file we need sudo privileges.\n[Only continue if you know what your doing!]" --title "Give sudo Privileges" 15 112); then # give little heads-up showNotice "${USER^}, to save the changes you've made or to just close the file again press:\n\n[ctrl+x] with nano on ubuntu." 13 # lets open the file with nano for now sudo "${EDITOR:-nano}" /etc/hosts fi fi } # the manually edit the config file function editConfigFile() { # check if the file exist if [ -f "${VDM_SRC_PATH}/.env" ]; then # give little warning for less command ready people if (whiptail --yesno "Only continue if you know what your doing!\n\n\ ${USER^}, to save the changes you've made or to just close the file again press:\n\n\ [ctrl+x] with nano on ubuntu." --title "Continue to edit ${PROGRAM_NAME} global config." 15 112); then # all direct edit of the global config file "${EDITOR:-nano}" "${VDM_SRC_PATH}/.env" fi fi } #####################################################################################################################VDM ######################################## CLI MENU ʕ•ᴥ•ʔ # help message function showHelp() { cat < set type you would like to work with example: ${0##*/:-} --type joomla ====================================================== --task set type of task you would like to perform example: ${0##*/:-} --task setup ====================================================== --container Directly enabling or disabling a container with the type=joomla and task=enable/disable set The container must exist, which means it was setup previously Used without type and task Joomla-Enable is (default) example: ${0##*/:-} --container "io.vdm.dev" ====================================================== --update to update your install example: ${0##*/:-} --update ====================================================== --access-token to update the program you will need an access token from https://git.vdm.dev/user/settings/applications example: docker-deploy --access-token xxxxxxxxxxx ====================================================== --uninstall to uninstall this script example: ${0##*/:-} --uninstall ====================================================== AVAILABLE FOR TO ANY CONTAINER ====================================================== -k|--key set key for the docker compose container naming !! no spaces allowed in the key !! example: ${0##*/:-} -k="vdm" example: ${0##*/:-} --key="vdm" ====================================================== -e|--env-key set key for the environment variable naming !! no spaces allowed in the key & must be UPPERCASE !! example: ${0##*/:-} -e="VDM" example: ${0##*/:-} --env-key="VDM" ====================================================== -d|--domain set key website domain !! must be domain.tld !! example: ${0##*/:-} -d="joomla.org" example: ${0##*/:-} --domain="joomla.org" ====================================================== -s|--sub-domain set key website sub domain !! no spaces allowed in the sub domain !! example: ${0##*/:-} -s="jcb" example: ${0##*/:-} --sub-domain="jcb" ====================================================== AVAILABLE FOR JOOMLA CONTAINER ====================================================== -j|--joomla-version see available tags here https://hub.docker.com/_/joomla example: ${0##*/:-} -j=3.10 example: ${0##*/:-} --joomla-version=3.10 ====================================================== AVAILABLE FOR OPENSSH CONTAINER ====================================================== -u|--username set username of the container example: ${0##*/:-} -u="ubuntu" example: ${0##*/:-} --username="ubuntu" ====================================================== --uid set container user id example: ${0##*/:-} --uid=1000 ====================================================== --gid set container user group id example: ${0##*/:-} --gid=1000 ====================================================== -p|--port set ssh port to use !! do not use 22 !! example: ${0##*/:-} -p=2239 example: ${0##*/:-} --port=2239 ====================================================== --ssh-dir set ssh directory name found in the .ssh dir of this repo for the container keys This directory has separate files for each public key allowed to access the container example: ${0##*/:-} --ssh-dir="teamname" ====================================================== --sudo switch to add the container user to the sudo group of the container example: ${0##*/:-} --sudo ====================================================== -t|--time-zone