Class: ApartmentAcmeClient::Encryption
- Inherits:
-
Object
- Object
- ApartmentAcmeClient::Encryption
- Defined in:
- lib/apartment_acme_client/encryption.rb
Overview
rubocop:disable Metrics/ClassLength
Instance Method Summary collapse
-
#authorize_domain_with_http(domain_authorization) ⇒ Object
authorizes a single domain with letsencrypt server returns true on success, false otherwise.
-
#authorize_domains_with_dns(authorizations, wildcard_domain:) ⇒ Object
Authorize a wildcard cert domain.
-
#csr_private_key_string ⇒ Object
for use in order to store this on the machine for NGINX use.
-
#initialize ⇒ Encryption
constructor
A new instance of Encryption.
-
#register_new(email) ⇒ Object
Largely based on github.com/unixcharles/acme-client documentation.
-
#request_certificate(common_name:, domains:, wildcard_domain: nil) ⇒ Object
Create an order, perform authorization for each domain, and then request the certificate.
Constructor Details
#initialize ⇒ Encryption
Returns a new instance of Encryption.
25 26 27 |
# File 'lib/apartment_acme_client/encryption.rb', line 25 def initialize @certificate_storage = ApartmentAcmeClient::CertificateStorage::Proxy.singleton end |
Instance Method Details
#authorize_domain_with_http(domain_authorization) ⇒ Object
authorizes a single domain with letsencrypt server returns true on success, false otherwise.
from github.com/unixcharles/acme-client/tree/master#authorize-for-domain
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/apartment_acme_client/encryption.rb', line 114 def () # rubocop:disable Metrics/MethodLength challenge = .http puts "authorizing Domain: #{.domain}" # The http method will require you to respond to a HTTP request. # You can retrieve the challenge token challenge.token # => "some_token" # You can retrieve the expected path for the file. challenge.filename # => ".well-known/acme-challenge/:some_token" # You can generate the body of the expected response. challenge.file_content # => 'string token and JWK thumbprint' # You are not required to send a Content-Type. This method will return the right Content-Type should you decide to include one. challenge.content_type # Save the file. We'll create a public directory to serve it from, and inside it we'll create the challenge file. FileUtils.mkdir_p(File.join(ApartmentAcmeClient.public_folder, File.dirname(challenge.filename))) # We'll write the content of the file full_challenge_filename = File.join(ApartmentAcmeClient.public_folder, challenge.filename) File.write(full_challenge_filename, challenge.file_content) # Optionally save the challenge for use at another time (eg: by a background job processor) # File.write('challenge', challenge.to_h.to_json) # The challenge file can be served with a Ruby webserver. # You can run a webserver in another console for that purpose. You may need to forward ports on your router. # # $ ruby -run -e httpd public -p 8080 --bind-address 0.0.0.0 # 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'))) # Once you are ready to serve the confirmation request you can proceed. challenge.request_validation # => true 30.times do # may be 'pending' initially if challenge.status == 'valid' puts "authorized!" break end puts "Waiting for letsencrypt to authorize the single domain. Status: #{challenge.status}" # Wait a bit for the server to make the request, or just blink. It should be fast. sleep(2) challenge.reload end File.delete(full_challenge_filename) challenge.status == 'valid' end |
#authorize_domains_with_dns(authorizations, wildcard_domain:) ⇒ Object
Authorize a wildcard cert domain. to do this, we have to write to the Amazon Route53 DNS entry params:
- authorizations - a list of authorizations, which may be http or dns based (ignore the non-wildcard ones)
- wildcard_domain - the url of the wildcard's base domain (e.g. "site.example.com")
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 |
# File 'lib/apartment_acme_client/encryption.rb', line 51 def (, wildcard_domain:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity label = nil record_type = nil values = [] = [] .each do || next unless .wildcard || .http.nil? << .dns end .each do || label = "#{.record_name}.#{wildcard_domain}" record_type = .record_type value = .record_content values << value end return true unless values.any? route53 = ApartmentAcmeClient::DnsApi::Route53.new( requested_domain: wildcard_domain, dns_record_label: label, record_type: record_type, values: values ) puts "writing #{label} to Route53" route53.write_record check_dns = ApartmentAcmeClient::DnsApi::CheckDns.new(wildcard_domain, label) check_dns.wait_for_present(values.first) puts "waiting 60 seconds before requesting DNS check from LetsEncrypt" sleep(60) if check_dns.check_dns(values.first) # DNS is updated, proceed with cert request .each do || .request_validation 60.times do # may be 'pending' initially break if .status == 'valid' puts "Waiting for LetsEncrypt to authorize the domain. Status #{.status}" # Wait a bit for the server to make the request, or just blink. It should be fast. sleep(2) .reload end end else # ERROR, DNS not updated in time Rollbar.error("DNS Entry not found in timeout") end end |
#csr_private_key_string ⇒ Object
for use in order to store this on the machine for NGINX use
200 201 202 |
# File 'lib/apartment_acme_client/encryption.rb', line 200 def csr_private_key_string csr_private_key.to_s end |
#register_new(email) ⇒ Object
Largely based on github.com/unixcharles/acme-client documentation
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/apartment_acme_client/encryption.rb', line 30 def register_new(email) raise StandardError.new('Private key already exists') unless @certificate_storage.private_key.nil? private_key = create_private_key # Initialize the client new_client = ApartmentAcmeClient::AcmeClient::Proxy.singleton( acme_client_private_key: private_key, csr_private_key: nil, # not needed for 'register' call ) new_client.register(email) @certificate_storage.save_private_key(private_key) end |
#request_certificate(common_name:, domains:, wildcard_domain: nil) ⇒ Object
Create an order, perform authorization for each domain, and then request the certificate.
-
common name is used so that there is continuity of requests over time
-
domains are the list of individual http-based domains to be authorized
-
wildcard_domain is an optional wildcard domain to be authorized via DNS Record
Returns the certificate
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/apartment_acme_client/encryption.rb', line 178 def request_certificate(common_name:, domains:, wildcard_domain: nil) domain_names_requested = domains domain_names_requested += [wildcard_domain, "*.#{wildcard_domain}"] if wildcard_domain.present? order = client.new_order(identifiers: domain_names_requested) # Do the HTTP authorizations order..each do || next if .wildcard || .http.nil? () end # Do the DNS (wildcard) authorizations if (order., wildcard_domain: wildcard_domain) client.request_certificate(common_name: common_name, names: domain_names_requested, order: order) else # rubocop:disable Style/EmptyElse # error, not authorized nil end end |