#!/bin/bash # Цвета для вывода RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' MAGENTA='\033[0;35m' NC='\033[0m' # No Color # Функции для вывода print_success() { echo -e "${GREEN}[✓]${NC} $1"; } print_error() { echo -e "${RED}[✗]${NC} $1"; } print_info() { echo -e "${BLUE}[i]${NC} $1"; } print_warning() { echo -e "${YELLOW}[!]${NC} $1"; } print_step() { echo -e "${CYAN}[→]${NC} $1"; } print_header() { echo -e "${MAGENTA}▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬${NC}"; } # Глобальные переменные DOMAIN="" BASE_DIR="$PWD/npm-certs" CA_DIR="$BASE_DIR/ca" DOMAIN_DIR="" NPM_FORMAT="split" # split или single # Проверка зависимостей check_dependencies() { print_step "Проверка зависимостей..." local missing=0 if ! command -v openssl &> /dev/null; then print_error "OpenSSL не установлен!" print_info "Установите: sudo apt install openssl" missing=1 else print_success "OpenSSL обнаружен" fi if [ $missing -eq 1 ]; then exit 1 fi } # Выбор формата select_format() { echo "" print_header echo -e "${CYAN} ВЫБОР ФОРМАТА ДЛЯ NGINX PROXY MANAGER${NC}" print_header echo "" echo "NPM поддерживает 2 формата:" echo "" echo "1. ${GREEN}РАЗДЕЛЬНЫЙ (рекомендуется)${NC}" echo " • Certificate: только доменный сертификат" echo " • Intermediate: только промежуточный" echo " • Key: приватный ключ" echo "" echo "2. ОБЪЕДИНЕННЫЙ" echo " • Certificate: домен + intermediate" echo " • Intermediate: оставить пустым" echo " • Key: приватный ключ" echo "" read -p "Выберите формат (1 или 2, по умолчанию 1): " choice case "$choice" in 2) NPM_FORMAT="single" print_info "Выбран объединенный формат" ;; *) NPM_FORMAT="split" print_info "Выбран раздельный формат" ;; esac } # Получение домена get_domain() { echo "" while true; do read -p "Введите домен (например: beszel.smolkik.local): " DOMAIN if [ -z "$DOMAIN" ]; then print_error "Домен не может быть пустым!" continue fi # Простая проверка if [[ "$DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)+$ ]]; then DOMAIN_DIR="$BASE_DIR/$DOMAIN" mkdir -p "$DOMAIN_DIR" break else print_warning "Домен '$DOMAIN' выглядит подозрительно" read -p "Продолжить? (y/N): " confirm if [[ "$confirm" =~ ^[yYдД] ]]; then DOMAIN_DIR="$BASE_DIR/$DOMAIN" mkdir -p "$DOMAIN_DIR" break fi fi done # Запрос SAN echo "" read -p "Добавить wildcard (*.$DOMAIN)? (y/N): " add_wildcard if [[ "$add_wildcard" =~ ^[yYдД] ]]; then SAN="DNS:$DOMAIN, DNS:*.$DOMAIN, DNS:localhost, IP:127.0.0.1" print_info "Добавлен wildcard: *.$DOMAIN" else SAN="DNS:$DOMAIN, DNS:localhost, IP:127.0.0.1" fi # Дополнительные SAN read -p "Добавить дополнительные домены/IP? (через запятую): " extra_sans if [ -n "$extra_sans" ]; then IFS=',' read -ra EXTRA <<< "$extra_sans" for item in "${EXTRA[@]}"; do item=$(echo "$item" | xargs) if [[ "$item" =~ ^[0-9.]+\.[0-9.]+\.[0-9.]+\.[0-9.]+$ ]]; then SAN="$SAN, IP:$item" print_info "Добавлен IP: $item" else SAN="$SAN, DNS:$item" print_info "Добавлен домен: $item" fi done fi } # Создание корневого CA create_root_ca() { print_step "Создание корневого CA..." if [ -f "$CA_DIR/root.key" ] && [ -f "$CA_DIR/root.crt" ]; then print_info "Корневой CA уже существует, используем существующий" return 0 fi mkdir -p "$CA_DIR" # Создаем ключ openssl genrsa -out "$CA_DIR/root.key" 4096 2>/dev/null # Создаем самоподписанный сертификат openssl req -x509 -new -nodes -key "$CA_DIR/root.key" \ -sha256 -days 8250 -out "$CA_DIR/root.crt" \ -subj "/C=RU/ST=Moscow/L=Moscow/O=NPM Local CA/CN=NPM Root CA" \ -config <(cat </dev/null # CSR openssl req -new -key "$CA_DIR/intermediate.key" -out "$CA_DIR/intermediate.csr" \ -subj "/C=RU/ST=Moscow/L=Moscow/O=NPM Local CA/CN=NPM Intermediate CA" 2>/dev/null # Подпись корневым CA openssl x509 -req -in "$CA_DIR/intermediate.csr" \ -CA "$CA_DIR/root.crt" -CAkey "$CA_DIR/root.key" -CAcreateserial \ -out "$CA_DIR/intermediate.crt" -days 3650 -sha256 \ -extfile <(cat </dev/null rm -f "$CA_DIR/intermediate.csr" "$CA_DIR/root.srl" print_success "Промежуточный CA создан: $CA_DIR/intermediate.crt" } # Создание доменного сертификата create_domain_cert() { print_step "Создание сертификата для $DOMAIN..." # Ключ домена openssl genrsa -out "$DOMAIN_DIR/$DOMAIN.key" 2048 2>/dev/null # CSR для домена cat > "$DOMAIN_DIR/csr.cnf" << EOF [req] default_bits = 2048 prompt = no default_md = sha256 distinguished_name = dn req_extensions = req_ext [dn] C = RU ST = Moscow L = Moscow O = Local Development CN = $DOMAIN [req_ext] subjectAltName = $SAN EOF openssl req -new -key "$DOMAIN_DIR/$DOMAIN.key" \ -out "$DOMAIN_DIR/$DOMAIN.csr" \ -config "$DOMAIN_DIR/csr.cnf" 2>/dev/null # Конфиг для подписи cat > "$DOMAIN_DIR/cert.ext" << EOF authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment subjectAltName = $SAN EOF # Подпись промежуточным CA openssl x509 -req -in "$DOMAIN_DIR/$DOMAIN.csr" \ -CA "$CA_DIR/intermediate.crt" -CAkey "$CA_DIR/intermediate.key" -CAcreateserial \ -out "$DOMAIN_DIR/$DOMAIN.crt" -days 365 -sha256 \ -extfile "$DOMAIN_DIR/cert.ext" 2>/dev/null # Очистка временных файлов rm -f "$DOMAIN_DIR/csr.cnf" "$DOMAIN_DIR/cert.ext" "$DOMAIN_DIR/$DOMAIN.csr" "$CA_DIR/intermediate.srl" print_success "Доменный сертификат создан: $DOMAIN_DIR/$DOMAIN.crt" } # Подготовка файлов для NPM (РАЗДЕЛЬНЫЙ формат) prepare_npm_split() { print_step "Подготовка файлов для NPM (раздельный формат)..." # 1. Доменный сертификат (ТОЛЬКО один!) cp "$DOMAIN_DIR/$DOMAIN.crt" "$DOMAIN_DIR/npm-certificate.crt" # Проверяем что только один сертификат local cert_count=$(grep -c "BEGIN CERTIFICATE" "$DOMAIN_DIR/npm-certificate.crt") if [ "$cert_count" -gt 1 ]; then print_warning "В доменном сертификате $cert_count сертификатов, оставляем первый" awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/{if(++count==1)print}' "$DOMAIN_DIR/$DOMAIN.crt" > "$DOMAIN_DIR/npm-certificate.crt" fi # 2. Промежуточный сертификат (ТОЛЬКО один!) cp "$CA_DIR/intermediate.crt" "$DOMAIN_DIR/npm-intermediate.crt" cert_count=$(grep -c "BEGIN CERTIFICATE" "$DOMAIN_DIR/npm-intermediate.crt") if [ "$cert_count" -gt 1 ]; then print_warning "В промежуточном $cert_count сертификатов, оставляем первый" awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/{if(++count==1)print}' "$CA_DIR/intermediate.crt" > "$DOMAIN_DIR/npm-intermediate.crt" fi # 3. Приватный ключ cp "$DOMAIN_DIR/$DOMAIN.key" "$DOMAIN_DIR/npm-private.key" # Конвертируем ключ в PKCS#8 если нужно if grep -q "BEGIN RSA PRIVATE KEY" "$DOMAIN_DIR/npm-private.key"; then print_info "Конвертируем ключ в PKCS#8 формат" openssl pkcs8 -topk8 -nocrypt -in "$DOMAIN_DIR/npm-private.key" \ -out "$DOMAIN_DIR/npm-private-pkcs8.key" 2>/dev/null mv "$DOMAIN_DIR/npm-private-pkcs8.key" "$DOMAIN_DIR/npm-private.key" fi # 4. Создаем chain.pem для других целей cat "$DOMAIN_DIR/$DOMAIN.crt" "$CA_DIR/intermediate.crt" > "$DOMAIN_DIR/chain.pem" print_success "Файлы подготовлены (раздельный формат)" } # Подготовка файлов для NPM (ОБЪЕДИНЕННЫЙ формат) prepare_npm_single() { print_step "Подготовка файлов для NPM (объединенный формат)..." # 1. Доменный сертификат + промежуточный cat "$DOMAIN_DIR/$DOMAIN.crt" "$CA_DIR/intermediate.crt" > "$DOMAIN_DIR/npm-certificate-chain.crt" # 2. Приватный ключ cp "$DOMAIN_DIR/$DOMAIN.key" "$DOMAIN_DIR/npm-private.key" # Конвертируем ключ в PKCS#8 если нужно if grep -q "BEGIN RSA PRIVATE KEY" "$DOMAIN_DIR/npm-private.key"; then print_info "Конвертируем ключ в PKCS#8 формат" openssl pkcs8 -topk8 -nocrypt -in "$DOMAIN_DIR/npm-private.key" \ -out "$DOMAIN_DIR/npm-private-pkcs8.key" 2>/dev/null mv "$DOMAIN_DIR/npm-private-pkcs8.key" "$DOMAIN_DIR/npm-private.key" fi print_success "Файлы подготовлены (объединенный формат)" } # Проверка цепочки verify_chain() { print_step "Проверка цепочки сертификатов..." if openssl verify -CAfile "$CA_DIR/root.crt" \ -untrusted "$CA_DIR/intermediate.crt" \ "$DOMAIN_DIR/$DOMAIN.crt" &>/dev/null; then print_success "✅ Цепочка сертификатов валидна!" else print_error "❌ Ошибка проверки цепочки" print_info "Выполняем ручную проверку..." openssl verify -CAfile "$CA_DIR/root.crt" \ -untrusted "$CA_DIR/intermediate.crt" \ "$DOMAIN_DIR/$DOMAIN.crt" fi } # Показать отпечатки show_fingerprints() { print_step "Отпечатки сертификатов:" echo -e "${YELLOW}Root CA:${NC}" openssl x509 -in "$CA_DIR/root.crt" -fingerprint -sha256 -noout | sed 's/SHA256 Fingerprint=//' echo -e "${YELLOW}Intermediate CA:${NC}" openssl x509 -in "$CA_DIR/intermediate.crt" -fingerprint -sha256 -noout | sed 's/SHA256 Fingerprint=//' echo -e "${YELLOW}Domain ($DOMAIN):${NC}" openssl x509 -in "$DOMAIN_DIR/$DOMAIN.crt" -fingerprint -sha256 -noout | sed 's/SHA256 Fingerprint=//' } # Создать инструкцию для NPM create_npm_instructions() { print_step "Создание инструкции для NPM..." cat > "$DOMAIN_DIR/NPM_INSTRUCTIONS.md" << EOF # 🚀 Инструкция для Nginx Proxy Manager # Домен: $DOMAIN ## 📁 ФАЙЛЫ: $(if [ "$NPM_FORMAT" = "split" ]; then echo "1. \`npm-private.key\` - Приватный ключ" echo "2. \`npm-certificate.crt\` - Доменный сертификат" echo "3. \`npm-intermediate.crt\` - Промежуточный сертификат" else echo "1. \`npm-private.key\` - Приватный ключ" echo "2. \`npm-certificate-chain.crt\` - Домен + Intermediate" fi) ## 🖥️ КАК ДОБАВИТЬ В NPM: 1. Откройте **Nginx Proxy Manager** → **SSL Certificates** → **Add SSL Certificate** 2. Заполните поля: **Name:** $DOMAIN **Certificate Key:** $(if [ "$NPM_FORMAT" = "split" ]; then echo "Выберите файл \`npm-private.key\`"; else echo "Выберите файл \`npm-private.key\`"; fi) **Certificate:** $(if [ "$NPM_FORMAT" = "split" ]; then echo "Выберите файл \`npm-certificate.crt\`"; else echo "Выберите файл \`npm-certificate-chain.crt\`"; fi) $(if [ "$NPM_FORMAT" = "split" ]; then echo "**Intermediate Certificate:** Выберите файл \`npm-intermediate.crt\`"; else echo "**Intermediate Certificate:** ОСТАВЬТЕ ПУСТЫМ"; fi) 3. Нажмите **Save** ## 🔧 НАСТРОЙКА ПРОКСИ ХОСТА: 1. Откройте **Proxy Hosts** 2. Выберите или создайте хост для \`$DOMAIN\` 3. Перейдите на вкладку **SSL** 4. Выберите созданный сертификат 5. Включите опции: - ☑ Force SSL - ☑ HTTP/2 Support - ☐ HSTS Enabled (опционально) 6. Нажмите **Save** ## 🌐 УСТАНОВКА КОРНЕВОГО СЕРТИФИКАТА: Для доверия в браузерах установите корневой сертификат: ### Linux: \`\`\`bash sudo cp $CA_DIR/root.crt /usr/local/share/ca-certificates/ sudo update-ca-certificates \`\`\` ### macOS: \`\`\`bash sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain $CA_DIR/root.crt \`\`\` ## 📋 ПРОВЕРКА: \`\`\`bash # Проверка цепочки openssl verify -CAfile $CA_DIR/root.crt \\ -untrusted $CA_DIR/intermediate.crt \\ $DOMAIN_DIR/$DOMAIN.crt # Просмотр SAN openssl x509 -in $DOMAIN_DIR/$DOMAIN.crt -text -noout | grep -A 5 "Subject Alternative Name" \`\`\` ## 🎯 ОТПЕЧАТКИ: - **Root CA:** $(openssl x509 -in "$CA_DIR/root.crt" -fingerprint -sha256 -noout | cut -d= -f2) - **Intermediate:** $(openssl x509 -in "$CA_DIR/intermediate.crt" -fingerprint -sha256 -noout | cut -d= -f2) - **Domain:** $(openssl x509 -in "$DOMAIN_DIR/$DOMAIN.crt" -fingerprint -sha256 -noout | cut -d= -f2) --- *Создано $(date)* EOF print_success "Инструкция создана: $DOMAIN_DIR/NPM_INSTRUCTIONS.md" } # Создать скрипт для копирования create_copy_script() { cat > "$DOMAIN_DIR/copy-to-server.sh" << 'EOF' #!/bin/bash # Скрипт для копирования сертификатов на сервер if [ -z "$1" ]; then echo "Использование: $0 " exit 1 fi SERVER="$1" DOMAIN="$(basename "$(pwd)")" echo "Копирование сертификатов для $DOMAIN на $SERVER..." # Копируем файлы scp npm-private.key npm-certificate.crt npm-intermediate.crt "$SERVER:/tmp/" echo "" echo "✅ Файлы скопированы!" echo "" echo "На сервере выполните:" echo "sudo mkdir -p /etc/nginx-proxy-manager/certs/" echo "sudo cp /tmp/npm-*.crt /tmp/npm-*.key /etc/nginx-proxy-manager/certs/" echo "sudo chown -R 1000:1000 /etc/nginx-proxy-manager/certs/" echo "sudo docker restart nginx-proxy-manager" EOF chmod +x "$DOMAIN_DIR/copy-to-server.sh" } # Главное меню show_main_menu() { echo "" print_header echo -e "${CYAN} ГЕНЕРАТОР SSL ДЛЯ NGINX PROXY MANAGER${NC}" print_header echo "" } # Основная функция main() { show_main_menu check_dependencies select_format get_domain create_root_ca create_intermediate_ca create_domain_cert if [ "$NPM_FORMAT" = "split" ]; then prepare_npm_split else prepare_npm_single fi verify_chain show_fingerprints create_npm_instructions create_copy_script # Финальный вывод echo "" print_header echo -e "${GREEN} УСПЕШНО ЗАВЕРШЕНО!${NC}" print_header echo "" echo -e "${YELLOW}📁 Ваши файлы:${NC}" echo "Директория: $DOMAIN_DIR/" echo "" ls -la "$DOMAIN_DIR/" | grep -E "\.(crt|key|pem)$" | awk '{print " " $9 " (" $5 " bytes)"}' echo "" echo -e "${YELLOW}🎯 Для NPM используйте:${NC}" if [ "$NPM_FORMAT" = "split" ]; then echo "1. Certificate Key: $(basename "$DOMAIN_DIR/npm-private.key")" echo "2. Certificate: $(basename "$DOMAIN_DIR/npm-certificate.crt")" echo "3. Intermediate: $(basename "$DOMAIN_DIR/npm-intermediate.crt")" else echo "1. Certificate Key: $(basename "$DOMAIN_DIR/npm-private.key")" echo "2. Certificate: $(basename "$DOMAIN_DIR/npm-certificate-chain.crt")" echo "3. Intermediate: (ОСТАВЬТЕ ПУСТЫМ)" fi echo "" echo -e "${YELLOW}📄 Подробная инструкция:${NC}" echo " $DOMAIN_DIR/NPM_INSTRUCTIONS.md" echo "" echo -e "${YELLOW}🚀 Быстрое копирование:${NC}" echo " cd $DOMAIN_DIR" echo " ./copy-to-server.sh user@your-server" echo "" print_header } # Запуск main "$@"