rails-letsencrypt 0.5.5 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a5aeeebd0dd776f4fcc05752ea8b438878398d79
4
- data.tar.gz: fdff5b40a3526cbff50a83c7811b5a9970ccdf19
2
+ SHA256:
3
+ metadata.gz: 803822003496186ae2408760697b9ee88f136051e1cfed2ec6364ac6cfd1d90e
4
+ data.tar.gz: 8132cddcd9d2c6ceb6b039aec68a517873994041ef798a74d396fa92f59240f5
5
5
  SHA512:
6
- metadata.gz: d8759789aae5935de7951c65bb1281de060485ed0ff0d5532f825cd1bb6a0087a2fab3e659c94d42dc76a81f5bf3da7a974eda33e8214d6d94d8f8666a86903f
7
- data.tar.gz: d092e84fd25d14dd4e22546f922d9e74cce4a9357f5f0d1948f43c444fa1c8453170fbf313d51817648fcabefccf75e9c6b7ca9529a9bf3a45ef1d7d8d88f2d9
6
+ metadata.gz: 36ef4db53e0d1652115908850deb8a229f5566964b01415fd6ad4399f47401bd493fa556a4de6c21d7406cf17b2b7f009aef44ec98d4753ebaa24508ab63b8ab
7
+ data.tar.gz: 6fe213a8aaa548b1cead27f95aa56771aa5924b182d52902d5b584e17fe526674ba945ebb2b834c374be73f0ef335f42afa9b6d4a280806b6dd65c1106a1e924
data/README.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  Provide manageable Let's Encrypt Certificate for Rails.
4
4
 
5
+ ## Requirement
6
+
7
+ * Rails 5+
8
+ * Ruby 2.5+
9
+
5
10
  ## Installation
6
11
 
7
12
  Puts this in your Gemfile:
@@ -56,6 +61,10 @@ LetsEncrypt.config do |config|
56
61
  # The redis server url
57
62
  # Default is nil
58
63
  config.redis_url = 'redis://localhost:6379/1'
64
+
65
+ # Enable it if you want to customize the model
66
+ # Default is LetsEncrypt::Certificate
67
+ #config.certificate_model = 'MyCertificate'
59
68
  end
