Initial commit
This commit is contained in:
522
classes/cert.rb
Normal file
522
classes/cert.rb
Normal file
@@ -0,0 +1,522 @@
|
||||
require 'date'
|
||||
require 'zip'
|
||||
require 'i18n'
|
||||
require_relative 'runner'
|
||||
|
||||
class CertManager
|
||||
attr_accessor :error, :log, :root_ca
|
||||
|
||||
def initialize()
|
||||
@root_ca = gt_root_dir
|
||||
if @root_ca.nil?
|
||||
@error = I18n.t('errors.cannot_determine_root_dir')
|
||||
end
|
||||
end
|
||||
|
||||
def log?
|
||||
@log
|
||||
end
|
||||
|
||||
def rootca?
|
||||
@root_ca
|
||||
end
|
||||
|
||||
def error?
|
||||
!(@error.nil? || @error.strip == '')
|
||||
end
|
||||
|
||||
def add_cert(days, domains_ips_list)
|
||||
begin
|
||||
@error = nil
|
||||
@log = nil
|
||||
new_cert_info = nil
|
||||
days = begin
|
||||
Integer(days)
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
raise ArgumentError, I18n.t('errors.argument_error_days') if days.nil? || days <= 0
|
||||
|
||||
domains_ips = domains_ips_list.split(/[\s,]+/).reject(&:empty?).uniq
|
||||
raise ArgumentError, I18n.t('errors.argument_error_domains_ips') if domains_ips.empty?
|
||||
|
||||
result = ''
|
||||
current_directory = Dir.pwd
|
||||
Dir.chdir('utils') do
|
||||
cmd_args = %Q(bash ./make_server_cert.sh -t #{days} #{domains_ips.join(' ')} 2>&1)
|
||||
cmd = Runner.new(cmd_args)
|
||||
cmd.run_clean
|
||||
result = cmd.stdout
|
||||
raise StandardError, I18n.t('errors.command_execution_error', command: cmd_args) if cmd.exit_status != 0
|
||||
result.each_line do |line|
|
||||
if line =~ /\[OUTPUTDATA_CERT\]/
|
||||
match = line.match(/([^\/]*?)\.cert\.pem\.(\d+)/)
|
||||
if match
|
||||
new_cert_info = { name: match[1], seq: match[2] }
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
raise StandardError, I18n.t('errors.no_result_file') if new_cert_info.nil?
|
||||
end
|
||||
rescue ArgumentError => e
|
||||
@error = e.message
|
||||
rescue StandardError => e
|
||||
@error = e.message
|
||||
@log = result
|
||||
Dir.chdir(current_directory)
|
||||
ensure
|
||||
end
|
||||
@log = result
|
||||
if new_cert_info.nil?
|
||||
nil
|
||||
else
|
||||
get_cert_id_by_name(domains_ips[0], new_cert_info[:seq], new_cert_info[:name], 's')
|
||||
end
|
||||
end
|
||||
|
||||
def add_client_cert(server_domain, client_id, days)
|
||||
begin
|
||||
@error = nil
|
||||
@log = nil
|
||||
new_cert_info = nil
|
||||
raise ArgumentError, I18n.t('errors.argument_error_server_domain') if server_domain.strip.empty?
|
||||
raise ArgumentError, I18n.t('errors.argument_error_client_id') if client_id.strip.empty?
|
||||
|
||||
days = begin
|
||||
Integer(days)
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
raise ArgumentError, I18n.t('errors.argument_error_days') if days.nil? || days <= 0
|
||||
|
||||
@error = nil
|
||||
result = ''
|
||||
current_directory = Dir.pwd
|
||||
Dir.chdir('utils') do
|
||||
cmd_args = %Q(bash ./make_client_cert.sh -s #{server_domain} -c #{client_id} -d #{days} 2>&1)
|
||||
cmd = Runner.new(cmd_args)
|
||||
cmd.run_clean
|
||||
result = cmd.stdout
|
||||
raise StandardError, I18n.t('errors.command_execution_error', command: cmd_args) if cmd.exit_status != 0
|
||||
result.each_line do |line|
|
||||
if line =~ /\[OUTPUTDATA_CERT\]/
|
||||
match = line.match(/([^\/]*?)\.cert\.pem\.(\d+)/)
|
||||
if match
|
||||
new_cert_info = { name: match[1], seq: match[2] }
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
raise StandardError, I18n.t('errors.no_result_file') if new_cert_info.nil?
|
||||
end
|
||||
rescue ArgumentError => e
|
||||
@error = e.message
|
||||
rescue StandardError => e
|
||||
@error = e.message
|
||||
Dir.chdir(current_directory)
|
||||
ensure
|
||||
end
|
||||
@log = result
|
||||
if new_cert_info.nil?
|
||||
nil
|
||||
else
|
||||
get_cert_id_by_name(server_domain, new_cert_info[:seq], new_cert_info[:name], 'c')
|
||||
end
|
||||
end
|
||||
|
||||
def get_server_certs
|
||||
get_list_certs('s')
|
||||
end
|
||||
|
||||
def get_clients_certs(server_domain)
|
||||
list = get_list_certs('c')
|
||||
if server_domain == ''
|
||||
list
|
||||
else
|
||||
filtered_list = list.select { |entry| entry[:ui][:CN] == server_domain }
|
||||
filtered_list.sort_by! { |entry| entry[:id] }
|
||||
end
|
||||
end
|
||||
|
||||
def get_cert_info(id)
|
||||
@log = ""
|
||||
@error = nil
|
||||
list_certs = get_list_certs('*')
|
||||
target_id = id
|
||||
found_entry = list_certs.find do |entry|
|
||||
entry[:id] == target_id
|
||||
end
|
||||
if found_entry
|
||||
return found_entry
|
||||
else
|
||||
@error = I18n.t('errors.record_not_found')
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def get_detail_cert_info(id)
|
||||
@log = nil
|
||||
@error = nil
|
||||
cert_info = { common: nil, revoke: nil, is_client: nil, name: nil, id: id }
|
||||
|
||||
if @root_ca.nil?
|
||||
@error = I18n.t('errors.root_ca_not_detected')
|
||||
return cert_info
|
||||
end
|
||||
|
||||
cert_item_data = get_cert_info(id)
|
||||
|
||||
if !@error.nil? || cert_item_data.nil?
|
||||
return cert_info
|
||||
end
|
||||
|
||||
cert_item = get_cert_path(cert_item_data)
|
||||
cert_path = if cert_item[:is_client]
|
||||
cert_item[:client]
|
||||
else
|
||||
cert_item[:server]
|
||||
end
|
||||
|
||||
unless File.exist?(cert_path)
|
||||
@error = I18n.t('errors.root_ca_not_detected')
|
||||
return cert_info
|
||||
end
|
||||
|
||||
cmd_args = %Q(openssl x509 -in "#{cert_path}" -text -noout 2>&1)
|
||||
cmd = Runner.new(cmd_args)
|
||||
cmd.run_clean
|
||||
if cmd.exit_status != 0
|
||||
@error = I18n.t('errors.cannot_get_certificate_info')
|
||||
@log = cmd.stdout
|
||||
return cert_info
|
||||
end
|
||||
|
||||
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 = Runner.new(cmd_args)
|
||||
cmd.run_clean
|
||||
cert_info[:revoke] = cmd.stdout
|
||||
cert_info[:name] = "/#{cert_item_data[:ui][:O]}/#{cert_item_data[:ui][:CN]}/"
|
||||
|
||||
cert_info
|
||||
end
|
||||
|
||||
def revoke_certificat(id)
|
||||
@error = nil
|
||||
@log = nil
|
||||
cert_info = get_cert_info(id)
|
||||
if cert_info.nil?
|
||||
nil
|
||||
else
|
||||
cert_data = get_cert_path(cert_info)
|
||||
if cert_data[:is_client]
|
||||
revoke_client_cert(cert_data[:server_name], cert_data[:client_id], cert_data[:seq])
|
||||
else
|
||||
revoke_cert(cert_data[:server_name], cert_data[:seq])
|
||||
end
|
||||
if @error.nil?
|
||||
cert_info = get_cert_info(id)
|
||||
if cert_info.nil?
|
||||
nil
|
||||
else
|
||||
cert_info[:is_client] = cert_data[:is_client]
|
||||
cert_info
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_cert_binary(id)
|
||||
@error = nil
|
||||
@log = nil
|
||||
files_list = []
|
||||
readme_txt = ""
|
||||
cert_data = get_cert_info(id)
|
||||
if cert_data.nil?
|
||||
nil
|
||||
else
|
||||
cert_path = get_cert_path(cert_data)
|
||||
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"
|
||||
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"
|
||||
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) }
|
||||
zip_memory = Zip::OutputStream.write_buffer do |zos|
|
||||
files_list.each do |file|
|
||||
zos.put_next_entry(File.basename(file))
|
||||
File.open(file, 'rb') { |f| zos.write f.read }
|
||||
end
|
||||
text_entry_name = 'readme.txt'
|
||||
zos.put_next_entry(text_entry_name)
|
||||
zos.write readme_txt
|
||||
end
|
||||
{ zip: zip_memory.string, is_client: cert_path[:is_client] }
|
||||
else
|
||||
@error = I18n.t('errors.root_ca_not_detected')
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_root_info
|
||||
@log = nil
|
||||
@error = nil
|
||||
cert_info = { common: nil, revoke: nil, is_client: nil, name: nil, id: nil }
|
||||
|
||||
if @root_ca.nil?
|
||||
@error = I18n.t('errors.root_ca_not_detected')
|
||||
return cert_info
|
||||
end
|
||||
|
||||
org_nm = nil
|
||||
config_sh = File.read('utils/custom_config.sh')
|
||||
match = config_sh.match(/ORG_NAME="([^"]+)"/)
|
||||
org_nm = match[1] if match
|
||||
return nil if org_nm.nil?
|
||||
|
||||
cert_path = "#{root_ca}/ca/root/certs/ca.cert.pem"
|
||||
|
||||
unless File.exist?(cert_path)
|
||||
@error = I18n.t('errors.root_ca_not_detected')
|
||||
return cert_info
|
||||
end
|
||||
|
||||
cmd_args = %Q(openssl x509 -in "#{cert_path}" -text -noout 2>&1)
|
||||
cmd = Runner.new(cmd_args)
|
||||
cmd.run_clean
|
||||
if cmd.exit_status != 0
|
||||
@error = I18n.t('errors.cannot_get_certificate_info')
|
||||
@log = cmd.stdout
|
||||
return cert_info
|
||||
end
|
||||
|
||||
cert_info[:common] = cmd.stdout
|
||||
|
||||
cmd_args = %Q(openssl verify -crl_check_all -CAfile "#{cert_path}" -CRLfile "#{@root_ca}/ca/root/crl/ca.crl.pem" "#{cert_path}" 2>&1)
|
||||
cmd = Runner.new(cmd_args)
|
||||
cmd.run_clean
|
||||
cert_info[:revoke] = cmd.stdout
|
||||
cert_info[:name] = "/CN=#{org_nm}/"
|
||||
|
||||
cert_info
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def revoke_cert(server_domain, seq)
|
||||
current_dir = Dir.pwd
|
||||
begin
|
||||
@log = nil
|
||||
raise ArgumentError, I18n.t('errors.argument_error_server_domain') if server_domain.strip.empty?
|
||||
|
||||
@error = nil
|
||||
result = ''
|
||||
Dir.chdir('utils') do
|
||||
cmd_args = if seq.nil? || seq.empty?
|
||||
%Q(bash ./make_server_revoke.sh -s #{server_domain} 2>&1)
|
||||
else
|
||||
%Q(bash ./make_server_revoke.sh -n #{seq} -s #{server_domain} 2>&1)
|
||||
end
|
||||
cmd = Runner.new(cmd_args)
|
||||
cmd.run_clean
|
||||
result = cmd.stdout
|
||||
raise StandardError, I18n.t('errors.command_execution_error', command: cmd_args) if cmd.exit_status != 0
|
||||
end
|
||||
rescue ArgumentError => e
|
||||
@error = e.message
|
||||
rescue StandardError => e
|
||||
@error = e.message
|
||||
Dir.chdir(current_dir)
|
||||
ensure
|
||||
end
|
||||
@log = result
|
||||
end
|
||||
|
||||
def revoke_client_cert(server_domain, client_id, seq)
|
||||
current_dir = Dir.pwd
|
||||
begin
|
||||
@log = nil
|
||||
raise ArgumentError, I18n.t('errors.argument_error_server_domain') if server_domain.strip.empty?
|
||||
raise ArgumentError, I18n.t('errors.argument_error_client_id') if client_id.strip.empty?
|
||||
|
||||
@error = nil
|
||||
result = ''
|
||||
Dir.chdir('utils') do
|
||||
cmd_args = if seq.nil? || seq.empty?
|
||||
%Q(bash ./make_client_revoke.sh -s #{server_domain} -c #{client_id} 2>&1)
|
||||
else
|
||||
%Q(bash ./make_client_revoke.sh -s #{server_domain} -c #{client_id} -n #{seq} 2>&1)
|
||||
end
|
||||
cmd = Runner.new(cmd_args)
|
||||
cmd.run_clean
|
||||
result = cmd.stdout
|
||||
raise StandardError, I18n.t('errors.command_execution_error', command: cmd_args) if cmd.exit_status != 0
|
||||
end
|
||||
rescue ArgumentError => e
|
||||
@error = e.message
|
||||
rescue StandardError => e
|
||||
@error = e.message
|
||||
Dir.chdir(current_dir)
|
||||
ensure
|
||||
end
|
||||
@log = result
|
||||
end
|
||||
|
||||
def gt_root_dir
|
||||
root_ca = nil
|
||||
|
||||
config_sh = File.read('utils/custom_config.sh')
|
||||
match = config_sh.match(/ROOT_DIR="([^"]+)"/)
|
||||
root_ca = match[1] if match
|
||||
root_ca
|
||||
end
|
||||
|
||||
def get_cert_path(item)
|
||||
cl_name = item[:ui][:O].split(":")
|
||||
cert_file = if cl_name.length > 1
|
||||
"#{@root_ca}/ca/client_certs/#{item[:ui][:CN]}/#{cl_name[0]}.cert.pem.#{cl_name[1]}"
|
||||
else
|
||||
"#{@root_ca}/ca/client_certs/#{item[:ui][:CN]}/#{cl_name[0]}.cert.pem"
|
||||
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]}"
|
||||
else
|
||||
"#{@root_ca}/ca/intermediate/certs/#{sr_name}.cert.pem"
|
||||
end
|
||||
is_client = File.exist?(cert_file)
|
||||
seq = if cl_name.length > 1
|
||||
cl_name[1]
|
||||
else
|
||||
nil
|
||||
end
|
||||
{ client: cert_file, server: serv_file, is_client: is_client, server_name: sr_name, seq: seq, client_id: cl_name[0] }
|
||||
end
|
||||
|
||||
def get_list_certs(type)
|
||||
|
||||
if @root_ca.nil?
|
||||
@error = I18n.t('errors.root_ca_not_detected')
|
||||
return []
|
||||
end
|
||||
|
||||
index_txt_path = "#{@root_ca}/ca/intermediate/index.txt"
|
||||
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|
|
||||
match = line.split("\t")
|
||||
next if match.length != 6
|
||||
|
||||
exp = false
|
||||
date_tm = parse_time_string(match[1])
|
||||
if date_tm.nil?
|
||||
date_tm = "нет даты"
|
||||
else
|
||||
exp = date_tm[1] < DateTime.now
|
||||
date_tm = date_tm[0]
|
||||
end
|
||||
|
||||
date_tm_revoke = parse_time_string(match[2])
|
||||
if date_tm_revoke.nil?
|
||||
date_tm_revoke = "нет даты"
|
||||
else
|
||||
date_tm_revoke = date_tm_revoke[0]
|
||||
end
|
||||
|
||||
prep = { id: match[3], status: match[0], date: date_tm, fld: match[4], ui: nil, revoke_date: date_tm_revoke, expired: exp }
|
||||
|
||||
parts = match[5].split('/').reject(&:empty?).map(&:strip)
|
||||
cert_info = {}
|
||||
parts.each do |part|
|
||||
key, value = part.split('=', 2)
|
||||
key_downcased = key.upcase
|
||||
cert_info[key_downcased.to_sym] = value || 'default_value'
|
||||
end
|
||||
|
||||
prep[:ui] = cert_info
|
||||
|
||||
cl_name = prep[:ui][:O].split(":")
|
||||
cert_file = if cl_name.length > 1
|
||||
"#{@root_ca}/ca/client_certs/#{prep[:ui][:CN]}/#{cl_name[0]}.cert.pem.#{cl_name[1]}"
|
||||
else
|
||||
"#{@root_ca}/ca/client_certs/#{prep[:ui][:CN]}/#{cl_name[0]}.cert.pem"
|
||||
end
|
||||
|
||||
if type == '*'
|
||||
entries << prep
|
||||
else
|
||||
if File.exist?(cert_file)
|
||||
entries << prep if type == 'c'
|
||||
elsif type == 's'
|
||||
entries << prep
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ca_index_txt.sort_by! { |entry| entry[:id] }
|
||||
|
||||
ca_index_txt
|
||||
end
|
||||
|
||||
def parse_time_string(str)
|
||||
return nil if str.nil? || str.length != 13 || !str[10..11].match?(/[0-9]{2}/)
|
||||
year = str[0..1].to_i + 2000 # Первые два символа - год
|
||||
month = str[2..3].to_i # Следующие два символа - месяц
|
||||
day = str[4..5].to_i # Еще два символа - день
|
||||
hour = str[6..7].to_i # Следующие два символа - часы
|
||||
minute = str[8..9].to_i # Еще два символа - минуты
|
||||
second = str[10..11].to_i # Последние два символа - секунды
|
||||
utc_offset = 0 # По умолчанию считаем, что строка содержит Z, т.е. UTC
|
||||
[ DateTime.new(year, month, day, hour, minute, second, utc_offset).strftime('%d-%m-%y %H:%M:%S'),
|
||||
DateTime.new(year, month, day, hour, minute, second, utc_offset) ]
|
||||
end
|
||||
|
||||
def get_cert_id_by_name(server_name, seq, org_name, type)
|
||||
org_nm = nil
|
||||
config_sh = File.read('utils/custom_config.sh')
|
||||
match = config_sh.match(/ORG_NAME="([^"]+)"/)
|
||||
org_nm = match[1] if match
|
||||
return nil if org_nm.nil?
|
||||
list_certs = get_list_certs('*')
|
||||
|
||||
if type == 's'
|
||||
|
||||
found_entry = list_certs.find do |entry|
|
||||
if seq == ''
|
||||
"#{org_nm}" == entry[:ui][:O] && server_name == entry[:ui][:CN]
|
||||
else
|
||||
"#{org_nm}:#{seq}" == entry[:ui][:O] && server_name == entry[:ui][:CN]
|
||||
end
|
||||
end
|
||||
else
|
||||
found_entry = list_certs.find do |entry|
|
||||
if seq == ''
|
||||
"#{org_name}" == entry[:ui][:O] && server_name == entry[:ui][:CN]
|
||||
else
|
||||
"#{org_name}:#{seq}" == entry[:ui][:O] && server_name == entry[:ui][:CN]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if found_entry
|
||||
found_entry
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user