rails-letsencrypt 0.5.5 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.md +9 -0
- data/app/controllers/lets_encrypt/verifications_controller.rb +1 -1
- data/app/jobs/lets_encrypt/renew_certificates_job.rb +1 -1
- data/app/models/concerns/lets_encrypt/certificate_issuable.rb +13 -12
- data/app/models/concerns/lets_encrypt/certificate_verifiable.rb +12 -11
- data/app/models/lets_encrypt/certificate.rb +11 -1
- data/lib/letsencrypt.rb +10 -7
- data/lib/letsencrypt/configuration.rb +4 -0
- data/lib/letsencrypt/redis.rb +11 -3
- data/lib/letsencrypt/version.rb +1 -1
- data/lib/tasks/letsencrypt_tasks.rake +7 -4
- metadata +18 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 803822003496186ae2408760697b9ee88f136051e1cfed2ec6364ac6cfd1d90e
|
4
|
+
data.tar.gz: 8132cddcd9d2c6ceb6b039aec68a517873994041ef798a74d396fa92f59240f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
|
@@ -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
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
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
|
21
|
-
|
22
|
-
@challenge =
|
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.
|
30
|
+
@challenge.request_validation
|
31
31
|
end
|
32
32
|
|
33
33
|
def wait_verify_status
|
34
34
|
checks = 0
|
35
|
-
until @challenge.
|
35
|
+
until @challenge.status != 'pending'
|
36
36
|
checks += 1
|
37
37
|
if checks > 30
|
38
|
-
logger.info
|
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.
|
47
|
-
logger.info "Status was not valid (was: #{@challenge.
|
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
|
data/lib/letsencrypt.rb
CHANGED
@@ -12,18 +12,18 @@ require 'letsencrypt/redis'
|
|
12
12
|
# :nodoc:
|
13
13
|
module LetsEncrypt
|
14
14
|
# Production mode API Endpoint
|
15
|
-
ENDPOINT = 'https://acme-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
data/lib/letsencrypt/redis.rb
CHANGED
@@ -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.
|
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.
|
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
|
data/lib/letsencrypt/version.rb
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
namespace :letsencrypt do
|
4
|
-
desc
|
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
|
-
|
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
|
-
|
15
|
+
puts "Could not renew domain: #{certificate.domain}"
|
13
16
|
end
|
14
17
|
|
15
|
-
puts "
|
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.
|
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:
|
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: '
|
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: '
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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
|
-
|
175
|
-
|
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: []
|