From 6806881e8eb5c94f93f8ee20a99202d47505e309 Mon Sep 17 00:00:00 2001 From: Bellord Date: Thu, 1 Oct 2026 01:08:37 +0300 Subject: [PATCH] fix --- .env.example | 19 +++++++ .gitignore | 20 +++++++ README.md | 105 ++++++++++++++++++++++++++++++++++++ build-intermediate-cert.sh | 75 ++++++++++++++++++++++++++ build-issuing-cert.sh | 62 +++++++++++++++++++++ build-root-cert.sh | 61 +++++++++++++++++++++ intermediate_ca.ini.example | 9 ++++ issuing_san.ini.example | 24 +++++++++ root_ca.ini.example | 5 ++ 9 files changed, 380 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 README.md create mode 100644 build-intermediate-cert.sh create mode 100644 build-issuing-cert.sh create mode 100644 build-root-cert.sh create mode 100644 intermediate_ca.ini.example create mode 100644 issuing_san.ini.example create mode 100644 root_ca.ini.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..adfe615 --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +# Общие данные +CERT_COUNTRY="EU" +CERT_LOCALITY="Local Network" +CERT_ORG="Ixrs" + +# Корневой сертификат (Root CA) +ROOT_CN="Ixrs Root CA" +CERT_DAYS=3650 +ROOT_EXT_SECTION="v3_ca" # Секция внутри root_ca.ini + +# Промежуточный сертификат (Intermediate CA) +INT_CN="Ixrs Intermediate CA" +INT_DAYS=1825 +INT_EXT_SECTION="v3_intermediate" # Секция внутри intermediate_ca.ini + +# Выпускающий сертификат (Доменный / Конечный) +DOMAIN_CN="ixrs.ru" +ISSUING_DAYS=365 +ISSUING_EXT_SECTION="v3_cert" # Секция внутри issuing_san.ini diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57bef56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# Конфигурационные файлы (рабочие копии) +.env +root_ca.ini +intermediate_ca.ini +issuing_san.ini + +# Любые приватные ключи (главный секрет!) +*.key +privkey.pem + +# Любые запросы на сертификат и файлы индексов OpenSSL +*.csr +*.srl + +# Любые сгенерированные сертификаты и цепочки +*.crt +*.pem + +# Папка с конечными сертификатами +certs/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..0e58388 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +# Локальная инфраструктура сертификатов (Local PKI) + +Набор bash-скриптов и конфигурационных файлов для автоматического создания собственного доверенного центра сертификации (Root CA), промежуточного центра (Intermediate CA) и выпуска универсальных мультидоменных SSL-сертификатов (для Nginx, Nginx Proxy Manager, Docker-сервисов и любых других веб-серверов). + +--- + +## 📋 Описание полей сертификата (Subject) + +При настройке метаданных сертификатов в файле конфигурации используются следующие стандартные атрибуты: + +* **CN** (*Common Name*) — Имя создаваемого объекта. Для Root/Intermediate CA указывается понятное название центра (например, `Ixrs Root CA`), а для конечных сертификатов — базовый домен организации. +* **O** (*Organization*) — Название компании или организации (например, `Ixrs`). +* **L** (*Locality*) — Город, населенный пункт или описание сети (например, `Local Network`). +* **C** (*Country*) — Двухбуквенный код страны в формате ISO (например, `RU`, `EU`, `US`). +* **ST** (*State*) — Регион, область, штат или край. +* **emailAddress** — Электронная почта администратора для связи. + +--- + +## 📂 Структура проекта и генерируемые файлы + +После успешного выполнения всех скриптов структура директории будет выглядеть следующим образом: + +```text +. +├── .env # Активный файл конфигурации (копия из .env.example) +│ +├── root_ca.ini # Конфигурация OpenSSL для Root CA +├── intermediate_ca.ini # Конфигурация OpenSSL для Intermediate CA +├── issuing_san.ini # Список доменов, поддоменов и IP для конечного сертификата +│ +├── build-root-cert.sh # Скрипт сборки Root CA +├── build-intermediate-cert.sh # Скрипт сборки Intermediate CA +├── build-issuing-cert.sh # Скрипт сборки универсального мультидоменного сертификата +│ +# ── Сгенерированные файлы (Артефакты) ────────────────────────────────────────── +│ +├── root_ca.key # [🔒 SECRET] Приватный ключ Корневого ЦС +├── root_ca.crt # Публичный сертификат Корневого ЦС +├── root_ca.pem # Корневой сертификат в формате PEM (для совместимости) +├── root_ca.srl # Файл серийных номеров для отслеживания Root CA +│ +├── intermediate_ca.key # [🔒 SECRET] Приватный ключ Промежуточного ЦС +├── intermediate_ca.csr # Запрос на подпись (CSR) Промежуточного ЦС +├── intermediate_ca.crt # Публичный сертификат Промежуточного ЦС +├── intermediate_ca.srl # Файл серийных номеров для отслеживания Intermediate CA +│ +├── issuing.key # [🔒 SECRET] Исходный приватный ключ доменов +├── issuing.csr # Запрос на подпись (CSR) для конечных доменов +├── issuing.crt # Исходный конечный сертификат (только домены) +│ +# ── Готовые контейнеры для веб-серверов (Nginx / NPM / Apache) ───────────────── +│ +├── privkey.pem # [🔒 SECRET] Универсальный приватный ключ для веб-сервера +└── fullchain.pem # Полная цепочка (Конечный серт + Промежуточный серт) +``` + +--- + +## ⚙️ Настройка окружения + +Для конфигурации скриптов используются файлы `.env` и `.ini`. Создайте их на основе шаблонов (команда безопасно пропустит копирование, если файлы уже созданы и настроены): + +```bash +cp -n .env.example .env +cp -n root_ca.ini.example root_ca.ini +cp -n intermediate_ca.ini.example intermediate_ca.ini +cp -n issuing_san.ini.example issuing_san.ini +``` + +--- + +## 🚀 Порядок запуска и использования + +Перед запуском сделайте скрипты исполняемыми: +```bash +chmod +x build-root-cert.sh build-intermediate-cert.sh build-issuing-cert.sh +``` + +### Шаг 1. Создание Корневого ЦС +Запустите скрипт создания главного сертификата. Если файлы уже существуют, скрипт выведет предупреждение для предотвращения случайной перезаписи. +```bash +./build-root-cert.sh +``` +* Скрипт интерактивно спросит, хотите ли вы установить корневой сертификат в системное хранилище доверенных сертификатов ОС (Debian/Ubuntu). В случае согласия он сам запросит права `sudo` в нужный момент. + +### Шаг 2. Создание Промежуточного ЦС +Запустите скрипт создания промежуточного центра, который будет использоваться непосредственно для подписи конечных адресов. Он защищен флагами ограничения области видимости доменов (`nameConstraints`) и запретом создания цепочек ниже уровнем (`pathlen:0`). +```bash +./build-intermediate-cert.sh +``` + +### Шаг 3. Выпуск мультидоменного сертификата +Запустите скрипт генерации универсального сертификата. Он подпишет ключ, строго опираясь на правила и список альтернативных имен (DNS и IP) из файла `issuing_san.ini`. +```bash +./build-issuing-cert.sh +``` +* В конце процесса скрипт предложит автоматически собрать универсальные PEM-контейнеры `privkey.pem` (приватный ключ) и `fullchain.pem` (полная цепочка сертификатов), которые готовы к копированию в Nginx, Nginx Proxy Manager, Apache или бэкенд-приложения. + +--- + +## 🛡️ Безопасность + +* **Конфиденциальность ключей:** Приватные файлы ключей (`*.key` и `privkey.pem`) дают полный контроль над вашей локальной безопасностью. Никогда не передавайте их третьим лицам и ограничьте к ним доступ на машине (`chmod 400`). +* **Игнорирование в Git:** Обязательно добавьте секретные файлы в `.gitignore`, чтобы случайно не отправить личные ключи и доступы в публичный репозиторий. diff --git a/build-intermediate-cert.sh b/build-intermediate-cert.sh new file mode 100644 index 0000000..e610fc9 --- /dev/null +++ b/build-intermediate-cert.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# Определяем путь к файлу .env (в той же папке, что и скрипт) +ENV_FILE="$(dirname "$0")/.env" + +# Проверяем, существует ли файл .env, и загружаем его +if [ -f "$ENV_FILE" ]; then + source "$ENV_FILE" +else + echo "Ошибка: Файл конфигурации $ENV_FILE не найден!" + exit 1 +fi + +# 1. Проверяем, существуют ли файлы Root CA, необходимые для подписи +if [ ! -f "root_ca.crt" ] || [ ! -f "root_ca.key" ]; then + echo "Ошибка: Не найдены файлы root_ca.crt или root_ca.key!" + echo "Сначала запустите скрипт build-root-cert.sh" + exit 1 +fi + +# 2. ПРОВЕРКА НАЛИЧИЯ УЖЕ ГОТОВОГО ПРОМЕЖУТОЧНОГО СЕРТИФИКАТА +if [ -f "intermediate_ca.key" ] || [ -f "intermediate_ca.crt" ]; then + echo "Внимание: Файлы промежуточного сертификата (intermediate_ca.key/crt) УЖЕ СУЩЕСТВУЮТ!" + echo "⚠️ ПЕРЕЗАПИСЬ ЭТИХ ФАЙЛОВ ПРИВЕДЕТ К ТОМУ, ЧТО ВСЕ ВЫПУЩЕННЫЕ РАНЕЕ СЕРТИФИКАТЫ САЙТОВ ПЕРЕСТАНУТ РАБОТАТЬ!" + echo "" + read -p "Вы уверены, что хотите перевыпустить Промежуточный ЦС? [y/N]: " overwrite_response + + overwrite_response=$(echo "$overwrite_response" | tr '[:upper:]' '[:lower:]') + + # По умолчанию (если просто нажат Enter) или если ответ не "y"/"yes" — отменяем выполнение + if [[ ! "$overwrite_response" =~ ^(y|yes)$ ]]; then + echo "Генерация отменена во избежание порчи действующих сертификатов." + exit 0 + fi + echo "Продолжаем генерацию и перезапись файлов..." +fi + + +echo "Генерация ключа и CSR для Промежуточного ЦС..." +# Создаем ключ и запрос (CSR) для промежуточного ЦС +openssl req -new -nodes -newkey rsa:4096 \ + -keyout intermediate_ca.key -out intermediate_ca.csr \ + -subj "/C=${CERT_COUNTRY}/L=${CERT_LOCALITY}/O=${CERT_ORG}/CN=${INT_CN}" + +echo "Подпись промежуточного сертификата с помощью Root CA..." +# Подписываем промежуточный сертификат с помощью Root CA +openssl x509 -req -sha512 -days "$INT_DAYS" \ + -extfile intermediate_ca.ini -extensions "$INT_EXT_SECTION" \ + -CA root_ca.crt -CAkey root_ca.key -CAcreateserial \ + -in intermediate_ca.csr \ + -out intermediate_ca.crt + +echo "" +# Запрашиваем подтверждение у пользователя (Y - да, n - нет) +read -p "Хотите установить промежуточный сертификат в систему как доверенный? [Y/n]: " response + +# Переводим ответ в нижний регистр +response=$(echo "$response" | tr '[:upper:]' '[:lower:]') + +# Если пользователь нажал Enter или ввел y/yes +if [[ -z "$response" || "$response" =~ ^(y|yes)$ ]]; then + echo "Установка промежуточного сертификата..." + + if [ "$EUID" -ne 0 ]; then + sudo cp intermediate_ca.crt /usr/local/share/ca-certificates/intermediate_ca.crt + sudo update-ca-certificates + else + cp intermediate_ca.crt /usr/local/share/ca-certificates/intermediate_ca.crt + update-ca-certificates + fi + + echo "Промежуточный сертификат успешно установлен!" +else + echo "Установка отменена." +fi diff --git a/build-issuing-cert.sh b/build-issuing-cert.sh new file mode 100644 index 0000000..682b675 --- /dev/null +++ b/build-issuing-cert.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# Определяем путь к файлу .env и загружаем его +ENV_FILE="$(dirname "$0")/.env" +if [ -f "$ENV_FILE" ]; then + source "$ENV_FILE" +else + echo "Ошибка: Файл конфигурации $ENV_FILE не найден!" + exit 1 +fi + +# Проверяем наличие Промежуточного ЦС для подписи +if [ ! -f "intermediate_ca.crt" ] || [ ! -f "intermediate_ca.key" ]; then + echo "Ошибка: Не найдены файлы intermediate_ca.crt или intermediate_ca.key!" + echo "Сначала запустите скрипт build-intermediate-cert.sh" + exit 1 +fi + +# Проверяем наличие файла конфигурации SAN +if [ ! -f "issuing_san.ini" ]; then + echo "Ошибка: Файл конфигурации issuing_san.ini не найден!" + exit 1 +fi + +echo "Генерация универсального ключа и запроса (CSR) по файлу конфигурации..." +# Создаем один общий запрос. В CN подставляем базовый домен из .env +openssl req -new -nodes -newkey rsa:4096 \ + -keyout issuing.key -out issuing.csr \ + -subj "/C=${CERT_COUNTRY}/L=${CERT_LOCALITY}/O=${CERT_ORG}/CN=${DOMAIN_CN}" + +echo "Подпись мультидоменного сертификата через Intermediate CA (на $ISSUING_DAYS дн)..." +# Подписываем, строго используя расширения SAN из файла issuing_san.ini +openssl x509 -req -sha512 -days "$ISSUING_DAYS" \ + -extfile issuing_san.ini -extensions "$ISSUING_EXT_SECTION" \ + -CA intermediate_ca.crt -CAkey intermediate_ca.key -CAserial intermediate_ca.srl -CAcreateserial \ + -in issuing.csr \ + -out issuing.crt + +echo "" +# Запрашиваем подтверждение сборки пакета для NPM +read -p "Хотите собрать файлы privkey.pem и fullchain.pem? [Y/n]: " npm_response + +# Переводим ответ в нижний регистр +npm_response=$(echo "$npm_response" | tr '[:upper:]' '[:lower:]') + +# Если пользователь нажал Enter или ввел y/yes +if [[ -z "$npm_response" || "$npm_response" =~ ^(y|yes)$ ]]; then + echo "Сборка универсальных файлов privkey.pem и fullchain.pem..." + # Копируем приватный ключ + cat issuing.key > privkey.pem + # Объединяем доменный и промежуточный сертификаты в единую цепочку (Full Chain) + cat issuing.crt > fullchain.pem + cat intermediate_ca.crt >> fullchain.pem + + echo "Файлы сертификата (PEM-контейнеры) готовы в текущей папке:" + echo " -> Приватный ключ: privkey.pem" + echo " -> Цепочка сертификатов: fullchain.pem" +fi + + +echo "" +echo "✅ Процесс выпуска сертификата успешно завершен!" diff --git a/build-root-cert.sh b/build-root-cert.sh new file mode 100644 index 0000000..e72d279 --- /dev/null +++ b/build-root-cert.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# Определяем путь к файлу .env (в той же папке, что и скрипт) +ENV_FILE="$(dirname "$0")/.env" + +# Проверяем, существует ли файл .env, и загружаем его +if [ -f "$ENV_FILE" ]; then + source "$ENV_FILE" +else + echo "Ошибка: Файл конфигурации $ENV_FILE не найден!" + exit 1 +fi + +# ПРОВЕРКА НАЛИЧИЯ УЖЕ ГОТОВОГО КОРНЕВОГО СЕРТИФИКАТА +if [ -f "root_ca.key" ] || [ -f "root_ca.crt" ]; then + echo "Внимание: Файлы Корневого сертификата (root_ca.key/crt) УЖЕ СУЩЕСТВУЮТ!" + echo "⚠️ ПЕРЕЗАПИСЬ КОРНЯ СЛОМАЕТ ДОВЕРИЕ КО ВСЕМ ВЫПУЩЕННЫМ РАНЕЕ ПРОМЕЖУТОЧНЫМ И ДОМЕННЫМ СЕРТИФИКАТАМ!" + echo "" + read -p "Вы абсолютно уверены, что хотите перевыпустить Корневой ЦС? [y/N]: " overwrite_response + + overwrite_response=$(echo "$overwrite_response" | tr '[:upper:]' '[:lower:]') + + # По умолчанию (Enter) или при любом ответе, кроме y/yes — выходим + if [[ ! "$overwrite_response" =~ ^(y|yes)$ ]]; then + echo "Генерация отменена. Текущий корневой сертификат сохранен." + exit 0 + fi + echo "Продолжаем генерацию и перезапись Корневого ЦС..." +fi + +# Root certificate (Root CA) +openssl req -x509 -nodes -new -sha512 -days "$CERT_DAYS" \ + -newkey rsa:4096 -keyout root_ca.key -out root_ca.crt \ + -extfile root_ca.ini -extensions "$ROOT_EXT_SECTION" \ + -subj "/C=${CERT_COUNTRY}/L=${CERT_LOCALITY}/O=${CERT_ORG}/CN=${ROOT_CN}" + + + +echo "" +# Запрашиваем подтверждение у пользователя (Y - да, n - нет) +read -p "Хотите установить сертификат в систему как доверенный? [Y/n]: " response + +# Переводим ответ в нижний регистр для надежности +response=$(echo "$response" | tr '[:upper:]' '[:lower:]') + +# Если пользователь нажал Enter или ввел y/yes +if [[ -z "$response" || "$response" =~ ^(y|yes)$ ]]; then + echo "Установка сертификата..." + + if [ "$EUID" -ne 0 ]; then + sudo cp root_ca.crt /usr/local/share/ca-certificates/root_ca.crt + sudo update-ca-certificates + else + cp root_ca.crt /usr/local/share/ca-certificates/root_ca.crt + update-ca-certificates + fi + + echo "Сертификат успешно установлен!" +else + echo "Установка отменена." +fi diff --git a/intermediate_ca.ini.example b/intermediate_ca.ini.example new file mode 100644 index 0000000..ad4b7cf --- /dev/null +++ b/intermediate_ca.ini.example @@ -0,0 +1,9 @@ +[ v3_intermediate ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true, pathlen:0 +keyUsage = critical, digitalSignature, cRLSign, keyCertSign + +nameConstraints = critical, permit;DNS:ixrs.ru, permit;DNS:.ixrs.ru +# Разрешает домен ixrs.ru, localhost и локальные IP-адреса +nameConstraints = critical, permit;DNS:ixrs.ru, permit;DNS:.ixrs.ru, permit;DNS:localhost, permit;IP:127.0.0.1/255.255.255.255, permit;IP:0:0:0:0:0:0:0:1/128 diff --git a/issuing_san.ini.example b/issuing_san.ini.example new file mode 100644 index 0000000..b3f9d36 --- /dev/null +++ b/issuing_san.ini.example @@ -0,0 +1,24 @@ +[ v3_cert ] +subjectKeyIdentifier = hash +authorityKeyIdentifier=keyid,issuer +basicConstraints=CA:FALSE +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth, clientAuth +nsComment = "IXRS Local Certificate" +subjectAltName = @alt_names + +[alt_names] +# IP-адреса +IP.1 = 127.0.0.1 +IP.2 = ::1 + +# Доменные имена +DNS.1 = localhost +DNS.2 = ixrs.ru +DNS.3 = *.ixrs.ru +DNS.4 = nova.ixrs.ru +DNS.5 = *.nova.ixrs.ru +DNS.6 = mini.ixrs.ru +DNS.7 = *.mini.ixrs.ru +DNS.8 = *.s3.mini.ixrs.ru +DNS.9 = *.bellord.ixrs.ru diff --git a/root_ca.ini.example b/root_ca.ini.example new file mode 100644 index 0000000..bcce144 --- /dev/null +++ b/root_ca.ini.example @@ -0,0 +1,5 @@ +[ v3_ca ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true +keyUsage = critical, digitalSignature, cRLSign, keyCertSign \ No newline at end of file