website-scripts/Odoo/Install-Odoo16-Debian12.sh

439 lines
15 KiB
Bash

#!/bin/bash
#
# Install Odoo 16 on Debian 12 with LetsEncrypt SSL certificate
# Copyright 2023 Robert Fauls. Licensed under the GNU GPLv3 License.
# Created by Rob Fauls 10/27/2022
#
# Description:
# This script automates the installation of Odoo 16 on a fresh Debian 12 system.
# It configures Nginx as a reverse proxy and sets up Let's Encrypt SSL certificate.
#
# Usage:
# - Run the script as a user with sudo privileges.
# - Make sure you have a fresh install of Debian 12 with a domain name and accessible ports 80 and 443.
# - Follow the prompts to enter the domain name and email address associated with the SSL certificate.
# - The script will update the system, install prerequisites, install Odoo 16, configure Let's Encrypt, and set up Nginx.
#
# Dependencies:
# - wget, gnupg2, certbot, python3-certbot-nginx
#
# Note: Please review the script and modify it to fit your specific requirements.
#
# Feel free to use and modify this script, ensuring that you contribute any improvements back to the source.
#
clear
if [ "$EUID" -ne 0 ]
then
echo "This script must be run with sudo privileges. Exiting."
exit 1
fi
#If anything fails, exit the script.
set -e
# Set the log file path
LOG_FILE="$(dirname "$0")/OdooInstall.log"
# Redirect stdout to a log file
exec >"$LOG_FILE"
# Append both stderr and stdout to the log file and display stderr on the console
exec 2> >(tee -a "$LOG_FILE" >&2)
# Function to print messages to the terminal and log them to the file
print_to_terminal() {
# Print to the terminal
echo "$@" > /dev/tty
# Log to the file
echo "$@" >> "$LOG_FILE"
}
# Get the current date and time
current_date_time=$(date)
echo "Beginning of the Odoo installation script log file. It is currently: "$current_date_time > $LOG_FILE
print_to_terminal "SYSTEM REQUIREMENTS"
print_to_terminal "Fresh install of Debian 12"
print_to_terminal "Domain name"
print_to_terminal "Public IP with ports 80 and 443 accessible (LetsEncrypt)"
print_to_terminal ""
print_to_terminal "Please be sure everything is properly configured."
print_to_terminal "If you are missing the domain name or ports, LetsEncrypt and Nginx will not be properly configured"
#Install prerequisites
print_to_terminal "Making sure we have the basics installed before beginning..."
DEBIAN_FRONTEND=noninteractive apt-get install -qq -y wget gnupg2 curl jq
# Check the installation status
if [ $? -ne 0 ]; then
print_to_terminal "Error: Failed to install prerequisites. Exiting."
exit 1
fi
# Create an empty crontab for root if it doesn't exist
if ! crontab -l -u root >/dev/null 2>&1; then
echo | crontab -u root -
fi
###START Check if user would like to update the script###
read -rp "Would you like to check for an updated script? [y/n]: " response
# Convert the response to lowercase for case-insensitive comparison
response=${response,,}
# Check if the user wants to check for an updated script
if [[ $response == "y" || $response == "yes" ]]; then
# Retrieve the latest version of the script
curl -s -o odoo_install_update.sh https://code.flatironnetworks.com/RobFauls-Com/website-scripts/raw/branch/main/Odoo/Install-Odoo16-Debian12.sh
# Calculate the hash values
current_hash=$(md5sum "$0" | awk '{print $1}')
updated_hash=$(md5sum odoo_install_update.sh | awk '{print $1}')
# Compare the hash values
if [[ $current_hash != $updated_hash ]]; then
print_to_terminal "An update is available. Performing self-update..."
# Replace the existing script with the updated script
mv odoo_install_update.sh "$0"
chmod +x "$0"
# Run the updated script
exec "$0" "$@"
else
print_to_terminal "The script is already up to date."
fi
else
print_to_terminal "Skipping script update."
fi
###START Unattended Upgrades###
read -rp "Would you like to enable unattended upgrades for automatic security updates? [y/n]: " response
# Convert the response to lowercase for case-insensitive comparison
response=${response,,}
if [[ $response == "y" || $response == "yes" ]]; then
# Install unattended-upgrades package
apt-get update >/dev/null 2>&1
apt-get install unattended-upgrades -y >/dev/null 2>&1
# Configure unattended-upgrades
cat <<EOT > /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
EOT
# Customize the configuration as needed
cat <<EOT > /etc/apt/apt.conf.d/50unattended-upgrades
Unattended-Upgrade::Allowed-Origins {
"\${distro_id}:\${distro_codename}-security";
"\${distro_id}:\${distro_codename}-updates";
"\${distro_id}:\${distro_codename}-proposed";
"\${distro_id}:\${distro_codename}-backports";
};
Unattended-Upgrade::Package-Blacklist {
// Uncomment the following line to exclude specific packages from automatic upgrades
// "package-name";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::MinimalSteps "true";
Unattended-Upgrade::Mail "false";
EOT
# Enable and start the unattended-upgrades service
systemctl enable unattended-upgrades
systemctl start unattended-upgrades
print_to_terminal "Unattended upgrades have been enabled."
else
print_to_terminal "Unattended upgrades have not been enabled."
fi
###FINISH Unattended Upgrades###
###BEGIN Gather user input###
print_to_terminal "Let's collect some information about your environment"
# Validate domain name
while true; do
print_to_terminal "Please enter your domain name:"
read -r DOMAIN
# Validate domain name using regular expression
if [[ ! $DOMAIN =~ ^[a-zA-Z0-9]+([-.][a-zA-Z0-9]+)*\.[a-zA-Z]{2,}$ ]]; then
print_to_terminal "Invalid domain name. Please enter a valid domain name."
else
break
fi
done
# Validate email address
while true; do
print_to_terminal "Please enter the email address you'd like to associate with the SSL certificate:"
read -r EMAIL
# Validate email address using regular expression
if [[ ! $EMAIL =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then
print_to_terminal "Invalid email address. Please enter a valid email address."
else
break
fi
done
# Get the number of CPU cores minus one
DEFAULT_CORES=$(( $(nproc) - 1 ))
#Gather CPU cores information
while true; do
print_to_terminal "Please enter the number of CPU cores you'd like to make available to Odoo (default: $DEFAULT_CORES)."
print_to_terminal "Default is # of system cores -1:"
read -r CPU_CORES
# If user input is empty, set it to default value
if [[ -z $CPU_CORES ]]; then
CPU_CORES=$DEFAULT_CORES
fi
# Validate the input to ensure it's a positive integer
if ! [[ $CPU_CORES =~ ^[1-9][0-9]*$ ]]; then
print_to_terminal "Invalid input. Please enter a positive integer for the number of CPU cores."
else
break
fi
done
#Define max cron threads as # of workers, minus one. Minimum of 1.
CRON_THREADS=$(( CPU_CORES - 1 ))
if (( CRON_THREADS < 1 )); then
CRON_THREADS=1
fi
###END Gather user input###
#Make sure system is up to date and remove any unnecessary packages
print_to_terminal "Checking for updates."
apt-get update -qq >> "$LOG_FILE" 2>&1
if [ $? -ne 0 ]; then
print_to_terminal "Error: Failed to update packages. Exiting."
exit 1
fi
print_to_terminal "Applying system updates."
apt-get upgrade -qq -y >> "$LOG_FILE" 2>&1
if [ $? -ne 0 ]; then
print_to_terminal "Error: Failed to install packages. Exiting."
exit 1
fi
print_to_terminal "Autoremoving unnecessary packages."
apt-get autoremove -qq -y >> "$LOG_FILE" 2>&1
if [ $? -ne 0 ]; then
print_to_terminal "Error: Failed to auto-remove packages. Exiting."
exit 1
fi
#Install wkhtmltopdf
print_to_terminal "Installing wkhtmltopdf"
print_to_terminal "Latest version as of 30OCT23: 0.12.6.1-3"
wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.bookworm_amd64.deb >> "$LOG_FILE" 2>&1
apt-get install -qq ./wkhtmltox*bookworm_amd64.deb -y >> "$LOG_FILE" 2>&1
if [ $? -ne 0 ]; then
print_to_terminal "Error: Failed to install wkhtmltopdf. Exiting."
exit 1
fi
#Install Odoo 16
print_to_terminal "Downloading Odoo 16."
print_to_terminal "This may take a few minutes..."
wget https://nightly.odoo.com/odoo.key >> "$LOG_FILE" 2>&1
cat odoo.key | gpg --dearmor | tee /etc/apt/trusted.gpg.d/odoo.gpg >/dev/null 2>&1
echo "deb http://nightly.odoo.com/16.0/nightly/deb/ ./" | tee -a /etc/apt/sources.list.d/odoo.list >/dev/null
print_to_terminal "Installing Odoo 16."
print_to_terminal "This also takes about a minute (depending on system speed)."
apt-get update -qq >> "$LOG_FILE"
apt-get install -qq odoo -y >> "$LOG_FILE"
if [ $? -ne 0 ]; then
print_to_terminal "Error: Failed to install Odoo. Exiting."
exit 1
fi
#Install LetsEncrypt and configure SSL certificates
print_to_terminal "Installing LetsEncrypt and configuring SSL certificates."
apt-get install certbot python3-certbot-nginx -y >> "$LOG_FILE" 2>&1
if [ $? -ne 0 ]; then
print_to_terminal "Error: Failed to install Let's Encrypt and Certbot. Exiting."
exit 1
fi
systemctl stop nginx
/usr/bin/certbot certonly --standalone -d ${DOMAIN} --preferred-challenges http --agree-tos -n -m ${EMAIL} --keep-until-expiring
if [ $? -ne 0 ]; then
print_to_terminal "Error: Failed to configure SSL certificates with Let's Encrypt. Exiting."
exit 1
fi
#Insert cron job for LetsEncypt refresh certs into crontab
print_to_terminal "Configuring cron job to renew LetsEncrypt Certificates"
crontab -l; echo "15 3 * * * /usr/bin/certbot renew --pre-hook 'systemctl stop nginx' --post-hook 'systemctl start nginx'" | sort -u | crontab - >/dev/null 2>&1
#Create Nginx configuration file:
cat <<EOF >/etc/nginx/sites-available/odoo.conf
#odoo server
upstream odoo {
server 127.0.0.1:8069;
}
upstream odoochat {
server 127.0.0.1:8072;
}
map \$http_upgrade \$connection_upgrade {
default upgrade;
'' close;
}
# http -> https
server {
listen 80;
server_name $DOMAIN;
rewrite ^(.*) https://\$host$1 permanent;
}
server {
listen 443 ssl;
server_name $DOMAIN;
proxy_read_timeout 720s;
proxy_connect_timeout 720s;
proxy_send_timeout 720s;
# Add Headers for odoo proxy mode
proxy_set_header X-Forwarded-Host \$host;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_set_header X-Real-IP \$remote_addr;
# SSL parameters
ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/$DOMAIN/chain.pem;
ssl_session_timeout 30m;
ssl_protocols TLSv1.2;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# log
access_log /var/log/nginx/odoo.access.log;
error_log /var/log/nginx/odoo.error.log;
# Redirect websocket requests to odoo gevent port
location /websocket {
proxy_pass http://odoochat;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection \$connection_upgrade;
proxy_set_header X-Forwarded-Host \$host;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_set_header X-Real-IP \$remote_addr;
}
# Redirect requests to odoo backend server
location / {
proxy_redirect off;
proxy_pass http://odoo;
}
# common gzip
gzip_types text/css text/scss text/plain text/xml application/xml application/json application/javascript;
gzip on;
}
EOF
#Create symlink to enable the site
ln -s /etc/nginx/sites-available/odoo.conf /etc/nginx/sites-enabled/
if [ $? -ne 0 ]; then
print_to_terminal "Error: Failed to create symlink for Nginx configuration. Exiting."
exit 1
fi
# Check health of Nginx configuration
print_to_terminal "Checking health of NGINX configuration."
if ! nginx -t; then
print_to_terminal "Error: Nginx configuration test failed. Exiting."
exit 1
fi
#Modify odoo.conf to properly use Nginx, allow chat to work, and define the default addons path
cat <<EOF >>/etc/odoo/odoo.conf
;Certain values copied from: https://www.odoo.com/documentation/16.0/administration/install/deploy.html
proxy_mode = True
max_cron_threads = $CRON_THREADS
workers = $CPU_CORES
longpolling_port = 8072
addons_path = /var/lib/odoo/.local/share/Odoo/addons/16.0
;Memory and CPU limits are commented out.
;Moft limit for virtual memory, after which new requests will spawn new workers.
;limit_memory_soft = 629145600
;Maximum allowed virtual memory per Odoo worker, beyond which the worker is killed and recycled.
;limit_memory_hard = 1677721600
;Maximum number of requests a worker can handle before it is recycled.
;limit_request = 8192
;Maximum allowed CPU time per request.
;limit_time_cpu = 600
;Maximum allowed real time per request.
;limit_time_real = 1200
;regular XML-RPC
xmlrpc = True
xmlrpc_interface =
xmlrpc_port = 8069
;XML-RPC over SSL
xmlrpcs = True
xmlrpcs_interface =
xmlrpcs_port = 8071
EOF
#Restart Odoo and Nginx
systemctl restart odoo
systemctl restart nginx
#Fix logged IP addresses in CloudFlare
print_to_terminal "If you are using CloudFlare proxy (do not have DNS only selected), you will see CloudFlare IP Addresses"
print_to_terminal "in your logs. If you are using the feature and would like to see the users' real IP, select yes below."
print_to_terminal "NOTE: this function will download and execute new script from:"
print_to_terminal "https://code.flatironnetworks.com/RobFauls-Com/website-scripts/raw/branch/main/Cloudflare/Fix_IP_Logging.sh"
print_to_terminal ""
print_to_terminal "You'll see some messages repeated. This is intentional, as I want to ensure this script stays updated."
print_to_terminal "The other script is designed to be used on it's own, so please be patient with the extra prompts"
print_to_terminal ""
read -p "Would you like to download and execute the Cloudflare IP Logging fix script? (yes/no): " response
if [[ $response == "y" || $response == "yes" ]]; then
# Specify the URL of the script
script_url="https://code.flatironnetworks.com/RobFauls-Com/website-scripts/raw/branch/main/Cloudflare/Fix_IP_Logging.sh"
# Specify the local filename
local_file="/tmp/Fix_IP_Logging.sh"
#Download the script
if command -v curl > /dev/null 2>&1; then
curl -o $local_file $script_url
elif command -v wget > /dev/null 2>&1; then
wget -O $local_file $script_url
else
print_to_terminal "Error: Neither curl nor wget are available on this system. Please install one of them to continue."
exit 1
fi
# Make the script executable
chmod +x $local_file
# Execute the downloaded script
$local_file
print_to_terminal "Script executed successfully."
else
print_to_terminal "Continuing without downloading and executing the script."
fi
print_to_terminal
print_to_terminal
print_to_terminal "Odoo16 installation has completed!"
print_to_terminal "If you're using CloudFlare, you may need to modify the SSL/TLS encryption mode."
print_to_terminal "Without modifying the encryption mode, you may be unable to access the site"
print_to_terminal
print_to_terminal "Please go to https://"${DOMAIN}"/ to access and secure your installation."