60
69
  ```
61
70
 
@@ -17,7 +17,7 @@ module LetsEncrypt
17
17
  end
18
18
 
19
19
  def certificate
20
- LetsEncrypt::Certificate.find_by(verification_path: filename)
20
+ LetsEncrypt.certificate_model.find_by(verification_path: filename)
21
21
  end
22
22
 
23
23
  def filename
@@ -6,7 +6,7 @@ module LetsEncrypt
6
6
  queue_as :default
7
7
 
8
8
  def perform
9
- LetsEncrypt::Certificate.renewable.each do |certificate|
9
+ LetsEncrypt.certificate_model.renewable.each do |certificate|
10
10
  next if certificate.renew
11
11
  certificate.update(renew_after: 1.day.from_now)
12
12
  end
@@ -10,7 +10,7 @@ module LetsEncrypt
10
10
  logger.info "Getting certificate for #{domain}"
11
11
  create_certificate
12
12
  # rubocop:disable Metrics/LineLength
13
- logger.info "Certificate issued (expires on #{expires_at}, will renew after #{renew_after})"
13
+ logger.info "Certificate issued for #{domain} (expires on #{expires_at}, will renew after #{renew_after})"
14
14
  # rubocop:enable Metrics/LineLength
15
15
  true
16
16
  end
@@ -18,21 +18,22 @@ module LetsEncrypt
18
18
  private
19
19
 
20
20
  def csr
21
- csr = OpenSSL::X509::Request.new
22
- csr.subject = OpenSSL::X509::Name.new(
23
- [['CN', domain, OpenSSL::ASN1::UTF8STRING]]
21
+ Acme::Client::CertificateRequest.new(
22
+ private_key: OpenSSL::PKey::RSA.new(key),
23
+ subject: {
24
+ common_name: domain
25
+ }
24
26
  )
25
- private_key = OpenSSL::PKey::RSA.new(key)
26
- csr.public_key = private_key.public_key
27
- csr.sign(private_key, OpenSSL::Digest::SHA256.new)
28
- csr
29
27
  end
30
28
 
31
29
  def create_certificate
32
- https_cert = LetsEncrypt.client.new_certificate(csr)
33
- self.certificate = https_cert.to_pem
34
- self.intermediaries = https_cert.chain_to_pem
35
- self.expires_at = https_cert.x509.not_after
30
+ order.finalize(csr: csr)
31
+ sleep 1 while order.status == 'processing'
32
+ fullchain = order.certificate.split("\n\n")
33
+ cert = OpenSSL::X509::Certificate.new(fullchain.shift)
34
+ self.certificate = cert.to_pem
35
+ self.intermediaries = fullchain.join("\n\n")
36
+ self.expires_at = cert.not_after
36
37
  self.renew_after = (expires_at - 1.month) + rand(10).days
37
38
  save!
38
39
  end
@@ -7,7 +7,7 @@ module LetsEncrypt
7
7
 
8
8
  # Returns true if verify domain is succeed.
9
9
  def verify
10
- start_authorize
10
+ create_order
11
11
  start_challenge
12
12
  wait_verify_status
13
13
  check_verify_status
@@ -17,9 +17,9 @@ module LetsEncrypt
17
17
 
18
18
  private
19
19
 
20
- def start_authorize
21
- authorization = LetsEncrypt.client.authorize(domain: domain)
22
- @challenge = authorization.http01
20
+ def create_order
21
+ # TODO: Support multiple domain
22
+ @challenge = order.authorizations.first.http
23
23
  self.verification_path = @challenge.filename
24
24
  self.verification_string = @challenge.file_content
25
25
  save!
@@ -27,24 +27,25 @@ module LetsEncrypt
27
27
 
28
28
  def start_challenge
29
29
  logger.info "Attempting verification of #{domain}"
30
- @challenge.request_verification
30
+ @challenge.request_validation
31
31
  end
32
32
 
33
33
  def wait_verify_status
34
34
  checks = 0
35
- until @challenge.verify_status != 'pending'
35
+ until @challenge.status != 'pending'
36
36
  checks += 1
37
37
  if checks > 30
38
- logger.info 'Status remained at pending for 30 checks'
38
+ logger.info "#{domain}: Status remained at pending for 30 checks"
39
39
  return false
40
40
  end
41
41
  sleep 1
42
+ @challenge.reload
42
43
  end
43
44
  end
44
45
 
45
46
  def check_verify_status
46
- unless @challenge.verify_status == 'valid'
47
- logger.info "Status was not valid (was: #{@challenge.verify_status})"
47
+ unless @challenge.status == 'valid'
48
+ logger.info "#{domain}: Status was not valid (was: #{@challenge.status})"
48
49
  return false
49
50
  end
50
51
 
@@ -55,11 +56,11 @@ module LetsEncrypt
55
56
  @retries ||= 0
56
57
  if e.is_a?(Acme::Client::Error::BadNonce) && @retries < 5
57
58
  @retries += 1
58
- logger.info "Bad nounce encountered. Retrying (#{@retries} of 5 attempts)"
59
+ logger.info "#{domain}: Bad nounce encountered. Retrying (#{@retries} of 5 attempts)"
59
60
  sleep 1
60
61
  verify
61
62
  else
62
- logger.info "Error: #{e.class} (#{e.message})"
63
+ logger.info "#{domain}: Error: #{e.class} (#{e.message})"
63
64
  return false
64
65
  end
65
66
  end
@@ -34,6 +34,7 @@ module LetsEncrypt
34
34
 
35
35
  before_create -> { self.key = OpenSSL::PKey::RSA.new(4096).to_s }
36
36
  after_save -> { save_to_redis }, if: -> { LetsEncrypt.config.use_redis? && active? }
37
+ after_destroy -> { delete_from_redis }, if: -> { LetsEncrypt.config.use_redis? && active? }
37
38
 
38
39
  # Returns false if certificate is not issued.
39
40
  #
@@ -57,7 +58,7 @@ module LetsEncrypt
57
58
 
58
59
  # Returns full-chain bundled certificates
59
60
  def bundle
60
- certificate + intermediaries
61
+ (certificate || '') + (intermediaries || '')
61
62
  end
62
63
 
63
64
  def certificate_object
@@ -73,10 +74,19 @@ module LetsEncrypt
73
74
  LetsEncrypt::Redis.save(self)
74
75
  end
75
76
 
77
+ # Delete certificate from redis
78
+ def delete_from_redis
79
+ LetsEncrypt::Redis.delete(self)
80
+ end
81
+
76
82
  protected
77
83
 
78
84
  def logger
79
85
  LetsEncrypt.logger
80
86
  end
87
+
88
+ def order
89
+ @order ||= LetsEncrypt.client.new_order(identifiers: [domain])
90
+ end
81
91
  end
82
92
  end
@@ -12,18 +12,18 @@ require 'letsencrypt/redis'
12
12
  # :nodoc:
13
13
  module LetsEncrypt
14
14
  # Production mode API Endpoint
15
- ENDPOINT = 'https://acme-v01.api.letsencrypt.org/'
15
+ ENDPOINT = 'https://acme-v02.api.letsencrypt.org/directory'
16
16
 
17
17
  # Staging mode API Endpoint, the rate limit is higher
18
18
  # but got invalid certificate for testing
19
- ENDPOINT_STAGING = 'https://acme-staging.api.letsencrypt.org'
19
+ ENDPOINT_STAGING = 'https://acme-staging-v02.api.letsencrypt.org/directory'
20
20
 
21
21
  class << self
22
22
  # Create the ACME Client to Let's Encrypt
23
23
  def client
24
24
  @client ||= ::Acme::Client.new(
25
25
  private_key: private_key,
26
- endpoint: endpoint
26
+ directory: directory
27
27
  )
28
28
  end
29
29
 
@@ -38,7 +38,7 @@ module LetsEncrypt
38
38
  end
39
39
 
40
40
  # Get current using Let's Encrypt endpoint
41
- def endpoint
41
+ def directory
42
42
  @endpoint ||= config.use_staging? ? ENDPOINT_STAGING : ENDPOINT
43
43
  end
44
44
 
@@ -49,10 +49,9 @@ module LetsEncrypt
49
49
  # connect with domain and assign the owner who can
50
50
  # renew and revoked.
51
51
  def register(email)
52
- registration = client.register(contact: "mailto:#{email}")
52
+ account = client.new_account(contact: "mailto:#{email}", terms_of_service_agreed: true)
53
53
  logger.info "Successfully registered private key with address #{email}"
54
- registration.agree_terms
55
- logger.info 'Terms have been accepted'
54
+ account.kid # TODO: Save KID
56
55
  true
57
56
  end
58
57
 
@@ -87,5 +86,9 @@ module LetsEncrypt
87
86
  def table_name_prefix
88
87
  'letsencrypt_'
89
88
  end
89
+
90
+ def certificate_model
91
+ @certificate_model ||= config.certificate_model.constantize
92
+ end
90
93
  end
91
94
  end
@@ -16,6 +16,10 @@ module LetsEncrypt
16
16
  config_accessor :save_to_redis
17
17
  config_accessor :redis_url
18
18
 
19
+ config_accessor :certificate_model do
20
+ 'LetsEncrypt::Certificate'
21
+ end
22
+
19
23
  # Returns true if enabled `save_to_redis` feature
20
24
  def use_redis?
21
25
  save_to_redis == true
@@ -10,10 +10,18 @@ module LetsEncrypt
10
10
 
11
11
  # Save certificate into redis.
12
12
  def save(cert)
13
- return unless cert.key.present? && cert.certificate.present?
14
- LetsEncrypt.logger.info "Save #{cert.domain}'s certificate to redis"
13
+ return unless cert.key.present? && cert.bundle.present?
14
+ LetsEncrypt.logger.info "Save #{cert.domain}'s certificate (bundle) to redis"
15
15
  connection.set "#{cert.domain}.key", cert.key
16
- connection.set "#{cert.domain}.crt", cert.certificate
16
+ connection.set "#{cert.domain}.crt", cert.bundle
17
+ end
18
+
19
+ # Delete certificate from redis.
20
+ def delete(cert)
21
+ return unless cert.key.present? && cert.certificate.present?
22
+ LetsEncrypt.logger.info "Delete #{cert.domain}'s certificate from redis"
23
+ connection.del "#{cert.domain}.key"
24
+ connection.del "#{cert.domain}.crt"
17
25
  end
18
26
  end
19
27
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LetsEncrypt
4
- VERSION = '0.5.5'
4
+ VERSION = '0.10.0'
5
5
  end
@@ -1,17 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  namespace :letsencrypt do
4
- desc 'Renew the certificates will epxired'
4
+ desc "Renew certificates that already expired or expiring soon"
5
5
  task renew: :environment do
6
6
  count = 0
7
7
  failed = 0
8
- LetsEncrypt::Certificate.renewable do |certificate|
8
+
9
+ LetsEncrypt.certificate_model.renewable.each do |certificate|
9
10
  count += 1
11
+
10
12
  next if certificate.renew
13
+
11
14
  failed += 1
12
- log "Could not renew domain: #{certificate.domain}"
15
+ puts "Could not renew domain: #{certificate.domain}"
13
16
  end
14
17
 
15
- puts "Total #{count} domains should renew, and #{failed} domains cannot be renewed."
18
+ puts "Renewed #{count - failed} out of #{count} domains"
16
19
  end
17
20
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-letsencrypt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.5
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - 蒼時弦也
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-20 00:00:00.000000000 Z
11
+ date: 2020-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '4.1'
19
+ version: '5.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '4.1'
26
+ version: '5.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: acme-client
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.0'
33
+ version: 2.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.0'
40
+ version: 2.0.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: redis
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -84,30 +84,30 @@ dependencies:
84
84
  name: simplecov
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: 0.16.1
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: 0.16.1
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: coveralls
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: 0.8.23
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: 0.8.23
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: codeclimate-test-reporter
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -156,7 +156,7 @@ homepage: https://github.com/elct9620/rails-letsencrypt
156
156
  licenses:
157
157
  - MIT
158
158
  metadata: {}
159
- post_install_message:
159
+ post_install_message:
160
160
  rdoc_options: []
161
161
  require_paths:
162
162
  - lib
@@ -171,9 +171,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
171
171
  - !ruby/object:Gem::Version
172
172
  version: '0'
173
173
  requirements: []
174
- rubyforge_project:
175
- rubygems_version: 2.6.14
176
- signing_key:
174
+ rubygems_version: 3.0.3
175
+ signing_key:
177
176
  specification_version: 4
178
177
  summary: The Let's Encrypt certificate manager for rails
179
178
  test_files: []