openpanel/INSTALL.sh
2024-09-12 18:43:48 +02:00

1733 lines
59 KiB
Bash

#!/bin/bash
################################################################################
#
# Install the latest version of OpenPanel ✌️
# https://openpanel.com/install
#
# Supported OS: Ubuntu, Debian, AlmaLinux, RockyLinux, Fedora, CentOS
# Supported Python 3.8 3.9 3.10 3.11 3.12
#
# Usage: bash <(curl -sSL https://openpanel.org)
# Author: Stefan Pejcic <stefan@pejcic.rs>
# Created: 11.07.2023
# Last Modified: 12.09.2024
#
################################################################################
# COLORS
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
RESET='\033[0m'
# tput: No value for $TERM and no -T specified
export TERM=xterm-256color
# DEFAULTS
CUSTOM_VERSION=false # default version is latest
INSTALL_TIMEOUT=600 # after 10min, consider the install failed
DEBUG=false # verbose output for debugging failed install
SKIP_APT_UPDATE=false
SKIP_IMAGES=false # they are auto-pulled on account creation
REPAIR=false
LOCALES=true # only en
NO_SSH=false # deny port 22
INSTALL_MAIL=false # no ui yet
IPSETS=true # currently only works with ufw
SET_HOSTNAME_NOW=false # must be a FQDN
CUSTOM_GB_DOCKER=false # space in gb, if not set fallback to 50% of available du
SETUP_SWAP_ANYWAY=false
SWAP_FILE="1" # calculated based on ram
SEND_EMAIL_AFTER_INSTALL=false
SET_PREMIUM=false # added in 0.2.1
UFW_SETUP=false # previous default on <0.2.3
CSF_SETUP=true # default since >0.2.2
SET_ADMIN_USERNAME=false # random
SET_ADMIN_PASSWORD=false # random
SCREENSHOTS_API_URL="http://screenshots-api.openpanel.com/screenshot" # default since 0.2.1
# PATHS
ETC_DIR="/etc/openpanel/" # https://github.com/stefanpejcic/openpanel-configuration
LOG_FILE="openpanel_install.log" # install log
LOCK_FILE="/root/openpanel.lock" # install running
OPENPANEL_DIR="/usr/local/panel" # currently only used to store version
OPENPADMIN_DIR="/usr/local/admin/" # https://github.com/stefanpejcic/openadmin/branches
OPENCLI_DIR="/usr/local/admin/scripts/" # https://dev.openpanel.com/cli/commands.html
OPENPANEL_ERR_DIR="/var/log/openpanel/" # https://dev.openpanel.com/logs.html
SERVICES_DIR="/etc/systemd/system/" # used for admin, sentinel and floatingip services
CONFIG_FILE="${ETC_DIR}openpanel/conf/openpanel.config" # main config file for openpanel
# 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 " |_| version: ${GREEN}$PANEL_VERSION${RESET} "
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -
}
install_started_message(){
echo -e ""
echo -e "\nStarting the installation of OpenPanel. This process will take approximately 3-5 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
}
# if --debug flag then we print each command that is executed and display output on terminal.
# if debug flag is not provided, we simply run the command and hide all output by redirecting to /dev/null
debug_log() {
if [ "$DEBUG" = true ]; then
local timestamp=$(date +'%Y-%m-%d %H:%M:%S')
echo "[$timestamp] $message"
"$@"
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 package manager.."
fi
}
detect_filesystem(){
FS_TYPE=$(df -T "/var" | awk 'NR==2 {print $2}')
}
get_server_ipv4(){
# Get server ipv4
# list of ip servers for checks
IP_SERVER_1="https://ip.openpanel.com"
IP_SERVER_2="https://ipv4.openpanel.com"
IP_SERVER_3="https://ifconfig.me"
current_ip=$(curl --silent --max-time 2 -4 $IP_SERVER_1 || \
wget --timeout=2 -qO- $IP_SERVER_2 || \
curl --silent --max-time 2 -4 $IP_SERVER_3)
# If site is not available, get the ipv4 from the hostname -I
if [ -z "$current_ip" ]; then
# current_ip=$(hostname -I | awk '{print $1}')
# ip addr command is more reliable then hostname - to avoid getting private ip
current_ip=$(ip addr|grep 'inet '|grep global|head -n1|awk '{print $2}'|cut -f1 -d/)
fi
is_valid_ipv4() {
local ip=$1
# Check if IPv4 is valid
[[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] && \
# Check if IP is not private
! [[ $ip =~ ^10\. ]] && \
! [[ $ip =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. ]] && \
! [[ $ip =~ ^192\.168\. ]]
}
if ! is_valid_ipv4 "$current_ip"; then
echo "Invalid or private IPv4 address: $current_ip. OpenPanel requires a public IPv4 address to bind Nginx configuration files."
fi
}
set_version_to_install(){
if [ "$CUSTOM_VERSION" = false ]; then
# Fetch the latest version
PANEL_VERSION=$(curl --silent --max-time 10 -4 https://openpanel.org/version)
if [[ $PANEL_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
PANEL_VERSION=$PANEL_VERSION
else
PANEL_VERSION="0.2.9"
fi
fi
}
# print fullwidth line
print_space_and_line() {
echo " "
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -
echo " "
}
setup_progress_bar_script(){
# Progress bar script
PROGRESS_BAR_URL="https://raw.githubusercontent.com/pollev/bash_progress_bar/master/progress_bar.sh"
PROGRESS_BAR_FILE="progress_bar.sh"
# Check if wget is available
if command -v wget &> /dev/null; then
wget "$PROGRESS_BAR_URL" -O "$PROGRESS_BAR_FILE" > /dev/null 2>&1
# If wget is not available, check if curl is available *(fallback for fedora)
elif command -v curl &> /dev/null; then
curl -s "$PROGRESS_BAR_URL" -o "$PROGRESS_BAR_FILE" > /dev/null 2>&1
else
echo "Neither wget nor curl is available. Please install one of them to proceed."
exit 1
fi
if [ ! -f "$PROGRESS_BAR_FILE" ]; then
echo "ERROR: Failed to download progress_bar.sh - Github is not reachable by your server: https://raw.githubusercontent.com"
exit 1
fi
}
display_what_will_be_installed(){
echo -e "[ OK ] DETECTED OPERATING SYSTEM: ${GREEN} ${NAME^^} $VERSION_ID ${RESET}"
echo -e "[ OK ] PACKAGE MANAGEMENT SYSTEM: ${GREEN} ${PACKAGE_MANAGER^^} ${RESET}"
echo -e "[ OK ] INSTALLED PYTHON VERSION: ${GREEN} ${current_python_version} ${RESET}"
if [ "$FS_TYPE" = "xfs" ]; then
echo -e "[ OK ] BACKING FILESYSTEM TYPE: ${GREEN} ${FS_TYPE^^} ${RESET}"
else
echo -e "[PASS] BACKING FILESYSTEM TYPE: ${YELLOW} ${FS_TYPE^^} ${RESET}"
fi
echo -e "[ OK ] PUBLIC IPV4 ADDRESS: ${GREEN} ${current_ip} ${RESET}"
echo ""
}
setup_progress_bar_script
# Source the progress bar script
source "$PROGRESS_BAR_FILE"
# Dsiplay progress bar
FUNCTIONS=(
detect_os_and_package_manager # detect os and package manager
display_what_will_be_installed # display os, version, ip
update_package_manager # update dnf/yum/apt-get
install_packages # install docker, csf/ufw, sqlite, etc.
download_skeleton_directory_from_github # download configuration to /etc/openpanel/
setup_bind # must run after -configuration
install_openadmin # set admin interface
opencli_setup # set terminal commands
configure_docker # set overlay2 and xfs backing filesystem
download_and_import_docker_images # openpanel/nginx and openpanel/apache
panel_customize # customizations
configure_nginx # setup nginx configuration files
docker_compose_up # must be after configure_nginx
set_premium_features # must be after docker_compose_up
configure_modsecurity # TEMPORARY OFF FROM 0.2.5
#setup_email # TEMPORARY OFF FROM 0.2.5
set_custom_hostname # set hostname if provided
generate_and_set_ssl_for_panels # if FQDN then lets setup https
setup_firewall_service # setup firewall
set_system_cronjob # setup drons, must be after csf
set_logrotate # setup logrotate, ignored on fedora
tweak_ssh # basic ssh
setup_swap # swap space
clean_apt_and_dnf_cache # clear
verify_license # ping our server
)
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
architecture=$(lscpu | grep Architecture | awk '{print $2}')
if [ "$architecture" == "aarch64" ]; then
# https://github.com/stefanpejcic/openpanel/issues/63
echo -e "${RED}Error: ARM CPU is not supported! Feature request: https://github.com/stefanpejcic/openpanel/issues/63 ${RESET}" >&2
exit 1
fi
# check if the current user is 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() {
show_help() {
echo "Available options:"
echo " --key=<key_here> Set the license key for OpenPanel Enterprise edition."
echo " --hostname=<hostname> Set the hostname - must be FQDN, example: server.example.net."
echo " --username=<username> Set Admin username - random generated if not provided."
echo " --password=<password> Set Admin Password - random generated if not provided."
echo " --version=<version> Set a custom OpenPanel version to be installed."
echo " --email=<stefan@example.net> Set email address to receive email with admin credentials and future notifications."
echo " --skip-requirements Skip the requirements check."
echo " --skip-panel-check Skip checking if existing panels are installed."
echo " --skip-apt-update Skip the APT update."
echo " --skip-firewall Skip installing UFW or CSF - Only do this if you will set another external firewall!"
echo " --csf Install and setup ConfigServer Firewall (default from >0.2.3)"
echo " --ufw Install and setup Uncomplicated Firewall (was default in <0.2.3)"
echo " --skip-images Skip installing openpanel/nginx and openpanel/apache docker images."
echo " --skip-blacklists Do not set up IP sets and blacklists."
echo " --skip-ssl Skip SSL setup."
echo " --with_modsec Enable ModSecurity for Nginx."
echo " --no-ssh Disable port 22 and whitelist the IP address of user installing the panel."
echo " --enable-mail Install Mail (experimental)."
echo " --post_install=<path> Specify the post install script path."
echo " --screenshots=<url> Set the screenshots API URL."
echo " --swap=<2> Set space in GB to be allocated for SWAP."
echo " --docker-space=<2> Set space in GB to be allocated for Docker containers (default 50% of available storage)."
echo " --debug Display debug information during installation."
echo " --repair Retry and overwrite everything."
echo " -h, --help Show this help message and exit."
}
while [[ $# -gt 0 ]]; do
case $1 in
--key=*)
SET_PREMIUM=true
license_key="${1#*=}"
;;
--hostname=*)
SET_HOSTNAME_NOW=true
new_hostname="${1#*=}"
;;
--username=*)
SET_ADMIN_USERNAME=true
custom_username="${1#*=}"
;;
--password=*)
SET_ADMIN_PASSWORD=true
custom_password="${1#*=}"
;;
--skip-requirements)
SKIP_REQUIREMENTS=true
;;
--skip-panel-check)
SKIP_PANEL_CHECK=true
;;
--skip-apt-update)
SKIP_APT_UPDATE=true
;;
--repair)
REPAIR=true
SKIP_PANEL_CHECK=true
#SKIP_REQUIREMENTS=true
;;
--skip-firewall)
SKIP_FIREWALL=true
;;
--csf)
UFW_SETUP=false
CSF_SETUP=true
;;
--ufw)
UFW_SETUP=true
CSF_SETUP=false
;;
--skip-images)
SKIP_IMAGES=true
;;
--skip-blacklists)
IPSETS=false
;;
--skip-ssl)
SKIP_SSL=true
;;
--with_modsec)
MODSEC=true
;;
--debug)
DEBUG=true
;;
--no-ssh)
NO_SSH=true
;;
--enable-mail)
INSTALL_MAIL=true
;;
--post_install=*)
post_install_path="${1#*=}"
;;
--screenshots=*)
SCREENSHOTS_API_URL="${1#*=}"
;;
--version=*)
CUSTOM_VERSION=true
PANEL_VERSION="${1#*=}"
;;
--swap=*)
SETUP_SWAP_ANYWAY=true
SWAP="${1#*=}"
;;
--docker-space=*)
CUSTOM_GB_DOCKER=true
SPACE_FOR_DOCKER_FILE="${1#*=}"
;;
--email=*)
SEND_EMAIL_AFTER_INSTALL=true
EMAIL="${1#*=}"
;;
-h|--help)
show_help
exit 0
;;
*)
echo "Unknown option: $1"
show_help
exit 1
;;
esac
shift
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=(
["$OPENPANEL_DIR"]="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. Starting the installation process.${RESET}"
fi
}
detect_os_and_package_manager() {
if [ -f "/etc/os-release" ]; then
. /etc/os-release
case $ID in
ubuntu)
PACKAGE_MANAGER="apt-get"
py_enchoded_for_distro="$current_python_version"
;;
debian)
PACKAGE_MANAGER="apt-get"
py_enchoded_for_distro="debian-$current_python_version"
;;
fedora)
PACKAGE_MANAGER="dnf"
py_enchoded_for_distro="$current_python_version"
;;
rocky)
PACKAGE_MANAGER="dnf"
py_enchoded_for_distro="$current_python_version"
;;
centos)
PACKAGE_MANAGER="yum"
py_enchoded_for_distro="$current_python_version"
;;
almalinux|alma)
PACKAGE_MANAGER="dnf"
py_enchoded_for_distro="$current_python_version"
;;
*)
echo -e "${RED}Unsupported Operating System: $ID. Exiting.${RESET}"
echo -e "${RED}INSTALL FAILED${RESET}"
exit 1
;;
esac
else
echo -e "${RED}Could not detect Linux distribution from /etc/os-release${RESET}"
echo -e "${RED}INSTALL FAILED${RESET}"
exit 1
fi
}
download_and_import_docker_images() {
echo "Downloading docker images in the background.."
if [ "$SKIP_IMAGES" = false ]; then
# See https://github.com/moby/moby/issues/16106#issuecomment-310781836 for pulling images in parallel
# Nohup (no hang up) with trailing ampersand allows running the command in the background
# The </dev/null bits redirects the outputs to files rather than strout/err
if [ "$SET_PREMIUM" = true ]; then
# 4 images
nohup sh -c "echo openpanel/nginx:latest openpanel/apache:latest openpanel/nginx-mariadb:latest openpanel/apache-mariadb:latest | xargs -P4 -n1 docker pull" </dev/null >nohup.out 2>nohup.err &
else
# 2 images
nohup sh -c "echo openpanel/nginx:latest openpanel/apache:latest | xargs -P4 -n1 docker pull" </dev/null >nohup.out 2>nohup.err &
fi
fi
}
check_lock_file_age() {
if [ "$REPAIR" = true ]; 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
}
configure_docker() {
docker_daemon_json_path="/etc/docker/daemon.json"
mkdir -p $(dirname "$docker_daemon_json_path")
echo "Setting 'overlay2' as the default storage driver for Docker.."
create_storage_file_xfs_and_mount(){
# disk size to use for XFS storage file
if [ "$CUSTOM_GB_DOCKER" = true ]; then
gb_size=${SPACE_FOR_DOCKER_FILE}
else
# default is 50% of available disk space on / partition
available_space=$(df --output=avail / | tail -1)
available_gb=$((available_space / 1024 / 1024))
gb_size=$((available_gb * 50 / 100))
fi
echo "Creating a storage file of ${gb_size}GB (50% of available disk) to be used for /var/lib/docker - this can take a few minutes.."
dd if=/dev/zero of=/var/lib/docker.img bs=1G count=${gb_size} status=progress
debug_log mkfs.xfs /var/lib/docker.img
debug_log systemctl stop docker
debug_log mount -o loop,pquota /var/lib/docker.img /var/lib/docker
echo "/var/lib/docker.img /var/lib/docker xfs loop,pquota 0 0" >> /etc/fstab
}
# added in 0.2.6
if [ "$FS_TYPE" = "xfs" ]; then
if mount | grep -q "pquota"; then
echo "Backing Filesystem is already XFS with 'pquota' mount. Skipping creating storage file."
else
echo "Overlay2 docker storage driver requires the XFS filesystem to be mounted with 'pquota'."
create_storage_file_xfs_and_mount
fi
else
echo "Overlay2 docker storage driver requires backing filesystem to use XFS."
create_storage_file_xfs_and_mount
fi
if [ -f /etc/fedora-release ]; then
# On Fedora journald handles docker log-driver
cp ${ETC_DIR}docker/overlay2/fedora.json "$docker_daemon_json_path"
# fix for bug https://github.com/containers/podman/issues/13684
restorecon -R -v /var/lib/docker >/dev/null 2>&1
else
cp ${ETC_DIR}docker/overlay2/xfs_file.json "$docker_daemon_json_path"
fi
systemctl daemon-reload
systemctl start docker
if command -v docker >/dev/null 2>&1; then
echo -e "[${GREEN} OK ${RESET}] Docker is configured."
else
radovan 1 "Docker command is not available!"
fi
}
docker_compose_up(){
echo "Setting docker-compose.."
# install docker compose
DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
mkdir -p $DOCKER_CONFIG/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.27.1/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose > /dev/null 2>&1
chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
if [ "$SET_PREMIUM" = true ]; then
# setup 4 plans in database
cp /etc/openpanel/mysql/initialize/mariadb_plans.sql /root/initialize.sql > /dev/null 2>&1
else
# setup 2 plans in database
cp /etc/openpanel/mysql/initialize/mysql_plans.sql /root/initialize.sql > /dev/null 2>&1
fi
# compose doesnt alllow /
cd /root
# generate random password for mysql
MYSQL_ROOT_PASSWORD=$(openssl rand -base64 -hex 9)
echo "MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD" > .env
echo "MYSQL_ROOT_PASSWORD = $MYSQL_ROOT_PASSWORD"
# save it to /etc/my.cnf
rm -rf /etc/my.cnf > /dev/null 2>&1 # on centos we get default mycnf..
ln -s /etc/openpanel/mysql/db.cnf /etc/my.cnf > /dev/null 2>&1
sed -i 's/password = .*/password = '"${MYSQL_ROOT_PASSWORD}"'/g' ${ETC_DIR}mysql/db.cnf > /dev/null 2>&1
cp /etc/openpanel/docker/compose/new-docker-compose.yml /root/docker-compose.yml > /dev/null 2>&1 # from 0.2.5 new-docker-compose.yml instead of docker-compose.yml
# added in 0.2.9
# fix for bug with mysql-server image on Almalinux 9.2
os_name=$(grep ^ID= /etc/os-release | cut -d'=' -f2 | tr -d '"')
if [ "$os_name" == "almalinux" ]; then
sed -i 's/mysql\/mysql-server/mysql/g' /root/docker-compose.yml
echo "mysql/mysql-server docker image has known issues on AlmaLinux - editing docker compose to use the mysql:latest instead"
fi
# from 0.2.5 we only start mysql by default
cd /root && docker compose up -d openpanel_mysql > /dev/null 2>&1
# check if compose started the mysql container, and if is currently running
if [ -z `docker ps -q --no-trunc | grep $(docker compose ps -q openpanel_mysql)` ]; then
radovan 1 "ERROR: MySQL container is not running. Please retry installation with '--retry' flag."
else
echo -e "[${GREEN} OK ${RESET}] MySQL service started successfuly"
fi
}
clean_apt_and_dnf_cache(){
if [ "$PACKAGE_MANAGER" == "dnf" ]; then
dnf clean > /dev/null > /dev/null 2>&1
elif [ "$PACKAGE_MANAGER" == "apt-get" ]; then
# clear /var/cache/apt/archives/ # TODO: cover https://github.com/debuerreotype/debuerreotype/issues/95
apt-get clean > /dev/null > /dev/null 2>&1
fi
}
tweak_ssh(){
echo "Tweaking SSH service.."
sed -i "s/[#]LoginGraceTime [[:digit:]]m/LoginGraceTime 1m/g" /etc/ssh/sshd_config
if [ "$PACKAGE_MANAGER" == "apt-get" ]; then
if [ -z "$(grep "^DebianBanner no" /etc/ssh/sshd_config)" ]; then
sed -i '/^[#]Banner .*/a DebianBanner no' /etc/ssh/sshd_config
if [ -z "$(grep "^DebianBanner no" /etc/ssh/sshd_config)" ]; then
echo '' >> /etc/ssh/sshd_config # fallback
echo 'DebianBanner no' >> /etc/ssh/sshd_config
fi
fi
fi
# ssh on debian, sshd on rhel
if [ "$PACKAGE_MANAGER" == "dnf" ] || [ "$PACKAGE_MANAGER" == "yum" ]; then
systemctl restart sshd > /dev/null 2>&1
else
systemctl restart ssh > /dev/null 2>&1
fi
}
setup_email() {
if [ "$INSTALL_MAIL" = true ]; then
echo "Installing experimental Email service."
curl -sSL https://raw.githubusercontent.com/stefanpejcic/OpenMail/master/setup.sh | bash --dovecot
fi
}
setup_firewall_service() {
if [ -z "$SKIP_FIREWALL" ]; then
echo "Setting up the firewall.."
if [ "$CSF_SETUP" = true ]; then
echo "Installing ConfigServer Firewall.."
install_csf() {
wget https://download.configserver.com/csf.tgz > /dev/null 2>&1
debug_log tar -xzf csf.tgz
rm csf.tgz
cd csf
sh install.sh > /dev/null 2>&1
cd ..
rm -rf csf
#perl /usr/local/csf/bin/csftest.pl
echo "Setting CSF auto-login from OpenAdmin interface.."
if [ "$PACKAGE_MANAGER" == "dnf" ]; then
debug_log dnf install -y wget curl unzip yum-utils policycoreutils-python-utils libwww-perl
# fixes bug when starting csf: Can't locate locale.pm in @INC (you may need to install the locale module)
if [ -f /etc/fedora-release ]; then
debug_log yum --allowerasing install perl -y
fi
elif [ "$PACKAGE_MANAGER" == "apt-get" ]; then
debug_log apt-get install -y perl libwww-perl libgd-dev libgd-perl libgd-graph-perl
fi
# autologin from openpanel
ln -s /etc/csf/ui/images/ /usr/local/admin/static/configservercsf
chmod +x /usr/local/admin/modules/security/csf.pl
# play nice with docker
git clone https://github.com/stefanpejcic/csfpost-docker.sh > /dev/null 2>&1
mv csfpost-docker.sh/csfpost.sh /usr/local/csf/bin/csfpost.sh
chmod +x /usr/local/csf/bin/csfpost.sh
rm -rf csfpost-docker.sh
}
function open_port_csf() {
local port=$1
local csf_conf="/etc/csf/csf.conf"
# Check if port is already open
port_opened=$(grep "TCP_IN = .*${port}" "$csf_conf")
if [ -z "$port_opened" ]; then
# Open port
sed -i "s/TCP_IN = \"\(.*\)\"/TCP_IN = \"\1,${port}\"/" "$csf_conf"
echo -e "Port ${GREEN} ${port} ${RESET} is now open."
ports_opened=1
else
echo -e "Port ${GREEN} ${port} ${RESET} is already open."
fi
}
function open_tcpout_csf() {
local port=$1
local csf_conf="/etc/csf/csf.conf"
# Check if port is already open
port_opened=$(grep "TCP_OUT = .*${port}" "$csf_conf")
if [ -z "$port_opened" ]; then
# Open port
sed -i "s/TCP_OUT = \"\(.*\)\"/TCP_OUT = \"\1,${port}\"/" "$csf_conf"
echo -e "Outgoing Port ${GREEN} ${port} ${RESET} is now open."
ports_opened=1
else
echo -e "Port ${GREEN} ${port} ${RESET} is already open."
fi
}
edit_csf_conf() {
sed -i 's/TESTING = "1"/TESTING = "0"/' /etc/csf/csf.conf
sed -i 's/RESTRICT_SYSLOG = "0"/RESTRICT_SYSLOG = "3"/' /etc/csf/csf.conf
sed -i 's/ETH_DEVICE_SKIP = ""/ETH_DEVICE_SKIP = "docker0"/' /etc/csf/csf.conf
sed -i 's/DOCKER = "0"/DOCKER = "1"/' /etc/csf/csf.conf
}
set_csf_email_address() {
email_address=$(grep -E "^e-mail=" $CONFIG_FILE | cut -d "=" -f2)
if [[ -n "$email_address" ]]; then
sed -i "s/LF_ALERT_TO = \"\"/LF_ALERT_TO = \"$email_address\"/" /etc/csf/csf.conf
fi
}
function extract_port_from_file() {
local file_path=$1
local pattern=$2
local port=$(grep -Po "(?<=${pattern}[ =])\d+" "$file_path")
echo "$port"
}
install_csf
edit_csf_conf
open_tcpout_csf 3306 #mysql tcp_out only
open_port_csf 22 #ssh
open_port_csf 53 #dns
open_port_csf 80 #http
open_port_csf 443 #https
open_port_csf 2083 #user
open_port_csf 2087 #admin
open_port_csf $(extract_port_from_file "/etc/ssh/sshd_config" "Port") #ssh
open_port_csf 32768:60999 #docker
open_port_csf 21 #ftp
open_port_csf 21000:21010 #passive ftp
set_csf_email_address
csf -r > /dev/null 2>&1
echo "Restarting CSF service"
systemctl restart docker # not sure why
systemctl enable csf
service csf restart # also restarts docker at csfpost.sh
if command -v csf > /dev/null 2>&1; then
echo -e "[${GREEN} OK ${RESET}] ConfigServer Firewall is installed and configured."
else
echo -e "[${RED} X ${RESET}] ConfigServer Firewall is not installed properly."
fi
elif [ "$UFW_SETUP" = true ]; then
echo "Setting up UncomplicatedFirewall.."
if [ "$PACKAGE_MANAGER" == "dnf" ]; then
dnf makecache --refresh > /dev/null 2>&1
dnf install -y ufw > /dev/null 2>&1
elif [ "$PACKAGE_MANAGER" == "apt-get" ]; then
apt-get install -y ufw > /dev/null 2>&1
fi
# set ufw to be monitored instead of csf
sed -i 's/csf/ufw/g' "${ETC_DIR}openadmin/config/notifications.ini" > /dev/null 2>&1
sed -i 's/ConfigServer Firewall/Uncomplicated Firewall/g' "${ETC_DIR}openadmin/config/services.json" > /dev/null 2>&1
sed -i 's/csf/ufw/g' "${ETC_DIR}openadmin/config/services.json" > /dev/null 2>&1
# set ufw logs instead of csf
sed -i 's/"CSF Deny Log"/"UFW Logs"/' "${ETC_DIR}openadmin/config/log_paths.json" > /dev/null 2>&1
sed -i 's/\/etc\/csf\/csf.deny/\/var\/log\/ufw.log/' "${ETC_DIR}openadmin/config/log_paths.json" > /dev/null 2>&1
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
debug_log ufw allow 21/tcp #ftp
debug_log ufw allow 21000:21010/tcp #passive ftp
debug_log "yes | ufw enable"
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
# set https://github.com/stefanpejcic/ipset-blacklist
if [ "$IPSETS" = true ]; then
if [ "$REPAIR" = true ]; then
rm -rf ipset-blacklist-master
fi
if [ "$DEBUG" = true ]; then
bash <(curl -sSL https://raw.githubusercontent.com/stefanpejcic/ipset-blacklist/master/setup.sh)
else
bash <(curl -sSL https://raw.githubusercontent.com/stefanpejcic/ipset-blacklist/master/setup.sh) > /dev/null 2>&1
fi
fi
debug_log ufw --force enable
debug_log ufw reload
debug_log service ufw restart
if command -v ufw > /dev/null 2>&1; then
echo -e "[${GREEN} OK ${RESET}] UncomplicatedFirewall (UFW) is installed and configured."
else
echo -e "[${RED} X ${RESET}] Uncomplicated Firewall (UFW) is not installed properly."
fi
fi
fi
}
update_package_manager() {
if [ "$SKIP_APT_UPDATE" = false ]; then
echo "Updating $PACKAGE_MANAGER package manager.."
debug_log $PACKAGE_MANAGER update -y
fi
}
set_logrotate(){
echo "Setting Logrotate for Nginx.."
bash /usr/local/admin/scripts/server/logrotate
echo "Setting Logrotate for OpenPanel logs.."
cat <<EOF > "/etc/logrotate.d/openpanel"
/var/log/openpanel/**/*.log {
su root adm
size 50M
rotate 5
missingok
notifempty
compress
delaycompress
copytruncate
create 640 root adm
postrotate
endscript
}
EOF
logrotate -f /etc/logrotate.d/openpanel
echo "Setting Logrotate for Syslogs.."
cat <<EOF > "/etc/logrotate.d/syslog"
/var/log/syslog {
su root adm
weekly
rotate 4
missingok
notifempty
compress
delaycompress
postrotate
/usr/bin/systemctl reload rsyslog > /dev/null 2>&1 || true
endscript
}
EOF
logrotate -f /etc/logrotate.d/syslog
}
install_packages() {
echo "Installing required services.."
if [ "$PACKAGE_MANAGER" == "apt-get" ]; then
packages=("docker.io" "default-mysql-client" "python3-pip" "pip" "gunicorn" "jc" "sqlite3" "geoip-bin" "xfsprogs")
# 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
debug_log $PACKAGE_MANAGER -qq install apt-transport-https ca-certificates -y
# configure apt to retry downloading on error
if [ ! -f /etc/apt/apt.conf.d/80-retries ]; then
echo "APT::Acquire::Retries \"3\";" > /etc/apt/apt.conf.d/80-retries
fi
echo "Updating certificates.."
debug_log update-ca-certificates
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. Retrying.."
$PACKAGE_MANAGER -qq install "$package"
if [ $? -ne 0 ]; then
radovan 1 "ERROR: Installation failed. Please retry installation with '--retry' flag."
exit 1
fi
fi
fi
done
elif [ "$PACKAGE_MANAGER" == "yum" ]; then
# otherwise we get podman..
dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
packages=("wget" "git" "docker-ce" "mysql" "python3-pip" "pip" "gunicorn" "jc" "sqlite" "geoip" "perl-Math-BigInt") #sqlite for almalinux and perl-Math-BigInt is needed for csf
for package in "${packages[@]}"; do
echo -e "Installing ${GREEN}$package${RESET}"
debug_log $PACKAGE_MANAGER install "$package" -y
done
elif [ "$PACKAGE_MANAGER" == "dnf" ]; then
# otherwise we get podman..
dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
# special case for fedora,
if [ -f /etc/fedora-release ]; then
packages=("git" "wget" "python3-flask" "python3-pip" "docker" "docker-compose" "mysql" "docker-compose-plugin" "sqlite" "sqlite-devel" "perl-Math-BigInt")
else
packages=("git" "wget" "python3-flask" "python3-pip" "docker-ce" "docker-compose" "docker-ce-cli" "mysql" "containerd.io" "docker-compose-plugin" "sqlite" "sqlite-devel" "geoip" "perl-Math-BigInt")
fi
debug_log dnf install yum-utils -y
debug_log yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo -y # need confirm on alma, rocky and centos
# needed for csf
debug_log dnf --allowerasing install perl -y
# needed for ufw and gunicorn
debug_log dnf install epel-release -y
# needed for admin panel
debug_log dnf install python3-pip python3-devel gcc -y
for package in "${packages[@]}"; do
echo -e "Installing ${GREEN}$package${RESET}"
debug_log $PACKAGE_MANAGER install "$package" -y
debug_log $PACKAGE_MANAGER -y install "$package"
done
# gunicorn needs to be installed over pip for alma
debug_log pip3 install gunicorn flask
fi
}
configure_modsecurity() {
echo "Warning: modsecurity is currently disabled and will not be installed"
: '
# ModSecurity
#
# https://openpanel.com/docs/admin/settings/waf/#install-modsecurity
#
if [ "$MODSEC" ]; then
echo "ModSecurity is temporary disabled and will not be installed."
#echo "Installing ModSecurity and setting OWASP core ruleset.."
#debug_log opencli nginx-install_modsec
fi
'
}
set_system_cronjob(){
echo "Setting cronjobs.."
mv ${ETC_DIR}cron /etc/cron.d/openpanel
chown root:root /etc/cron.d/openpanel
chmod 0600 /etc/cron.d/openpanel
if [ "$PACKAGE_MANAGER" == "dnf" ] || [ "$PACKAGE_MANAGER" == "yum" ]; then
# extra steps for SELinux
restorecon -R /etc/cron.d/ > /dev/null 2>&1
restorecon -R /etc/cron.d/openpanel > /dev/null 2>&1
systemctl restart crond.service > /dev/null 2>&1
fi
}
set_custom_hostname(){
if [ "$SET_HOSTNAME_NOW" = true ]; then
# Check if the provided hostname is a valid FQDN
if [[ $new_hostname =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
# Check if PTR record is set to the provided hostname
ptr=$(dig +short -x $current_ip)
if [ "$ptr" != "$new_hostname." ]; then
echo "Warning: PTR record is not set to $new_hostname"
fi
# Check if A record for provided hostname points to server IP
a_record_ip=$(dig +short $new_hostname)
if [ "$a_record_ip" != "$current_ip" ]; then
echo "WARNING: A record for $new_hostname does not point to server IP: $current_ip"
echo "After pointing the domain run this command to set domain for panel: opencli config update force_domain $new_hostname"
else
opencli config update force_domain "$new_hostname"
fi
else
echo "Hostname provided: $new_hostname is not a valid FQDN, OpenPanel will use IP address $current_ip for access."
fi
# Set the provided hostname as the system hostname
hostnamectl set-hostname $new_hostname
fi
}
opencli_setup(){
echo "Downloading OpenCLI and adding to path.."
mkdir -p /usr/local/admin
wget --timeout=30 -O /tmp/opencli.tar.gz "https://storage.googleapis.com/openpanel/${PANEL_VERSION}/get.openpanel.co/downloads/${PANEL_VERSION}/opencli/opencli-main.tar.gz" > /dev/null 2>&1 || curl --silent --max-time 20 -4 -o /tmp/opencli.tar.gz "https://storage.googleapis.com/openpanel/${PANEL_VERSION}/get.openpanel.co/downloads/${PANEL_VERSION}/opencli/opencli-main.tar.gz" || radovan 1 "download failed for https://storage.googleapis.com/openpanel/${PANEL_VERSION}/get.openpanel.co/downloads/${PANEL_VERSION}/opencli/opencli-main.tar.gz"
mkdir -p /tmp/opencli
cd /tmp/ && tar -xzf opencli.tar.gz -C /tmp/opencli
mkdir -p /usr/local/admin/scripts
cp -r /tmp/opencli/* /usr/local/admin/scripts > /dev/null 2>&1 || cp -r /tmp/opencli/opencli-main /usr/local/admin/scripts > /dev/null 2>&1 || radovan 1 "Fatal error extracting OpenCLI.."
rm /tmp/opencli.tar.gz > /dev/null 2>&1
rm -rf /tmp/opencli > /dev/null 2>&1
cp /usr/local/admin/scripts/opencli /usr/local/bin/opencli
chmod +x /usr/local/bin/opencli > /dev/null 2>&1
chmod +x -R /usr/local/admin/scripts/ > /dev/null 2>&1
#opencli commands
echo "# opencli aliases
ALIASES_FILE=\"${OPENCLI_DIR}aliases.txt\"
generate_autocomplete() {
awk '{print \$NF}' \"\$ALIASES_FILE\"
}
complete -W \"\$(generate_autocomplete)\" opencli" >> ~/.bashrc
# The command could not be located because '/usr/local/bin' is not included in the PATH environment variable.
export PATH="/usr/bin:$PATH"
source ~/.bashrc
echo "Testing 'opencli' commands:"
if [ -x "/usr/local/bin/opencli" ]; then
echo "opencli commands are available."
else
radovan 1 "'opencli --version' command failed."
fi
}
configure_nginx() {
# Nginx
echo "Setting Nginx configuration.."
mkdir -p /etc/nginx/{sites-available,sites-enabled} /etc/letsencrypt /var/log/nginx/domlogs /usr/share/nginx/html
ln -s /etc/openpanel/nginx/options-ssl-nginx.conf /etc/letsencrypt/options-ssl-nginx.conf
openssl dhparam -out /etc/letsencrypt/ssl-dhparams.pem 2048 > /dev/null 2>&1
# https://dev.openpanel.com/services/nginx
ln -s /etc/openpanel/nginx/nginx.conf /etc/nginx/nginx.conf
# Setting pretty error pages for nginx, but need to add them inside containers also!
mkdir /etc/nginx/snippets/ > /dev/null 2>&1
mkdir /srv/http/ > /dev/null 2>&1
ln -s /etc/openpanel/nginx/error_pages /srv/http/default
ln -s /etc/openpanel/nginx/error_pages/snippets/error_pages.conf /etc/nginx/snippets/error_pages.conf
ln -s /etc/openpanel/nginx/error_pages/snippets/error_pages_content.conf /etc/nginx/snippets/error_pages_content.conf
}
set_premium_features(){
if [ "$SET_PREMIUM" = true ]; then
LICENSE="Enterprise"
echo "Setting OpenPanel enterprise version license key $license_key"
opencli config update key "$license_key"
#added in 0.2.5 https://community.openpanel.com/d/91-email-support-for-openpanel-enterprise-edition
echo "Setting mailserver.."
opencli email-server install
else
LICENSE="Community"
fi
}
set_email_address_and_email_admin_logins(){
if [ "$SEND_EMAIL_AFTER_INSTALL" = true ]; then
# Check if the provided email is valid
if [[ $EMAIL =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "Setting email address $EMAIL for notifications"
opencli config update email "$EMAIL"
# Send an email alert
generate_random_token_one_time_only() {
local config_file="${CONFIG_FILE}"
TOKEN_ONE_TIME="$(tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 64)"
local new_value="mail_security_token=$TOKEN_ONE_TIME"
sed -i "s|^mail_security_token=.*$|$new_value|" "${CONFIG_FILE}"
}
email_notification() {
local title="$1"
local message="$2"
generate_random_token_one_time_only
TRANSIENT=$(awk -F'=' '/^mail_security_token/ {print $2}' "${CONFIG_FILE}")
SSL=$(awk -F'=' '/^ssl/ {print $2}' "${CONFIG_FILE}")
# Determine protocol based on SSL configuration
if [ "$SSL" = "yes" ]; then
PROTOCOL="https"
else
PROTOCOL="http"
fi
# Send email using appropriate protocol
curl -k -X POST "$PROTOCOL://127.0.0.1:2087/send_email" -F "transient=$TRANSIENT" -F "recipient=$EMAIL" -F "subject=$title" -F "body=$message"
}
server_hostname=$(hostname)
email_notification "OpenPanel successfully installed" "OpenAdmin URL: http://$server_hostname:2087/ | username: $new_username | password: $new_password"
else
echo "Address provided: $EMAIL is not a valid email address. Admin login credentials and future notifications will not be sent."
fi
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.com/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"
}
download_skeleton_directory_from_github(){
echo "Downloading configuration files to ${ETC_DIR}"
git clone https://github.com/stefanpejcic/openpanel-configuration ${ETC_DIR} > /dev/null 2>&1
# added in 0.2.9
chmod +x /etc/openpanel/ftp/start_vsftpd.sh
# added in 0.2.6
cp -fr /etc/openpanel/services/floatingip.service ${SERVICES_DIR}floatingip.service > /dev/null 2>&1
systemctl daemon-reload > /dev/null 2>&1
service floatingip start > /dev/null 2>&1
systemctl enable floatingip > /dev/null 2>&1
if [ -f "${CONFIG_FILE}" ]; then
echo -e "[${GREEN} OK ${RESET}] Configuration created successfully."
else
radovan 1 "Dowloading configuration files from GitHub failed, main conf file ${CONFIG_FILE} is missing."
fi
}
setup_bind(){
echo "Setting DNS service.."
mkdir -p /etc/bind/
cp -r /etc/openpanel/bind9/* /etc/bind/
# only on ubuntu systemd-resolved is installed
if [ -f /etc/os-release ] && grep -q "Ubuntu" /etc/os-release; then
echo " DNSStubListener=no" >> /etc/systemd/resolved.conf && systemctl restart systemd-resolved
# debian12 also!
elif [ -f /etc/os-release ] && grep -q "Debian" /etc/os-release; then
echo " DNSStubListener=no" >> /etc/systemd/resolved.conf && systemctl restart systemd-resolved
fi
echo "Generating rndc.key for DNS zone management."
# generate unique rndc.key
debug_log docker run -it --rm \
-v /etc/bind/:/etc/bind/ \
--entrypoint=/bin/sh \
ubuntu/bind9:latest \
-c 'rndc-confgen -a -A hmac-sha256 -b 256 -c /etc/bind/rndc.key'
chmod 0777 -R /etc/bind
}
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
}
setup_swap(){
# Function to create swap file
create_swap() {
fallocate -l ${SWAP_FILE}G /swapfile > /dev/null 2>&1
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo "/swapfile none swap sw 0 0" >> /etc/fstab
}
# Check if swap space already exists
if [ -n "$(swapon -s)" ]; then
echo "ERROR: Skipping creating swap space as there already exists a swap partition."
return
fi
# Check if we should set up swap anyway
if [ "$SETUP_SWAP_ANYWAY" = true ]; then
create_swap
else
# Only create swap if RAM is less than 8GB
memory_kb=$(grep 'MemTotal' /proc/meminfo | awk '{print $2}')
memory_gb=$(awk "BEGIN {print $memory_kb/1024/1024}")
if [ $(awk "BEGIN {print ($memory_gb < 8)}") -eq 1 ]; then
create_swap
else
echo "Total available memory is ${memory_gb}GB, skipping creating swap file."
fi
fi
}
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.com/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.com/docs/admin/intro/"
echo ""
echo "💬 Forums: Join our community forum to ask questions, share tips, and connect with fellow admins:"
echo "👉 https://community.openpanel.com/"
echo ""
echo "🎮 Discord: For real-time chat and support, hop into our Discord server:"
echo "👉 https://discord.openpanel.com/"
echo ""
echo "We're thrilled to have you with us. Let's make something amazing together! 🚀"
echo ""
}
panel_customize(){
if [ "$SCREENSHOTS_API_URL" == "local" ]; then
echo "Setting the local API service for website screenshots.. (additional 1GB of disk space will be used for the self-hosted Playwright service)"
debug_log playwright install
debug_log playwright install-deps
sed -i 's#screenshots=.*#screenshots=''#' "${CONFIG_FILE}" # must use '#' as delimiter
else
echo "Setting the remote API service '$SCREENSHOTS_API_URL' for website screenshots.."
sed -i 's#screenshots=.*#screenshots='"$SCREENSHOTS_API_URL"'#' "${CONFIG_FILE}" # must use '#' as delimiter
fi
}
install_openadmin(){
# OpenAdmin
#
# https://openpanel.com/docs/admin/intro/
#
echo "Setting up OpenAdmin panel.."
if [ "$REPAIR" = true ]; then
rm -rf $OPENPADMIN_DIR
fi
mkdir -p $OPENPADMIN_DIR
debug_log echo "Downloading OpenAdmin files for $pretty_os_name OS and Python version $current_python_version"
git clone -b $py_enchoded_for_distro --single-branch https://github.com/stefanpejcic/openadmin $OPENPADMIN_DIR
cd $OPENPADMIN_DIR
echo "pyyaml" >> requirements.txt # temp fix for debian12 missing yaml on some builds
pip install --default-timeout=3600 -r requirements.txt > /dev/null 2>&1 || pip install --default-timeout=3600 -r requirements.txt --break-system-packages > /dev/null 2>&1
cp -fr /usr/local/admin/service/admin.service ${SERVICES_DIR}admin.service > /dev/null 2>&1
cp -fr /usr/local/admin/service/watcher.service ${SERVICES_DIR}watcher.service > /dev/null 2>&1
# temporary for 0.2.9
# Detect the package manager and install inotifywait-tools
if command -v apt-get &> /dev/null; then
sudo apt-get install -y -qq inotify-tools > /dev/null 2>&1
elif command -v yum &> /dev/null; then
sudo yum install -y -q inotify-tools > /dev/null 2>&1
elif command -v dnf &> /dev/null; then
sudo dnf install -y -q inotify-tools > /dev/null 2>&1
else
echo "Error: No compatible package manager found. Please install inotify-tools manually for DNS zone reload to work."
fi
systemctl daemon-reload > /dev/null 2>&1
service admin start > /dev/null 2>&1
systemctl enable admin > /dev/null 2>&1
# added in 0.2.8 for reloading bind9 zones fom withon certbot container - needed for dns validation and wildcard ssl
chmod +x /usr/local/admin/service/watcher.sh
service watcher start > /dev/null 2>&1
systemctl enable watcher > /dev/null 2>&1
echo "Testing if OpenAdmin service is available on default port '2087':"
if ss -tuln | grep ':2087' >/dev/null; then
echo -e "[${GREEN} OK ${RESET}] OpenAdmin service is running."
else
radovan 1 "OpenAdmin service is NOT listening on port 2087."
fi
}
create_admin_and_show_logins_success_message() {
#motd
ln -s ${ETC_DIR}ssh/admin_welcome.sh /etc/profile.d/welcome.sh
chmod +x /etc/profile.d/welcome.sh
#cp version file
mkdir -p $OPENPANEL_DIR > /dev/null 2>&1
echo "$PANEL_VERSION" > $OPENPANEL_DIR/version
######docker cp openpanel:$OPENPANEL_DIR/version $OPENPANEL_DIR/version > /dev/null 2>&1
echo -e "${GREEN}OpenPanel ${LICENSE} [$(cat $OPENPANEL_DIR/version)] 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
# added in 0.2.3
# option to specify logins
if [ "$SET_ADMIN_USERNAME" = true ]; then
new_username=($custom_username)
else
wget -O /tmp/generate.sh https://gist.githubusercontent.com/stefanpejcic/905b7880d342438e9a2d2ffed799c8c6/raw/a1cdd0d2f7b28f4e9c3198e14539c4ebb9249910/random_username_generator_docker.sh > /dev/null 2>&1
if [ -f "/tmp/generate.sh" ]; then
source /tmp/generate.sh
new_username=($random_name)
else
new_username="admin"
fi
fi
if [ "$SET_ADMIN_PASSWORD" = true ]; then
new_password=($custom_password)
else
new_password=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 16)
fi
sqlite3 /etc/openpanel/openadmin/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);" > /dev/null 2>&1 &&
opencli admin new "$new_username" "$new_password" > /dev/null 2>&1 &&
opencli admin
echo -e "- Username: ${GREEN} ${new_username} ${RESET}"
echo -e "- Password: ${GREEN} ${new_password} ${RESET}"
echo " "
print_space_and_line
# added in 0.2.0
# email to user the new logins
set_email_address_and_email_admin_logins
# Redirect again stdout and stderr to the log file
exec > >(tee -a "$LOG_FILE")
exec 2>&1
}
# END main functions
#####################################################################
# #
# START main script execution #
# #
#####################################################################
parse_args "$@"
get_server_ipv4
detect_filesystem
set_version_to_install
print_header
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
create_admin_and_show_logins_success_message
run_custom_postinstall_script
# END main script execution