#!/bin/bash # SSL Certificate Management для CatLink # Автоматическая настройка SSL с Let's Encrypt и управление сертификатами set -e # Цвета RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log() { echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"; } success() { echo -e "${GREEN}✅ $1${NC}"; } warning() { echo -e "${YELLOW}⚠️ $1${NC}"; } error() { echo -e "${RED}❌ $1${NC}"; exit 1; } # Проверка переменных окружения check_env() { if [ ! -f ".env" ]; then error ".env файл не найден. Запустите make deploy для его создания." fi source .env if [ -z "$MAIN_DOMAIN" ]; then error "MAIN_DOMAIN не задан в .env файле" fi if [ -z "$LETSENCRYPT_EMAIL" ]; then error "LETSENCRYPT_EMAIL не задан в .env файле" fi } # Установка certbot если не установлен install_certbot() { if ! command -v certbot &> /dev/null; then log "Установка certbot..." sudo apt update sudo apt install -y certbot python3-certbot-nginx success "certbot установлен" else success "certbot уже установлен" fi } # Проверка DNS записей check_dns() { log "Проверка DNS записей для $MAIN_DOMAIN..." # Получаем внешний IP сервера SERVER_IP=$(curl -s https://ipinfo.io/ip || curl -s https://icanhazip.com || echo "unknown") log "IP сервера: $SERVER_IP" # Проверяем A запись DOMAIN_IP=$(nslookup $MAIN_DOMAIN | grep -A 1 "Name:" | tail -n1 | awk '{print $2}' || echo "unknown") log "IP домена $MAIN_DOMAIN: $DOMAIN_IP" if [ "$SERVER_IP" = "$DOMAIN_IP" ]; then success "DNS запись настроена корректно" return 0 else warning "DNS запись может быть настроена неверно" warning "Сервер: $SERVER_IP, Домен: $DOMAIN_IP" read -p "Продолжить? (yes/no): " CONTINUE if [ "$CONTINUE" != "yes" ]; then error "Настройте DNS запись и попробуйте снова" fi fi } # Получение Let's Encrypt сертификата obtain_letsencrypt() { log "Получение Let's Encrypt сертификата для $MAIN_DOMAIN..." # Формируем список доменов CERT_DOMAINS="-d $MAIN_DOMAIN" if [ -n "$ADDITIONAL_DOMAINS" ]; then IFS=',' read -ra DOMAINS <<< "$ADDITIONAL_DOMAINS" for domain in "${DOMAINS[@]}"; do domain=$(echo $domain | xargs) # trim whitespace if [ -n "$domain" ]; then CERT_DOMAINS="$CERT_DOMAINS -d $domain" log "Добавлен домен: $domain" fi done fi log "Домены для сертификата: $CERT_DOMAINS" # Временно останавливаем nginx для standalone режима if systemctl is-active --quiet nginx; then log "Остановка nginx для получения сертификата..." sudo systemctl stop nginx NGINX_WAS_RUNNING=true else NGINX_WAS_RUNNING=false fi # Получаем сертификат в standalone режиме if sudo certbot certonly --standalone \ $CERT_DOMAINS \ --email "$LETSENCRYPT_EMAIL" \ --agree-tos \ --non-interactive \ --expand; then success "Сертификат получен успешно" # Запускаем nginx обратно if [ "$NGINX_WAS_RUNNING" = true ]; then sudo systemctl start nginx fi return 0 else error "Не удалось получить сертификат" fi } # Создание самоподписанного сертификата create_selfsigned() { log "Создание самоподписанного сертификата для $MAIN_DOMAIN..." # Создаем директории sudo mkdir -p /etc/ssl/private /etc/ssl/certs # Генерируем ключ и сертификат sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout "/etc/ssl/private/${MAIN_DOMAIN}.key" \ -out "/etc/ssl/certs/${MAIN_DOMAIN}.crt" \ -subj "/C=US/ST=State/L=City/O=CatLink/CN=${MAIN_DOMAIN}" \ -extensions v3_req \ -config <(cat < HTTPS redirect server { listen 80; server_name $MAIN_DOMAIN${ADDITIONAL_DOMAINS:+ $ADDITIONAL_DOMAINS}; # Let's Encrypt challenge location /.well-known/acme-challenge/ { root /var/www/html; } # Redirect all other requests to HTTPS location / { return 301 https://\$server_name\$request_uri; } } # HTTPS server server { listen 443 ssl http2; server_name $MAIN_DOMAIN${ADDITIONAL_DOMAINS:+ $ADDITIONAL_DOMAINS}; # SSL Configuration ssl_certificate $SSL_CERT; ssl_certificate_key $SSL_KEY; # SSL Security ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # Security headers add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; # Gzip compression gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json; # Frontend (Next.js) location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection 'upgrade'; 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_cache_bypass \$http_upgrade; # Timeout settings proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } # Backend API location /api/ { proxy_pass http://127.0.0.1:8000; 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; # Timeout settings proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } # Admin interface location /admin/ { proxy_pass http://127.0.0.1:8000; 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; } # Static files location /static/ { proxy_pass http://127.0.0.1:8000; expires 1y; add_header Cache-Control "public, immutable"; } # Media files location /media/ { proxy_pass http://127.0.0.1:8000; expires 1y; add_header Cache-Control "public"; } # Security location ~ /\\.ht { deny all; } location = /robots.txt { proxy_pass http://127.0.0.1:8000; access_log off; } location = /favicon.ico { proxy_pass http://127.0.0.1:3000; access_log off; } } EOF # Включаем SSL конфигурацию sudo ln -sf /etc/nginx/sites-available/catlink-ssl /etc/nginx/sites-enabled/catlink # Проверяем конфигурацию if sudo nginx -t; then success "Конфигурация nginx обновлена" sudo systemctl reload nginx else error "Ошибка в конфигурации nginx" fi } # Настройка автообновления сертификатов setup_auto_renewal() { log "Настройка автообновления Let's Encrypt сертификатов..." # Создаем скрипт обновления sudo tee /usr/local/bin/certbot-renew-catlink << 'EOF' #!/bin/bash # CatLink SSL certificate renewal script certbot renew --quiet --pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx" # Проверяем успешность обновления if [ $? -eq 0 ]; then echo "$(date): SSL certificates renewed successfully" >> /var/log/catlink-ssl.log else echo "$(date): SSL certificate renewal failed" >> /var/log/catlink-ssl.log fi EOF sudo chmod +x /usr/local/bin/certbot-renew-catlink # Добавляем в crontab (sudo crontab -l 2>/dev/null | grep -v certbot-renew-catlink; echo "0 12 * * * /usr/local/bin/certbot-renew-catlink") | sudo crontab - success "Автообновление сертификатов настроено" } # Проверка статуса SSL check_ssl_status() { log "Проверка статуса SSL..." if [ -f "/etc/letsencrypt/live/${MAIN_DOMAIN}/fullchain.pem" ]; then CERT_EXPIRY=$(openssl x509 -enddate -noout -in "/etc/letsencrypt/live/${MAIN_DOMAIN}/fullchain.pem" | cut -d= -f2) success "Let's Encrypt сертификат активен до: $CERT_EXPIRY" elif [ -f "/etc/ssl/certs/${MAIN_DOMAIN}.crt" ]; then CERT_EXPIRY=$(openssl x509 -enddate -noout -in "/etc/ssl/certs/${MAIN_DOMAIN}.crt" | cut -d= -f2) warning "Самоподписанный сертификат активен до: $CERT_EXPIRY" else error "SSL сертификат не найден" fi # Проверяем HTTPS соединение if command -v curl &> /dev/null; then if curl -s -I "https://${MAIN_DOMAIN}" | grep -q "HTTP"; then success "HTTPS соединение работает" else warning "HTTPS соединение может не работать" fi fi } # Основное меню main() { echo "🔒 ===== CatLink SSL Certificate Manager =====" echo "" check_env echo "Выберите действие:" echo "1) Получить Let's Encrypt сертификат (рекомендуется)" echo "2) Создать самоподписанный сертификат (для тестирования)" echo "3) Проверить статус существующих сертификатов" echo "4) Настроить автообновление" echo "5) Обновить nginx конфигурацию" echo "" read -p "Выберите опцию (1-5): " choice case $choice in 1) install_certbot check_dns obtain_letsencrypt update_nginx_ssl "letsencrypt" setup_auto_renewal check_ssl_status ;; 2) create_selfsigned update_nginx_ssl "selfsigned" check_ssl_status ;; 3) check_ssl_status ;; 4) setup_auto_renewal ;; 5) echo "Какой тип SSL использовать?" echo "1) Let's Encrypt" echo "2) Самоподписанный" read -p "Выберите (1-2): " ssl_type if [ "$ssl_type" = "1" ]; then update_nginx_ssl "letsencrypt" else update_nginx_ssl "selfsigned" fi ;; *) error "Неверный выбор" ;; esac echo "" success "SSL настройка завершена!" echo "" echo "🌐 Проверьте сайт: https://${MAIN_DOMAIN}" } # Запуск main "$@"