240 lines
7.6 KiB
JavaScript
240 lines
7.6 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
import fs from 'fs'
|
||
import path from 'path'
|
||
import { execSync } from 'child_process'
|
||
import { fileURLToPath } from 'url'
|
||
|
||
const __filename = fileURLToPath(import.meta.url)
|
||
const __dirname = path.dirname(__filename)
|
||
|
||
const sslDir = path.join(__dirname, 'ssl')
|
||
const keyPath = path.join(sslDir, 'server.key')
|
||
const certPath = path.join(sslDir, 'server.crt')
|
||
|
||
const colors = {
|
||
reset: '\x1b[0m',
|
||
red: '\x1b[31m',
|
||
green: '\x1b[32m',
|
||
yellow: '\x1b[33m',
|
||
blue: '\x1b[34m',
|
||
magenta: '\x1b[35m',
|
||
cyan: '\x1b[36m'
|
||
}
|
||
|
||
const log = (message, color = 'reset') => {
|
||
console.log(`${colors[color]}${message}${colors.reset}`)
|
||
}
|
||
|
||
const checkOpenSSL = () => {
|
||
try {
|
||
execSync('openssl version', { stdio: 'pipe' })
|
||
return true
|
||
} catch (error) {
|
||
return false
|
||
}
|
||
}
|
||
|
||
const installInstructions = () => {
|
||
log('\n📋 Инструкции по установке OpenSSL:', 'cyan')
|
||
log('Windows: https://slproweb.com/products/Win32OpenSSL.html', 'blue')
|
||
log('macOS: brew install openssl', 'blue')
|
||
log('Ubuntu/Debian: sudo apt-get install openssl', 'blue')
|
||
log('CentOS/RHEL: sudo yum install openssl', 'blue')
|
||
}
|
||
|
||
const createCertificates = (domain = 'localhost', days = 365) => {
|
||
if (!fs.existsSync(sslDir)) {
|
||
fs.mkdirSync(sslDir, { recursive: true })
|
||
log(`📁 Создана папка: ${sslDir}`, 'green')
|
||
}
|
||
|
||
try {
|
||
log('🔐 Создание SSL сертификатов...', 'yellow')
|
||
|
||
// Генерируем приватный ключ
|
||
log('📝 Генерация приватного ключа...', 'blue')
|
||
execSync(`openssl genrsa -out "${keyPath}" 2048`, { stdio: 'pipe' })
|
||
|
||
// Конфигурация для сертификата с SAN
|
||
const configContent = `
|
||
[req]
|
||
distinguished_name = req_distinguished_name
|
||
req_extensions = v3_req
|
||
prompt = no
|
||
|
||
[req_distinguished_name]
|
||
C = RU
|
||
ST = Development
|
||
L = Local
|
||
O = Development Server
|
||
OU = IT Department
|
||
CN = ${domain}
|
||
|
||
[v3_req]
|
||
keyUsage = keyEncipherment, dataEncipherment
|
||
extendedKeyUsage = serverAuth
|
||
subjectAltName = @alt_names
|
||
|
||
[alt_names]
|
||
DNS.1 = ${domain}
|
||
DNS.2 = *.${domain}
|
||
DNS.3 = localhost
|
||
DNS.4 = *.localhost
|
||
IP.1 = 127.0.0.1
|
||
IP.2 = ::1
|
||
`
|
||
|
||
const configPath = path.join(sslDir, 'openssl.conf')
|
||
fs.writeFileSync(configPath, configContent.trim())
|
||
|
||
// Генерируем сертификат
|
||
log('📜 Генерация сертификата...', 'blue')
|
||
const certCommand = `openssl req -new -x509 -key "${keyPath}" -out "${certPath}" -days ${days} -config "${configPath}" -extensions v3_req`
|
||
execSync(certCommand, { stdio: 'pipe' })
|
||
|
||
// Удаляем временный конфиг
|
||
fs.unlinkSync(configPath)
|
||
|
||
log('\n✅ SSL сертификаты успешно созданы!', 'green')
|
||
log(`🔑 Ключ: ${keyPath}`, 'cyan')
|
||
log(`📄 Сертификат: ${certPath}`, 'cyan')
|
||
log(`🌐 Домен: ${domain}`, 'cyan')
|
||
log(`⏰ Действителен: ${days} дней`, 'cyan')
|
||
|
||
// Показываем информацию о сертификате
|
||
showCertInfo()
|
||
|
||
log('\n⚠️ Важно:', 'yellow')
|
||
log('• Это сертификаты только для разработки', 'yellow')
|
||
log('• Добавьте сертификат в доверенные в браузере для устранения предупреждений', 'yellow')
|
||
|
||
} catch (error) {
|
||
log('\n❌ Ошибка создания SSL сертификатов:', 'red')
|
||
log(error.message, 'red')
|
||
installInstructions()
|
||
}
|
||
}
|
||
|
||
const showCertInfo = () => {
|
||
if (!fs.existsSync(certPath)) {
|
||
log('❌ Сертификат не найден', 'red')
|
||
return
|
||
}
|
||
|
||
try {
|
||
log('\n📋 Информация о сертификате:', 'cyan')
|
||
const certInfo = execSync(`openssl x509 -in "${certPath}" -text -noout`, { encoding: 'utf8' })
|
||
|
||
// Извлекаем нужную информацию
|
||
const subjectMatch = certInfo.match(/Subject: (.+)/);
|
||
const validFromMatch = certInfo.match(/Not Before: (.+)/);
|
||
const validToMatch = certInfo.match(/Not After : (.+)/);
|
||
const sanMatch = certInfo.match(/DNS:([^,\n]+)/g);
|
||
|
||
if (subjectMatch) log(`📝 Subject: ${subjectMatch[1].trim()}`, 'blue')
|
||
if (validFromMatch) log(`📅 Действителен с: ${validFromMatch[1].trim()}`, 'blue')
|
||
if (validToMatch) log(`📅 Действителен до: ${validToMatch[1].trim()}`, 'blue')
|
||
if (sanMatch) {
|
||
log(`🌐 Домены: ${sanMatch.map(d => d.replace('DNS:', '')).join(', ')}`, 'blue')
|
||
}
|
||
|
||
} catch (error) {
|
||
log('❌ Ошибка чтения информации о сертификате', 'red')
|
||
}
|
||
}
|
||
|
||
const deleteCertificates = () => {
|
||
let deleted = false
|
||
|
||
if (fs.existsSync(keyPath)) {
|
||
fs.unlinkSync(keyPath)
|
||
log(`🗑️ Удален ключ: ${keyPath}`, 'yellow')
|
||
deleted = true
|
||
}
|
||
|
||
if (fs.existsSync(certPath)) {
|
||
fs.unlinkSync(certPath)
|
||
log(`🗑️ Удален сертификат: ${certPath}`, 'yellow')
|
||
deleted = true
|
||
}
|
||
|
||
if (deleted) {
|
||
log('✅ SSL сертификаты удалены', 'green')
|
||
} else {
|
||
log('ℹ️ SSL сертификаты не найдены', 'blue')
|
||
}
|
||
}
|
||
|
||
const checkCertificates = () => {
|
||
const keyExists = fs.existsSync(keyPath)
|
||
const certExists = fs.existsSync(certPath)
|
||
|
||
log('\n🔍 Проверка SSL сертификатов:', 'cyan')
|
||
log(`🔑 Ключ: ${keyExists ? '✅ Существует' : '❌ Не найден'}`, keyExists ? 'green' : 'red')
|
||
log(`📄 Сертификат: ${certExists ? '✅ Существует' : '❌ Не найден'}`, certExists ? 'green' : 'red')
|
||
|
||
if (keyExists && certExists) {
|
||
showCertInfo()
|
||
}
|
||
|
||
return keyExists && certExists
|
||
}
|
||
|
||
const showHelp = () => {
|
||
log('\n🔧 SSL Manager - Утилита для управления SSL сертификатами\n', 'cyan')
|
||
log('Использование:', 'yellow')
|
||
log(' node ssl-manager.js <команда> [опции]\n', 'blue')
|
||
log('Команды:', 'yellow')
|
||
log(' create [domain] [days] Создать новые сертификаты (по умолчанию: localhost, 365 дней)', 'blue')
|
||
log(' check Проверить существующие сертификаты', 'blue')
|
||
log(' info Показать информацию о сертификате', 'blue')
|
||
log(' delete Удалить существующие сертификаты', 'blue')
|
||
log(' help Показать эту справку\n', 'blue')
|
||
log('Примеры:', 'yellow')
|
||
log(' node ssl-manager.js create', 'green')
|
||
log(' node ssl-manager.js create mysite.local 30', 'green')
|
||
log(' node ssl-manager.js check', 'green')
|
||
}
|
||
|
||
// Основная логика
|
||
const command = process.argv[2]
|
||
const arg1 = process.argv[3]
|
||
const arg2 = process.argv[4]
|
||
|
||
// Проверяем наличие OpenSSL
|
||
if (!checkOpenSSL() && command !== 'help' && command !== 'check') {
|
||
log('❌ OpenSSL не найден в системе', 'red')
|
||
installInstructions()
|
||
process.exit(1)
|
||
}
|
||
|
||
switch (command) {
|
||
case 'create':
|
||
const domain = arg1 || 'localhost'
|
||
const days = parseInt(arg2) || 365
|
||
createCertificates(domain, days)
|
||
break
|
||
|
||
case 'check':
|
||
checkCertificates()
|
||
break
|
||
|
||
case 'info':
|
||
showCertInfo()
|
||
break
|
||
|
||
case 'delete':
|
||
deleteCertificates()
|
||
break
|
||
|
||
case 'help':
|
||
case undefined:
|
||
showHelp()
|
||
break
|
||
|
||
default:
|
||
log(`❌ Неизвестная команда: ${command}`, 'red')
|
||
showHelp()
|
||
process.exit(1)
|
||
} |