- Create Dockerfile for backend with Python 3.12 and gunicorn - Add docker-compose.yml with PostgreSQL, backend, nginx, certbot - Configure nginx reverse proxy with SSL and rate limiting - Add deployment scripts: deploy.sh, backup-db.sh, setup-ssl.sh - Include environment template and deployment documentation
175 lines
4.7 KiB
Bash
Executable File
175 lines
4.7 KiB
Bash
Executable File
#!/bin/bash
|
|
set -e
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
DEPLOYMENT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
|
|
echo -e "${GREEN}=== SSL Certificate Setup ===${NC}"
|
|
|
|
# Load environment variables
|
|
if [ ! -f "$DEPLOYMENT_DIR/.env" ]; then
|
|
echo -e "${RED}Error: .env file not found!${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
source "$DEPLOYMENT_DIR/.env"
|
|
|
|
# Validate required variables
|
|
if [ -z "$DOMAIN" ] || [ -z "$EMAIL" ]; then
|
|
echo -e "${RED}Error: DOMAIN and EMAIL must be set in .env${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
echo -e "${YELLOW}Domain: $DOMAIN${NC}"
|
|
echo -e "${YELLOW}Email: $EMAIL${NC}"
|
|
|
|
cd "$DEPLOYMENT_DIR/docker"
|
|
|
|
# Create a temporary nginx config for initial certificate request
|
|
echo -e "${GREEN}Creating temporary nginx config for certificate request...${NC}"
|
|
|
|
# Create temp config directory if it doesn't exist
|
|
mkdir -p "$DEPLOYMENT_DIR/nginx/conf.d"
|
|
|
|
# Create temporary HTTP-only config
|
|
cat > "$DEPLOYMENT_DIR/nginx/conf.d/default.conf" << EOF
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name $DOMAIN;
|
|
|
|
location /.well-known/acme-challenge/ {
|
|
root /var/www/certbot;
|
|
}
|
|
|
|
location / {
|
|
return 200 'Waiting for SSL certificate...';
|
|
add_header Content-Type text/plain;
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# Restart nginx with temporary config
|
|
echo -e "${GREEN}Restarting nginx with temporary config...${NC}"
|
|
docker compose up -d nginx
|
|
|
|
# Wait for nginx to start
|
|
sleep 5
|
|
|
|
# Request certificate
|
|
echo -e "${GREEN}Requesting SSL certificate from Let's Encrypt...${NC}"
|
|
|
|
docker compose run --rm certbot certonly \
|
|
--webroot \
|
|
--webroot-path=/var/www/certbot \
|
|
--email "$EMAIL" \
|
|
--agree-tos \
|
|
--no-eff-email \
|
|
-d "$DOMAIN"
|
|
|
|
# Check if certificate was created
|
|
if docker compose run --rm certbot certificates | grep -q "$DOMAIN"; then
|
|
echo -e "${GREEN}Certificate obtained successfully!${NC}"
|
|
else
|
|
echo -e "${RED}Failed to obtain certificate!${NC}"
|
|
echo "Please check:"
|
|
echo "1. DNS is pointing to this server"
|
|
echo "2. Port 80 is accessible from the internet"
|
|
exit 1
|
|
fi
|
|
|
|
# Restore the full nginx config with SSL
|
|
echo -e "${GREEN}Restoring full nginx config with SSL...${NC}"
|
|
|
|
cat > "$DEPLOYMENT_DIR/nginx/conf.d/default.conf" << EOF
|
|
# HTTP redirect to HTTPS
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name $DOMAIN;
|
|
|
|
location /.well-known/acme-challenge/ {
|
|
root /var/www/certbot;
|
|
}
|
|
|
|
location / {
|
|
return 301 https://\$host\$request_uri;
|
|
}
|
|
}
|
|
|
|
# HTTPS server
|
|
server {
|
|
listen 443 ssl http2;
|
|
listen [::]:443 ssl http2;
|
|
server_name $DOMAIN;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;
|
|
|
|
ssl_session_timeout 1d;
|
|
ssl_session_cache shared:SSL:50m;
|
|
ssl_session_tickets off;
|
|
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
|
ssl_prefer_server_ciphers off;
|
|
|
|
add_header Strict-Transport-Security "max-age=63072000" always;
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-XSS-Protection "1; mode=block" always;
|
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
|
|
location ~ ^/auth/(login|register)\$ {
|
|
limit_req zone=auth_limit burst=10 nodelay;
|
|
|
|
proxy_pass http://backend;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
proxy_set_header Connection "";
|
|
}
|
|
|
|
location / {
|
|
limit_req zone=api_limit burst=20 nodelay;
|
|
|
|
proxy_pass http://backend;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
proxy_set_header Connection "";
|
|
|
|
proxy_connect_timeout 60s;
|
|
proxy_send_timeout 60s;
|
|
proxy_read_timeout 60s;
|
|
}
|
|
|
|
location /health {
|
|
proxy_pass http://backend/docs;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Connection "";
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# Reload nginx
|
|
echo -e "${GREEN}Reloading nginx with SSL config...${NC}"
|
|
docker compose exec nginx nginx -s reload
|
|
|
|
echo -e "${GREEN}=== SSL setup complete! ===${NC}"
|
|
echo ""
|
|
echo "Your API is now available at: https://$DOMAIN"
|
|
echo ""
|
|
echo "Certificate will auto-renew via the certbot container."
|