/
letsencrypt.rake
115 lines (89 loc) · 3.82 KB
/
letsencrypt.rake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
require 'open-uri'
require 'openssl'
require 'acme-client'
require 'platform-api'
namespace :letsencrypt do
desc 'Renew your LetsEncrypt certificate'
task :renew do
# Check configuration looks OK
abort "letsencrypt-rails-heroku is configured incorrectly. Are you missing an environment variable or other configuration? You should have a heroku_token, heroku_app, acmp_email and acme_domain configured either via a `Letsencrypt.configure` block in an initializer or as environment variables." unless Letsencrypt.configuration.valid?
# Set up Heroku client
heroku = PlatformAPI.connect_oauth Letsencrypt.configuration.heroku_token
heroku_app = Letsencrypt.configuration.heroku_app
# Create a private key
print "Creating account key..."
private_key = OpenSSL::PKey::RSA.new(4096)
puts "Done!"
client = Acme::Client.new(private_key: private_key, endpoint: Letsencrypt.configuration.acme_endpoint, connection_options: { request: { open_timeout: 5, timeout: 5 } })
print "Registering with LetsEncrypt..."
registration = client.register(contact: "mailto:#{Letsencrypt.configuration.acme_email}")
registration.agree_terms
puts "Done!"
domains = Letsencrypt.configuration.acme_domain.split(',').map(&:strip)
domains.each do |domain|
puts "Performing verification for #{domain}:"
authorization = client.authorize(domain: domain)
challenge = authorization.http01
print "Setting config vars on Heroku..."
heroku.config_var.update(heroku_app, {
'ACME_CHALLENGE_FILENAME' => challenge.filename,
'ACME_CHALLENGE_FILE_CONTENT' => challenge.file_content
})
puts "Done!"
# Wait for request to go through
print "Giving config vars time to change..."
sleep(5)
puts "Done!"
# Wait for app to come up
print "Testing filename works (to bring up app)..."
# Get the domain name from Heroku
hostname = heroku.domain.list(heroku_app).first['hostname']
open("http://#{hostname}/#{challenge.filename}").read
puts "Done!"
print "Giving LetsEncrypt some time to verify..."
# Once you are ready to serve the confirmation request you can proceed.
challenge.request_verification # => true
challenge.verify_status # => 'pending'
sleep(3)
puts "Done!"
unless challenge.verify_status == 'valid'
puts "Problem verifying challenge."
abort "Status: #{challenge.verify_status}, Error: #{challenge.error}"
end
puts ""
end
# Unset temporary config vars. We don't care about waiting for this to
# restart
heroku.config_var.update(heroku_app, {
'ACME_CHALLENGE_FILENAME' => nil,
'ACME_CHALLENGE_FILE_CONTENT' => nil
})
# Create CSR
csr = Acme::Client::CertificateRequest.new(names: domains)
# Get certificate
certificate = client.new_certificate(csr) # => #<Acme::Client::Certificate ....>
# Send certificates to Heroku via API
# First check for existing certificates:
certificates = heroku.sni_endpoint.list(heroku_app)
begin
if certificates.any?
print "Updating existing certificate #{certificates[0]['name']}..."
heroku.sni_endpoint.update(heroku_app, certificates[0]['name'], {
certificate_chain: certificate.fullchain_to_pem,
private_key: certificate.request.private_key.to_pem
})
puts "Done!"
else
print "Adding new certificate..."
heroku.sni_endpoint.create(heroku_app, {
certificate_chain: certificate.fullchain_to_pem,
private_key: certificate.request.private_key.to_pem
})
puts "Done!"
end
rescue Excon::Error::UnprocessableEntity => e
warn "Error adding certificate to Heroku. Response from Heroku’s API follows:"
abort e.response.body
end
end
end