#!/bin/bash # =============================================== # Esse script foi desenvolvido por Lucas F. # This script was developed by Lucas F. # # Novembro de 2019 # 2019, November # # =============================================== # Arquivo de configuração # Config filename ARQ_CONF="centos.conf" # Nome do usuário no sistema # Sistem's username USERNAME="centos" # Nome do App no repositório # Application name in repository APP="appname" # Nome do ambiente virtual python # Virtual environment name VENV=".venv" # Nome de usuário do repositório ou grupo # Git username or group GIT_USER="username" # Endereço do projeto no repositório # Git project url GITURL="https://repo.com/$GIT_USER/$APP.git" # IP público ou domínio. Se deixar em branco o script vai capturar o endereço de IP com o comando hostname # Public IP address or domain. If you leave it blank, the script'll catch the IP address with hostname command PUB_IP="" # Nome da pasta que está no mesmo nível do arquivo wsgi.py # Folder name that is at the same level as wsgi.py file WSGI_FOLDER_NAME="appdirname" # Nome desejado para o serviço no Systemctl # Systemctl desirable name SERVICE="app.service" # Descrição do serviço no Systemctl # Systemctl service description DESCRIPTION="Django VPS uWSGI Emperor" # Adicionar o certificado digital na aplicação com Certbot. Escreva sim, se já possuir um domínio na internet # Apply certbot ssl certificate, hit yes if you already have a domain CERTBOT="no" # Adicionar renovação do certbot no crontab. Escreva sim, apenas se já possuir um domínio na internet # Create a renew certbot cron job, hit yes if you already have a domain CRON_CERTBOT="no" # Incrementar IP em ALLOWED_HOSTS caso não tenha incrementado # Increase ALLOWED_HOSTS IP on .env file IP_INCRE="no" # Mensagens em Português Brasil # PT_BR messages PTBR="yes" HAS_ERROS="no" ERRORS=() declare -A MSGS declare -A MSG_PT MSG_PT[errors]="Por favor verifique os seguintes erros:" MSG_PT[before]="Antes de começarmos verifique se as variáveis estão corretas:" MSG_PT[entry]="Digite sim ou s para continuar (sim/não): " MSG_PT[error_root]="Você precisa executar o script como root" MSG_PT[error_requirements]="Não encontrei o arquivo requirements.txt" MSG_PT[error_env]="Não encontrei o arquivo .env" MSG_PT[sugest_env]="Posso criar esse arquivo pra você? Eu criarei com as variáveis [SECRET_KEY, ALLOWED_HOSTS, DEBUG]. Sim! vou criar uma SECRET_KEY como Django apenas para dar sequência e testar o servidor, claro que você pode alterar depois." MSG_PT[error_allwhost]="Não encontrei a variável ALLOWED_HOSTS, você lembrou de extrair do settings.py?" MSG_PT[error_debug]="Não encontrei a variável DEBUG, você lembrou de extrair do settings.py?" MSG_PT[repo]="Por favor verificar o repositório ou suas credenciais" MSG_PT[done]="TUDO PRONTO! Só curtir!" MSG_PT[fail]="FALHOU" declare -A MSG_EN MSG_EN[errors]="Please check this errors:" MSG_EN[before]="Please check the variables before starting:" MSG_EN[entry]="Shall we proceed? (yes/no): " MSG_EN[error_root]="You must be root" MSG_EN[error_req]="Can't find requirements.txt file" MSG_EN[error_env]="Can't find .env file" MSG_EN[sugest_env]="Can i create this file for you? I'll create basic variables like [SECRET_KEY, ALLOWED_HOSTS, DEBUG]. Yep! i'll create a SECRET_KEY like Django just for continue and test server, although you can change it after." MSG_EN[error_allwhost]="Can't find ALLOWED_HOSTS variable did you extract it from settings.py?" MSG_EN[error_debug]="Can't find DEBUG variable did you extract it from settings.py?" MSG_EN[repo]="Please check your repo or credentials" MSG_EN[done]="ALL DONE! Enjoy it!" MSG_EN[fail]="FAIL" STEP=0 SCRIPT_PATH=$(dirname $0) SCRIPT_STEP_FILE="$SCRIPT_PATH/centos_django.step" CHARS="abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)" for ((i=0;i<${#CHARS};i++)); do ARRAY[$i]="${CHARS:i:1}"; done # Cria uma sequência de 50 caracteres aleatórios # Create 50 random chars key_gen() { for ((c=1; c<=50; c++)); do KEY="$KEY${ARRAY[$((RANDOM % 50))]}" done echo $KEY } # Lê o arquivo de configuração e atualiza os valores das variáveis # Read config file and set all variables if [[ -f "$SCRIPT_PATH/$ARQ_CONF" ]]; then . "$SCRIPT_PATH/$ARQ_CONF" fi # Lê o arquivo de passos e atualiza o valor da variável # Read step file and update the value if [[ -f "$SCRIPT_STEP_FILE" ]]; then . "$SCRIPT_STEP_FILE" fi # Verifica se deve exibir as mensagens em português do Brasil # Check if the language pt_br has been chosen if [[ ${PTBR,,} == "yes" ]] || [[ ${PTBR,,} == "sim" ]]; then for i in ${!MSG_PT[@]}; do MSGS[$i]=${MSG_PT[$i]} done else for i in ${!MSG_EN[@]}; do MSGS[$i]=${MSG_EN[$i]} done fi # Verifica se o usuário ativo é o root # Verify if is a root user if [[ "$EUID" -ne 0 ]];then ERRORS+=("${MSGS[error_root]}") echo -e "\033[31m${MSGS[error_root]}\033[0m" exit 1 fi # Verifica a existência do usuário no sistema # Check if user exists CNU=$(grep -c $USERNAME /etc/passwd) if [[ $CNU -lt 1 ]]; then useradd --create-home $USERNAME fi # Sanitiza a variável SERVICE # Sanitize SERVICE var if [[ ! $SERVICE =~ ".service" ]]; then SERVICE+=".service" fi # Obtém o IP # Get IP address if [[ ! -n $PUB_IP ]]; then PUB_IP=$(hostname -I | cut -d" " -f1) fi update_step_error() { STEP="$x" echo "STEP=$STEP" > "$SCRIPT_STEP_FILE" exit 1 } # Exibe todas as variáveis # Display all variables DISPLAY=" \033[33mUSERNAME \033[0m= \033[37m$USERNAME \033[33mAPP \033[0m= \033[37m$APP \033[33mVENV \033[0m= \033[37m$VENV \033[33mGIT_USER \033[0m= \033[37m$GIT_USER \033[33mGITURL \033[0m= \033[37m$GITURL \033[33mPUB_IP \033[0m= \033[37m$PUB_IP \033[33mWSGI_FOLDER_NAME \033[0m= \033[37m$WSGI_FOLDER_NAME \033[33mSERVICE \033[0m= \033[37m$SERVICE \033[33mDESCRIPTION \033[0m= \033[37m$DESCRIPTION \033[33mCERTBOT \033[0m= \033[37m$CERTBOT \033[33mCRON_CERTBOT \033[0m= \033[37m$CRON_CERTBOT \033[33mIP_INCRE \033[0m= \033[37m$IP_INCRE \033[0m" # Clonar o projeto # Clone project clone_project() { if ! command -v git &> /dev/null; then yum install -y git-core fi git clone $GITURL "/home/$USERNAME/$APP" if [[ $? -ge 1 ]]; then echo -e "\033[31m${MSGS[repo]}\033[0m" update_step_error "$x" fi } # Verifica a existência do arquivo requirements.txt # Check for existence of requirements.txt file validate_requirements() { if [[ ! -f "/home/$USERNAME/$APP/requirements.txt" ]]; then echo -e "\033[31m${MSGS[errors]}" && echo -e "\033[0m - \033[37m${MSGS[error_req]}\033[0m" && update_step_error "$x" fi } # Verifica a existência do arquivo .env # Check for existence of .env file validate_env() { if [[ ! -f "/home/$USERNAME/$APP/.env" ]]; then echo -e "\033[31m${MSGS[errors]}" echo -e "\033[0m - \033[37m${MSGS[error_env]}\033[0m" && echo -e "\n${MSGS[sugest_env]}\n" && read -p "${MSGS[entry]}" resp_env && if [[ -n resp_env ]]; then if [[ ${resp_env,,} == "sim" ]] || [[ ${resp_env,,} == "s" ]] || [[ ${resp_env,,} == "yes" ]] || [[ ${resp_env,,} == "y" ]]; then make_env_file else update_step_error "$x" fi fi fi } # Cria um arquivo .env # Create a .env file make_env_file() { ENV="SECRET_KEY='$(key_gen)'\n ALLOWED_HOSTS=localhost, 127.0.0.1\n DEBUG=False " $(echo -e $ENV | sed -e 's/^[ \t]*//' > /home/$USERNAME/$APP/.env) } # Incrementar IP em ALLOWED_HOSTS no arquivo .env caso não tenha incrementado # Increase ALLOWED_HOSTS IP on .env file if it doesn't exist increment_ip() { if [[ ${IP_INCRE,,} == "sim" ]] || [[ ${IP_INCRE,,} == "yes" ]]; then if [[ ! -n $(awk '{FS="="} /ALLOWED_HOSTS=/ {print $1}' /home/$USERNAME/$APP/.env) ]]; then echo -e "\033[31m${MSGS[error_allwhost]}\033[0m\n" update_step_error "$x" fi if [[ ! -n $(awk '{FS="="} /ALLOWED_HOSTS=/ {print $2}' /home/$USERNAME/$APP/.env) ]]; then sed -i "/ALLOWED_HOST/ s/$/$PUB_IP,/g" /home/$USERNAME/$APP/.env else SUBS_IP=$(sed -n "/ALLOWED_HOSTS/p" /home/$USERNAME/$APP/.env | sed "/$PUB_IP/g") if [[ ${#SUBS_IP} -ge 1 ]]; then if [[ ${SUBS_IP: -1} == "," ]]; then sed -i "/ALLOWED_HOST/ s/$/$PUB_IP,/g" /home/$USERNAME/$APP/.env else sed -i "/ALLOWED_HOST/ s/$/,$PUB_IP,/g" /home/$USERNAME/$APP/.env fi fi fi fi } # Muda a variável DEBUG para valor Falso # Update DEBUG variable to False update_debug() { if [[ ! -n $(awk '{FS="="} /DEBUG=/ {print $1}' /home/$USERNAME/$APP/.env) ]]; then echo -e "\033[31m${MSGS[error_debug]}\033[0m\n" update_step_error "$x" fi sed -i "/DEBUG/ s/=.*/=False/g" /home/$USERNAME/$APP/.env } # Instala pacotes necessários # Install required packages install_packages() { dnf config-manager --set-enabled crb && dnf update && yum -y install vim && yum -y install epel-release && yum -y install bind-utils && yum -y install wget && yum -y install python3 && yum -y install python3-devel && yum -y install gcc && yum -y install nginx && yum -y install net-tools && yum -y install policycoreutils-python-utils.noarch && if [[ $? -ge 1 ]]; then update_step_error "$x" fi } # Criar o ambiente Virtual # Create venv create_venv() { python3 -m venv /home/$USERNAME/$APP/$VENV if [[ $? -ge 1 ]]; then update_step_error "$x" fi } # Instalar as dependências do projeto # Install project dependencies install_dependencies() { /home/$USERNAME/$APP/$VENV/bin/pip3 install --upgrade pip && /home/$USERNAME/$APP/$VENV/bin/pip3 install uwsgi && /home/$USERNAME/$APP/$VENV/bin/pip3 install -r /home/$USERNAME/$APP/requirements.txt if [[ $? -ge 1 ]]; then update_step_error "$x" fi } # Coletar arquivos estáticos, criar tabelas e criar super usuário # Perform collectstatic, migrate and createsuperuser execute_collec_mig_createsup() { mkdir -p /home/$USERNAME/$APP/staticfiles && /home/$USERNAME/$APP/$VENV/bin/python3 /home/$USERNAME/$APP/manage.py collectstatic --noinput && /home/$USERNAME/$APP/$VENV/bin/python3 /home/$USERNAME/$APP/manage.py migrate if [[ $? -ge 1 ]]; then update_step_error "$x" fi } # Instala o firewall # Install firewall install_firewall() { yum install -y firewalld && systemctl start firewalld && firewall-cmd --permanent --add-service=ssh firewall-cmd --permanent --add-service=http && firewall-cmd --permanent --add-service=https && firewall-cmd --reload && systemctl enable firewalld if [[ $? -ge 1 ]]; then update_step_error "$x" fi } # Autorizar as portas # Enable Ports enable_ports() { semanage port -m -t http_port_t -p tcp 80 && semanage permissive -a httpd_t && iptables -I INPUT -p tcp -m tcp --dport 80 -j ACCEPT firewall-cmd --zone=public --add-port=80/tcp --permanent firewall-cmd --reload if [[ $? -ge 1 ]]; then update_step_error "$x" fi } # Corrigir a configuração inicial do nginx # Manage default nginx config file manage_default_nginx_file() { mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf~ && cat > /etc/nginx/nginx.conf < /etc/nginx/conf.d/$APP.conf < /home/$USERNAME/$APP/uwsgi.ini < /etc/systemd/system/$SERVICE < /usr/local/bin/$SERVICE.sh < /dev/null fi } declare -a commands=( "install_packages" "clone_project" "validate_requirements" "validate_env" "increment_ip" "update_debug" "create_venv" "install_dependencies" "execute_collec_mig_createsup" "install_firewall" "enable_ports" "manage_default_nginx_file" "create_nginx_file" "create_uwsgi_ini_file" "criar_emperor_uwsgi" "create_service_file" "create_script_emperor" "run_service" ) REPORT=() if [[ ${#ERRORS[@]} -ge 1 ]]; then echo "${MSGS[errors]}" for error in "${ERRORS[@]}"; do echo " - $error" done else echo -e "\n${MSGS[before]}\n$DISPLAY" read -p "${MSGS[entry]} " resp if [[ -n $resp ]]; then if [[ ${resp,,} == "sim" ]] || [[ ${resp,,} == "s" ]] || [[ ${resp,,} == "yes" ]] || [[ ${resp,,} == "y" ]]; then for ((x=$STEP; x<${#commands[@]};x++)); do echo "${commands[$x]}_$x" ${commands[$x]} $x done echo "\033[32m${MSGS[done]}\033[0m" fi fi fi