From 374f2e0da0cb43ce0a9bab9c4d46f51d3aaefc3c Mon Sep 17 00:00:00 2001 From: Stefan Pejcic Date: Wed, 8 May 2024 20:28:30 +0200 Subject: [PATCH] Create INSTALL.sh --- INSTALL.sh | 1107 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1107 insertions(+) create mode 100644 INSTALL.sh diff --git a/INSTALL.sh b/INSTALL.sh new file mode 100644 index 0000000..ab222a5 --- /dev/null +++ b/INSTALL.sh @@ -0,0 +1,1107 @@ +#!/bin/bash +################################################################################ +# Script Name: INSTALL.sh +# Description: Install the latest version of OpenPanel +# Usage: cd /home && (curl -sSL https://get.openpanel.co || wget -O - https://get.openpanel.co) | bash +# Author: Stefan Pejcic +# Created: 11.07.2023 +# Last Modified: 08.05.2024 +# Company: openpanel.co +# Copyright (c) OPENPANEL +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +################################################################################ + +# Colors for output +GREEN='\033[0;32m' +RED='\033[0;31m' +RESET='\033[0m' + +# Defaults +INSTALL_TIMEOUT=1800 # 30 min +DEBUG=false +SKIP_APT_UPDATE=false +SKIP_IMAGES=false +REPAIR=false +LOCALES=true +NO_SSH=false + +# Paths +LOG_FILE="openpanel_install.log" +LOCK_FILE="/root/openpanel.lock" +OPENPANEL_DIR="/usr/local/panel/" +OPENPADMIN_DIR="/usr/local/admin/" +OPENCLI_DIR="/usr/local/admin/scripts/" +OPENPANEL_ERR_DIR="/var/log/openpanel/" +SERVICES_DIR="/etc/systemd/system/" +TEMP_DIR="/tmp/" + +# Redirect output to the log file +exec > >(tee -a "$LOG_FILE") 2>&1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +##################################################################### +# # +# START helper functions # +# # +##################################################################### + + +# logo +print_header() { + printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' - + echo -e " ____ _____ _ " + echo -e " / __ \ | __ \ | | " + echo -e " | | | | _ __ ___ _ __ | |__) | __ _ _ __ ___ | | " + echo -e " | | | || '_ \ / _ \| '_ \ | ___/ / _\" || '_ \ / _ \| | " + echo -e " | |__| || |_) || __/| | | | | | | (_| || | | || __/| | " + echo -e " \____/ | .__/ \___||_| |_| |_| \__,_||_| |_| \___||_| " + echo -e " | | " + echo -e " |_| " + + printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' - +} + + +install_started_message(){ + echo -e "" + echo -e "\nStarting the installation of OpenPanel. This process will take approximately 5-10 minutes." + echo -e "During this time, we will:" + echo -e "- Install necessary services and tools." + echo -e "- Create an admin account for you." + echo -e "- Set up the firewall for enhanced security." + echo -e "- Install needed Docker images." + echo -e "- Set up basic hosting plans so you can start right away." + echo -e "\nThank you for your patience. We're setting everything up for your seamless OpenPanel experience!\n" + printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' - + echo -e "" +} + + + +# Display error and exit +radovan() { + echo -e "${RED}Error: $2${RESET}" >&2 + exit $1 +} + + +# print the command and its output if debug, else run and echo to /dev/null +debug_log() { + if [ "$DEBUG" = true ]; then + echo "Running: $@" + "$@" + else + "$@" > /dev/null 2>&1 + fi +} + +# Check if a package is already installed +is_package_installed() { + if [ "$DEBUG" = false ]; then + $PACKAGE_MANAGER -qq list "$1" 2>/dev/null | grep -qE "^ii" + else + $PACKAGE_MANAGER -qq list "$1" | grep -qE "^ii" + echo "Updating package manager.." + fi +} + +# Get server ipv4 from ip.openpanel.co +current_ip=$(curl -s https://ip.openpanel.co || wget -qO- https://ip.openpanel.co) + +# If site is not available, get the ipv4 from the hostname -I +if [ -z "$current_ip" ]; then + current_ip=$(hostname -I | awk '{print $1}') +fi + + +# Fetch the latest version +version=$(curl -s https://update.openpanel.co/) +if [[ $version =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then + version=$version +else + version="0.1.7" +fi + + +# print fullwidth line +print_space_and_line() { + echo " " + printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' - + echo " " +} + + +# Progress bar script + +PROGRESS_BAR_URL="https://raw.githubusercontent.com/pollev/bash_progress_bar/master/progress_bar.sh" +PROGRESS_BAR_FILE="progress_bar.sh" + +wget "$PROGRESS_BAR_URL" -O "$PROGRESS_BAR_FILE" > /dev/null 2>&1 + +if [ ! -f "$PROGRESS_BAR_FILE" ]; then + echo "Failed to download progress_bar.sh" + exit 1 +fi + +# Source the progress bar script +source "$PROGRESS_BAR_FILE" + +# Dsiplay progress bar +FUNCTIONS=( + detect_os_and_package_manager + update_package_manager + install_packages + setup_openpanel + setup_openadmin + configure_docker + + configure_nginx + configure_modsecurity + + + run_mysql_docker_container + setup_ufw + setup_opencli + set_system_cronjob + install_all_locales + helper_function_for_nginx_on_aws_and_azure + download_and_import_docker_images + + configure_mysql + + start_services + cleanup + generate_and_set_ssl_for_panels + clean_apt_cache + verify_license +) + +TOTAL_STEPS=${#FUNCTIONS[@]} +CURRENT_STEP=0 + +update_progress() { + CURRENT_STEP=$((CURRENT_STEP + 1)) + PERCENTAGE=$(($CURRENT_STEP * 100 / $TOTAL_STEPS)) + draw_progress_bar $PERCENTAGE +} + +main() { + # Make sure that the progress bar is cleaned up when user presses ctrl+c + enable_trapping + + # Create progress bar + setup_scroll_area + for func in "${FUNCTIONS[@]}" + do + # Execute each function + $func + update_progress + done + destroy_scroll_area +} + + + +# END helper functions + + + + + + + + + + + +##################################################################### +# # +# START main functions # +# # +##################################################################### + +check_requirements() { + if [ -z "$SKIP_REQUIREMENTS" ]; then + + # https://github.com/stefanpejcic/openpanel/issues/63 + + architecture=$(lscpu | grep Architecture | awk '{print $2}') + + if [ "$architecture" == "aarch64" ]; then + echo -e "${RED}Error: ARM CPU is not supported!${RESET}" >&2 + exit 1 + fi + + # check if the current user is not root + if [ "$(id -u)" != "0" ]; then + echo -e "${RED}Error: you must be root to execute this script.${RESET}" >&2 + exit 1 + # check if OS is MacOS + elif [ "$(uname)" = "Darwin" ]; then + echo -e "${RED}Error: MacOS is not currently supported.${RESET}" >&2 + exit 1 + # check if running inside a container + elif [[ -f /.dockerenv || $(grep -sq 'docker\|lxc' /proc/1/cgroup) ]]; then + echo -e "${RED}Error: running openpanel inside a container is not supported.${RESET}" >&2 + exit 1 + fi + # check if python version is supported + current_python_version=$(python3 --version 2>&1 | cut -d " " -f 2 | cut -d "." -f 1,2 | tr -d '.') + allowed_versions=("39" "310" "311" "312" "38") + if [[ ! " ${allowed_versions[@]} " =~ " ${current_python_version} " ]]; then + echo -e "${RED}Error: Unsupported Python version $current_python_version. No corresponding branch available.${RESET}" >&2 + exit 1 + fi + fi +} + +parse_args() { + for arg in "$@"; do + case $arg in + --skip-requirements) + SKIP_REQUIREMENTS=true + ;; + --skip-panel-check) + SKIP_PANEL_CHECK=true + ;; + --skip-apt-update) + SKIP_APT_UPDATE=true + ;; + --no-locales) + LOCALES=false + ;; + --repair) + REPAIR=true + SKIP_PANEL_CHECK=true + SKIP_REQUIREMENTS=true + ;; + --skip-firewall) + SKIP_FIREWALL=true + ;; + --skip-images) + SKIP_IMAGES=true + ;; + --skip-ssl) + SKIP_SSL=true + ;; + --with_modsec) + MODSEC=true + ;; + --debug) + DEBUG=true + ;; + --ips) + SUPPORT_IPS=true + ;; + --no-ssh) + NO_SSH=true + ;; + --post_install=*) + # Extract path after "--post_install=" + post_install_path="${1#*=}" + ;; + *) + echo "Unknown option: $arg" + exit 1 + ;; + esac + done +} + +detect_installed_panels() { + if [ -z "$SKIP_PANEL_CHECK" ]; then + # Define an associative array with key as the directory path and value as the error message + declare -A paths=( + ["/usr/local/panel"]="You already have OpenPanel installed. ${RESET}\nInstead, did you want to update? Run ${GREEN}'opencli update --force' to update OpenPanel." + ["/usr/local/cpanel/whostmgr"]="cPanel WHM is installed. OpenPanel only supports servers without any hosting control panel installed." + ["/opt/psa/version"]="Plesk is installed. OpenPanel only supports servers without any hosting control panel installed." + ["/usr/local/psa/version"]="Plesk is installed. OpenPanel only supports servers without any hosting control panel installed." + ["/usr/local/CyberPanel"]="CyberPanel is installed. OpenPanel only supports servers without any hosting control panel installed." + ["/usr/local/directadmin"]="DirectAdmin is installed. OpenPanel only supports servers without any hosting control panel installed." + ["/usr/local/cwpsrv"]="CentOS Web Panel (CWP) is installed. OpenPanel only supports servers without any hosting control panel installed." + ["/usr/local/httpd"]="Apache WebServer is already installed. OpenPanel only supports servers without any webservers installed." + ["/usr/local/apache2"]="Apache WebServer is already installed. OpenPanel only supports servers without any webservers installed." + ["/usr/sbin/httpd"]="Apache WebServer is already installed. OpenPanel only supports servers without any webservers installed." + ["/usr/lib/nginx"]="Nginx WebServer is already installed. OpenPanel only supports servers without any webservers installed." + ) + + for path in "${!paths[@]}"; do + if [ -d "$path" ] || [ -e "$path" ]; then + radovan 1 "${paths[$path]}" + fi + done + + echo -e "${GREEN}No currently installed hosting control panels or webservers found. Proceeding with the installation process.${RESET}" + fi +} + +detect_os_and_package_manager() { + if [ -f "/etc/os-release" ]; then + . /etc/os-release + case "$ID" in + "debian"|"ubuntu") + PACKAGE_MANAGER="apt-get" + ;; + "centos"|"cloudlinux"|"rhel"|"fedora"|"almalinux") + PACKAGE_MANAGER="yum" + if [ "$(command -v dnf)" ]; then + PACKAGE_MANAGER="dnf" + fi + ;; + *) + echo -e "${RED}Unsupported distribution: $ID. Exiting.${RESET}" + echo -e "${RED}INSTALL FAILED${RESET}" + exit 1 + ;; + esac + else + echo -e "${RED}Could not detect Linux distribution. Exiting..${RESET}" + echo -e "${RED}INSTALL FAILED${RESET}" + exit 1 + fi +} + + +install_all_locales() { + if [ -z "$LOCALES" ]; then + + # OpenPanel translations + # + # https://dev.openpanel.co/localization.html + # + + debug_log "Installing FR, DE, TR locales." + + # FR + debug_log "cd ${OPENPANEL_DIR} && pybabel init -i messages.pot -d translations -l fr" + debug_log "wget -O ${OPENPANEL_DIR}translations/fr/LC_MESSAGES/messages.po https://raw.githubusercontent.com/stefanpejcic/openpanel-translations/main/fr-fr/messages.pot" + + # DE + debug_log "cd ${OPENPANEL_DIR} && pybabel init -i messages.pot -d translations -l de" + debug_log "wget -O ${OPENPANEL_DIR}translations/de/LC_MESSAGES/messages.po https://raw.githubusercontent.com/stefanpejcic/openpanel-translations/main/de-de/messages.pot" + + # TR + debug_log "cd ${OPENPANEL_DIR} && pybabel init -i messages.pot -d translations -l tr" + debug_log "wget -O ${OPENPANEL_DIR}translations/tr/LC_MESSAGES/messages.po https://raw.githubusercontent.com/stefanpejcic/openpanel-translations/main/tr-tr/messages.pot" + + debug_log "pybabel compile -d translations" + + fi +} + + + +check_lock_file_age() { + if [ -z "$REPAIR" ]; then + rm "$LOCK_FILE" + # and if lock file exists + if [ -e "$LOCK_FILE" ]; then + local current_time=$(date +%s) + local file_time=$(stat -c %Y "$LOCK_FILE") + local age=$((current_time - file_time)) + + if [ "$age" -ge "$INSTALL_TIMEOUT" ]; then + echo -e "${GREEN}Identified a prior interrupted OpenPanel installation; initiating a fresh installation attempt.${RESET}" + rm "$LOCK_FILE" # Remove the old lock file + else + echo -e "${RED}Detected another OpenPanel installation already running. Exiting.${RESET}" + exit 1 + fi + else + # Create the lock file + touch "$LOCK_FILE" + echo "OpenPanel installation started at: $(date)" + fi + fi +} + + +clean_apt_cache(){ + # clear /var/cache/apt/archives/ + apt-get clean + + # TODO: cover https://github.com/debuerreotype/debuerreotype/issues/95 +} + +setup_ufw() { + if [ -z "$SKIP_FIREWALL" ]; then + echo "Setting up the firewall.." + debug_log wget -qO /usr/local/bin/ufw-docker https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker > /dev/null 2>&1 && + debug_log chmod +x /usr/local/bin/ufw-docker + + + + # block all docker ports so we can manually open only what is needed + debug_log ufw-docker install + debug_log ufw allow 80/tcp #http + debug_log ufw allow 53 #dns + debug_log ufw allow 443/tcp # https + debug_log ufw allow 2083/tcp #openpanel + debug_log ufw allow 2087/tcp #openadmin + + + if [ "$NO_SSH" = false ]; then + + # whitelist user running the script + ip_of_user_running_the_script=$(w -h | grep -m1 -oP '\d+\.\d+\.\d+\.\d+') + debug_log ufw allow from $ip_of_user_running_the_script + + # close port 22 + debug_log ufw allow 22 #ssh + fi + + if [ "$SUPPORT_IPS" = true ]; then + # Whitelisting our VPN ip addresses from https://ip.openpanel.co/ips/ + ip_list=$(curl -s https://ip.openpanel.co/ips/) + ip_list=$(echo "$ip_list" | sed 's/
/\n/g') + + debug_log "Whitelisting IPs from https://ip.openpanel.co/ips/" + + while IFS= read -r ip; do + ip=$(echo "$ip" | tr -d '[:space:]') + debug_log ufw allow from $ip + done <<< "$ip_list" + fi + + debug_log ufw --force enable + debug_log ufw reload + fi +} + +update_package_manager() { + if [ "$SKIP_APT_UPDATE" = false ]; then + echo "Updating package manager.." + debug_log $PACKAGE_MANAGER update -y + fi +} + +install_packages() { + + echo "Installing required services.." + + # https://www.faqforge.com/linux/fixed-ubuntu-apt-get-upgrade-auto-restart-services/ + + debug_log sed -i 's/#$nrconf{restart} = '"'"'i'"'"';/$nrconf{restart} = '"'"'a'"'"';/g' /etc/needrestart/needrestart.conf + + packages=("python3-flask" "python3-pip" "docker.io" "mysql-client-core-8.0" "docker-compose" "nginx" "zip" "unzip" "bind9" "gunicorn" "jq" "ufw" "jc" "certbot" "python3-certbot-nginx" "sqlite3" "geoip-bin") + + if [ "$PACKAGE_MANAGER" == "apt-get" ]; then + #only once.. + debug_log $PACKAGE_MANAGER -qq install apt-transport-https ca-certificates -y + + echo "Updating certificates.." + if [ "$DEBUG" = false ]; then + update-ca-certificates > /dev/null 2>&1 + else + update-ca-certificates + fi + + echo -e "Installing services.." + + for package in "${packages[@]}"; do + echo -e "Installing ${GREEN}$package${RESET}" + debug_log $PACKAGE_MANAGER -qq install "$package" -y + done + + for package in "${packages[@]}"; do + if is_package_installed "$package"; then + echo -e "${GREEN}$package is already installed. Skipping.${RESET}" + else + debug_log $PACKAGE_MANAGER -qq install "$package" + if [ $? -ne 0 ]; then + echo "Error: Installation of $package failed." + exit 1 + fi + fi + done + + + elif [ "$PACKAGE_MANAGER" == "yum" ]; then + for package in "${packages[@]}"; do + echo -e "Installing ${GREEN}$package${RESET}" + $PACKAGE_MANAGER install "$package" -y + done + elif [ "$PACKAGE_MANAGER" == "dnf" ]; then + # MORA DRUGI ZA ALMU.. + packages=("python3-flask" "python3-pip" "docker-ce" "docker-compose" "docker-ce-cli" "mysql-client-core-8.0" "containerd.io" "docker-compose-plugin" "nginx" "zip" "unzip" "jq" "ufw" "certbot" "python3-certbot-nginx" "sqlite3" "geoip-bin") + + #utils must be added first, then install from that repo + dnf install yum-utils -y + yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo + + # needed for ufw and gunicorn + dnf install epel-release + + # ovo za gunicorn + dnf install python3-pip python3-devel gcc -y + + # bind radi ovako + dnf install bind bind-utils -y + + for package in "${packages[@]}"; do + echo -e "Installing ${GREEN}$package${RESET}" + $PACKAGE_MANAGER install "$package" -y + done + #gunicorn mora preko pip na almi.. + pip3 install gunicorn flask + else + echo -e "${RED}Unsupported package manager: $PACKAGE_MANAGER${RESET}" + return 1 + fi +} + + +configure_nginx() { + + # Nginx + + echo "Setting Nginx configuration.." + + # https://dev.openpanel.co/services/nginx + debug_log cp -fr ${OPENPANEL_DIR}conf/nginx.conf /etc/nginx/nginx.conf + + # dir for domlogs + debug_log mkdir -p /var/log/nginx/domlogs + + # 444 status for domains pointed to the IP but not added to nginx + debug_log cp -fr ${OPENPANEL_DIR}conf/default.nginx.conf /etc/nginx/sites-enabled/default + + # Replace IP_HERE with the value of $current_ip + debug_log sed -i "s/IP_HERE/$current_ip/" /etc/nginx/sites-enabled/default + + # Setting pretty error pages for nginx, but need to add them inside containers also! + debug_log mkdir -p /srv/http/default + debug_log git clone https://github.com/denysvitali/nginx-error-pages /srv/http/default > /dev/null 2>&1 + + debug_log mkdir /etc/nginx/snippets/ > /dev/null 2>&1 + + debug_log ln -s /srv/http/default/snippets/error_pages.conf /etc/nginx/snippets/error_pages.conf + debug_log ln -s /srv/http/default/snippets/error_pages_content.conf /etc/nginx/snippets/error_pages_content.conf +} + + +run_mysql_docker_container() { + + # MySQL + + # FIX for Ubuntu24: docker: failed to register layer: devicemapper: Error running deviceSuspend dm_task_run failed. + #docker pull --platform linux/amd64 mysql + + # set random password + MYSQL_ROOT_PASSWORD=$(openssl rand -base64 -hex 9) + + # run the container + docker pull mysql > /dev/null 2>&1 + docker run -d -p 3306:3306 --name openpanel_mysql \ + -e MYSQL_ROOT_PASSWORD="$MYSQL_ROOT_PASSWORD" \ + -v openpanel_mysql_data:/var/lib/mysql \ + --memory="1g" --cpus="1" \ + --restart=always \ + --oom-kill-disable \ + mysql + + if docker ps -a --format '{{.Names}}' | grep -q "openpanel_mysql"; then + + # show password + echo "Generated MySQL password: $MYSQL_ROOT_PASSWORD" + + ln -s /usr/local/admin/db.cnf /etc/my.cnf + + # Update configuration files with new password + sed -i "s/\"mysql_password\": \".*\"/\"mysql_password\": \"${MYSQL_ROOT_PASSWORD}\"/g" /usr/local/admin/config.json + sed -i "s/password = \".*\"/password = \"${MYSQL_ROOT_PASSWORD}\"/g" /usr/local/admin/db.cnf + + else + radovan 1 "Installation failed! Unable to start docker container for MySQL." + fi +} + +configure_mysql() { + + # Check if the Docker container exists + if docker ps -a --format '{{.Names}}' | grep -q "openpanel_mysql"; then + + # Fix for: ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 2 + + # Function to check if MySQL is running + mysql_is_running() { + if mysqladmin --defaults-extra-file="/usr/local/admin/db.cnf" ping &> /dev/null; then + return 0 # MySQL is running + else + return 1 # MySQL is not running + fi + } + + # Wait for MySQL to start + wait_for_mysql() { + retries=5 + while [ $retries -gt 0 ]; do + if mysql_is_running; then + return 0 # MySQL is running + else + echo "Waiting for MySQL to start..." + sleep 5 + retries=$((retries - 1)) + fi + done + return 1 # MySQL did not start after retries + } + + # Wait for MySQL to start + wait_for_mysql + + # Create database + mysql --defaults-extra-file="/usr/local/admin/db.cnf" -e "CREATE DATABASE IF NOT EXISTS panel;" + mysql --defaults-extra-file="/usr/local/admin/db.cnf" -D "panel" < ${OPENPANEL_DIR}DATABASE.sql + + # Check if SQL file was imported successfully + if mysql --defaults-extra-file="/usr/local/admin/db.cnf" -D "panel" -e "SELECT 1 FROM plans LIMIT 1;" &> /dev/null; then + echo -e "${GREEN}Database is ready.${RESET}" + else + echo "SQL file import failed or database is not ready." + radovan 1 "Installation failed!" + fi + + else + echo "Docker container 'openpanel_mysql' does not exist. Please make sure the container is running." + radovan 1 "Installation failed! " + fi +} + + +configure_docker() { + + # Docker + + debug_log "Changing default storage driver for Docker from 'overlay2' to 'devicemapper'.." + docker_daemon_json_path="/etc/docker/daemon.json" + + debug_log mkdir -p $(dirname "$docker_daemon_json_path") + + + + ### to be removed in 0.1.8 + daemon_json_content='{ + "storage-driver": "devicemapper" + }' + echo "$daemon_json_content" > "$docker_daemon_json_path" + ### + + + # to be used in 0.1.8+ + #cp /usr/local/panel/conf/docker.daemon.json $docker_daemon_json_path + + echo -e "${GREEN}Docker is configured.${RESET}" + debug_log systemctl daemon-reload + systemctl restart docker +} + + +configure_modsecurity() { + + # ModSecurity + # + # https://openpanel.co/docs/admin/settings/waf/#install-modsecurity + # + + if [ "$MODSEC" ]; then + echo "Installing ModSecurity and setting OWASP core ruleset.." + debug_log opencli nginx-install_modsec + fi +} + +setup_openpanel() { + + if [ -z "$REPAIR" ]; then + rm -rf $OPENPANEL_DIR + fi + + # OpenPanel + # + # https://openpanel.co/docs/panel/intro/ + # + echo "Setting up User panel.." + + # check python version again, in case if --skip-requirements or --repair flags are used + current_python_version=$(python3 --version 2>&1 | cut -d " " -f 2 | cut -d "." -f 1,2 | tr -d '.') + + mkdir -p $OPENPANEL_DIR + mkdir -p ${OPENPANEL_DIR}users + + # Clone the git branch for that python version + wget -O ${TEMP_DIR}openpanel.tar.gz "https://storage.googleapis.com/openpanel/$version/get.openpanel.co/downloads/$version/openpanel/$current_python_version/compressed.tar.gz" > /dev/null 2>&1 || radovan 1 "wget failed for https://storage.googleapis.com/openpanel/$version/get.openpanel.co/downloads/$version/openpanel/$current_python_version/compressed.tar.gz" + cd ${TEMP_DIR} && tar -xzf openpanel.tar.gz -C $OPENPANEL_DIR + rm ${TEMP_DIR}openpanel.tar.gz + + export PYTHONPATH=$OPENPANEL_DIR:$PYTHONPATH + + cd $OPENPANEL_DIR + cp -fr services/panel.service ${SERVICES_DIR}panel.service + + echo "Installing PIP requirements for User panel.." + + # FIX FOR: https://peps.python.org/pep-0668/ + if [[ ($current_python_version == "311" || $current_python_version == "312") ]]; then + debug_log "Installing PIP requirements for OpenPanel with break-system-packages..." + debug_log pip install -r requirements.txt --break-system-packages + else + debug_log "Installing PIP requirements for OpenPanel without break-system-packages..." + debug_log pip install -r requirements.txt + fi + + + echo "Setting the API service for website screenshots.." + debug_log playwright install + debug_log playwright install-deps + + mv ${OPENPANEL_DIR}icons/ ${OPENPANEL_DIR}static/images/icons +} + +setup_openadmin() { + + # OpenAdmin + # + # https://openpanel.co/docs/admin/intro/ + # + echo "Setting up Admin panel.." + + if [ -z "$REPAIR" ]; then + rm -rf $OPENPADMIN_DIR + fi + + + mkdir -p $OPENPADMIN_DIR + + # Clone the branch for that python version + wget -O ${TEMP_DIR}openadmin.tar.gz "https://storage.googleapis.com/openpanel/$version/get.openpanel.co/downloads/$version/openadmin/$current_python_version/compressed.tar.gz" > /dev/null 2>&1 || radovan 1 "wget failed for https://storage.googleapis.com/openpanel/$version/get.openpanel.co/downloads/$version/openadmin/$current_python_version/compressed.tar.gz" + debug_log cd ${TEMP_DIR} + debug_log tar -xzf openadmin.tar.gz -C $OPENPADMIN_DIR + debug_log unzip ${OPENPADMIN_DIR}static/dist.zip -d ${OPENPADMIN_DIR}static/dist/ + debug_log rm ${TEMP_DIR}openadmin.tar.gz ${OPENPADMIN_DIR}static/dist.zip + + debug_log cd $OPENPADMIN_DIR + + cp -fr service/admin.service ${SERVICES_DIR}admin.service + + # Fix for: ModuleNotFoundError: No module named 'pyarmor_runtime_000000' + wget -O ${OPENPADMIN_DIR}service/service.config.py https://gist.githubusercontent.com/stefanpejcic/37805c6781dc3beb1730fec82ee5ae34/raw/d7e8a6c1608c265aed89e97dcecea518b222ac86/service.config.py > /dev/null 2>&1 + + + echo "Installing PIP requirements for Admin panel.." + # FIX FOR: https://peps.python.org/pep-0668/ + if [[ ($current_python_version == "311" || $current_python_version == "312") ]]; then + debug_log "Installing PIP requirements for OpenAdmin with break-system-packages..." + debug_log pip install -r requirements.txt --break-system-packages + else + debug_log "Installing PIP requirements for OpenAdmin without break-system-packages..." + debug_log pip install -r requirements.txt + fi + + echo "Creating Admin user.." + + touch ${OPENPADMIN_DIR}users.db + + export PYTHONPATH=$OPENPADMIN_DIR:$PYTHONPATH + + admin_password=$(openssl rand -base64 12 | tr -d '=+/') + password_hash=$(python3 ${OPENPADMIN_DIR}core/users/hash $admin_password) + + debug_log sqlite3 ${OPENPADMIN_DIR}users.db "CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY, username TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'user', is_active BOOLEAN DEFAULT 1 NOT NULL);" "INSERT INTO user (username, password_hash, role) VALUES ('admin', \"$password_hash\", 'admin');" +} + + + +set_system_cronjob(){ + + echo "Setting cronjobs.." + + mv /usr/local/panel/conf/cron /etc/cron.d/openpanel + chown root:root /etc/cron.d/openpanel + chmod 0600 /etc/cron.d/openpanel +} + +setup_opencli() { + + # OpenCLI + # + # https://dev.openpanel.co/cli/ + # + echo "Setting OpenPanel CLI scripts.." + + if [ -z "$REPAIR" ]; then + rm -rf $OPENCLI_DIR + fi + + debug_log mkdir -p $OPENCLI_DIR + + wget -O ${TEMP_DIR}opencli.tar.gz "https://storage.googleapis.com/openpanel/$version/get.openpanel.co/downloads/$version/opencli/compressed.tar.gz" > /dev/null 2>&1 || radovan 1 "wget failed for https://storage.googleapis.com/openpanel/$version/get.openpanel.co/downloads/$version/opencli/compressed.tar.gz" + + debug_log cd ${TEMP_DIR} && tar -xzf opencli.tar.gz -C $OPENCLI_DIR + debug_log rm ${TEMP_DIR}opencli.tar.gz + debug_log bash ${OPENCLI_DIR}install.sh + debug_log source ~/.bashrc + echo -e "${GREEN}opencli commands are now available.${RESET}" + debug_log chmod +x -R ${OPENCLI_DIR} + + echo "Creating directories for logs.." + debug_log mkdir -p ${OPENPANEL_ERR_DIR}admin ${OPENPANEL_ERR_DIR}user + debug_log -e "${GREEN}OpenCLI has been successfully installed.${RESET}" + +} + +cleanup() { + echo "Cleaning up.." + # https://www.faqforge.com/linux/fixed-ubuntu-apt-get-upgrade-auto-restart-services/ + sed -i 's/$nrconf{restart} = '"'"'a'"'"';/#$nrconf{restart} = '"'"'i'"'"';/g' /etc/needrestart/needrestart.conf + rm -rf ${OPENPANEL_DIR}INSTALL.sh ${OPENPANEL_DIR}DATABASE.sql ${OPENPANEL_DIR}requirements.txt ${OPENPANEL_DIR}conf/nginx.conf ${OPENPANEL_DIR}conf/mysqld.cnf ${OPENPANEL_DIR}conf/named.conf.options ${OPENPANEL_DIR}conf/default.nginx.conf ${OPENPANEL_DIR}conf/.gitkeep ${OPENPANEL_DIR}templates/dist.zip ${OPENPANEL_DIR}install + rm -rf ${OPENPANEL_DIR}services +} + + +start_services() { + echo "Starting services.." + systemctl restart panel admin nginx ufw + systemctl enable panel admin nginx ufw +} + + +helper_function_for_nginx_on_aws_and_azure(){ + # + # FIX FOR: + # + # https://stackoverflow.com/questions/3191509/nginx-error-99-cannot-assign-requested-address/13141104#13141104 + # + + # Check the status of nginx service and capture the output + nginx_status=$(systemctl status nginx 2>&1) + + # Search for "Cannot assign requested address" in the output + if echo "$nginx_status" | grep -q "Cannot assign requested address"; then + + # If found, append the required line to /etc/sysctl.conf + echo "net.ipv4.ip_nonlocal_bind = 1" >> /etc/sysctl.conf + + # Reload the sysctl configuration + sysctl -p /etc/sysctl.conf + + # Change the bind ip in default nginx config + sed -i "s/IP_HERE/*/" /etc/nginx/sites-enabled/default + + debug_log "echo Configuration updated and applied." + else + debug_log "echo Nginx started normally." + fi +} + +download_and_import_docker_images() { + echo "Downloading docker images in the background.." + + if [ "$SKIP_IMAGES" = false ]; then + opencli docker-update_images & + pid1=$! + disown $pid1 + fi +} + + +generate_and_set_ssl_for_panels() { + if [ -z "$SKIP_SSL" ]; then + echo "Checking if SSL can be generated for the server hostname.." + debug_log opencli ssl-hostname + fi +} + +run_custom_postinstall_script() { + if [ -n "$post_install_path" ]; then + # run the custom script + echo " " + echo "Running post install script.." + debug_log "https://dev.openpanel.co/customize.html#After-installation" + debug_log bash $post_install_path + fi +} + +verify_license() { + debug_log "echo Current time: $(date +%T)" + server_hostname=$(hostname) + license_data='{"hostname": "'"$server_hostname"'", "public_ip": "'"$current_ip"'"}' + response=$(curl -s -X POST -H "Content-Type: application/json" -d "$license_data" https://api.openpanel.co/license-check) + debug_log "echo Checking OpenPanel license for IP address: $current_ip" + debug_log "echo Response: $response" +} + +send_install_log(){ + # Restore normal output to the terminal, so we dont save generated admin password in log file! + exec > /dev/tty + exec 2>&1 + opencli report --public >> "$LOG_FILE" + curl -F "file=@/root/$LOG_FILE" http://support.openpanel.co/install_logs.php + # Redirect again stdout and stderr to the log file + exec > >(tee -a "$LOG_FILE") + exec 2>&1 +} + + +rm_helpers(){ + rm -rf $PROGRESS_BAR_FILE +} + + +support_message() { + echo "" + echo "🎉 Welcome aboard and thank you for choosing OpenPanel! 🎉" + echo "" + echo "Your journey with OpenPanel has just begun, and we're here to help every step of the way." + echo "" + echo "To get started, check out our Getting Started guide:" + echo "👉 https://openpanel.co/docs/admin/intro/#post-install-steps" + echo "" + echo "Need assistance or looking to learn more? We've got you covered:" + echo "" + echo "📚 Admin Docs: Dive into our comprehensive documentation for all things OpenPanel:" + echo "👉 https://openpanel.co/docs/admin/intro/" + echo "" + echo "💬 Forums: Join our community forum to ask questions, share tips, and connect with fellow admins:" + echo "👉 https://community.openpanel.co/" + echo "" + echo "🎮 Discord: For real-time chat and support, hop into our Discord server:" + echo "👉 https://discord.openpanel.co/" + echo "" + echo "We're thrilled to have you with us. Let's make something amazing together! 🚀" + echo "" +} + + + +success_message() { + + echo -e "${GREEN}OpenPanel installation complete.${RESET}" + echo "" + + # Restore normal output to the terminal, so we dont save generated admin password in log file! + exec > /dev/tty + exec 2>&1 + + opencli admin + echo "Username: admin" + echo "Password: $admin_password" + echo " " + print_space_and_line + + # Redirect again stdout and stderr to the log file + exec > >(tee -a "$LOG_FILE") + exec 2>&1 + +} + +# END main functions + + + + + + + + + + + + + + + + +##################################################################### +# # +# START main script execution # +# # +##################################################################### + +print_header + +parse_args "$@" + +check_requirements + +detect_installed_panels + +check_lock_file_age + +install_started_message + +main + +send_install_log + +rm_helpers + +print_space_and_line + +support_message + +print_space_and_line + +success_message + +run_custom_postinstall_script + + +# END main script execution