Files
deceptifeed/scripts/install.sh
Ryan Smith fe8947bd54 Change default permissions to 755/644
This commit changes to default permissions on created files and directories from 775/664 to 755/644.
2024-10-23 10:10:52 -07:00

426 lines
14 KiB
Bash

#!/usr/bin/env bash
# =============================================================================
# Variable declarations.
# =============================================================================
INSTALL_DIR="/opt/deceptifeed"
USERNAME="deceptifeed"
TARGET_BIN="${INSTALL_DIR}/bin/deceptifeed"
TARGET_CFG="${INSTALL_DIR}/etc/config.xml"
SOURCE_BIN="deceptifeed"
SOURCE_CFG="default-config.xml"
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
SYSTEMD_CHECK_DIR="/run/systemd/system"
SYSTEMD_DIR="/etc/systemd/system"
SERVICE_SHORT_NAME="deceptifeed"
SYSTEMD_UNIT="${SERVICE_SHORT_NAME}.service"
# =============================================================================
# startup_checks:
# Performs initial checks before the script runs, including:
# 1. If supported, enable colored output.
# 2. Ensure the script is running as root. If not, exit with an error.
# =============================================================================
startup_checks() {
#
# If supported, enable colored output.
#
if [ -t 1 ]; then
# Detect color support.
local NCOLORS=$(tput colors 2>/dev/null)
if [ -n "${NCOLORS}" ] && [ ${NCOLORS} -ge 8 ]; then
# Color support detected. Enable colored output.
RED='\033[1;31m'
GREEN='\033[1;32m'
YELLOW='\033[1;33m'
BLUE='\033[1;34m'
MAGENTA='\033[1;35m'
CYAN='\033[1;36m'
WHITE='\033[1;37m'
DGRAY='\033[1;30m'
LGRAY='\033[0;37m'
CLEAR='\033[m'
fi
fi
#
# Require systemd.
#
if [[ ! -d "${SYSTEMD_CHECK_DIR}" || ! -d "${SYSTEMD_DIR}" ]] || ! command -v systemctl &>/dev/null; then
echo -e "${DGRAY}[${RED}Error${DGRAY}] ${WHITE}This script requires a systemd-based system.${CLEAR}" >&2
echo
exit 1
fi
#
# Ensure the script is running as root.
#
if [ "$(id --user)" -ne 0 ]; then
echo -e "${DGRAY}[${RED}Error${DGRAY}] ${WHITE}This script must be run as root.${CLEAR}" >&2
echo
exit 1
fi
}
# =============================================================================
# upgrade_app:
# Executes the upgrade process. This includes:
# 1. Stop the service.
# 2. Copy the binary to the installation directory.
# 3. Add execute permissions to the binary.
# 4. Run setcap on the binary to allow it to bind to ports < 1024 when
# running as a non-root user.
# 5. Start the service.
# =============================================================================
upgrade_app() {
#
# Prompt for upgrade.
#
echo
echo -e "${YELLOW}Deceptifeed is already installed to: ${BLUE}${INSTALL_DIR}/${CLEAR}"
echo -e "${YELLOW}Would you like to upgrade? ${WHITE}(y/N) ${CLEAR}"
read -r CONFIRM
if [[ "${CONFIRM}" != "y" && "${CONFIRM}" != "Y" ]]; then
echo
echo -e "${WHITE}Upgrade process canceled.${CLEAR}"
echo
exit 0
fi
#
# Print upgrade banner.
#
echo
echo -e " ${WHITE}Upgrading Deceptifeed${CLEAR}"
echo -e " ${DGRAY}=====================${CLEAR}"
echo
#
# Stop the service.
#
echo -e " ${DGRAY}- ${LGRAY}Stopping service: ${CYAN}${SYSTEMD_UNIT}${CLEAR}"
systemctl stop "${SYSTEMD_UNIT}"
#
# Copy the binary.
#
echo -e " ${DGRAY}- ${LGRAY}Replacing binary: ${CYAN}${TARGET_BIN}${CLEAR}"
cp --force "${SOURCE_BIN}" "${TARGET_BIN}"
if [ $? -ne 0 ]; then
echo -e "${DGRAY}[${RED}Error${DGRAY}] ${WHITE}Failed to copy file: ${YELLOW}'${SOURCE_BIN}' ${WHITE}to: ${YELLOW}'${TARGET_BIN}'${CLEAR}" >&2
echo
exit 1
fi
#
# Set file permissions.
#
echo -e " ${DGRAY}- ${LGRAY}Adjusting file permissions.${CLEAR}"
if id "${USERNAME}" >/dev/null 2>&1; then
chown "${USERNAME}":"${USERNAME}" "${TARGET_BIN}"
fi
chmod 755 "${TARGET_BIN}"
setcap cap_net_bind_service=+ep "${TARGET_BIN}"
#
# Start the service.
#
echo -e " ${DGRAY}- ${LGRAY}Starting the service.${CLEAR}"
systemctl start "${SYSTEMD_UNIT}"
#
# Upgrade complete.
#
echo
echo -e "${WHITE} Upgrade complete${BLUE}${CLEAR}"
echo -e "${DGRAY} ================${CLEAR}"
echo
echo -e "${YELLOW} Check service status with: ${CYAN}systemctl status ${SERVICE_SHORT_NAME}${CLEAR}"
echo -e "${YELLOW} Logs are located at: ${CYAN}${INSTALL_DIR}/logs/${CLEAR}"
echo -e "${YELLOW} Configuration file is at: ${CYAN}${TARGET_CFG}${CLEAR}"
echo
echo
}
# =============================================================================
# install_app:
# Executes the installation process. This includes:
# 1. Run the upgrade_app function if a previous installation is detected.
# 2. Create the directory structure.
# 3. Copy the binary and default config to the installation directory.
# 4. Create a service account user for running the application.
# 5. Assign the user ownership and write permissions on the installation
# directory.
# 6. Run setcap on the binary to allow it to bind to ports < 1024 when
# running as a non-root user.
# 7. Create a systemd service, start the service, and configure for automatic
# startup.
# =============================================================================
install_app() {
#
# Locate the application's binary relative to the script's path.
#
if [ -f "${SCRIPT_DIR}/${SOURCE_BIN}" ]; then
# Found in the same directory as the script.
SOURCE_BIN="${SCRIPT_DIR}/${SOURCE_BIN}"
elif [ -f "${SCRIPT_DIR}/../out/${SOURCE_BIN}" ]; then
# Found in ../out relative to the script.
SOURCE_BIN="${SCRIPT_DIR}/../out/${SOURCE_BIN}"
else
# Could not locate.
echo -e "${DGRAY}[${RED}Error${DGRAY}] ${WHITE}Unable to locate the file: ${YELLOW}'${SOURCE_BIN}'${CLEAR}" >&2
echo
exit 1
fi
#
# Locate the configuration file relative to the script's path.
#
if [ -f "${SCRIPT_DIR}/${SOURCE_CFG}" ]; then
# Found in the same directory as the script.
SOURCE_CFG="${SCRIPT_DIR}/${SOURCE_CFG}"
elif [ -f "${SCRIPT_DIR}/../configs/${SOURCE_CFG}" ]; then
# Found in ../configs relative to the script.
SOURCE_CFG="${SCRIPT_DIR}/../configs/${SOURCE_CFG}"
else
# Could not locate.
echo -e "${DGRAY}[${RED}Error${DGRAY}] ${WHITE}Unable to locate the file: ${YELLOW}'${SOURCE_CFG}'${CLEAR}" >&2
echo
exit 1
fi
#
# Upgrade check.
#
if [[ -f "${TARGET_BIN}" && -f "${SYSTEMD_DIR}/${SYSTEMD_UNIT}" ]]; then
# Call the upgrade function.
upgrade_app
exit 0
fi
#
# Print install banner.
#
echo
echo -e " ${WHITE}Installing Deceptifeed${CLEAR}"
echo -e " ${DGRAY}======================${CLEAR}"
echo
echo -e " ${DGRAY}- ${LGRAY}Installing to: ${CYAN}'${INSTALL_DIR}/'"
#
# Create the directory structure.
#
mkdir --parents "${INSTALL_DIR}/bin/" "${INSTALL_DIR}/certs/" "${INSTALL_DIR}/etc/" "${INSTALL_DIR}/logs/"
#
# Copy the binary.
#
cp --force "${SOURCE_BIN}" "${TARGET_BIN}"
if [ $? -ne 0 ]; then
echo -e "${DGRAY}[${RED}Error${DGRAY}] ${WHITE}Failed to copy file: ${YELLOW}'${SOURCE_BIN}' ${WHITE}to: ${YELLOW}'${TARGET_BIN}'${CLEAR}" >&2
echo
exit 1
fi
#
# Copy the configuration file, if it doesn't already exist.
#
if [ -f "${TARGET_CFG}" ]; then
# Don't copy anything. An existing configuration file already exists.
echo -e " ${DGRAY}- ${LGRAY}Keeping existing configuration found at: ${CYAN}'${TARGET_CFG}'"
else
cp --force "${SOURCE_CFG}" "${TARGET_CFG}"
if [ $? -ne 0 ]; then
echo -e "${DGRAY}[${RED}Error${DGRAY}] ${WHITE}Failed to copy file: ${YELLOW}'${SOURCE_CFG}' ${WHITE}to: ${YELLOW}'${TARGET_CFG}'${CLEAR}" >&2
echo
exit 1
fi
fi
#
# Create a new user for running the application.
#
if id "${USERNAME}" >/dev/null 2>&1; then
#
# User already exists.
#
echo -e " ${RED}- ${LGRAY}User ${WHITE}'${USERNAME}' ${LGRAY}already exists. Skipping creation.${CLEAR}"
else
#
# Create the user.
#
echo -e " ${DGRAY}- ${LGRAY}Creating user: ${CYAN}'${USERNAME}'${CLEAR}"
useradd --home-dir "${INSTALL_DIR}" --no-create-home --system --shell /usr/sbin/nologin --user-group "${USERNAME}"
if [ $? -ne 0 ]; then
echo -e "${DGRAY}[${RED}Error${DGRAY}] ${WHITE}Failed to create user: ${YELLOW}'${USERNAME}'${CLEAR}" >&2
echo
exit 1
fi
fi
#
# Set file and directory permissions.
#
echo -e " ${DGRAY}- ${LGRAY}Setting file and directory permissions.${CLEAR}"
chown --recursive "${USERNAME}":"${USERNAME}" "${INSTALL_DIR}"
chmod 755 "${TARGET_BIN}"
chmod 644 "${TARGET_CFG}"
#
# Allow the app to bind to a port < 1024 when running as a non-root user.
#
setcap cap_net_bind_service=+ep "${TARGET_BIN}"
#
# Create a systemd unit file.
#
echo -e " ${DGRAY}- ${LGRAY}Creating service: ${CYAN}'${SYSTEMD_DIR}/${SYSTEMD_UNIT}'${CLEAR}"
if [ ! -f "${SYSTEMD_DIR}/${SYSTEMD_UNIT}" ]; then
cat > "${SYSTEMD_DIR}/${SYSTEMD_UNIT}" << EOF
[Unit]
Description=Deceptifeed
ConditionPathExists=${TARGET_BIN}
After=network.target
[Service]
Type=simple
User=${USERNAME}
Group=${USERNAME}
Restart=on-failure
RestartSec=10
ExecStart=${TARGET_BIN} -config ${TARGET_CFG}
[Install]
WantedBy=multi-user.target
EOF
#
# Reload systemd, enable, and start the service.
#
echo -e " ${DGRAY}- ${LGRAY}Reloading systemd configuration.${CLEAR}"
systemctl daemon-reload
echo -e " ${DGRAY}- ${LGRAY}Configuring the service to start automatically.${CLEAR}"
systemctl enable "${SYSTEMD_UNIT}" &>/dev/null
echo -e " ${DGRAY}- ${LGRAY}Starting the service.${CLEAR}"
systemctl start "${SYSTEMD_UNIT}"
else
#
# Service already exists. Restart it.
#
echo -e " ${RED}- ${LGRAY}Service already exists. Skipping creation.${CLEAR}"
echo -e " ${DGRAY}- ${LGRAY}Restarting the service.${CLEAR}"
systemctl restart "${SYSTEMD_UNIT}"
fi
echo
echo -e "${WHITE} Installation complete${BLUE}${CLEAR}"
echo -e "${DGRAY} =====================${CLEAR}"
echo
echo -e "${YELLOW} Check service status with: ${CYAN}systemctl status ${SERVICE_SHORT_NAME}${CLEAR}"
echo -e "${YELLOW} Logs are located at: ${CYAN}${INSTALL_DIR}/logs/${CLEAR}"
echo -e "${YELLOW} Configuration file is at: ${CYAN}${TARGET_CFG}${CLEAR}"
echo
echo
}
# =============================================================================
# uninstall_app:
# Executes the uninstallation process. This includes:
# 1. Stop, disable, and delete the systemd service.
# 2. Delete the service account user.
# 3. Delete the installation directory.
# =============================================================================
uninstall_app() {
#
# Print uninstall banner.
#
echo
echo -e " ${WHITE}Uninstalling Deceptifeed${CLEAR}"
echo -e " ${DGRAY}========================${CLEAR}"
echo
#
# If the service exists: stop, disable, delete the service, and run daemon-reload.
#
if [ -f "${SYSTEMD_DIR}/${SYSTEMD_UNIT}" ]; then
# Stop the service.
echo -e " ${DGRAY}- ${LGRAY}Stopping service: ${CYAN}'${SYSTEMD_UNIT}'${CLEAR}"
systemctl stop "${SYSTEMD_UNIT}"
# Disable the service.
echo -e " ${DGRAY}- ${LGRAY}Disabling service: ${CYAN}'${SYSTEMD_UNIT}'${CLEAR}"
systemctl disable "${SYSTEMD_UNIT}" &>/dev/null
# Delete the service.
echo -e " ${DGRAY}- ${LGRAY}Deleting: ${CYAN}'${SYSTEMD_DIR}/${SYSTEMD_UNIT}'${CLEAR}"
rm --force "${SYSTEMD_DIR}/${SYSTEMD_UNIT}"
# Reload systemd configuration.
echo -e " ${DGRAY}- ${LGRAY}Reloading the systemd configuration.${CLEAR}"
systemctl daemon-reload
else
echo -e " ${RED}- ${LGRAY}Service does not exist: ${WHITE}'${SYSTEMD_DIR}/${SYSTEMD_UNIT}'${CLEAR}"
echo -e " ${LGRAY}Skipping systemd service cleanup."
fi
#
# Delete the user, if it exists.
#
if id "${USERNAME}" &> /dev/null; then
echo
echo -e "${YELLOW}Delete the user ${BLUE}'${USERNAME}' ${YELLOW}from your system? ${WHITE}(y/N) ${CLEAR}"
read -r CONFIRM
if [[ "${CONFIRM}" == "y" || "${CONFIRM}" == "Y" ]]; then
echo -e " ${DGRAY}- ${LGRAY}Deleting user: ${CYAN}'${USERNAME}'${CLEAR}"
userdel "${USERNAME}"
fi
else
echo -e " ${RED}- ${LGRAY}User ${WHITE}'${USERNAME}' ${LGRAY}does not exist. Skipping deletion."
fi
#
# Delete the installation directory, if it exists.
#
if [ -d "${INSTALL_DIR}" ]; then
echo
echo -e "${YELLOW}The installation directory may contain log files and configuration files."
echo -e "${YELLOW}Are you ready to delete ${BLUE}'${INSTALL_DIR}'${YELLOW}? ${WHITE}(y/N) ${CLEAR}"
read -r CONFIRM
if [[ "${CONFIRM}" == "y" || "${CONFIRM}" == "Y" ]]; then
echo -e " ${DGRAY}- ${LGRAY}Deleting installation directory: ${CYAN}'${INSTALL_DIR}/'${CLEAR}"
rm --recursive --force "${INSTALL_DIR}"
fi
else
echo -e " ${RED}- ${LGRAY}Directory ${WHITE}'${INSTALL_DIR}/' ${LGRAY}does not exist. Skipping deletion."
fi
#
# Uninstall complete.
#
echo
echo -e " ${WHITE}Uninstallation complete${CLEAR}"
echo -e " ${DGRAY}=======================${CLEAR}"
echo
echo -e " ${GREEN}Success${CLEAR}"
echo -e " ${LGRAY}Deceptifeed uninstallation is complete.${CLEAR}"
echo
echo
}
# =============================================================================
# main:
# The primary entry point of the script. This function:
# 1. Calls the startup_checks function to perform initial setup and checks.
# 2. Checks command-line arguments to determine whether to install (default)
# or uninstall the application.
# =============================================================================
main() {
startup_checks
if [[ "$1" == "--uninstall" ]]; then
uninstall_app
exit 0
else
install_app
exit 0
fi
}
# Script execution starts here by calling the main function.
main "$@"