#!/bin/bash # # Production SSL Certificate Setup Script for michaelschiemer.de # Sets up Let's Encrypt SSL certificates on the production server # set -euo pipefail # Configuration SERVER_USER="deploy" SERVER_IP="94.16.110.151" REMOTE_PATH="/home/deploy/michaelschiemer" DOMAIN="michaelschiemer.de" EMAIL="michael@michaelschiemer.de" # Change this to your email SSH_OPTS="-i ~/.ssh/production -o StrictHostKeyChecking=no" # Colors for output GREEN="\e[32m" YELLOW="\e[33m" RED="\e[31m" BLUE="\e[34m" RESET="\e[0m" log_info() { echo -e "${BLUE}[INFO]${RESET} $1" } log_success() { echo -e "${GREEN}[SUCCESS]${RESET} $1" } log_warning() { echo -e "${YELLOW}[WARNING]${RESET} $1" } log_error() { echo -e "${RED}[ERROR]${RESET} $1" } # Check if domain resolves to production server check_dns() { log_info "Checking DNS resolution for $DOMAIN..." local resolved_ip=$(dig +short "$DOMAIN" | head -1) if [[ "$resolved_ip" == "$SERVER_IP" ]]; then log_success "Domain $DOMAIN resolves correctly to $SERVER_IP" else log_warning "Domain $DOMAIN resolves to $resolved_ip (expected: $SERVER_IP)" log_warning "SSL certificate might fail if DNS is incorrect" fi } # Install certbot on production server install_certbot() { log_info "Installing certbot on production server..." ssh $SSH_OPTS "$SERVER_USER@$SERVER_IP" ' # Update package list sudo apt update # Install snapd if not present if ! command -v snap &> /dev/null; then sudo apt install -y snapd fi # Install certbot via snap if ! command -v certbot &> /dev/null; then sudo snap install core; sudo snap refresh core sudo snap install --classic certbot sudo ln -sf /snap/bin/certbot /usr/bin/certbot fi # Verify certbot installation certbot --version ' log_success "Certbot installed successfully" } # Stop nginx temporarily for certificate generation prepare_nginx() { log_info "Preparing nginx for certificate generation..." ssh $SSH_OPTS "$SERVER_USER@$SERVER_IP" " cd $REMOTE_PATH # Stop nginx container temporarily docker compose down web 2>/dev/null || true # Create ssl directory mkdir -p ssl # Create temporary nginx config for certbot cat > ssl/nginx-temp.conf << 'EOF' server { listen 80; server_name $DOMAIN; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://\$server_name\$request_uri; } } EOF # Start temporary nginx for certificate validation docker run -d --name nginx-temp \\ -p 80:80 \\ -v \$(pwd)/ssl/nginx-temp.conf:/etc/nginx/conf.d/default.conf \\ -v \$(pwd)/ssl/certbot-webroot:/var/www/certbot \\ nginx:alpine " log_success "Nginx prepared for certificate generation" } # Generate Let's Encrypt certificate generate_certificate() { log_info "Generating Let's Encrypt certificate for $DOMAIN..." ssh $SSH_OPTS "$SERVER_USER@$SERVER_IP" " cd $REMOTE_PATH # Create webroot directory mkdir -p ssl/certbot-webroot # Generate certificate docker run --rm \\ -v \$(pwd)/ssl/certbot-webroot:/var/www/certbot \\ -v \$(pwd)/ssl:/etc/letsencrypt \\ certbot/certbot certonly \\ --webroot \\ --webroot-path=/var/www/certbot \\ --email $EMAIL \\ --agree-tos \\ --no-eff-email \\ -d $DOMAIN # Copy certificates to expected location if [ -d \"ssl/live/$DOMAIN\" ]; then cp ssl/live/$DOMAIN/fullchain.pem ssl/fullchain.pem cp ssl/live/$DOMAIN/privkey.pem ssl/privkey.pem chmod 644 ssl/fullchain.pem ssl/privkey.pem log_success 'SSL certificates generated and copied' else log_error 'Certificate generation failed' exit 1 fi " log_success "SSL certificate generated successfully" } # Clean up temporary nginx cleanup_nginx() { log_info "Cleaning up temporary nginx..." ssh $SSH_OPTS "$SERVER_USER@$SERVER_IP" " cd $REMOTE_PATH # Stop and remove temporary nginx docker stop nginx-temp 2>/dev/null || true docker rm nginx-temp 2>/dev/null || true # Clean up temporary files rm -f ssl/nginx-temp.conf rm -rf ssl/certbot-webroot " log_success "Temporary nginx cleaned up" } # Start the application with SSL start_application() { log_info "Starting application with SSL certificates..." ssh $SSH_OPTS "$SERVER_USER@$SERVER_IP" " cd $REMOTE_PATH # Start all services docker compose up -d # Wait for services to start sleep 10 # Check container status docker compose ps " log_success "Application started with SSL certificates" } # Test SSL certificate test_ssl() { log_info "Testing SSL certificate..." # Test HTTPS connection if curl -sSf "https://$DOMAIN" > /dev/null 2>&1; then log_success "HTTPS connection successful!" else log_warning "HTTPS connection test failed - checking certificate..." fi # Check certificate details echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN:443" 2>/dev/null | openssl x509 -noout -dates log_info "SSL setup completed!" log_success "Your site should now be accessible at: https://$DOMAIN" } # Setup certificate renewal setup_renewal() { log_info "Setting up automatic certificate renewal..." ssh $SSH_OPTS "$SERVER_USER@$SERVER_IP" " # Create renewal script cat > /home/deploy/renew-certificates.sh << 'RENEWAL_SCRIPT' #!/bin/bash cd /home/deploy/michaelschiemer # Stop nginx container docker compose down web # Renew certificate docker run --rm \\ -v \$(pwd)/ssl:/etc/letsencrypt \\ -v \$(pwd)/ssl/certbot-webroot:/var/www/certbot \\ certbot/certbot renew \\ --webroot \\ --webroot-path=/var/www/certbot # Copy renewed certificates if [ -f \"ssl/live/$DOMAIN/fullchain.pem\" ]; then cp ssl/live/$DOMAIN/fullchain.pem ssl/fullchain.pem cp ssl/live/$DOMAIN/privkey.pem ssl/privkey.pem chmod 644 ssl/fullchain.pem ssl/privkey.pem fi # Restart services docker compose up -d RENEWAL_SCRIPT chmod +x /home/deploy/renew-certificates.sh # Add cron job for automatic renewal (runs twice daily) (crontab -l 2>/dev/null; echo '0 12,0 * * * /home/deploy/renew-certificates.sh >> /var/log/letsencrypt-renewal.log 2>&1') | crontab - " log_success "Automatic certificate renewal configured" } # Main execution main() { log_info "Starting SSL certificate setup for $DOMAIN" # Check DNS check_dns # Install certbot install_certbot # Prepare nginx prepare_nginx # Generate certificate generate_certificate # Clean up temporary nginx cleanup_nginx # Start application start_application # Test SSL test_ssl # Setup renewal setup_renewal log_success "SSL certificate setup completed successfully!" log_info "Next steps:" echo " 1. Test your site: https://$DOMAIN" echo " 2. Certificates will auto-renew twice daily" echo " 3. Check renewal logs: ssh $SERVER_USER@$SERVER_IP 'tail -f /var/log/letsencrypt-renewal.log'" } # Show usage show_usage() { echo "Usage: $0 [--help]" echo "" echo "This script sets up Let's Encrypt SSL certificates for $DOMAIN" echo "" echo "Prerequisites:" echo " - Domain $DOMAIN must point to $SERVER_IP" echo " - SSH access to production server configured" echo " - Docker and docker-compose installed on server" echo "" echo "Options:" echo " --help Show this help message" } # Parse arguments case "${1:-}" in --help|-h) show_usage exit 0 ;; "") main ;; *) log_error "Unknown option: $1" show_usage exit 1 ;; esac