Last active
April 5, 2025 08:10
-
-
Save 0x7466/5394292c560bdd17267bd38c35ae21ac 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!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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
Usage
Install ACME client
The ACME client is needed to communicate with the Let's Encrypt servers.
Store the script somewhere
The script stores certificates and keys in the same directory as the script is located.
Run the script
Add the TXT records to your DNS admin panel
The script automatically checks if the records are set and requests the Let's encrypt validation automatically.
Have fun!
Everything done. After a successful validation you get your certificate immediately in the terminal. They are also in files stored in a directory with the current timestamp (E.g.
1490177596
).Next time
In the directory is also a file called
client_key.pem
. If you pass this key the next time you run the script, you don't have to revalidate the domains you've already validated.