class Amarillo
Public Class Methods
new(amarilloHome)
click to toggle source
# File lib/amarillo.rb, line 37 def initialize(amarilloHome) @environment = Amarillo::Environment.new(amarilloHome: amarilloHome) if not @environment.verify then raise "Cannot initialize amarillo" end @environment.load_config @certificatePath = @environment.certificatePath @keyPath = @environment.keyPath @config = @environment.config @awsEnvFile = @environment.awsEnvFile @configsPath = @environment.configsPath @logger = Logger.new(STDOUT) @logger.level = Logger::INFO end
Public Instance Methods
check_dns(domainName, nameservers, value)
click to toggle source
# File lib/amarillo.rb, line 56 def check_dns(domainName, nameservers, value) valid = true nameservers.each do |nameserver| begin records = Resolv::DNS.open(nameserver: nameserver) do |dns| dns.getresources( "_acme-challenge.#{domainName}", Resolv::DNS::Resource::IN::TXT ) end records = records.map(&:strings).flatten valid = value == records.first rescue Resolv::ResolvError return false end return false if !valid end valid end
cleanup(label, record_type, challengeValue)
click to toggle source
# File lib/amarillo.rb, line 220 def cleanup(label, record_type, challengeValue) @logger.info "Cleaning up..." change = { action: 'DELETE', resource_record_set: { name: label, type: record_type, ttl: 1, resource_records: [ { value: "\"#{challengeValue}\"" } ] } } options = { hosted_zone_id: @hzone.id, change_batch: { changes: [change] } } @route53.change_resource_record_sets(options) end
deleteCertificate(commonName)
click to toggle source
# File lib/amarillo.rb, line 271 def deleteCertificate(commonName) @logger.info "Deleting certificate #{commonName}" certConfigFile = @configsPath + "/#{commonName}.yml" certificatePath = @certificatePath + "/#{commonName}.crt" keyPath = @keyPath + "/#{commonName}.key" `rm -f #{certConfigFile} #{certificatePath} #{keyPath}` end
get_route53()
click to toggle source
# File lib/amarillo.rb, line 78 def get_route53 shared_creds = Aws::SharedCredentials.new(path: "#{@awsEnvFile}") Aws.config.update(credentials: shared_creds) region = @config["defaults"]["region"] ? @config["defaults"]["region"] : 'us-east-2' @route53 = Aws::Route53::Client.new(region: region) @hzone = @route53.list_hosted_zones(max_items: 100).hosted_zones.detect { |z| z.name == "#{@zone}." } end
listCertificates()
click to toggle source
# File lib/amarillo.rb, line 249 def listCertificates rows = [] Dir["#{@configsPath}/*.yml"].each do |c| config = YAML.load(File.read(c)) cn = config["commonName"] certificatePath = "#{@certificatePath}/#{cn}.crt" raw = File.read certificatePath certificate = OpenSSL::X509::Certificate.new raw rows << [config["commonName"], config["email"], config["zone"], config["key_type"], certificate.not_after] end t = Terminal::Table.new :headings => ['commonName','email','zone','keytype','expiration'], :rows => rows puts t end
renewCertificate(zone, commonName, email)
click to toggle source
# File lib/amarillo.rb, line 245 def renewCertificate(zone, commonName, email) end
renewCertificates()
click to toggle source
# File lib/amarillo.rb, line 282 def renewCertificates t = Time.now @logger.info "Renewing certificates" Dir["#{@configsPath}/*.yml"].each do |c| config = YAML.load(File.read(c)) cn = config["commonName"] email = config["email"] zone = config["zone"] key_type = config["key_type"] certificatePath = "#{@certificatePath}/#{cn}.crt" raw = File.read certificatePath certificate = OpenSSL::X509::Certificate.new raw daysToExpiration = (certificate.not_after - t).to_i / (24 * 60 * 60) if daysToExpiration < 30 then @logger.info "#{cn} certificate needs to be renewed" self.requestCertificate zone, cn, email, key_type else @logger.info "#{cn} certificate does not need to be renewed" end end end
requestCertificate(zone, commonName, email, key_type)
click to toggle source
# File lib/amarillo.rb, line 89 def requestCertificate(zone, commonName, email, key_type) @zone = zone acmeUrl = @config["defaults"]["acme_url"] ? @config["defaults"]["acme_url"] : 'https://acme-v02.api.letsencrypt.org/directory' # Load private key @logger.info "Loading 4096-bit RSA private key for Let's Encrypt account" @logger.info "Let's Encrypt directory set to #{acmeUrl}" key = OpenSSL::PKey::RSA.new File.read "#{@keyPath}/letsencrypt.key" client = Acme::Client.new private_key: key, directory: acmeUrl account = client.new_account contact: "mailto:#{email}", terms_of_service_agreed: true # Generate a certificate order @logger.info "Creating certificate order request for #{commonName}" order = client.new_order(identifiers: [commonName]) authorization = order.authorizations.first label = "_acme-challenge.#{commonName}" record_type = authorization.dns.record_type challengeValue = authorization.dns.record_content @logger.info "Challenge value for #{commonName} in #{zone} zone is #{challengeValue}" self.get_route53 change = { action: 'UPSERT', resource_record_set: { name: label, type: record_type, ttl: 1, resource_records: [ { value: "\"#{challengeValue}\"" } ] } } options = { hosted_zone_id: @hzone.id, change_batch: { changes: [change] } } @route53.change_resource_record_sets(options) nameservers = @environment.get_zone_nameservers @logger.info "Waiting for DNS record to propagate" while !check_dns(commonName, nameservers, challengeValue) sleep 2 @logger.info "Still waiting..." end authorization.dns.request_validation @logger.info "Requesting validation..." authorization.dns.reload while authorization.dns.status == 'pending' sleep 2 @logger.info "DNS status: #{authorization.dns.status}" authorization.dns.reload end @logger.info "Generating key" # Create certificate yml certConfig = { "commonName" => commonName, "email" => email, "zone" => zone } if key_type certConfig["key_type"] = key_type else key_type = @config["defaults"]["key_type"] certConfig["key_type"] = key_type end type, args = key_type.split(',') if type == 'ec' then certPrivateKey = OpenSSL::PKey::EC.new(args).generate_key elsif type == 'rsa' then certPrivateKey = OpenSSL::PKey::RSA.new(args) end @logger.info "Requesting certificate..." csr = Acme::Client::CertificateRequest.new private_key: certPrivateKey, names: [commonName] begin order.finalize(csr: csr) rescue @logger.error("ERROR") self.cleanup label, record_type, challengeValue end sleep(1) while order.status == 'processing' keyOutputPath = "#{@keyPath}/#{commonName}.key" certOutputPath = "#{@certificatePath}/#{commonName}.crt" @logger.info "Saving private key to #{keyOutputPath}" File.open(keyOutputPath, "w") do |f| f.puts certPrivateKey.to_pem.to_s end File.chmod(0600, keyOutputPath) @logger.info "Saving certificate to #{certOutputPath}" File.open(certOutputPath, "w") do |f| f.puts order.certificate end certConfigFile = "#{@configsPath}/#{commonName}.yml" File.write(certConfigFile, certConfig.to_yaml) self.cleanup label, record_type, challengeValue end