Projeto final: script completo de provisionamento de servidor
1. Visão geral do projeto e objetivos
Este artigo apresenta um script Bash completo para provisionamento automatizado de um servidor web com banco de dados, seguindo o modelo LEMP (Linux, Nginx, MySQL/MariaDB, PHP) em distribuições baseadas em Debian/Ubuntu. O objetivo é criar um ambiente funcional e seguro para hospedar aplicações web com o mínimo de intervenção manual.
Escopo do projeto:
- Instalação e configuração do servidor web Nginx
- Instalação e configuração do MariaDB (banco de dados)
- Criação de usuário de serviço, virtual hosts e banco de dados dedicado
- Geração de certificado SSL autoassinado
- Configuração de backup automático e hardening básico
Pré-requisitos:
- Sistema Ubuntu 20.04+ ou Debian 11+
- Acesso root ou usuário com permissões sudo
- Conexão com internet para download de pacotes
Estrutura do script:
provisionamento.sh
config/
└── nginx-default.conf
└── mysql-secure.sql
logs/
└── provisionamento.log
backups/
└── config-originais/
2. Validação de ambiente e funções auxiliares
Antes de iniciar o provisionamento, validamos o ambiente e criamos funções essenciais para log, tratamento de erros e rollback.
#!/bin/bash
# Validação de ambiente
if [ "$(id -u)" -ne 0 ]; then
echo "Este script deve ser executado como root!"
exit 1
fi
OS=$(cat /etc/os-release | grep "^ID=" | cut -d= -f2 | tr -d '"')
if [ "$OS" != "ubuntu" ] && [ "$OS" != "debian" ]; then
echo "Sistema operacional não suportado: $OS"
exit 1
fi
# Função de log
LOG_FILE="/var/log/provisionamento.log"
log() {
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
echo "$msg" | tee -a "$LOG_FILE"
}
# Função de tratamento de erro
error_handler() {
local exit_code=$?
log "ERRO: Comando falhou com código $exit_code na linha $1"
rollback
exit $exit_code
}
trap 'error_handler $LINENO' ERR
# Função de rollback básico
rollback() {
log "Iniciando rollback..."
systemctl stop nginx 2>/dev/null || true
systemctl stop mariadb 2>/dev/null || true
apt-get remove --purge -y nginx mariadb-server 2>/dev/null || true
log "Rollback concluído"
}
# Verificação de conectividade
check_internet() {
if ! ping -c 1 8.8.8.8 &>/dev/null; then
log "ERRO: Sem conexão com internet"
exit 1
fi
log "Conexão com internet OK"
}
# Inicialização
log "Iniciando provisionamento do servidor"
check_internet
3. Configuração inicial do sistema
Realizamos as configurações básicas do sistema antes de instalar os serviços.
# Atualização de pacotes
log "Atualizando pacotes do sistema..."
apt-get update && apt-get upgrade -y
# Instalação de dependências básicas
log "Instalando dependências..."
apt-get install -y curl wget git unzip software-properties-common
# Configuração de hostname
NEW_HOSTNAME="webserver-prod"
hostnamectl set-hostname "$NEW_HOSTNAME"
echo "127.0.1.1 $NEW_HOSTNAME" >> /etc/hosts
log "Hostname configurado: $NEW_HOSTNAME"
# Configuração de fuso horário
timedatectl set-timezone America/Sao_Paulo
log "Fuso horário configurado: America/Sao_Paulo"
# Configuração de locale
locale-gen pt_BR.UTF-8
update-locale LANG=pt_BR.UTF-8
log "Locale configurado: pt_BR.UTF-8"
# Criação de usuário de serviço
SERVICE_USER="webapp"
useradd -m -s /bin/bash -d /home/$SERVICE_USER $SERVICE_USER
log "Usuário de serviço criado: $SERVICE_USER"
# Ajuste de limites do sistema
echo "* soft nofile 65536" >> /etc/security/limits.conf
echo "* hard nofile 65536" >> /etc/security/limits.conf
echo "fs.file-max = 2097152" >> /etc/sysctl.conf
sysctl -p
log "Limites do sistema ajustados"
4. Instalação e configuração do servidor web (Nginx)
Instalamos o Nginx com módulos essenciais, geramos certificado SSL e criamos um virtual host seguro.
# Instalação do Nginx
log "Instalando Nginx..."
apt-get install -y nginx nginx-extras
# Geração de certificado SSL autoassinado
SSL_DIR="/etc/nginx/ssl"
mkdir -p $SSL_DIR
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout $SSL_DIR/server.key \
-out $SSL_DIR/server.crt \
-subj "/C=BR/ST=SP/L=SaoPaulo/O=Empresa/CN=$NEW_HOSTNAME"
log "Certificado SSL autoassinado gerado"
# Configuração de virtual host
cat > /etc/nginx/sites-available/app.conf << 'EOF'
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name _;
root /var/www/html;
index index.php index.html;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
access_log /var/log/nginx/app_access.log;
error_log /var/log/nginx/app_error.log;
}
EOF
ln -sf /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
# Hardening básico
sed -i 's/# server_tokens off;/server_tokens off;/' /etc/nginx/nginx.conf
echo "add_header X-Frame-Options DENY;" >> /etc/nginx/nginx.conf
echo "add_header X-Content-Type-Options nosniff;" >> /etc/nginx/nginx.conf
nginx -t && systemctl enable nginx && systemctl restart nginx
log "Nginx configurado e habilitado"
5. Instalação e configuração do banco de dados (MariaDB)
Instalamos o MariaDB, aplicamos segurança e criamos banco e usuário dedicados para a aplicação.
# Instalação do MariaDB
log "Instalando MariaDB..."
apt-get install -y mariadb-server mariadb-client
# Segurança pós-instalação
mysql_secure_installation << EOF
y
root_password123
root_password123
y
y
y
y
EOF
log "MariaDB seguro configurado"
# Criação de banco e usuário dedicado
DB_NAME="app_database"
DB_USER="app_user"
DB_PASS="AppSecurePassword2024!"
mysql -u root -proot_password123 << EOF
CREATE DATABASE IF NOT EXISTS $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';
GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost';
FLUSH PRIVILEGES;
EOF
log "Banco de dados $DB_NAME e usuário $DB_USER criados"
# Configuração de backup automático com cron
BACKUP_DIR="/var/backups/mysql"
mkdir -p $BACKUP_DIR
cat > /usr/local/bin/backup-mysql.sh << 'BACKUPEOF'
#!/bin/bash
BACKUP_DIR="/var/backups/mysql"
DB_NAME="app_database"
DB_USER="app_user"
DB_PASS="AppSecurePassword2024!"
DATE=$(date +%Y%m%d_%H%M%S)
mysqldump -u $DB_USER -p$DB_PASS $DB_NAME | gzip > $BACKUP_DIR/backup_$DATE.sql.gz
find $BACKUP_DIR -type f -mtime +7 -delete
BACKUPEOF
chmod +x /usr/local/bin/backup-mysql.sh
echo "0 3 * * * root /usr/local/bin/backup-mysql.sh" >> /etc/crontab
log "Backup automático configurado para 3h diárias"
6. Deploy da aplicação e ajustes finos
Realizamos o deploy de uma aplicação de exemplo e configuramos permissões e ambiente.
# Criação da estrutura da aplicação
APP_DIR="/var/www/html"
mkdir -p $APP_DIR
# Exemplo de página PHP de teste
cat > $APP_DIR/index.php << 'EOF'
<?php
echo "<h1>Servidor Provisionado com Sucesso!</h1>";
echo "<p>Servidor: " . gethostname() . "</p>";
echo "<p>Data: " . date('d/m/Y H:i:s') . "</p>";
// Teste de conexão com banco
try {
$pdo = new PDO('mysql:host=localhost;dbname=app_database;charset=utf8mb4', 'app_user', 'AppSecurePassword2024!');
echo "<p>Conexão com banco de dados: OK</p>";
} catch (PDOException $e) {
echo "<p>Erro na conexão: " . $e->getMessage() . "</p>";
}
?>
EOF
# Configuração de permissões
chown -R $SERVICE_USER:www-data $APP_DIR
chmod -R 755 $APP_DIR
log "Aplicação deployada em $APP_DIR"
# Testes de sanidade
echo "--- Testes de Sanidade ---"
echo "Porta 80: $(ss -tlnp | grep -c ':80')"
echo "Porta 443: $(ss -tlnp | grep -c ':443')"
echo "Porta 3306: $(ss -tlnp | grep -c ':3306')"
echo "Nginx: $(systemctl is-active nginx)"
echo "MariaDB: $(systemctl is-active mariadb)"
# Teste de página inicial
curl -k -o /dev/null -s -w "%{http_code}" https://localhost/
log "Teste de página concluído"
7. Script de limpeza e rollback
Disponibilizamos uma função para desfazer todo o provisionamento, com backup de configurações originais.
# Backup de configurações originais
BACKUP_ORIG="/root/backup-provisionamento"
mkdir -p $BACKUP_ORIG
cp -r /etc/nginx /etc/mysql /var/www $BACKUP_ORIG/
log "Backup das configurações originais salvo em $BACKUP_ORIG"
# Função de desfazer provisionamento
desfazer_provisionamento() {
log "Iniciando desfazimento do provisionamento..."
# Parar serviços
systemctl stop nginx mariadb
# Desinstalar pacotes
apt-get remove --purge -y nginx* mariadb-server* php*
# Remover configurações
rm -rf /etc/nginx /etc/mysql /var/www/html
# Remover usuário e banco
userdel -r $SERVICE_USER 2>/dev/null || true
mysql -u root -proot_password123 -e "DROP DATABASE IF EXISTS $DB_NAME;"
# Restaurar backups se existirem
if [ -d "$BACKUP_ORIG" ]; then
cp -r $BACKUP_ORIG/* /
log "Configurações originais restauradas"
fi
log "Provisionamento desfeito com sucesso"
}
# Log final com resumo
echo "=========================================" >> $LOG_FILE
echo "RESUMO DO PROVISIONAMENTO" >> $LOG_FILE
echo "=========================================" >> $LOG_FILE
echo "Servidor: $NEW_HOSTNAME" >> $LOG_FILE
echo "Usuário serviço: $SERVICE_USER" >> $LOG_FILE
echo "Banco: $DB_NAME / Usuário: $DB_USER" >> $LOG_FILE
echo "Aplicação: https://localhost/" >> $LOG_FILE
echo "Backup: /var/backups/mysql/" >> $LOG_FILE
echo "Log: $LOG_FILE" >> $LOG_FILE
echo "=========================================" >> $LOG_FILE
log "Provisionamento concluído com sucesso!"
8. Considerações finais e próximos passos
Este script oferece uma base sólida para provisionamento automatizado de servidores web com banco de dados. Para ambientes de produção, considere as seguintes extensões:
Para múltiplos servidores:
- Adapte o script para usar variáveis de ambiente e templates
- Implemente loops para provisionar vários servidores via SSH
- Considere ferramentas como Ansible para orquestração mais robusta
Integração com notificações:
# Exemplo de notificação Slack
notify_slack() {
local message="$1"
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"$message\"}" \
https://hooks.slack.com/services/SEU/WEBHOOK/AQUI
}
Checklist de segurança pós-provisionamento:
- [ ] Alterar senhas padrão (root MySQL, usuário app)
- [ ] Configurar firewall (UFW/iptables)
- [ ] Implementar fail2ban para proteção contra ataques
- [ ] Configurar monitoramento com Prometheus + Grafana
- [ ] Revisar logs de auditoria regularmente
- [ ] Manter sistema atualizado com unattended-upgrades
O script completo pode ser estendido para incluir instalação de PHP-FPM, Redis, Node.js ou outros serviços conforme necessidade do projeto. A automação reduz erros humanos e garante consistência entre ambientes de desenvolvimento, homologação e produção.
Referências
- Documentação Oficial do Bash — Guia completo de referência para shell scripting no GNU Bash
- DigitalOcean - How to Install Linux, Nginx, MySQL, PHP (LEMP stack) on Ubuntu — Tutorial prático passo a passo para montagem do stack LEMP
- MariaDB Knowledge Base - mysql_secure_installation — Documentação oficial sobre segurança pós-instalação do MariaDB
- Nginx Admin Guide - SSL/TLS Configuration — Guia oficial da Nginx para configuração segura de SSL/TLS
- Linuxize - How to Create a Bash Script — Tutorial introdutório sobre criação de scripts Bash com boas práticas
- OWASP - Web Server Security Testing Guide — Guia de testes de segurança para servidores web, incluindo hardening