#!/bin/bash PURPLE='\033[35m' ORANGE='\033[33m' BLUE='\033[34m' BOLD='\033[1m' RESET='\033[0m' install_pkg() { pkg="$1" if ! command -v "$pkg" &> /dev/null; then apt install -y "$pkg" echo -e "${PURPLE}${BOLD}${pkg} installed${RESET}" fi } get_username() { if [[ -z "$username" ]]; then username=$(getent passwd 1000 | cut -d: -f1) fi } get_ip() { if [[ -z "$ip" ]]; then ip=$(hostname -I) if [[ "${ip: -1}" == " " ]]; then ip="${ip%?}" fi fi } get_ssh_port() { if [[ -z "$ssh_port" ]]; then ssh_port=$(cat /etc/ssh/sshd_config.d/custom.conf | grep "Port " | sed 's/^Port //') fi } echo -e "${PURPLE}${BOLD}Deployment Debian + Caddy + Directus + Nuxt${RESET}" if [ "$EUID" -ne 0 ]; then echo "Please run as root" exit fi apt install -y php php-fpm systemctl disable --now apache2 # # USER # echo -e "${PURPLE}${BOLD}Create a user ? (y/N) ${RESET}" read answer if [[ "$answer" == "y" ]]; then if id "debian" &>/dev/null; then sudo deluser --remove-home "debian" fi echo -e "${PURPLE}${BOLD}Create user${RESET}" read -p "Enter username: " username if id "$username" &>/dev/null; then echo "User '$username' already exists." exit 1 fi echo -e "${ORANGE}${BOLD}Generate and store the password somewhere safe${RESET}" read -s -p "Enter password: " password echo useradd -m "$username" chsh -s /bin/bash $username echo "$username:$password" | chpasswd usermod -aG sudo $username echo -e "${PURPLE}${BOLD}User '$username' created with password successfully.${RESET}" fi # # SSH # echo -e "${PURPLE}${BOLD}Setup SSH ? (y/N) ${RESET}" read answer if [[ "$answer" == "y" ]]; then echo -e "${PURPLE}${BOLD}Setup SSH${RESET}" ssh_port=$((RANDOM % (65536 - 1024 + 1) + 1024)) touch /etc/ssh/sshd_config.d/custom.conf echo "PermitRootLogin no" >> /etc/ssh/sshd_config.d/custom.conf echo "PermitEmptyPasswords no" >> /etc/ssh/sshd_config.d/custom.conf echo "Port ${ssh_port}" >> /etc/ssh/sshd_config.d/custom.conf systemctl reload ssh echo -e "${ORANGE}${BOLD}Store the ssh port ${ssh_port} somewhere safe${RESET}" fi # # FIREWALL AND FAIL2BAN # echo -e "${PURPLE}${BOLD}Setup Firewall and Fail2ban ? (y/N) ${RESET}" read answer if [[ "$answer" == "y" ]]; then echo -e "${PURPLE}${BOLD}Setup Firewall and Fail2ban${RESET}" apt install -y ufw fail2ban systemctl enable fail2ban ufw allow ssh ufw allow http ufw allow https fi # # CADDY # echo -e "${PURPLE}${BOLD}Install Caddy webserver ? (y/N) ${RESET}" read answer if [[ "$answer" == "y" ]]; then echo -e "${PURPLE}${BOLD}Install Caddy Webserver${RESET}" apt install -y debian-keyring debian-archive-keyring apt-transport-https curl curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list apt update apt install -y caddy > /etc/caddy/Caddyfile fi # # MARIADB # echo -e "${PURPLE}${BOLD}Install MariaDB ? (y/N) ${RESET}" read answer if [[ "$answer" == "y" ]]; then apt install -y mariadb-server echo -e "${ORANGE}${BOLD}Generate and store the password somewhere safe${RESET}" echo -e "${PURPLE}${BOLD}Enter the MariaDB root password : ${RESET}" read -s db_root_password echo install_pkg expect SECURE_MYSQL=$(expect -c " set timeout 3 spawn mysql_secure_installation expect \"Enter current password for root (enter for none):\" send \"\r\" expect \"Switch to unix_socket authentication \\[Y/n\\]\" send \"n\r\" expect \"Change the root password? \\[Y/n\\]\" send \"y\r\" expect \"New password:\" send \"$db_root_password\r\" expect \"Re-enter new password:\" send \"$db_root_password\r\" expect \"Remove anonymous users?\" send \"y\r\" expect \"Disallow root login remotely?\" send \"y\r\" expect \"Remove test database and access to it?\" send \"y\r\" expect \"Reload privilege tables now?\" send \"y\r\" expect eof ") echo "${SECURE_MYSQL}" # https://gist.github.com/coderua/5592d95970038944d099 fi # # DIRECTUS DB # echo -e "${PURPLE}${BOLD}Setup Directus database ? (y/N) ${RESET}" read answer if [[ "$answer" == "y" ]]; then echo -e "${ORANGE}${BOLD}Generate and store the password somewhere safe${RESET}" echo -e "${PURPLE}${BOLD}Enter the MariaDB Directus password : ${RESET}" read -s db_directus_password echo if [[ -z "$db_root_password" ]]; then echo -e "${PURPLE}${BOLD}Enter the MariaDB root password : ${RESET}" read -s db_root_password echo fi install_pkg expect CREATE_DIRECTUS_DB=$(expect -c " spawn mariadb -u root -p expect \"Enter password:\" send \"$db_root_password\r\" expect \"mysql>\" send \"CREATE USER 'directus'@'localhost' IDENTIFIED BY '${db_directus_password}';\r\" send \"CREATE DATABASE directus;\r\" send \"GRANT ALL PRIVILEGES ON directus.* TO 'directus'@'localhost' IDENTIFIED BY '${db_directus_password}';\r\" send \"FLUSH PRIVILEGES;\r\" expect \"mysql>\" send \"quit;\r\" expect eof ") echo "${CREATE_DIRECTUS_DB}" >& /dev/null echo -e "${PURPLE}${BOLD}Directus database created${RESET}" fi # # NODE # echo -e "${PURPLE}${BOLD}Install Node ? (y/N) ${RESET}" read answer if [[ "$answer" == "y" ]]; then touch /var/www/.bashrc chown www-data:www-data /var/www/.bashrc mkdir /var/www/.nvm chown www-data:www-data /var/www/.nvm mkdir /var/www/.npm chown www-data:www-data /var/www/.npm su -s /bin/bash -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash &&\ export NVM_DIR="$HOME/.nvm" &&\ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" &&\ [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" &&\ nvm install v18' www-data ln -sf /var/www/.nvm/versions/node/v18.19.1/bin/node /usr/bin/node ln -sf /var/www/.nvm/versions/node/v18.19.1/bin/npm /usr/bin/npm ln -sf /var/www/.nvm/versions/node/v18.19.1/bin/npx /usr/bin/npx echo -e "${PURPLE}${BOLD}Node installed${RESET}"; fi # # SET THE URL # echo -e "${PURPLE}${BOLD}Enter the domain name of the website${RESET}" read domain_name get_ip echo -e "${PURPLE}${BOLD}Configure the ${domain_name} DNS ZONE as the following${RESET}" echo -e "${BLUE}Domain : ${domain_name} | Type : A | Target : ${ip}${RESET}" echo -e "${BLUE}Domain : cms.${domain_name} | Type : A | Target : ${ip}${RESET}" echo -e "${BLUE}Domain : www.${domain_name} | Type : A | Target : ${ip}${RESET}" echo -e "${PURPLE}${BOLD}Press any key when done${RESET}" read repo_directory="/var/www/repositories" mkdir $repo_directory chown www-data:www-data $repo_directory cms_directory="${repo_directory}/cms_${domain_name}" # # DIRECTUS # echo -e "${PURPLE}${BOLD}Install Directus ? (y/N) ${RESET}" read answer if [[ "$answer" == "y" ]]; then install_pkg tmux if [[ -z "$db_directus_password" ]]; then echo -e "${PURPLE}${BOLD}Enter the MariaDB Directus password : ${RESET}" read -s db_directus_password echo fi echo "set -g default-shell /bin/sh" >> /etc/tmux.conf su -s /bin/bash -c "mkdir ${cms_directory}" www-data echo -e "${PURPLE}${BOLD}Import the Directus database ? (y/N) ${RESET}" read answer if [[ "$answer" == "y" ]]; then get_ssh_port get_username get_ip echo -e "${PURPLE}${BOLD}Import the .tar.gz archive from your local storage${RESET}" echo -e "${PURPLE}The archive should contain the sql dump, the upload directory and the .env file${RESET}" echo -e "${BLUE}scp -P ${ssh_port} /local/path/to/archive.tar.gz ${username}@${ip}:/home/${username}/${RESET}" echo -e "${PURPLE}${BOLD}Press any key when done${RESET}" read tar -xzf "/home/${username}/*.tar.gz" -C "${cms_directory}" rm "/home/${username}/*.tar.gz" sed -i "s/^\(DB_PASSWORD=\)'.*'$/\1'$db_directus_password'/" "${cms_directory}/.env" port=$(cat ${cms_directory}/.env | grep "^PORT=" | sed 's/^PORT=//') sql_dump=$(ls ${cms_directory}/*.sql) install_pkg expect LOAD_DIRECTUS_DB=$(expect -c " spawn mariadb -u directus -p directus < $sql_dump expect \"Enter password:\" send \"$db_directus_password\r\" expect eof ") echo "${LOAD_DIRECTUS_DB}" >& /dev/null rm $sql_dump chown -R www-data:www-data $cms_directory su -s /bin/bash -c "cd ${cms_directory} &&\ npm init -y &&\ npx directus bootstrap --skipAdminInit &&\ npx directus database migrate:latest tmux new-session -d -s directus &&\ tmux send-keys -t directus \"cd ${cms_directory} && npx directus start\" C-m" www-data echo -e "${PURPLE}${BOLD}Directus migration complete${RESET}" else echo -e "${ORANGE}${BOLD}Generate and store the credentials somewhere safe${RESET}" echo -e "${PURPLE}${BOLD}Enter the Directus admin email : ${RESET}" read directus_admin_email echo -e "${PURPLE}${BOLD}Enter the Directus admin password : ${RESET}" read -s directus_admin_password env_file="${cms_directory}/.env" port=8055 key=$(head -c 16 /dev/urandom | od -An -tx1 | tr -d ' \n') secret=$(head -c 16 /dev/urandom | od -An -tx1 | tr -d ' \n') su -s /bin/bash -c "mkdir ${cms_directory} &&\ mkdir ${cms_directory}/uploads &&\ echo \"HOST='${ip}'\" >> ${env_file} &&\ echo \"PORT=${port}\" >> ${env_file} &&\ echo \"PUBLIC_URL='https://cms.${domain_name}'\" >> ${env_file} &&\ echo \"DB_CLIENT='mysql'\" >> ${env_file} &&\ echo \"DB_HOST='127.0.0.1'\" >> ${env_file} &&\ echo \"DB_PORT='3306'\" >> ${env_file} &&\ echo \"DB_DATABASE='directus'\" >> ${env_file} &&\ echo \"DB_USER='directus'\" >> ${env_file} &&\ echo \"DB_PASSWORD='${db_directus_password}'\" >> ${env_file} &&\ echo \"SECRET='${secret}'\" >> ${env_file} &&\ echo \"KEY='${key}'\" >> ${env_file} &&\ echo \"CORS_ENABLED='true'\" >> ${env_file} &&\ echo \"CORS_ORIGIN='true'\" >> ${env_file} &&\ cd ${cms_directory} &&\ npm init -y &&\ npx directus bootstrap --skipAdminInit &&\ tmux new-session -d -s directus &&\ tmux send-keys -t directus \"cd ${cms_directory} && npx directus start\" C-m &&\ npx directus roles create --role Administrator --admin true &&\ npx directus roles create --role Website &&\ npx directus roles create --role User" www-data admin_role_uuid=$(echo $(mariadb -u directus -p${db_directus_password} \ -e "SELECT id FROM directus.directus_roles WHERE name='Administrator'") | awk '{print $2}') website_role_uuid=$(echo $(mariadb -u directus -p${db_directus_password} \ -e "SELECT id FROM directus.directus_roles WHERE name='Website'") | awk '{print $2}') user_role_uuid=$(echo $(mariadb -u directus -p${db_directus_password} \ -e "SELECT id FROM directus.directus_roles WHERE name='User'") | awk '{print $2}') website_password=$(head -c 16 /dev/urandom | od -An -tx1 | tr -d ' \n') su -s /bin/bash -c "cd ${cms_directory} &&\ npx directus users create --email \"${directus_admin_email}\" \ --password \"${directus_admin_password}\" --role \"${admin_role_uuid}\" &&\ npx directus users create --email \"website@${domain_name}\" --password \"${website_password}\" --role \"${website_role_uuid}\"" www-data website_token=$(head -c 16 /dev/urandom | od -An -tx1 | tr -d ' \n') mariadb -u directus -p${db_directus_password} -e "UPDATE directus.directus_roles SET icon='robot' WHERE name='Website'"; mariadb -u directus -p${db_directus_password} -e "UPDATE directus.directus_roles SET app_access='0' WHERE name='Website'"; mariadb -u directus -p${db_directus_password} -e "UPDATE directus.directus_users SET token=\"${website_token}\" WHERE email=\"website@${domain_name}\""; echo -e "${PURPLE}${BOLD}Import Directus data model ? (y/N) ${RESET}" read answer if [[ "$answer" == "y" ]]; then get_username get_ip get_ssh_port echo -e "${PURPLE}${BOLD}Import local Directus data model${RESET}" echo -e "${BLUE}npx directus schema snapshot ./snapshot.yaml${RESET}" echo -e "${BLUE}scp -P ${ssh_port} /local/path/to/snapshot.yaml ${username}@${ip}:/home/${username}/snapshot.yaml${RESET}" echo -e "${PURPLE}${BOLD}Press any key when done${RESET}" read su -s /bin/bash -c "cd ${cms_directory} &&\ npx directus schema apply --yes /home/${username}/snapshot.yaml" www-data fi echo -e "${PURPLE}${BOLD}You can now add some content${RESET}" echo -e "${ORANGE}${BOLD}Do not forget to set the permissions${RESET}" echo -e "${ORANGE}${BOLD}Website role ${RESET}${ORANGE}Read content collections and directus_files${RESET}" echo -e "${ORANGE}${BOLD}User role ${RESET}${ORANGE}All permissions on content collections, directus_files and directus_folders${RESET}" fi caddyfile="/etc/caddy/Caddyfile" echo "cms.${domain_name} {" >> $caddyfile echo "reverse_proxy ${ip}:${port}" >> $caddyfile echo "}" >> $caddyfile caddy fmt $caddyfile -w caddy reload -c $caddyfile echo -e "${PURPLE}${BOLD}Access Directus ${RESET}${PURPLE}https://cms.${domain_name}${RESET}" fi # # NUXT # echo -e "${PURPLE}${BOLD}Install the front-end ? (y/N) ${RESET}" read answer if [[ "$answer" == "y" ]]; then get_ip echo -e "${PURPLE}${BOLD}Create and push a prod branch on the repo${RESET}" echo -e "${BLUE}git checkout -b prod${RESET}" echo -e "${BLUE}git add . && git commit -m "first commit to prod"${RESET}" echo -e "${BLUE}git push origin prod${RESET}" echo -e "${PURPLE}${BOLD}Enter the .git url of the repo${RESET}" read repo_url front_repo_name=$(echo "$repo_url" | sed 's#.*/\([^/]*\)\.git#\1#') front_directory="${repo_directory}/${front_repo_name}" if [[ -z "$website_token" ]]; then echo -e "${PURPLE}${BOLD}Enter the Directus Website user static token${RESET}" read -s website_token fi touch /var/www/.nuxtrc echo "telemetry.consent=0" > /var/www/.nuxtrc echo "telemetry.enabled=false" >> /var/www/.nuxtrc chown -R www-data:www-data /var/www/.nuxtrc su -s /bin/bash -c "cd ${repo_directory} &&\ git clone ${repo_url} &&\ cd ${front_directory} &&\ git checkout prod &&\ echo \"DIRECTUS_API_TOKEN=${website_token}\" > .env &&\ echo \"URL=https://${domain_name}\" >> .env &&\ echo \"DIRECTUS_URL=https://cms.${domain_name}\" >> .env &&\ tmux send-keys -t directus C-c &&\ npm install -y &&\ NUXT_TELEMETRY_DISABLED=1 bash -c \"npm run build\" &&\ tmux new-session -d -s front &&\ tmux send-keys -t front \"cd ${front_directory} && node .output/server/index.mjs\" C-m &&\ tmux send-keys -t directus \"npx directus start\" C-m" www-data caddyfile="/etc/caddy/Caddyfile" echo "www.${domain_name} {" >> $caddyfile echo "redir ${domain_name}{uri} permanent" >> $caddyfile echo "}" >> $caddyfile echo "${domain_name} {" >> $caddyfile echo "reverse_proxy ${ip}:3000" >> $caddyfile echo "}" >> $caddyfile caddy fmt $caddyfile -w caddy reload -c $caddyfile echo -e "${PURPLE}${BOLD}Setup a webhook ? (y/N) ${RESET}" read answer if [[ "$answer" == "y" ]]; then echo -e "${PURPLE}${BOLD}If it does not already exists, create a webhook at the following url${RESET}" echo -e "${PURPLE}${BOLD}${repo_url}/settings/hooks/gitea/new${RESET}" echo -e "${BLUE}${BOLD}Target URL ${RESET}${BLUE}https://${domain_name}/webhook.php${RESET}" echo -e "${BLUE}${BOLD}Branch filter ${RESET}${BLUE}prod${RESET}" echo -e "${BLUE}${BOLD}Authorization Header ${RESET}${ORANGE}Generate a safe string using \`openssl rand -base64 32\`${RESET}" echo -e "${PURPLE}${BOLD}Enter the Authorization Header${RESET}" read -s auth_header apt install -y jq rm /var/www/html/index.html cp ./assets/webhook.php /var/www/html/ mkdir /var/www/webhook cp ./assets/webhook.sh /var/www/webhook chown www-data:www-data /var/www/webhook/webhook.sh chmod u+x /var/www/webhook/webhook.sh touch /var/www/webhook/webhook.log chown www-data:www-data /var/www/webhook/webhook.log head -n $(($(wc -l < $caddyfile) - 2)) $caddyfile > temp_Caddyfile && mv temp_Caddyfile $caddyfile echo "handle /webhook.php {" >> $caddyfile echo "@unauthorized not header Authorization \"${auth_header}\"" >> $caddyfile echo "respond @unauthorized \"Unauthorized access\"" >> $caddyfile echo "root * /var/www/html" >> $caddyfile echo "php_fastcgi unix//run/php/php8.2-fpm.sock" >> $caddyfile echo "file_server" >> $caddyfile echo "}" >> $caddyfile echo "handle {" >> $caddyfile echo "reverse_proxy ${ip}:3000" >> $caddyfile echo "}" >> $caddyfile echo "}" >> $caddyfile caddy fmt $caddyfile -w caddy reload -c $caddyfile fi fi apt purge expect # TODO # # ZABBIX # URBACKUP # DIRECTUS EMAIL # DIRECTUS REDIS # LOGING DE TOUT # IMPORT DIRECTUS FULL DB # CADDYFILE EN JSON # MÀJ # reboot a running system