Skip to content

Instantly share code, notes, and snippets.

@17twenty
Forked from 0x7466/le-cert.rb
Created March 23, 2017 10:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 17twenty/20d4b3d7ec1c973cddb5d920c9f2c2af to your computer and use it in GitHub Desktop.
Save 17twenty/20d4b3d7ec1c973cddb5d920c9f2c2af to your computer and use it in GitHub Desktop.
Easy to use Ruby script to register and authorize new domains on Let's Encrypt and getting certificates for them. Call 'ruby le-new.rb' and follow the instructions. Install OpenSSL and the ACMEClient gem before!
#!/usr/bin/env ruby
begin
require 'acme-client'
rescue LoadError
abort 'MISSING GEM! You haven\'t installed the ACME client. Install the gem with the command \'gem install acme-client\'.'
end
require 'openssl'
require 'resolv'
print 'Path to Let\'s Encrypt private key (path/to/key.pem): '
private_key_path = gets.chomp
unregistered_private_key = private_key_path == ''
session_dir = Time.now.to_i.to_s
Dir.mkdir session_dir
endpoint = 'https://acme-v01.api.letsencrypt.org/' # 'https://acme-staging.api.letsencrypt.org/'
private_key = begin
if private_key_path != ''
OpenSSL::PKey::RSA.new File.read(private_key_path)
else
key = OpenSSL::PKey::RSA.new 4096
File.write "#{session_dir}/client_key.pem", key.to_pem
key
end
end
client = Acme::Client.new(private_key: private_key, endpoint: endpoint)
if unregistered_private_key
email = ''
while email == ''
print 'Your email address: '
email = gets.chomp
if email == ''
puts 'Email address is required!'
end
end
registration = client.register(contact: "mailto:#{email}")
agree_status = ''
while true
print 'Agree service terms (Y/n) ? '
agree_status = gets.chomp
break if agree_status == 'Y' || agree_status == 'y' || agree_status == 'N' || agree_status == 'n'
puts 'Invalid input!'
end
if agree_status == 'Y' || agree_status == 'y'
registration.agree_terms
else
raise
end
end
domains = {}
puts 'Enter your domains you want to register. Leave empty if you are finished.'
while true
print 'Domain: '
domain = gets.chomp
break if domain == ''
unless unregistered_private_key
authorization_status = ''
while true
print 'Are you already authorized for this key (Y/n)? '
authorization_status = gets.chomp
break if authorization_status == 'Y' || authorization_status == 'y' || authorization_status == 'N' || authorization_status == 'n'
puts 'Invalid input!'
end
domains[domain] = { authorized: authorization_status == 'Y' || authorization_status == 'y' }
else
domains[domain] = { authorized: false }
end
end
domains.each do |domain, value|
unless value[:authorized]
authorization = client.authorize domain: domain
challenge = authorization.dns01
domains[domain][:authorization] = authorization
domains[domain][:challenge] = challenge
end
end
puts
puts 'Add the following DNS records to your domain names.'
puts
domains.each do |domain, value|
next if value[:authorized]
authorization = value[:authorization]
challenge = value[:challenge]
puts "==============================================="
puts
puts "Domain: #{domain}"
puts
puts 'DNS Record:'
puts
puts "Name: #{challenge.record_name}.#{domain}"
puts "Type: #{challenge.record_type}"
puts "Content: #{challenge.record_content}"
puts "TTL: Shortest possible time-to-live"
puts
end
general_dns = Resolv::DNS.new(nameserver: ['8.8.8.8', '8.8.4.4'])
puts "It's automatically checking if you have set the records and continues when you are authorized for all domains..."
dns_records_found = false
while not dns_records_found
domains.each do |domain, value|
next if value[:authorized]
next if value[:dns_record_found]
begin
dns_challenge = general_dns.getresource("_acme-challenge.#{domain}", Resolv::DNS::Resource::IN::TXT)
rescue Resolv::ResolvError
next
end
domains[domain][:dns_record_found] = dns_challenge.data == value[:challenge].record_content
if domains[domain][:dns_record_found]
puts "Record for domain \"#{domain}\" found."
end
end
dns_records_found = true
domains.each do |domain, value|
dns_records_found = false unless value[:dns_record_found]
end
break if dns_records_found
sleep(2)
end
puts
puts "Let's Encrypt verification:"
puts "==========================="
domains.each do |domain, value|
next if value[:authorized]
authorization = value[:authorization]
challenge = value[:challenge]
challenge.request_verification
sleep 1
puts "#{authorization.domain} : #{challenge.verify_status}"
end
csr = Acme::Client::CertificateRequest.new(names: domains.keys)
certificate = client.new_certificate(csr)
File.write("#{session_dir}/privkey.pem", certificate.request.private_key.to_pem)
File.write("#{session_dir}/cert.pem", certificate.to_pem)
File.write("#{session_dir}/chain.pem", certificate.chain_to_pem)
File.write("#{session_dir}/fullchain.pem", certificate.fullchain_to_pem)
puts
puts 'Here is your new certificate:'
puts
puts 'Private Key:'
puts
puts certificate.request.private_key.to_pem
puts
puts 'Certificate:'
puts
puts certificate.to_pem
puts
puts 'Chain:'
puts
puts certificate.chain_to_pem
puts
puts
puts
puts "You will find the certificates also in the session directory. This is in the same path as the script and the folder has the name #{session_dir}."
puts
puts 'Have fun with your new certificates ^^.'
puts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment