From 934b3f032ef946eab9a5110f44234dd415d7d3d0 Mon Sep 17 00:00:00 2001 From: Alexey Berezhok Date: Sat, 21 Feb 2026 23:14:43 +0300 Subject: [PATCH] Make own intermdiate ca for every server --- app.rb | 22 ++--- classes/cert.rb | 47 +++++++--- docs/UTILS_EXAMPLES.md | 4 +- utils/config.sh | 1 - utils/make_client_cert.sh | 2 + utils/make_client_revoke.sh | 2 + utils/make_server_cert.sh | 173 ++++++++++++++++++++++++++++++++++-- utils/make_server_revoke.sh | 2 + utils/prepare.sh | 127 +------------------------- 9 files changed, 222 insertions(+), 158 deletions(-) diff --git a/app.rb b/app.rb index 738402b..cf57094 100644 --- a/app.rb +++ b/app.rb @@ -35,17 +35,17 @@ require_relative 'models/userdata' require_relative 'classes/cert' require_relative 'classes/pagination' -configure do - Dir.mkdir('logs') unless Dir.exist?('logs') - unless File.exist?('logs/actions.log') - File.new('logs/actions.log', 'w').close - end - log_file = File.open('logs/actions.log', 'a+') - STDOUT.reopen(log_file) - STDERR.reopen(log_file) - STDOUT.sync = true - STDERR.sync = true -end +# configure do +# Dir.mkdir('logs') unless Dir.exist?('logs') +# unless File.exist?('logs/actions.log') +# File.new('logs/actions.log', 'w').close +# end +# log_file = File.open('logs/actions.log', 'a+') +# STDOUT.reopen(log_file) +# STDERR.reopen(log_file) +# STDOUT.sync = true +# STDERR.sync = true +# end set :bind, IPBIND set :port, PORT diff --git a/classes/cert.rb b/classes/cert.rb index a52fca8..9553dd2 100644 --- a/classes/cert.rb +++ b/classes/cert.rb @@ -1,6 +1,7 @@ require 'date' require 'zip' require 'i18n' +require 'digest/sha1' require_relative 'runner' class CertManager @@ -183,13 +184,13 @@ class CertManager if cert_item[:is_client] files_list << cert_item[:client] files_list << "#{@root_ca}/ca/client_certs/#{cert_item[:server_name]}/private/#{cert_item[:client_id]}_private.key.pem" - files_list << "#{@root_ca}/ca/intermediate/certs/ca-chain.cert.pem" + files_list << "#{@root_ca}/ca/#{cert_item[:server_name]}/certs/ca-chain.cert.pem" readme_txt = I18n.t('messages.client_readme', private_key: files_list[1], server_cert: files_list[0], ca_chain: files_list[2]) else files_list << cert_item[:server] - files_list << "#{@root_ca}/ca/intermediate/private/#{cert_item[:server_name]}.key.pem" - files_list << "#{@root_ca}/ca/intermediate/certs/ca-chain.cert.pem" - files_list << "#{@root_ca}/ca/intermediate/crl/ca-full.crl.pem" + files_list << "#{@root_ca}/ca/#{cert_item[:server_name]}/private/#{cert_item[:server_name]}.key.pem" + files_list << "#{@root_ca}/ca/#{cert_item[:server_name]}/certs/ca-chain.cert.pem" + files_list << "#{@root_ca}/ca/#{cert_item[:server_name]}/crl/ca-full.crl.pem" readme_txt = I18n.t('messages.server_readme', private_key: files_list[1], server_cert: files_list[0], ca_chain: files_list[2], crl: files_list[3]) end cert_info[:full] = readme_txt @@ -210,7 +211,7 @@ class CertManager cert_info[:common] = cmd.stdout - cmd_args = %Q(openssl verify -crl_check_all -CAfile "#{@root_ca}/ca/intermediate/certs/ca-chain.cert.pem" -CRLfile "#{@root_ca}/ca/intermediate/crl/ca-full.crl.pem" "#{cert_path}" 2>&1) + cmd_args = %Q(openssl verify -crl_check_all -CAfile "#{@root_ca}/ca/#{cert_item[:server_name]}/certs/ca-chain.cert.pem" -CRLfile "#{@root_ca}/ca/#{cert_item[:server_name]}/crl/ca-full.crl.pem" "#{cert_path}" 2>&1) cmd = Runner.new(cmd_args) cmd.run_clean cert_info[:revoke] = cmd.stdout @@ -259,13 +260,13 @@ class CertManager if cert_path[:is_client] files_list << cert_path[:client] files_list << "#{@root_ca}/ca/client_certs/#{cert_path[:server_name]}/private/#{cert_path[:client_id]}_private.key.pem" - files_list << "#{@root_ca}/ca/intermediate/certs/ca-chain.cert.pem" + files_list << "#{@root_ca}/ca/#{cert_path[:server_name]}/certs/ca-chain.cert.pem" readme_txt = I18n.t('messages.client_readme', private_key: File.basename(files_list[1]), server_cert: File.basename(files_list[0]), ca_chain: File.basename(files_list[2])) else files_list << cert_path[:server] - files_list << "#{@root_ca}/ca/intermediate/private/#{cert_path[:server_name]}.key.pem" - files_list << "#{@root_ca}/ca/intermediate/certs/ca-chain.cert.pem" - files_list << "#{@root_ca}/ca/intermediate/crl/ca-full.crl.pem" + files_list << "#{@root_ca}/ca/#{cert_path[:server_name]}/private/#{cert_path[:server_name]}.key.pem" + files_list << "#{@root_ca}/ca/#{cert_path[:server_name]}/certs/ca-chain.cert.pem" + files_list << "#{@root_ca}/ca/#{cert_path[:server_name]}/crl/ca-full.crl.pem" readme_txt = I18n.t('messages.server_readme', private_key: File.basename(files_list[1]), server_cert: File.basename(files_list[0]), ca_chain: File.basename(files_list[2]), crl: File.basename(files_list[3])) end if files_list.all? { |file| File.exist?(file) } @@ -408,9 +409,9 @@ class CertManager end sr_name = item[:ui][:CN] serv_file = if cl_name.length > 1 - "#{@root_ca}/ca/intermediate/certs/#{sr_name}.cert.pem.#{cl_name[1]}" + "#{@root_ca}/ca/#{sr_name}/certs/#{sr_name}.cert.pem.#{cl_name[1]}" else - "#{@root_ca}/ca/intermediate/certs/#{sr_name}.cert.pem" + "#{@root_ca}/ca/#{sr_name}/certs/#{sr_name}.cert.pem" end is_client = File.exist?(cert_file) seq = if cl_name.length > 1 @@ -428,13 +429,29 @@ class CertManager return [] end - index_txt_path = "#{@root_ca}/ca/intermediate/index.txt" + index_txt_path = "#{@root_ca}/ca/server_certs" unless File.exist?(index_txt_path) - @error = I18n.t('errors.root_ca_not_detected') return [] end - ca_index_txt = File.read(index_txt_path, encoding: 'utf-8').split("\n").each_with_object([]) do |line, entries| + # Retrieve names of subdirectories under index_txt_path (without nested dirs) + dir_names = Dir.children(index_txt_path).select do |entry| + File.directory?(File.join(index_txt_path, entry)) + end + + # Array to accumulate unique lines from all index.txt files + index_txt_entries = [] + + dir_names.each do |dir| + idx_file = File.join(@root_ca, 'ca', dir, 'index.txt') + next unless File.exist?(idx_file) + File.read(idx_file, encoding: 'utf-8').each_line do |line| + line.chomp! + index_txt_entries << line unless index_txt_entries.include?(line) + end + end + + ca_index_txt = index_txt_entries.each_with_object([]) do |line, entries| match = line.split("\t") next if match.length != 6 @@ -473,6 +490,8 @@ class CertManager "#{@root_ca}/ca/client_certs/#{prep[:ui][:CN]}/#{cl_name[0]}.cert.pem" end + prep[:id] = Digest::SHA1.hexdigest("#{prep[:ui][:CN]}/#{prep[:id]}") + if type == '*' entries << prep else diff --git a/docs/UTILS_EXAMPLES.md b/docs/UTILS_EXAMPLES.md index de322e0..ece8c14 100644 --- a/docs/UTILS_EXAMPLES.md +++ b/docs/UTILS_EXAMPLES.md @@ -19,7 +19,7 @@ ### Примеры использования: 1. Генерация серверного сертификата для домена `example1.com` и IP-адреса `192.168.3.145`: ```sh - bash make_server_cert.sh example1.com 192.168.3.145 + bash make_server_cert.sh -t 395 example1.com 192.168.3.145 ``` ## Утилита `make_client_cert.sh` @@ -39,7 +39,7 @@ ### Примеры использования: 1. Отозвать серверный сертификат для домена `brepo.ru`: ```sh - bash make_server_revoke.sh -n 1 brepo.ru + bash make_server_revoke.sh -n 1 -s brepo.ru ``` ## Утилита `make_client_revoke.sh` diff --git a/utils/config.sh b/utils/config.sh index 0f325ef..cc4ed40 100644 --- a/utils/config.sh +++ b/utils/config.sh @@ -24,5 +24,4 @@ fi PATH_TO_CA="$ROOT_DIR/ca" ROOT_CA="$PATH_TO_CA/root" -IMM_CA="$PATH_TO_CA/intermediate" CLI_CA="$PATH_TO_CA/client_certs" diff --git a/utils/make_client_cert.sh b/utils/make_client_cert.sh index d4b1ad4..819a1a7 100644 --- a/utils/make_client_cert.sh +++ b/utils/make_client_cert.sh @@ -64,6 +64,8 @@ pushd $PATH_TO_CA || { exit 1 } +IMM_CA="$PATH_TO_CA/$server" + mkdir -p client_certs pushd client_certs || { diff --git a/utils/make_client_revoke.sh b/utils/make_client_revoke.sh index cf254e4..4e425c3 100644 --- a/utils/make_client_revoke.sh +++ b/utils/make_client_revoke.sh @@ -58,6 +58,8 @@ if [ ! -e "$CLI_CA/$server/${client}_csr_req.cnf" ]; then exit 1 fi +IMM_CA="$PATH_TO_CA/$server" + pushd "$IMM_CA" || { msg "Ошибка: не удалось перейти в каталог $IMM_CA" "Error: Could not change directory to $IMM_CA" >&2 cd "$CURRENT_DIR" || exit 1 diff --git a/utils/make_server_cert.sh b/utils/make_server_cert.sh index 116d3ce..5bb5f87 100644 --- a/utils/make_server_cert.sh +++ b/utils/make_server_cert.sh @@ -14,6 +14,23 @@ else LANG_RU=0 fi +# Detect language and define error function +LANGUAGE="en" +if [[ "$LANG" == ru* || "$LANG" == *ru_* || "$LC_ALL" == ru* || "$LC_ALL" == *ru_* ]]; then + LANGUAGE="ru" +fi + +msg() { + local en_msg="$1" + local ru_msg="$2" + if [[ "$LANGUAGE" == "ru" ]]; then + echo "$ru_msg" + else + echo "$en_msg" + fi + exit 1 +} + # Handle -h option for help message if [ "$1" == "-h" ]; then if [ "$LANG_RU" -eq 1 ]; then @@ -69,10 +86,6 @@ CERT_DAYS=${CERT_DAYS:-3650} pushd $PATH_TO_CA || exit -mkdir -p server_certs - -pushd server_certs || exit - # Проверка, предоставлен ли первый параметр и не пуст ли он if [ -z "$1" ]; then if [ "$LANG_RU" -eq 1 ]; then @@ -94,7 +107,6 @@ if [ "${#items[@]}" -eq 0 ]; then echo "No elements found in the input" fi popd || exit - popd || exit exit 0 fi @@ -103,6 +115,157 @@ SEQ="1" # Извлечение первого элемента списка fst_elem="${items[0]}" +IMM_CA="$PATH_TO_CA/$fst_elem" + +# Создаем промежуточный CA +if [ ! -d "$IMM_CA" ]; then + + # Список каталогов, для которых создается структура + DIRECTORIES=("$fst_elem") + + # Создание необходимых директорий и настройка их прав доступа + for dir in "${DIRECTORIES[@]}"; do + # Создание поддиректорий в каждой директории из списка + mkdir -p "$dir/certs" "$dir/crl" "$dir/newcerts" "$dir/private" "$dir/csr" + chmod 700 "$dir/private" + + # Создание файлов базы CA + touch "$dir/index.txt" + echo -n 100000 >"$dir/serial" + + # Настройка файла для CRL (список отозванных сертификатов) + echo -n 100000 >"$dir/crlnumber" + done + + cat >$IMM_CA/immissuer.conf <"$IMM_CA/crl/ca-full.crl.pem" + + # Вернуться в исходную директорию или выйти с ошибкой, если это не удалось + popd || { msg "Can't return to old directory" "Невозможно вернуться к старому каталогу"; } + + # Создание цепочки сертификатов + cat "$IMM_CA/certs/intermediate.cert.pem" "$ROOT_CA/certs/ca.cert.pem" >"$IMM_CA/certs/ca-chain.cert.pem" || { msg "Error: Failed to create CA chain certificate" "Ошибка: Не удалось создать цепочку сертификатов ЦА"; } + + # Проверка сертификата промежуточного ЦА с использованием корневого центра сертификации + openssl verify -CAfile "$ROOT_CA/certs/ca.cert.pem" "$IMM_CA/certs/intermediate.cert.pem" || { msg "Error: Failed to verify intermediate CA certificate" "Ошибка: Не удалось проверить сертификат промежуточного ЦА"; } + +fi +# Конец создания промежуточного CA + +mkdir -p server_certs + +pushd server_certs || exit + # Создание директории с именем первого элемента, если она не существует mkdir -p "$fst_elem" || true diff --git a/utils/make_server_revoke.sh b/utils/make_server_revoke.sh index 0d0a365..9d02a69 100644 --- a/utils/make_server_revoke.sh +++ b/utils/make_server_revoke.sh @@ -52,6 +52,8 @@ if [ ! -e "$PATH_TO_CA/server_certs/$server" ]; then exit 1 fi +IMM_CA="$PATH_TO_CA/$server" + pushd "$IMM_CA" || { msg "Error: Could not change directory to $IMM_CA" "Ошибка: Не удалось перейти в каталог $IMM_CA" >&2 cd "$CURRENT_DIR" || exit 1 diff --git a/utils/prepare.sh b/utils/prepare.sh index 6245087..f2cb0ae 100644 --- a/utils/prepare.sh +++ b/utils/prepare.sh @@ -51,7 +51,7 @@ fi cd "$PATH_TO_CA" || { msg "Error: Failed to change directory to $PATH_TO_CA" "Ошибка: Не удалось перейти в каталог $PATH_TO_CA"; } # Список каталогов, для которых создается структура -DIRECTORIES=("root" "intermediate") +DIRECTORIES=("root") # Создание необходимых директорий и настройка их прав доступа for dir in "${DIRECTORIES[@]}"; do @@ -114,7 +114,7 @@ prompt = no [req_distinguished_name] countryName = $COUNTRY_NAME organizationName = $ORG_NAME -commonName = $ORG_NAME +commonName = $COMM_NAME [v3_ca] subjectKeyIdentifier = hash @@ -153,127 +153,4 @@ openssl x509 -noout -text -in certs/ca.cert.pem || { msg "Error: Failed to displ # Вернуться в исходную директорию или выйти с ошибкой, если это не удалось popd || { msg "Can't return to old directory" "Невозможно вернуться к старому каталогу"; } -# Создание конфигурационного файла для промежуточного ЦА -cat >intermediate/immissuer.conf <$IMM_CA/crl/ca-full.crl.pem - -# Вернуться в исходную директорию или выйти с ошибкой, если это не удалось -popd || { msg "Can't return to old directory" "Невозможно вернуться к старому каталогу"; } - -# Создание цепочки сертификатов -cat "$IMM_CA/certs/intermediate.cert.pem" "$ROOT_CA/certs/ca.cert.pem" >"$IMM_CA/certs/ca-chain.cert.pem" || { msg "Error: Failed to create CA chain certificate" "Ошибка: Не удалось создать цепочку сертификатов ЦА"; } - -# Проверка сертификата промежуточного ЦА с использованием корневого центра сертификации -openssl verify -CAfile $ROOT_CA/certs/ca.cert.pem $IMM_CA/certs/intermediate.cert.pem || { msg "Error: Failed to verify intermediate CA certificate" "Ошибка: Не удалось проверить сертификат промежуточного ЦА"; } - exit 0