مراية لـ
https://github.com/postalserver/postal.git
تم المزامنة 2025-11-30 21:32:30 +00:00
109 أسطر
3.1 KiB
Ruby
109 أسطر
3.1 KiB
Ruby
# == Schema Information
|
|
#
|
|
# Table name: track_certificates
|
|
#
|
|
# id :integer not null, primary key
|
|
# domain :string(255)
|
|
# certificate :text(65535)
|
|
# intermediaries :text(65535)
|
|
# key :text(65535)
|
|
# expires_at :datetime
|
|
# renew_after :datetime
|
|
# verification_path :string(255)
|
|
# verification_string :string(255)
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
#
|
|
# Indexes
|
|
#
|
|
# index_track_certificates_on_domain (domain)
|
|
#
|
|
|
|
class TrackCertificate < ApplicationRecord
|
|
|
|
validates :domain, :presence => true, :uniqueness => true
|
|
|
|
default_value :key, -> { OpenSSL::PKey::RSA.new(2048).to_s }
|
|
|
|
scope :active, -> { where("certificate IS NOT NULL AND expires_at > ?", Time.now) }
|
|
|
|
def active?
|
|
certificate.present?
|
|
end
|
|
|
|
def get
|
|
verify && issue
|
|
end
|
|
|
|
def verify
|
|
authorization = Postal::LetsEncrypt.client.authorize(:domain => self.domain)
|
|
challenge = authorization.http01
|
|
self.verification_path = challenge.filename
|
|
self.verification_string = challenge.file_content
|
|
self.save!
|
|
logger.info "Attempting verification of #{self.domain}"
|
|
challenge.request_verification
|
|
checks = 0
|
|
until challenge.verify_status != "pending"
|
|
checks += 1
|
|
if checks > 30
|
|
logger.info "Status remained at pending for 30 checks"
|
|
return false
|
|
end
|
|
sleep 1
|
|
end
|
|
|
|
unless challenge.verify_status == "valid"
|
|
logger.info "Status was not valid (was: #{challenge.verify_status})"
|
|
return false
|
|
end
|
|
|
|
return true
|
|
rescue Acme::Client::Error => e
|
|
@retries = 0
|
|
if e.is_a?(Acme::Client::Error::BadNonce) && @retries < 5
|
|
@retries += 1
|
|
logger.info "Bad nounce encountered. Retrying (#{@retries} of 5 attempts)"
|
|
sleep 1
|
|
verify
|
|
else
|
|
logger.info "Error: #{e.class} (#{e.message})"
|
|
return false
|
|
end
|
|
end
|
|
|
|
def issue
|
|
csr = OpenSSL::X509::Request.new
|
|
csr.subject = OpenSSL::X509::Name.new([['CN', self.domain, OpenSSL::ASN1::UTF8STRING]])
|
|
private_key = OpenSSL::PKey::RSA.new(self.key)
|
|
csr.public_key = private_key.public_key
|
|
csr.sign(private_key, OpenSSL::Digest::SHA256.new)
|
|
logger.info "Getting certificate for #{self.domain}"
|
|
https_cert = Postal::LetsEncrypt.client.new_certificate(csr)
|
|
self.certificate = https_cert.to_pem
|
|
self.intermediaries = https_cert.chain_to_pem
|
|
self.expires_at = https_cert.x509.not_after
|
|
self.renew_after = (self.expires_at - 1.month) + rand(10).days
|
|
self.save!
|
|
logger.info "Certificate issued (expires on #{self.expires_at}, will renew after #{self.renew_after})"
|
|
return true
|
|
end
|
|
|
|
def certificate_object
|
|
@certificate_object ||= OpenSSL::X509::Certificate.new(self.certificate)
|
|
end
|
|
|
|
def intermediaries_array
|
|
@intermediaries_array ||= self.intermediaries.to_s.scan(/-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----/m).map{|c| OpenSSL::X509::Certificate.new(c)}
|
|
end
|
|
|
|
def key_object
|
|
@key_object ||= OpenSSL::PKey::RSA.new(self.key)
|
|
end
|
|
|
|
def logger
|
|
Postal::LetsEncrypt.logger
|
|
end
|
|
|
|
end
|