#!/bin/bash # # Install Odoo 16 on Debian 11 with LetsEncrypt SSL certificate # Created by Rob Fauls 10/27/2022 # # Description: # This script automates the installation of Odoo 16 on a fresh Debian 11 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 11 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. # 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 and stderr to the log file exec > >(tee -a "$LOG_FILE") 2>&1 # Function to print print_to_terminal commands to the terminal print_to_terminal() { echo ">> $@" } print_to_terminal "SYSTEM REQUIREMENTS" print_to_terminal "Fresh install of Debian 11" 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 >/dev/null 2>&1 # 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-Debian11.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 ###FINISH Check if user would like to update the script### ###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 < /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 < /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 ###END Gather user input### #Make sure system is up to date and remove any unnecessary packages print_to_terminal "Checking for updates." apt update >/dev/null 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 upgrade -y >/dev/null 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 autoremove -y >/dev/null 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" wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.bullseye_amd64.deb >/dev/null 2>&1 apt install ./wkhtmltox*bullseye_amd64.deb -y >/dev/null 2>&1 if [ $? -ne 0 ]; then print_to_terminal "Error: Failed to install wkhtmltopdf. Exiting." exit 1 fi #Install Odoo 16 print_to_terminal "Installing Odoo 16." print_to_terminal "This may take a few minutes, as Odoo's servers can be a bit slow..." wget https://nightly.odoo.com/odoo.key >/dev/null 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 apt update >/dev/null 2>&1 apt install odoo -y >/dev/null 2>&1 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 install certbot python3-certbot-nginx -y >/dev/null 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 >/dev/null 2>&1 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 </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 <>/etc/odoo/odoo.conf proxy_mode = True max_cron_threads = 1 workers = 2 longpolling_port = 8072 addons_path = /var/lib/odoo/.local/share/Odoo/addons/16.0 EOF #Restart Odoo and Nginx systemctl restart odoo systemctl restart nginx 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 your installation." print_to_terminal ${RED}"https://"${DOMAIN}":8069 is also currently accessible."${RESET} print_to_terminal ${YELLOW}"You may consider blocking port 8069 to avoid unsecure or uncached connections to Odoo."${RESET}