Skip to content

Instantly share code, notes, and snippets.

@pachacamac
Created August 31, 2016 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 pachacamac/91fa65005d63fe05b21bc5433e9997f4 to your computer and use it in GitHub Desktop.
Save pachacamac/91fa65005d63fe05b21bc5433e9997f4 to your computer and use it in GitHub Desktop.
letsencrypt helper
#!/usr/bin/env ruby
require 'acme-client'
require 'openssl'
require 'uri'
require 'net/http'
class Acme::Client::Crypto
def generate_signed_jws(header:, payload:)
jwt = JSON::JWT.new(payload || {})
jwt.header.merge!(header || {})
jwt.header[:jwk] = jwk
jws = jwt.sign(private_key, :RS256)
jws.to_json(syntax: :flattened)
end
end
endpoint = 'https://acme-staging.api.letsencrypt.org/'
#endpoint = 'https://acme-v01.api.letsencrypt.org/'
domains = %w(foo.bar)
email = "webmaster@#{domains.first}"
http_challenge_base_dir = File.expand_path('~/var/www/public')
priv_key_path = File.expand_path('~/.ssh/id_rsa')
priv_key = OpenSSL::PKey::RSA.new(IO.read(priv_key_path))
# priv_key = OpenSSL::PKey::RSA.new(4096).tap do |key|
# File.write('/tmp/id_rsa', key.to_s)
# File.write('/tmp/id_rsa.pub', key.public_key.to_s)
# end
########################################################################################################################
# Create client, register it if it hasn't been registered yet
client = Acme::Client.new(private_key: priv_key,
endpoint: endpoint,
connection_options: { request: { open_timeout: 10, timeout: 10 } })
begin
print 'Trying to register client... '
registration = client.register(contact: "mailto:#{email}")
registration.agree_terms
puts 'done'
rescue Acme::Client::Error::Malformed
puts 'already registered'
end
########################################################################################################################
# Prove that you're the owner of said domain(s)
authorization = client.authorize(domain: domains.first)
challenge = authorization.http01
# Load a saved challenge. This is only required if you need to reuse a saved challenge as outlined above.
# challenge = client.challenge_from_hash(JSON.parse(File.read('challenge')))
challenge_dir = File.join(http_challenge_base_dir, File.dirname(challenge.filename))
challenge_file = File.basename(challenge.filename)
FileUtils.mkdir_p(challenge_dir)
File.write(File.join(challenge_dir, challenge_file), challenge.file_content)
File.write('letsencrypt_challenge.json', challenge.to_h.to_json)
challenge.request_verification
loop do
break if challenge.verify_status != 'pending'
puts 'waiting for challenge verification'
sleep 1
end
raise "Challenge could not be verified: #{challenge.verify_status}" if challenge.verify_status != 'valid'
########################################################################################################################
# Create certificate files for your domain(s)
csr = Acme::Client::CertificateRequest.new(names: domains)
certificate = client.new_certificate(csr)
def dhparam(opts = {})
opts[:bits] ||= 4096
if opts[:generate]
OpenSSL::PKey::DH.new(opts[:bits]).to_s
else
Net::HTTP.get(URI("https://2ton.com.au/dhparam/#{opts[:bits]}/#{rand(0..127)}"))
end
end
# Save the certificate and the private key to files
File.write('privkey.pem', certificate.request.private_key.to_pem)
File.write('cert.pem', certificate.to_pem)
File.write('chain.pem', certificate.chain_to_pem)
File.write('fullchain.pem', certificate.fullchain_to_pem)
File.write('dhparam.pem', dhparam(bits: 4096, generate: false))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment