letsencrypt-rails-heroku 1.1.3 → 2.0.2

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: 2e52fe099de5ee4ec2a76f81758b050ab66caf18
4
- data.tar.gz: 9b14169c10ca75a1c13528112ac3676b544eba46
2
+ SHA256:
3
+ metadata.gz: 34bfad9e6385886924b26bed9062f53175f4b1ca1ee7473c2c33828aa2cb34d0
4
+ data.tar.gz: e27e8e2fea3cdd4ebf9a253cd816c8af307fadae6c77e7535bc1c99baba41c0e
5
5
  SHA512:
6
- metadata.gz: 493348f0881e833bf90e356dc272a0a4d809da85d50cc7ac753d0a303a5318e30182014be2ffad622beb64bbc278d47b61b8fb287a53153dadd09154940f31dd
7
- data.tar.gz: 9832dd1809e3207587c479737db0e6d89f3cce519fa69566153713d298c7037f8fdb09b0dbbc916ddf48b07e1841397d0fa4cff0bcf7373565c474334f70ea3d
6
+ metadata.gz: cef68fb15ea48913116f1f65aa1f53a50202e9fb31cd53078546655cbd93b9b84b1c1cae8b381ebcfa7417243deedf5e7fcf9e9c8d12c8de0310840f37ee8378
7
+ data.tar.gz: ae93f4a569b1f40b3bec3c8fe9d19c43790c097570b814e29219d015344a948528268b68513dd047f5a5e5ff576e1c01664ecaf6f4479687f65dd18131ded062
@@ -1,3 +1,50 @@
1
+ # 2.0.2
2
+
3
+ - Include OpenSSL::SSL::SSLError as a valid error we retry for when waiting
4
+ for the app to come back up.
5
+
6
+ # 2.0.1
7
+
8
+ - Fixed a typo that broke renewals with existing certificates
9
+
10
+ # 2.0.0
11
+
12
+ Thanks to [@mashedkeyboard](https://github.com/mashedkeyboard) for their
13
+ work on ACME v2, saving registration, and DNS-based validation.
14
+
15
+ - *BREAKING* You must indicate your acceptance of Let's Encrypt's terms
16
+ and conditions by setting the `ACME_TERMS_AGREED` configuration variable.
17
+ - *BREAKING* Removed `ACME_ENDPOINT` environment variable reference. We never
18
+ documented that we support alternative endpoints, and we never tested it,
19
+ and the gem is called *letsencrypt*-rails-heroku, so let's not pretend.
20
+ Please get in touch if you were using this configuration variable, we'd
21
+ like to hear from you! Psst; you can still set `acme_directory` when
22
+ configuring the gem in an initializer.
23
+ - Use version 2 of the ACME API, paving the way for DNS validation.
24
+ - Save private key & key ID variables after registering with Let's Encrypt.
25
+ This will create two new permanent environment variables, `ACME_PRIVATE_KEY`
26
+ and `ACME_KEY_ID`.
27
+
28
+ # 1.2.1
29
+
30
+ - Update `rack` and `nokogiri` dependencies due to reported vulnerabilities
31
+ in those libraries. Note that these don't affect letsencrypt-rails-heroku
32
+ directly.
33
+ [CVE-2018-16471](https://nvd.nist.gov/vuln/detail/CVE-2018-16471),
34
+ [CVE-2016-4658](https://nvd.nist.gov/vuln/detail/CVE-2016-4658),
35
+ [CVE-2017-5029](https://nvd.nist.gov/vuln/detail/CVE-2017-5029),
36
+ [CVE-2018-14404](https://nvd.nist.gov/vuln/detail/CVE-2018-14404),
37
+ [CVE-2017-18258](https://nvd.nist.gov/vuln/detail/CVE-2017-18258),
38
+ [CVE-2017-9050](https://nvd.nist.gov/vuln/detail/CVE-2017-9050).
39
+
40
+ - Stop using [jalada/platform-api](https://github.com/jalada/platform-api)
41
+ because the newer version of the official version supports the API endpoints
42
+ we need now.
43
+
44
+ # 1.2.0
45
+
46
+ - Support SSL Endpoint configuration, as well as the default SNI.
47
+
1
48
  # 1.1.3
2
49
 
3
50
  - 1.1.1 wasn't a correct fix for catching redirects during polling, this
data/Gemfile CHANGED
@@ -1,9 +1,7 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gem 'acme-client', '~> 0.4.0'
4
- # SNI endpoints not supported yet:
5
- # <https://github.com/heroku/platform-api/issues/49>
6
- gem 'platform-api', github: 'jalada/platform-api', branch: 'master'
3
+ gem 'acme-client', '~> 2.0'
4
+ gem 'platform-api', '~> 2.2'
7
5
 
8
6
  group :development do
9
7
  gem "shoulda", ">= 0"
@@ -1,15 +1,7 @@
1
- GIT
2
- remote: git://github.com/jalada/platform-api.git
3
- revision: 45ddb3c1a7e2c7f85d979c0791db18e99affb237
4
- branch: master
5
- specs:
6
- platform-api (0.8.0)
7
- heroics (~> 0.0.17)
8
-
9
1
  GEM
10
2
  remote: https://rubygems.org/
11
3
  specs:
12
- acme-client (0.4.1)
4
+ acme-client (2.0.3)
13
5
  faraday (~> 0.9, >= 0.9.1)
14
6
  activesupport (5.0.0)
15
7
  concurrent-ruby (~> 1.0, >= 1.0.2)
@@ -23,7 +15,7 @@ GEM
23
15
  thread_safe (~> 0.3, >= 0.3.1)
24
16
  docile (1.1.5)
25
17
  erubis (2.7.0)
26
- excon (0.51.0)
18
+ excon (0.62.0)
27
19
  faraday (0.9.2)
28
20
  multipart-post (>= 1.2, < 3)
29
21
  git (1.3.0)
@@ -34,15 +26,14 @@ GEM
34
26
  hashie (>= 3.4)
35
27
  oauth2 (~> 1.0)
36
28
  hashie (3.4.6)
37
- heroics (0.0.17)
29
+ heroics (0.0.25)
38
30
  erubis (~> 2.0)
39
31
  excon
40
32
  moneta
41
33
  multi_json (>= 1.9.2)
42
- netrc
43
34
  highline (1.7.8)
44
35
  i18n (0.7.0)
45
- json (1.8.3)
36
+ json (1.8.6)
46
37
  juwelier (2.1.3)
47
38
  builder
48
39
  bundler (>= 1.13)
@@ -54,22 +45,24 @@ GEM
54
45
  rdoc
55
46
  semver
56
47
  jwt (1.5.6)
57
- mini_portile2 (2.1.0)
48
+ mini_portile2 (2.4.0)
58
49
  minitest (5.9.0)
59
- moneta (0.8.0)
60
- multi_json (1.12.1)
50
+ moneta (1.0.0)
51
+ multi_json (1.13.1)
61
52
  multi_xml (0.6.0)
62
53
  multipart-post (2.0.0)
63
- netrc (0.11.0)
64
- nokogiri (1.6.8.1)
65
- mini_portile2 (~> 2.1.0)
54
+ nokogiri (1.10.2)
55
+ mini_portile2 (~> 2.4.0)
66
56
  oauth2 (1.2.0)
67
57
  faraday (>= 0.8, < 0.10)
68
58
  jwt (~> 1.0)
69
59
  multi_json (~> 1.3)
70
60
  multi_xml (~> 0.5)
71
61
  rack (>= 1.2, < 3)
72
- rack (2.0.1)
62
+ platform-api (2.2.0)
63
+ heroics (~> 0.0.25)
64
+ moneta (~> 1.0.0)
65
+ rack (2.0.7)
73
66
  rake (12.0.0)
74
67
  rdoc (3.12.2)
75
68
  json (~> 1.4)
@@ -93,13 +86,13 @@ PLATFORMS
93
86
  ruby
94
87
 
95
88
  DEPENDENCIES
96
- acme-client (~> 0.4.0)
89
+ acme-client (~> 2.0)
97
90
  bundler (~> 1.0)
98
91
  juwelier (~> 2.1.0)
99
- platform-api!
92
+ platform-api (~> 2.2)
100
93
  rdoc (~> 3.12)
101
94
  shoulda
102
95
  simplecov
103
96
 
104
97
  BUNDLED WITH
105
- 1.13.6
98
+ 1.16.6
@@ -1,4 +1,4 @@
1
- Copyright (c) 2016 Pixie Labs
1
+ Copyright (c) 2020 Pixie Labs
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # LetsEncrypt & Rails & Heroku
2
2
 
3
+ ### Deprecated: Heroku now support [free automated SSL certificates for paid dynos](https://devcenter.heroku.com/articles/automated-certificate-management), you should use that instead of this gem unless your situation is covered by the [known limitations](https://devcenter.heroku.com/articles/automated-certificate-management#known-limitations) of ACM, e.g. your app runs in Heroku Private Spaces.
4
+
5
+
3
6
  [![Gem Version](https://badge.fury.io/rb/letsencrypt-rails-heroku.svg)](https://badge.fury.io/rb/letsencrypt-rails-heroku)
4
7
 
5
8
  This gem is a complete solution for securing your Ruby on Rails application
@@ -14,7 +17,9 @@ repository.
14
17
 
15
18
  ## Requirements
16
19
 
17
- - You must be using hobby or professional dynos to use free SNI-based SSL. Find out more on [Heroku's documentation page about SSL](https://devcenter.heroku.com/articles/ssl).
20
+ - You must be using hobby or professional dynos to use free SNI-based SSL.
21
+ Find out more on [Heroku's documentation page about
22
+ SSL](https://devcenter.heroku.com/articles/ssl).
18
23
 
19
24
  - You should have already configured your app DNS as per [Heroku's
20
25
  documentation](https://devcenter.heroku.com/articles/custom-domains).
@@ -24,10 +29,6 @@ repository.
24
29
  Add the gem to your Gemfile:
25
30
 
26
31
  ```
27
- # Until the new API calls are generally available, you must manually specify my fork
28
- # of the Heroku API gem:
29
- gem 'platform-api', git: 'https://github.com/jalada/platform-api', branch: 'master'
30
-
31
32
  gem 'letsencrypt-rails-heroku', group: 'production'
32
33
  ```
33
34
 
@@ -60,19 +61,27 @@ end
60
61
 
61
62
  ## Configuring
62
63
 
63
- By default the gem will try to use the following set of configuration variables,
64
- which you should set.
64
+ By default the gem will try to use the following set of configuration
65
+ variables. You must set:
66
+
67
+ * `ACME_EMAIL`: Your email address, should be valid.
68
+ * `ACME_TERMS_AGREED`: Existence of this environment variable represents your
69
+ agreement to [Let's Encrypt's terms of service](https://letsencrypt.org/repository/).
70
+ * `HEROKU_TOKEN`: An API token for this app. See below
71
+ * `HEROKU_APP`: Name of Heroku app e.g. bottomless-cavern-7173
72
+
73
+ You can also set:
65
74
 
66
75
  * `ACME_DOMAIN`: Comma separated list of domains for which you want
67
76
  certificates, e.g. `example.com,www.example.com`. Your Heroku app should be
68
- configured to answer to all these domains, because LetsEncrypt will make a
77
+ configured to answer to all these domains, because Let's Encrypt will make a
69
78
  request to verify ownership.
70
79
 
71
80
  If you leave this blank, the gem will try and use the Heroku API to get a
72
81
  list of configured domains for your app, and verify all of them.
73
- * `ACME_EMAIL`: Your email address, should be valid.
74
- * `HEROKU_TOKEN`: An API token for this app. See below
75
- * `HEROKU_APP`: Name of Heroku app e.g. bottomless-cavern-7173
82
+ * `SSL_TYPE`: Optional: One of `sni` or `endpoint`, defaults to `sni`.
83
+ `endpoint` requires your app to have an
84
+ [SSL endpoint addon](https://elements.heroku.com/addons/ssl) configured.
76
85
 
77
86
  The gem itself will temporarily create additional environment variables during
78
87
  the challenge / validation process:
@@ -80,6 +89,14 @@ the challenge / validation process:
80
89
  * `ACME_CHALLENGE_FILENAME`: The path of the file LetsEncrypt will request.
81
90
  * `ACME_CHALLENGE_FILE_CONTENT`: The content of that challenge file.
82
91
 
92
+ It will also create two permanent environment variables after the first run:
93
+
94
+ * `ACME_PRIVATE_KEY`: Private key used to create requests for certificates.
95
+ * `ACME_KEY_ID`: Key ID assigned to your private key by Let's Encrypt.
96
+
97
+ If you remove these, a new account will be created and new environment
98
+ variables will be set.
99
+
83
100
  ## Creating a Heroku token
84
101
 
85
102
  Use the `heroku-oauth` toolbelt plugin to generate an access token suitable
@@ -123,10 +140,13 @@ You can see these details by typing `heroku domains`.
123
140
 
124
141
  ## Adding a scheduled task
125
142
 
126
- You should add a scheduled task on Heroku to renew the certificate. The
127
- scheduled task should be configured to run `rake letsencrypt:renew` as often
128
- as you want to renew your certificate. Letsencrypt certificates are valid for
129
- 90 days, but there's no harm renewing them more frequently than that.
143
+ You should add a scheduled task on Heroku to renew the certificate. If you
144
+ are unfamiliar with how to do this, take a look at [Heroku's documentation
145
+ on their scheduler addon](https://devcenter.heroku.com/articles/scheduler).
146
+
147
+ The scheduled task should be configured to run `rake letsencrypt:renew` as
148
+ often as you want to renew your certificate. Letsencrypt certificates are valid
149
+ for 90 days, but there's no harm renewing them more frequently than that.
130
150
 
131
151
  Heroku Scheduler only lets you run a task as infrequently as once a day, but
132
152
  you don't want to renew your SSL certificate every day (you will hit
@@ -135,7 +155,7 @@ run less frequently using a shell control statement. For example to renew your
135
155
  certificate on the 1st day of every month:
136
156
 
137
157
  ```
138
- if [ "$(date +%d)" = 01 ]; then rake letsencrypt:renew; fi
158
+ if [ "$(date +%d)" = 01 ]; then bundle exec rake letsencrypt:renew; fi
139
159
  ```
140
160
 
141
161
  Source: [blog.dbrgn.ch](https://blog.dbrgn.ch/2013/10/4/heroku-schedule-weekly-monthly-tasks/)
@@ -145,12 +165,13 @@ Source: [blog.dbrgn.ch](https://blog.dbrgn.ch/2013/10/4/heroku-schedule-weekly-m
145
165
  Suggestions and pull requests are welcome in improving the situation with the
146
166
  following security considerations:
147
167
 
148
- - When configuring this gem you must add a non-expiring Heroku API token
149
- into your application environment. Your collaborators could use this
150
- token to impersonate the account it was created with when accessing
151
- the Heroku API. This is important if your account has access to other apps
152
- that your collaborators don’t. Additionally, if your application environment was
153
- leaked this would give the attacker access to the Heroku API as your user account.
168
+ - When configuring this gem you must add a non-expiring Heroku API token into
169
+ your application environment. Your collaborators could use this token to
170
+ impersonate the account it was created with when accessing the Heroku API.
171
+ This is important if your account has access to other apps that your
172
+ collaborators don’t. Additionally, if your application environment was
173
+ leaked this would give the attacker access to the Heroku API as your user
174
+ account.
154
175
  [More information about Heroku’s API and oAuth](https://devcenter.heroku.com/articles/oauth#direct-authorization).
155
176
 
156
177
  You should create the API token from a suitably locked-down account.
@@ -167,21 +188,19 @@ following security considerations:
167
188
 
168
189
  ### Common name invalid errors (security certificate is from *.herokuapp.com)
169
190
 
170
- Your domain is still configured as a CNAME or ALIAS to `your-app.herokuapp.com`. Check the output of `heroku domains` matches your DNS configuration. When you add an SNI cert to an app for the first time [the DNS target changes](https://devcenter.heroku.com/articles/custom-domains#view-existing-domains).
191
+ Your domain is still configured as a CNAME or ALIAS to
192
+ `your-app.herokuapp.com`. Check the output of `heroku domains` matches your DNS
193
+ configuration. When you add an SNI cert to an app for the first time
194
+ [the DNS target changes](https://devcenter.heroku.com/articles/custom-domains#view-existing-domains).
171
195
 
172
196
  ## To-do list
173
197
 
174
198
  - Persist account key, or at least give the option of using an existing one, so
175
199
  we don’t register with LetsEncrypt over and over.
176
200
 
177
- - Stop using a fork of the `platform-api` gem once it supports the SNI endpoint
178
- API calls. [See issue #49 of the platform-api gem](https://github.com/heroku/platform-api/issues/49).
179
-
180
- - Provide instructions for running the gem decoupled from the app it is
201
+ - Provide instructions for running the gem decoupled from the app it is
181
202
  securing, for the paranoid.
182
203
 
183
- - Support non-SNI Heroku SSL too.
184
-
185
204
  ## Contributing
186
205
 
187
206
  - Check out the latest master to make sure the feature hasn't been implemented
@@ -201,4 +220,5 @@ Your domain is still configured as a CNAME or ALIAS to `your-app.herokuapp.com`.
201
220
 
202
221
  1. Bump the version: `rake version:bump:{major,minor,patch}`.
203
222
  2. Update `CHANGELOG.md` & commit.
204
- 3. Use `rake release` to regenerate gemspec, push a tag to git, and push a new `.gem` to rubygems.org
223
+ 3. Use `rake release` to regenerate gemspec, push a tag to git, and push a new
224
+ `.gem` to rubygems.org.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.3
1
+ 2.0.2
@@ -2,19 +2,20 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: letsencrypt-rails-heroku 1.1.3 ruby lib
5
+ # stub: letsencrypt-rails-heroku 2.0.2 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
- s.name = "letsencrypt-rails-heroku"
9
- s.version = "1.1.3"
8
+ s.name = "letsencrypt-rails-heroku".freeze
9
+ s.version = "2.0.2"
10
10
 
11
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
- s.require_paths = ["lib"]
13
- s.authors = ["Pixie Labs", "David Somers", "Abigail McPhillips"]
14
- s.date = "2017-02-28"
15
- s.description = "This gem automatically handles creation, renewal, and applying SSL certificates from LetsEncrypt to your Heroku account."
16
- s.email = "team@pixielabs.io"
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib".freeze]
13
+ s.authors = ["Pixie Labs".freeze, "David Somers".freeze, "Abigail McPhillips".freeze]
14
+ s.date = "2020-07-24"
15
+ s.description = "This gem automatically handles creation, renewal, and applying SSL certificates from LetsEncrypt to your Heroku account.".freeze
16
+ s.email = "team@pixielabs.io".freeze
17
17
  s.extra_rdoc_files = [
18
+ "CHANGELOG.md",
18
19
  "LICENSE.txt",
19
20
  "README.md"
20
21
  ]
@@ -35,39 +36,39 @@ Gem::Specification.new do |s|
35
36
  "lib/letsencrypt-rails-heroku/railtie.rb",
36
37
  "lib/tasks/letsencrypt.rake"
37
38
  ]
38
- s.homepage = "https://github.com/pixielabs/letsencrypt-rails-heroku"
39
- s.licenses = ["MIT"]
40
- s.rubygems_version = "2.5.1"
41
- s.summary = "Automatic LetsEncrypt certificates in your Rails app on Heroku"
39
+ s.homepage = "https://github.com/pixielabs/letsencrypt-rails-heroku".freeze
40
+ s.licenses = ["MIT".freeze]
41
+ s.rubygems_version = "3.0.8".freeze
42
+ s.summary = "Automatic LetsEncrypt certificates in your Rails app on Heroku".freeze
42
43
 
43
44
  if s.respond_to? :specification_version then
44
45
  s.specification_version = 4
45
46
 
46
47
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
- s.add_runtime_dependency(%q<acme-client>, ["~> 0.4.0"])
48
- s.add_runtime_dependency(%q<platform-api>, [">= 0"])
49
- s.add_development_dependency(%q<shoulda>, [">= 0"])
50
- s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
51
- s.add_development_dependency(%q<bundler>, ["~> 1.0"])
52
- s.add_development_dependency(%q<juwelier>, ["~> 2.1.0"])
53
- s.add_development_dependency(%q<simplecov>, [">= 0"])
48
+ s.add_runtime_dependency(%q<acme-client>.freeze, ["~> 2.0"])
49
+ s.add_runtime_dependency(%q<platform-api>.freeze, ["~> 2.2"])
50
+ s.add_development_dependency(%q<shoulda>.freeze, [">= 0"])
51
+ s.add_development_dependency(%q<rdoc>.freeze, ["~> 3.12"])
52
+ s.add_development_dependency(%q<bundler>.freeze, ["~> 1.0"])
53
+ s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
54
+ s.add_development_dependency(%q<simplecov>.freeze, [">= 0"])
54
55
  else
55
- s.add_dependency(%q<acme-client>, ["~> 0.4.0"])
56
- s.add_dependency(%q<platform-api>, [">= 0"])
57
- s.add_dependency(%q<shoulda>, [">= 0"])
58
- s.add_dependency(%q<rdoc>, ["~> 3.12"])
59
- s.add_dependency(%q<bundler>, ["~> 1.0"])
60
- s.add_dependency(%q<juwelier>, ["~> 2.1.0"])
61
- s.add_dependency(%q<simplecov>, [">= 0"])
56
+ s.add_dependency(%q<acme-client>.freeze, ["~> 2.0"])
57
+ s.add_dependency(%q<platform-api>.freeze, ["~> 2.2"])
58
+ s.add_dependency(%q<shoulda>.freeze, [">= 0"])
59
+ s.add_dependency(%q<rdoc>.freeze, ["~> 3.12"])
60
+ s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
61
+ s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
62
+ s.add_dependency(%q<simplecov>.freeze, [">= 0"])
62
63
  end
63
64
  else
64
- s.add_dependency(%q<acme-client>, ["~> 0.4.0"])
65
- s.add_dependency(%q<platform-api>, [">= 0"])
66
- s.add_dependency(%q<shoulda>, [">= 0"])
67
- s.add_dependency(%q<rdoc>, ["~> 3.12"])
68
- s.add_dependency(%q<bundler>, ["~> 1.0"])
69
- s.add_dependency(%q<juwelier>, ["~> 2.1.0"])
70
- s.add_dependency(%q<simplecov>, [">= 0"])
65
+ s.add_dependency(%q<acme-client>.freeze, ["~> 2.0"])
66
+ s.add_dependency(%q<platform-api>.freeze, ["~> 2.2"])
67
+ s.add_dependency(%q<shoulda>.freeze, [">= 0"])
68
+ s.add_dependency(%q<rdoc>.freeze, ["~> 3.12"])
69
+ s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
70
+ s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
71
+ s.add_dependency(%q<simplecov>.freeze, [">= 0"])
71
72
  end
72
73
  end
73
74
 
@@ -1,12 +1,16 @@
1
1
  module Letsencrypt
2
2
  module Error
3
- # Exception raised when LetsEncrypt encounters an issue verifying the challenge.
3
+ # LetsEncrypt encountered an issue verifying the challenge.
4
4
  class VerificationError < StandardError; end
5
- # Exception raised when challenge URL is not available.
5
+ # LetsEncrypt encountered an issue finalizing the order.
6
+ class FinalizationError < StandardError; end
7
+ # Challenge URL is not available.
6
8
  class ChallengeUrlError < StandardError; end
7
- # Exception raised on timeout of challenge verification.
9
+ # Domain verification took longer than we'd like.
8
10
  class VerificationTimeoutError < StandardError; end
9
- # Exception raised when an error occurs adding the certificate to Heroku.
11
+ # Order finalization took longer than we'd like.
12
+ class FinalizationTimeoutError < StandardError; end
13
+ # Error adding the certificate to Heroku.
10
14
  class HerokuCertificateError < StandardError; end
11
15
  end
12
16
  end
@@ -14,24 +14,36 @@ module Letsencrypt
14
14
  configuration.acme_challenge_file_content
15
15
  end
16
16
 
17
+ def self.registered?
18
+ configuration.acme_private_key && configuration.acme_key_id
19
+ end
20
+
17
21
  class Configuration
18
- attr_accessor :heroku_token, :heroku_app, :acme_email, :acme_domain, :acme_endpoint
22
+ attr_accessor :heroku_token, :heroku_app, :acme_email, :acme_domain,
23
+ :acme_directory, :ssl_type, :acme_terms_agreed
19
24
 
20
25
  # Not settable by user; part of the gem's behaviour.
21
- attr_reader :acme_challenge_filename, :acme_challenge_file_content
26
+ attr_reader :acme_challenge_filename, :acme_challenge_file_content,
27
+ :acme_private_key, :acme_key_id
22
28
 
23
29
  def initialize
24
30
  @heroku_token = ENV["HEROKU_TOKEN"]
25
31
  @heroku_app = ENV["HEROKU_APP"]
26
32
  @acme_email = ENV["ACME_EMAIL"]
27
33
  @acme_domain = ENV["ACME_DOMAIN"]
28
- @acme_endpoint = ENV["ACME_ENDPOINT"] || 'https://acme-v01.api.letsencrypt.org/'
34
+ @acme_directory = 'https://acme-v02.api.letsencrypt.org/directory'
35
+ @acme_terms_agreed = ENV["ACME_TERMS_AGREED"]
36
+ @ssl_type = ENV["SSL_TYPE"] || 'sni'
37
+
29
38
  @acme_challenge_filename = ENV["ACME_CHALLENGE_FILENAME"]
30
39
  @acme_challenge_file_content = ENV["ACME_CHALLENGE_FILE_CONTENT"]
40
+
41
+ @acme_private_key = ENV["ACME_PRIVATE_KEY"]
42
+ @acme_key_id = ENV["ACME_KEY_ID"]
31
43
  end
32
44
 
33
45
  def valid?
34
- heroku_token && heroku_app && acme_email
46
+ heroku_token && heroku_app && acme_email && acme_terms_agreed
35
47
  end
36
48
  end
37
49
  end
@@ -8,24 +8,48 @@ namespace :letsencrypt do
8
8
  desc 'Renew your LetsEncrypt certificate'
9
9
  task :renew do
10
10
  # Check configuration looks OK
11
- abort "letsencrypt-rails-heroku is configured incorrectly. Are you missing an environment variable or other configuration? You should have a heroku_token, heroku_app and acme_email configured either via a `Letsencrypt.configure` block in an initializer or as environment variables." unless Letsencrypt.configuration.valid?
11
+ abort "letsencrypt-rails-heroku is configured incorrectly. Are you missing an environment variable or other configuration? You should have heroku_token, heroku_app, acme_email and acme_terms_agreed configured either via a `Letsencrypt.configure` block in an initializer or as environment variables." unless Letsencrypt.configuration.valid?
12
12
 
13
13
  # Set up Heroku client
14
14
  heroku = PlatformAPI.connect_oauth Letsencrypt.configuration.heroku_token
15
15
  heroku_app = Letsencrypt.configuration.heroku_app
16
16
 
17
- # Create a private key
18
- print "Creating account key..."
19
- private_key = OpenSSL::PKey::RSA.new(4096)
20
- puts "Done!"
17
+ if Letsencrypt.registered?
18
+ puts "Using existing registration details"
19
+ private_key = OpenSSL::PKey::RSA.new(Letsencrypt.configuration.acme_private_key)
20
+ key_id = Letsencrypt.configuration.acme_key_id
21
+ else
22
+ # Create a private key
23
+ print "Creating account key..."
24
+ private_key = OpenSSL::PKey::RSA.new(4096)
25
+ puts "Done!"
21
26
 
22
- client = Acme::Client.new(private_key: private_key, endpoint: Letsencrypt.configuration.acme_endpoint, connection_options: { request: { open_timeout: 5, timeout: 5 } })
27
+ client = Acme::Client.new(private_key: private_key,
28
+ directory: Letsencrypt.configuration.acme_directory,
29
+ connection_options: {
30
+ request: {
31
+ open_timeout: 5,
32
+ timeout: 5
33
+ }
34
+ })
23
35
 
24
- print "Registering with LetsEncrypt..."
25
- registration = client.register(contact: "mailto:#{Letsencrypt.configuration.acme_email}")
36
+ print "Registering with LetsEncrypt..."
37
+ account = client.new_account(contact: "mailto:#{Letsencrypt.configuration.acme_email}",
38
+ terms_of_service_agreed: true)
26
39
 
27
- registration.agree_terms
28
- puts "Done!"
40
+ key_id = account.kid
41
+ puts "Done!"
42
+ print "Saving account details as configuration variables..."
43
+ heroku.config_var.update(heroku_app,
44
+ 'ACME_PRIVATE_KEY' => private_key.to_pem,
45
+ 'ACME_KEY_ID' => account.kid)
46
+ puts "Done!"
47
+ end
48
+
49
+ # Make a new Acme::Client with whichever private_key & key_id we ended up with.
50
+ client = Acme::Client.new(private_key: private_key,
51
+ directory: Letsencrypt.configuration.acme_directory,
52
+ kid: key_id)
29
53
 
30
54
  domains = []
31
55
  if Letsencrypt.configuration.acme_domain
@@ -36,11 +60,12 @@ namespace :letsencrypt do
36
60
  puts "Using #{domains.length} configured Heroku domain(s) for this app..."
37
61
  end
38
62
 
39
- domains.each do |domain|
40
- puts "Performing verification for #{domain}:"
63
+ order = client.new_order(identifiers: domains)
41
64
 
42
- authorization = client.authorize(domain: domain)
43
- challenge = authorization.http01
65
+ order.authorizations.each do |authorization|
66
+ puts "Performing verification for #{authorization.domain}:"
67
+
68
+ challenge = authorization.http
44
69
 
45
70
  print "Setting config vars on Heroku..."
46
71
  heroku.config_var.update(heroku_app, {
@@ -62,7 +87,7 @@ namespace :letsencrypt do
62
87
 
63
88
  begin
64
89
  open("http://#{hostname}/#{challenge.filename}").read
65
- rescue OpenURI::HTTPError, RuntimeError => e
90
+ rescue OpenSSL::SSL::SSLError, OpenURI::HTTPError, RuntimeError => e
66
91
  raise e if e.is_a?(RuntimeError) && !e.message.include?("redirection forbidden")
67
92
  if Time.now - start_time <= 60
68
93
  puts "Error fetching challenge, retrying... #{e.message}"
@@ -78,24 +103,23 @@ namespace :letsencrypt do
78
103
 
79
104
  print "Giving LetsEncrypt some time to verify..."
80
105
  # Once you are ready to serve the confirmation request you can proceed.
81
- challenge.request_verification # => true
82
- challenge.verify_status # => 'pending'
106
+ challenge.request_validation
83
107
 
84
108
  start_time = Time.now
85
-
86
- while challenge.verify_status == 'pending'
109
+ while challenge.status == 'pending'
87
110
  if Time.now - start_time >= 30
88
111
  failure_message = "Failed - timed out waiting for challenge verification."
89
112
  raise Letsencrypt::Error::VerificationTimeoutError, failure_message
90
113
  end
91
- sleep(3)
114
+ sleep(2)
115
+ challenge.reload
92
116
  end
93
117
 
94
118
  puts "Done!"
95
119
 
96
- unless challenge.verify_status == 'valid'
120
+ unless challenge.status == 'valid'
97
121
  puts "Problem verifying challenge."
98
- failure_message = "Status: #{challenge.verify_status}, Error: #{challenge.error}"
122
+ failure_message = "Status: #{challenge.status}, Error: #{challenge.error}"
99
123
  raise Letsencrypt::Error::VerificationError, failure_message
100
124
  end
101
125
 
@@ -110,30 +134,61 @@ namespace :letsencrypt do
110
134
  })
111
135
 
112
136
  # Create CSR
113
- csr = Acme::Client::CertificateRequest.new(names: domains)
137
+ csr_private_key = OpenSSL::PKey::RSA.new 4096
138
+ csr = Acme::Client::CertificateRequest.new(names: domains,
139
+ private_key: csr_private_key)
114
140
 
141
+ print "Asking LetsEncrypt to finalize our certificate order..."
115
142
  # Get certificate
116
- certificate = client.new_certificate(csr) # => #<Acme::Client::Certificate ....>
143
+ order.finalize(csr: csr)
144
+
145
+ # Wait for order to process
146
+ start_time = Time.now
147
+ while order.status == 'processing'
148
+ if Time.now - start_time >= 30
149
+ failure_message = "Failed - timed out waiting for order finalization"
150
+ raise Letsencrypt::Error::FinalizationTimeoutError, failure_message
151
+ end
152
+ sleep(2)
153
+ order.reload
154
+ end
155
+
156
+ puts "Done!"
157
+
158
+ unless order.status == 'valid'
159
+ failure_message = "Problem finalizing order - status: #{order.status}"
160
+ raise Letsencrypt::Error::FinalizationError, failure_message
161
+ end
162
+
163
+ certificate = order.certificate # => PEM-formatted certificate
117
164
 
118
165
  # Send certificates to Heroku via API
119
166
 
120
- # First check for existing certificates:
121
- certificates = heroku.sni_endpoint.list(heroku_app)
167
+ endpoint = case Letsencrypt.configuration.ssl_type
168
+ when 'sni'
169
+ heroku.sni_endpoint
170
+ when 'endpoint'
171
+ heroku.ssl_endpoint
172
+ end
173
+
174
+ certificate_info = {
175
+ certificate_chain: certificate,
176
+ private_key: csr_private_key.to_pem
177
+ }
178
+
179
+ # Fetch existing certificate from Heroku (if any). We just use the first
180
+ # one; if someone has more than one, they're probably not actually using
181
+ # this gem. Could also be an error?
182
+ existing_certificate = endpoint.list(heroku_app)[0]
122
183
 
123
184
  begin
124
- if certificates.any?
125
- print "Updating existing certificate #{certificates[0]['name']}..."
126
- heroku.sni_endpoint.update(heroku_app, certificates[0]['name'], {
127
- certificate_chain: certificate.fullchain_to_pem,
128
- private_key: certificate.request.private_key.to_pem
129
- })
185
+ if existing_certificate
186
+ print "Updating existing certificate #{existing_certificate['name']}..."
187
+ endpoint.update(heroku_app, existing_certificate['name'], certificate_info)
130
188
  puts "Done!"
131
189
  else
132
190
  print "Adding new certificate..."
133
- heroku.sni_endpoint.create(heroku_app, {
134
- certificate_chain: certificate.fullchain_to_pem,
135
- private_key: certificate.request.private_key.to_pem
136
- })
191
+ endpoint.create(heroku_app, certificate_info)
137
192
  puts "Done!"
138
193
  end
139
194
  rescue Excon::Error::UnprocessableEntity => e
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: letsencrypt-rails-heroku
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pixie Labs
8
8
  - David Somers
9
9
  - Abigail McPhillips
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2017-02-28 00:00:00.000000000 Z
13
+ date: 2020-07-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: acme-client
@@ -18,28 +18,28 @@ dependencies:
18
18
  requirements:
19
19
  - - "~>"
20
20
  - !ruby/object:Gem::Version
21
- version: 0.4.0
21
+ version: '2.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - "~>"
27
27
  - !ruby/object:Gem::Version
28
- version: 0.4.0
28
+ version: '2.0'
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: platform-api
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
- - - ">="
33
+ - - "~>"
34
34
  - !ruby/object:Gem::Version
35
- version: '0'
35
+ version: '2.2'
36
36
  type: :runtime
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
- - - ">="
40
+ - - "~>"
41
41
  - !ruby/object:Gem::Version
42
- version: '0'
42
+ version: '2.2'
43
43
  - !ruby/object:Gem::Dependency
44
44
  name: shoulda
45
45
  requirement: !ruby/object:Gem::Requirement
@@ -116,6 +116,7 @@ email: team@pixielabs.io
116
116
  executables: []
117
117
  extensions: []
118
118
  extra_rdoc_files:
119
+ - CHANGELOG.md
119
120
  - LICENSE.txt
120
121
  - README.md
121
122
  files:
@@ -138,7 +139,7 @@ homepage: https://github.com/pixielabs/letsencrypt-rails-heroku
138
139
  licenses:
139
140
  - MIT
140
141
  metadata: {}
141
- post_install_message:
142
+ post_install_message:
142
143
  rdoc_options: []
143
144
  require_paths:
144
145
  - lib
@@ -153,9 +154,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
154
  - !ruby/object:Gem::Version
154
155
  version: '0'
155
156
  requirements: []
156
- rubyforge_project:
157
- rubygems_version: 2.5.1
158
- signing_key:
157
+ rubygems_version: 3.0.8
158
+ signing_key:
159
159
  specification_version: 4
160
160
  summary: Automatic LetsEncrypt certificates in your Rails app on Heroku
161
161
  test_files: []