unixcharles / acme-client Goto Github PK
View Code? Open in Web Editor NEWA Ruby client for the letsencrypt's ACME protocol.
License: MIT License
A Ruby client for the letsencrypt's ACME protocol.
License: MIT License
I've just noticed master contains some changes not available in the latest version on RubyGems (0.4.1).
Ironically, I needed a way to fetch an Authorization and I started working on a patch, when I realized that the method was there in master (but not in the gem I had installed) as it was merged in #99.
Any chance to get a new version released?
I tried running the stuff in the readme, but when I try to make Acme::Client, I get an error because directory isn't a keyword arg. (There is a directory_uri arg though, so I assume it got renamed) However when I run the following from the readme, I get:
client = Acme::Client.new(private_key: private_key, endpoint: endpoint, directory_uri: './certs')
NoMethodError: undefined method `gsub' for nil:NilClass
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/bundler/gems/acme-client-d08c04140f3a/lib/acme/faraday_middleware.rb:39:in `on_complete'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/bundler/gems/acme-client-d08c04140f3a/lib/acme/faraday_middleware.rb:12:in `block in call'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/faraday-0.9.2/lib/faraday/response.rb:57:in `on_complete'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/bundler/gems/acme-client-d08c04140f3a/lib/acme/faraday_middleware.rb:12:in `call'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/faraday-0.9.2/lib/faraday/rack_builder.rb:139:in `build_response'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/faraday-0.9.2/lib/faraday/connection.rb:377:in `run_request'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/faraday-0.9.2/lib/faraday/connection.rb:140:in `get'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/bundler/gems/acme-client-d08c04140f3a/lib/acme/client.rb:59:in `load_directory!'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/bundler/gems/acme-client-d08c04140f3a/lib/acme/client.rb:13:in `initialize'
from (irb):14:in `new'
from (irb):14
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/cli/console.rb:14:in `run'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/cli.rb:308:in `console'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/vendor/thor/lib/thor/invocation.rb:126:in `invoke_command'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/vendor/thor/lib/thor.rb:359:in `dispatch'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/vendor/thor/lib/thor/base.rb:440:in `start'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/cli.rb:10:in `start'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/bin/bundle:20:in `block in <top (required)>'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/friendly_errors.rb:7:in `with_friendly_errors'
from /Users/ryanstout/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/bin/bundle:18:in `<top (required)>'
from /Users/ryanstout/.rbenv/versions/2.2.0/bin/bundle:23:in `load'
from /Users/ryanstout/.rbenv/versions/2.2.0/bin/bundle:23:in `<main>'irb
Thanks!
According to the readme, I should be checking challenge.authorization.verify_status
or authorization.verify_status
of which both are undefined. Has the library changed so that I can just refer to challenge.verify_status
now?
Is there any documentation explaining how to renew a certificate that is about to expire?
In the readme you can find example:
# 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')))
but the method challenge_from_hash
doesn't exist. I found it was removed from code base:
3d41fa2...9a80e93
This is the diff between version 0.3.1 and 0.5.0.
Is there a reason why it was removed? What is recommended way to create challenge from file then?
Ubuntu 14.04 doesn't include support for Ruby 2.1. I know I could installed it through a PPA or rvm, but it would prefer not to. Is it possible to add support for Ruby 1.9?
Thanks!
Let's Encrypt comments in a ticket (letsencrypt/boulder#1902 (comment)) that they are looking at logs to see how people use them and to identify clients when they might break something.
Right now, it looks like Acme::Client uses the default Faraday header, which probably isn't especially helpful. Could we give them a hand and send an application specific header?
Thanks!
Boulder doesn't send json/application
format for /directory
, as a result the parsing not done in the faraday middleware.
Any thoughts about this?
*** Acme::Client::Error::Malformed Exception: Error parsing certificate request. Extensions in the CSR marked critical can cause this error: https://github.com/letsencrypt/boulder/issues/565
It occurs when I call @client.new_certificate(csr)
.
My certificate has an extension but it's not marked critical.
I set endpoint = 'https://acme-staging.api.letsencrypt.org/'
Is it a problem in my key? Or is it a bug generating the certificate request?
Thank you
Follow up to #84, this also needs to be done when using OpenSSL to generate self-signed certificates for tls-sni
type challenges.
I'm not yet sure if this is a bug related to my branch/merging or a documentation error.
Following the directions on the README:
csr = Acme::Client::CertificateRequest.new names: ["kyledrake.net", "www.kyledrake.net"]
=> #<Acme::Client::CertificateRequest:0xOBSCURED
@common_name="kyledrake.net",
@digest=#<OpenSSL::Digest::SHA256: OBSCURED>,
@names=["kyledrake.net", "www.kyledrake.net"],
@private_key=#<OpenSSL::PKey::RSA:0xOBSCURED>,
@subject={"CN"=>"kyledrake.net"}>
certificate = letsencrypt.new_certificate csr
Acme::Client::Error::Unauthorized: Error creating new cert :: Authorizations for these names not found or expired: www.kyledrake.net
from OBSCURED/ruby/2.3.0/acme-client-d0ced992bfe4/lib/acme/client/faraday_middleware.rb:52:in `on_complete'
If I remove the subdomain www.kyledrake.net
, it works fine. Is there an extra step for adding a subdomain that isn't demonstrated on the README (for example, a need to also verify the www.*
)? The code I've written for this so far is here: https://github.com/neocities/neocities/blob/master/workers/lets_encrypt_worker.rb#L22
Apologies in advance if this is a bad issue filing.
https://letsencrypt.org/2017/06/14/acme-v2-api.html coming in January 2018 with no plans for now to sunset v1.
From HN: https://github.com/letsencrypt/pebble should aid in development and testing
From the example code on the front page I keep getting 'invalid'.
Is there a way to query why?
xn-----xlcrekfobi.xn--p1ai - domain (Russian Cyrilic) not working
Name does not end in a public suffix
I am trying to register with an ECDSA key.
I modified Acme::Client::Crypto#initialize
to avoid problems encountered in #111:
def initialize(private_key)
if private_key.is_a?(OpenSSL::PKey::EC) && RbConfig::CONFIG['MAJOR'] == '2' &&
RbConfig::CONFIG['MINOR'].to_i < 4
@private_key = Acme::Client::CertificateRequest::ECKeyPatch.new(private_key)
else
@private_key = private_key
end
end
In my code, when calling client.register
, I have this exception:
Acme::Client::Error::Malformed: JWS verification error
# ./lib/acme/client/faraday_middleware.rb:43:in `raise_on_error!'
# ./lib/acme/client/faraday_middleware.rb:33:in `on_complete'
# ./lib/acme/client/faraday_middleware.rb:18:in `block in call'
# ./vendor/bundle/ruby/2.3.0/gems/faraday-0.10.0/lib/faraday/response.rb:61:in `on_complete'
# ./lib/acme/client/faraday_middleware.rb:18:in `call'
# ./vendor/bundle/ruby/2.3.0/gems/faraday-0.10.0/lib/faraday/rack_builder.rb:139:in `build_response'
# ./vendor/bundle/ruby/2.3.0/gems/faraday-0.10.0/lib/faraday/connection.rb:377:in `run_request'
# ./vendor/bundle/ruby/2.3.0/gems/faraday-0.10.0/lib/faraday/connection.rb:177:in `post'
# ./lib/acme/client.rb:45:in `register'
I may provide a PR with updated Crypto code and a spec on Acme::Client#register, if you need it.
DNS01 authorisation will never succeed if the record_name
parameter is used, since subdomain needs to be specified like:
_acme-challenge.www
for www.example.com
, however, the client suggests:
_acme-challenge
In your readme, the registration uses a "mailto:" in front of the email address when registering a new client. Is this required for emails or just a type-o?
So there's one bigger issue we maybe should discuss while we're still as early as we are currently.
The gem's name is acme-client
, per http://guides.rubygems.org/name-your-gem/ and community conventions this gives us the namespace Acme::Client
.
That means we do not own the namespace Acme
. In fact other gems are already using it too, although for nothing serious as far as I can tell. However I can easily imagine other serious gems popping up in this namespace. We shouldn't use any constant below Acme
except for Acme::Client
.
There are four ways forward:
Acme
besides Client
are acceptable use since generally useful to projects implementing the ACME protocol.Acme::Client
. Currently I would only consider the rename of Acme::CertificateRequest
to Acme::Client::CertificateRequest
a breaking change of public API.Acme
namespace, for example acme_client
(AcmeClient
, AcmeClient::CertificateRequest
). This would involve deprecating the currently published gem and publishing a new one.Acme
gem, effectively deprecating the current acme
and replacing it with a new one. Then we could decide how to reorganize this gem, for example moving parts of it into a acme-utils
gem or the acme
gem itself. (I think this is the least favourable alternative at the moment, so sorry for the ping in advance @smathy).Thoughts?
Will multiple domains be supported? Currently, it only authorizes example.com and www.example.com
I get the following when I put the acme-client in my Gemfile and run bundle console
....acme-client-d08c04140f3a/lib/acme/client.rb:1:in
<top (required)>': uninitialized constant Acme (NameError)`
Due to the way error_name
works, two word errors won't be instantiated when talking to ACME servers conforming to the spec.
error names look like: badNonce
, but error_name
in lib/acme/client/faraday_middleware.rb
uses capitalize
, which capitalizes the first character of a string and lowercase the rest - resulting in error_class
looking for Badnonce
, not the correct BadNonce
I can submit a PR with a fix for that, but I don't have a good idea for how to test for it with your test setup.
Hello, I am running an embedded version of Ruby that is still below 2.2.2. I saw the patch 0e54673, but even acme-client 0.3.6 complains that "activesupport requires Ruby version >= 2.2.2".
Now when seeing that patch I wonder if it shouldn't be greater-equal instead of less-equal?
Thanks!
Hi,
I've found in my environment, when run as:
bundle exec rake spec
Nearly every test fails. I've tracked it to an issue I can replicate in irb:
2.2.0 :001 > private_key = OpenSSL::PKey::RSA.new(2048)
=> #<OpenSSL::PKey::RSA:0x007faf5aed1fa0>
2.2.0 :002 > endpoint = 'https://www.letsencrypt-demo.org/acme'
=> "https://www.letsencrypt-demo.org/acme"
2.2.0 :003 > client = Acme::Client.new(private_key: private_key, endpoint: endpoint)
NameError: uninitialized constant Acme::Client::DIRECTORY_DEFAULT
I don't know where this constant is supposed to come from, but it only appears once in the codebase, the place it's used:
$ grep -r DIRECTORY_DEFAULT *
lib/acme/client.rb: DIRECTORY_DEFAULT
I'd be happy to try and create a default and submit a PR - but you have a lot of tests and it looks like you obviously had this defined in your environment. Is there a file that didn't get committed properly?
I've also noted a failure if I try to assign a directory uri. I'm unsure if it's related:
client = Acme::Client.new(private_key: private_key, endpoint: endpoint, directory_uri: "https://www.letsencrypt-demo.org/directory")
NoMethodError: undefined method `fetch' for #<String:0x007f7520bbe2b0>
from lib/acme/client.rb:56:in `load_directory!'
Hi. I believe this may be a problem with the keyAuthorization property. Here is a description of what I'm doing.
I am working on a cert ordering system that handles things asynchronously. The process is started with:
authorization = client.authorize(domain: 'some.domain.com')
challenge = authorization.http01
order.challenge = challenge.to_h # stored as jsonb in postgres
I also save the DV file info (path and file contents). The next step is that I have another system upload the DV file to the site. This all works fine. The problem is at the third step. I verify that the DV file was uploaded correctly (it was) and then I retrieve the challenge info from the database.
challenge = client.challenge_from_hash(order.challenge)
challenge.request_verficiation
It is at this point that the error is raised. Is there something other than the data retrieved in challenge.to_h
that I should be saving? The boulder source leads me to believe the problem is with the keyAuthorization which I'm not sure makes sense.
Any thoughts would be greatly appreciated.
I'm basically running through readme and I'm not sure why this is happening. (using any other email address doesn't help)
require 'openssl'
private_key = OpenSSL::PKey::RSA.new(4096)
endpoint = "https://acme-staging.api.letsencrypt.org/"
require 'acme-client'
client = Acme::Client.new(private_key: private_key, endpoint: endpoint)
registration = client.register(contact: 'mailto:[email protected]')
Acme::Client::Error::Malformed: Parse error reading JWS
from /usr/lib64/ruby/gems/2.2.0/gems/acme-client-0.3.1/lib/acme/client/faraday_middleware.rb:35:in `raise_on_error!'
from /usr/lib64/ruby/gems/2.2.0/gems/acme-client-0.3.1/lib/acme/client/faraday_middleware.rb:25:in `on_complete'
from /usr/lib64/ruby/gems/2.2.0/gems/acme-client-0.3.1/lib/acme/client/faraday_middleware.rb:12:in `block in call'
from /usr/lib64/ruby/gems/2.2.0/gems/faraday-0.9.2/lib/faraday/response.rb:57:in `on_complete'
from /usr/lib64/ruby/gems/2.2.0/gems/acme-client-0.3.1/lib/acme/client/faraday_middleware.rb:12:in `call'
from /usr/lib64/ruby/gems/2.2.0/gems/faraday-0.9.2/lib/faraday/rack_builder.rb:139:in `build_response'
from /usr/lib64/ruby/gems/2.2.0/gems/faraday-0.9.2/lib/faraday/connection.rb:377:in `run_request'
from /usr/lib64/ruby/gems/2.2.0/gems/faraday-0.9.2/lib/faraday/connection.rb:177:in `post'
from /usr/lib64/ruby/gems/2.2.0/gems/acme-client-0.3.1/lib/acme/client.rb:25:in `register'
from (irb):9
from /usr/bin/irb:11:in `<main>'
Thanks for developing this client - very useful!
I'm having a hard time updating my LE certificates due to verification issues. I'm currently using the rack-host-redirect
gem to strip www
from all requests:
use Rack::HostRedirect, {
'www.example.com' => 'example.com'
}
When LE verification (via the http-01 method) attempts to verify the response at www.example.com/.well-known/acme-challenge/*
(where *
is the challenge string), verification fails because of the 301 redirect, even though the server provides the correct response after the redirect. (Verification passes on the non-www root domain.)
Does the spec allow for following these sorts of 301 redirects during verification before parsing the response, like it allows for 301 redirects from http://
to https://
? It would make adding and updating LE certificates much easier, since www
redirection is extremely common.
Not sure if this is an acme-client
thing or a Let's Encrypt spec issue - apologies if I'm bringing this up in the wrong place.
FYI, I got the following error (same in latest 0.5 version): "/acme-client-0.4.1/lib/acme/client/crypto.rb:11:in `dup': [bug] frozen object (JSON::Ext::Generator::State) allocated (TypeError)"
It turned out that I needed to upgrade the json gem from version 1.8.3 I had installed to get things working. The current version is 2.0.2.
At the moment, a client with an existing registration can't interact with it (e.g. to accept the ToS) if the original Registration
object is no longer available. This could be useful if the ToS ever change, or to update the contact details, or list the currently authorized domains and generated certificates.
e.g.
client = Acme::Client.new(...)
orig_registration = client.register(contact: '...')
# ... some time later
client = Acme::Client.new(...)
registration = client.register(contact: '...')
# => Acme::Client::Error::Malformed: Registration key is already in use
In this scenario, the ACME server returns a 409 with the URL of the existing registration:
If the server already has a registration object with the provided account key, then it MUST return a 409 (Conflict) response and provide the URI of that registration in a Location header field. This allows a client that has an account key but not the corresponding registration URI to recover the registration URI.
At the moment there's no way to get the Location
header out of the Acme::Client::Error::Malformed
error, and if we had it there's no way to get a Registration
object out of it -- ACME requires a signed POST to this URL to retrieve the details:
If a client wishes to query the server for information about its account (e.g., to examine the โcontactโ or โcertificatesโ fields), then it SHOULD do so by sending a POST request with an empty update. That is, it should send a JWS whose payload is trivial ({โresourceโ:โregโ}). In this case the server reply MUST contain the same link headers sent for a new registration, to allow a client to retreive the โnew-authorizationโ and โterms-of-serviceโ URI
What are your thoughts on handling this? I could see this going a couple of ways - a method on the exception to go request the existing registration details; automatically handling the 409 and passing on the given contact details as an "update" to the existing account; or maybe a new method on the client to retrieve the existing registration which assumes it already exists.
If one of those were implemented, it's probably also worth adding another couple of methods on the Registration
object to get the current authorizations
, certificates
, and contact
information, and perhaps to update the contact
information. (For new registrations the authorizations and certificates are going to be empty anyway, and the contact is the one that was just passed in, so I can see why they're not exposed at the moment)
I've been using this gem without problems previously, but now that it's time to renew my certificates, i've run into a Faraday::ConnectionFailed execution expired error. It happens whenever i try to register my private key (and if i omit that, it happens on authorize).
Code snippet;
endpoint = 'https://acme-staging.api.letsencrypt.org/directory'
private_key = OpenSSL::PKey::RSA.new(4096)
client = Acme::Client.new(private_key: private_key, endpoint: endpoint, connection_options:{ request: { open_timeout: 5, timeout: 5}})
registration = client.register(contact: 'mailto:[email protected]')
Stack trace below;
`Faraday::ConnectionFailed: execution expired
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:82:in `perform_request'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:40:in `block in call'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:87:in `with_net_http_connection'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:32:in `call'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/request/url_encoded.rb:15:in `call'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/rack_builder.rb:139:in `build_response'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/connection.rb:377:in `run_request'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/connection.rb:140:in `head'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday.rb:99:in `method_missing'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.7/lib/acme/client/faraday_middleware.rb:106:in `get_nonce'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.7/lib/acme/client/faraday_middleware.rb:99:in `pop_nonce'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.7/lib/acme/client/faraday_middleware.rb:15:in `call'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/rack_builder.rb:139:in `build_response'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/connection.rb:377:in `run_request'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/connection.rb:177:in `post'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.7/lib/acme/client.rb:55:in `authorize'
/home/alexander/Desktop/capdesk/source/capdesk/lib/tasks/obtain_ssl.rake:63:in `block (3 levels) in <top (required)>'
/home/alexander/Desktop/capdesk/source/capdesk/lib/tasks/obtain_ssl.rake:60:in `each'
/home/alexander/Desktop/capdesk/source/capdesk/lib/tasks/obtain_ssl.rake:60:in `block (2 levels) in <top (required)>'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/rake-11.3.0/exe/rake:27:in `<top (required)>'
Net::OpenTimeout: execution expired
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:82:in `perform_request'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:40:in `block in call'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:87:in `with_net_http_connection'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:32:in `call'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/request/url_encoded.rb:15:in `call'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/rack_builder.rb:139:in `build_response'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/connection.rb:377:in `run_request'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/connection.rb:140:in `head'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday.rb:99:in `method_missing'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.7/lib/acme/client/faraday_middleware.rb:106:in `get_nonce'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.7/lib/acme/client/faraday_middleware.rb:99:in `pop_nonce'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.7/lib/acme/client/faraday_middleware.rb:15:in `call'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/rack_builder.rb:139:in `build_response'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/connection.rb:377:in `run_request'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/faraday-0.9.2/lib/faraday/connection.rb:177:in `post'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.7/lib/acme/client.rb:55:in `authorize'
/home/alexander/Desktop/capdesk/source/capdesk/lib/tasks/obtain_ssl.rake:63:in `block (3 levels) in <top (required)>'
/home/alexander/Desktop/capdesk/source/capdesk/lib/tasks/obtain_ssl.rake:60:in `each'
/home/alexander/Desktop/capdesk/source/capdesk/lib/tasks/obtain_ssl.rake:60:in `block (2 levels) in <top (required)>'
/home/alexander/.rvm/gems/ruby-2.3.0/gems/rake-11.3.0/exe/rake:27:in `<top (required)>'`
Hi,
Im trying to build a ruby-script based upon this gem, but I get
Connection reset by peer (Faraday::ConnectionFailed)
at this line:
certificate = client.new_certificate(csr)
All the other steps (registration and authorization) worked successfully.
I'm using the staging endpoint ('https://acme-staging.api.letsencrypt.org/')
Any idea?
Thanks in advance!
Not sure if this is a problem with acme-client, Faraday, or net/http, but the timeout doesn't appear to work.
irb(main):010:0> client.connection
=> #<Faraday::Connection:0x00000002633268 @parallel_manager=nil, @headers={"User-Agent"=>"Faraday v0.9.2"}, @params={}, @options=#<Faraday::RequestOptions timeout=5, open_timeout=5>, @ssl=#<Faraday::SSLOptions (empty)>, @default_parallel_manager=nil, @builder=#<Faraday::RackBuilder:0x00000002632c78 @handlers=[Acme::Client::FaradayMiddleware, Faraday::Adapter::NetHttp], @app=#<Acme::Client::FaradayMiddleware:0x00000001e66cb0 @app=#<Faraday::Adapter::NetHttp:0x00000001e66d78 @app=#<Proc:0x00000001e67318@/opt/acme-manager/vendor/bundle/ruby/2.3.0/gems/faraday-0.9.2/lib/faraday/rack_builder.rb:152 (lambda)>>, @client=#<Acme::Client:0x00000002652a28 @connection_options={:request=>{:open_timeout=>5, :timeout=>5}}, @directory_uri=nil, @private_key=#<OpenSSL::PKey::RSA:0x00000002652ac8>, @endpoint="https://acme-v01.api.letsencrypt.org/", @nonces=[], @operation_endpoints={"new-authz"=>"/acme/new-authz", "new-cert"=>"/acme/new-cert", "new-reg"=>"/acme/new-reg", "revoke-cert"=>"/acme/revoke-cert"}, @connection=#<Faraday::Connection:0x00000002633268 ...>>, @env=#<Faraday::Env @method=:get @url=#<URI::HTTPS https://acme-v01.api.letsencrypt.org/> @request=#<Faraday::RequestOptions timeout=5, open_timeout=5> @request_headers={"User-Agent"=>"Acme::Client v0.5.5 (https://github.com/unixcharles/acme-client)"} @ssl=#<Faraday::SSLOptions (empty)>>, @crypto=#<Acme::Client::Crypto:0x00000001e648e8 @private_key=#<OpenSSL::PKey::RSA:0x00000002652ac8>>>>, @url_prefix=#<URI::HTTPS https://acme-v01.api.letsencrypt.org/>, @proxy=nil>
irb(main):011:0> Timeout.timeout(10) do
irb(main):012:1* client.connection.get
irb(main):013:1> end
Timeout::Error: execution expired
from /usr/lib/ruby/2.3.0/net/protocol.rb:158:in `wait_readable'
from /usr/lib/ruby/2.3.0/net/protocol.rb:158:in `rbuf_fill'
from /usr/lib/ruby/2.3.0/net/protocol.rb:136:in `readuntil'
from /usr/lib/ruby/2.3.0/net/protocol.rb:146:in `readline'
from /usr/lib/ruby/2.3.0/net/http/response.rb:40:in `read_status_line'
from /usr/lib/ruby/2.3.0/net/http/response.rb:29:in `read_new'
from /usr/lib/ruby/2.3.0/net/http.rb:1437:in `block in transport_request'
from /usr/lib/ruby/2.3.0/net/http.rb:1434:in `catch'
from /usr/lib/ruby/2.3.0/net/http.rb:1434:in `transport_request'
from /usr/lib/ruby/2.3.0/net/http.rb:1407:in `request'
from /usr/lib/ruby/2.3.0/net/http.rb:1400:in `block in request'
from /usr/lib/ruby/2.3.0/net/http.rb:853:in `start'
from /usr/lib/ruby/2.3.0/net/http.rb:1398:in `request'
from /opt/acme-manager/vendor/bundle/ruby/2.3.0/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:82:in `perform_request'
from /opt/acme-manager/vendor/bundle/ruby/2.3.0/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:40:in `block in call'
from /opt/acme-manager/vendor/bundle/ruby/2.3.0/gems/faraday-0.9.2/lib/faraday/adapter/net_http.rb:87:in `with_net_http_connection'
... 15 levels...
from /usr/bin/irb:11:in `<top (required)>'
from /var/lib/gems/2.3.0/gems/bundler-1.14.6/lib/bundler/cli/exec.rb:74:in `load'
from /var/lib/gems/2.3.0/gems/bundler-1.14.6/lib/bundler/cli/exec.rb:74:in `kernel_load'
from /var/lib/gems/2.3.0/gems/bundler-1.14.6/lib/bundler/cli/exec.rb:27:in `run'
from /var/lib/gems/2.3.0/gems/bundler-1.14.6/lib/bundler/cli.rb:335:in `exec'
from /var/lib/gems/2.3.0/gems/bundler-1.14.6/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
from /var/lib/gems/2.3.0/gems/bundler-1.14.6/lib/bundler/vendor/thor/lib/thor/invocation.rb:126:in `invoke_command'
from /var/lib/gems/2.3.0/gems/bundler-1.14.6/lib/bundler/vendor/thor/lib/thor.rb:359:in `dispatch'
from /var/lib/gems/2.3.0/gems/bundler-1.14.6/lib/bundler/cli.rb:20:in `dispatch'
from /var/lib/gems/2.3.0/gems/bundler-1.14.6/lib/bundler/vendor/thor/lib/thor/base.rb:440:in `start'
from /var/lib/gems/2.3.0/gems/bundler-1.14.6/lib/bundler/cli.rb:11:in `start'
from /var/lib/gems/2.3.0/gems/bundler-1.14.6/exe/bundle:32:in `block in <top (required)>'
from /var/lib/gems/2.3.0/gems/bundler-1.14.6/lib/bundler/friendly_errors.rb:121:in `with_friendly_errors'
from /var/lib/gems/2.3.0/gems/bundler-1.14.6/exe/bundle:24:in `<top (required)>'
from /usr/local/bin/bundle:22:in `load'
from /usr/local/bin/bundle:22:in `<main>'
I'm trying to use our library to manage a "two steps" certificate validation:
acme_01.rb
):# We're going to need a private key.
require 'openssl'
private_key = OpenSSL::PKey::RSA.new(2048)
File.write('private_key', private_key)
print private_key
# We need an ACME server to talk to, see github.com/letsencrypt/boulder
endpoint = 'https://acme-v01.api.letsencrypt.org/'
# Initialize the client
require 'acme/client'
client = Acme::Client.new(private_key: private_key, endpoint: endpoint)
# If the private key is not known to the server, we need to register it for the first time.
registration = client.register(contact: 'mailto:[email protected]')
# You'll may need to agree to the term (that's up the to the server to require it or not but boulder does by default)
registration.agree_terms
# Let's try to optain a certificate for example.org
# We need to prove that we control the domain using one of the challenges method.
authorization = client.authorize(domain: 'sullivansenechal.com')
# For now the only challenge method supprted by the client is http-01.
challenge = authorization.http01
# The http-01 method will require you to response to an HTTP request.
# 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 can send no Content-Type at all but if you send one it has to be 'text/plain'.
challenge.content_type
# Save the file. We'll create a public directory to serve it from, and we'll creating the challenge directory.
FileUtils.mkdir_p( File.join( 'public', File.dirname( challenge.filename ) ) )
# Then writing the file
File.write( File.join( 'public', challenge.filename), challenge.file_content )
This one will just generate a private key, save it and get challenge file name and content.
After that, I'll push the challenge file to the server of the hosted domain.
acme_02.rb
):# We're going to need a private key.
require 'openssl'
key_file = File.new('private_key')
private_key = key_file.read()
print private_key
# We need an ACME server to talk to, see github.com/letsencrypt/boulder
endpoint = 'https://acme-v01.api.letsencrypt.org/'
# Initialize the client
require 'acme/client'
client = Acme::Client.new(private_key: private_key.to_s, endpoint: endpoint)
# If the private key is not known to the server, we need to register it for the first time.
registration = client.register(contact: 'mailto:[email protected]')
# You'll may need to agree to the term (that's up the to the server to require it or not but boulder does by default)
registration.agree_terms
# Let's try to optain a certificate for example.org
# We need to prove that we control the domain using one of the challenges method.
authorization = client.authorize(domain: 'sullivansenechal.com')
# For now the only challenge method supprted by the client is http-01.
challenge = authorization.http01
# The http-01 method will require you to response to an HTTP request.
# 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'
print challenge.filename
# You can send no Content-Type at all but if you send one it has to be 'text/plain'.
challenge.content_type
# Save the file. We'll create a public directory to serve it from, and we'll creating the challenge directory.
FileUtils.mkdir_p( File.join( 'public', File.dirname( challenge.filename ) ) )
# Then writing the file
File.write( File.join( 'public', challenge.filename), challenge.file_content )
# The challenge file can be server with a Ruby webserver such as run a webserver in another console. You may need to forward ports on your router
#ruby -run -e httpd public -p 8080 --bind-address 0.0.0.0
# Once you are ready to serve the confirmation request you can proceed.
challenge.request_verification # => true
challenge.verify_status # => 'pending'
# Wait a bit for the server to make the request, or really just blink, it should be fast.
sleep(1)
challenge.verify_status # => 'valid'
# We're going to need a certificate signing request. If not explicitly
# specified, the first name listed becomes the common name.
csr = Acme::Client::CertificateRequest.new(names: %w[example.org www.example.org])
# We can now request a certificate, you can pass anything that returns
# a valid DER encoded CSR when calling to_der on it, for example a
# OpenSSL::X509::Request too.
certificate = client.new_certificate(csr) # => #<Acme::Client::Certificate ....>
# Save the certificate and key
File.write("privkey.pem", certificate.request.private_key.to_pem)
File.write("cert.pem", certificate.to_pem)
File.write("chain.pem", certificate.chain_to_pem)
File.write("fullchain.pem", certificate.fullchain_to_pem)
This file will get the previously generated private key and do the same as the first script but, with validation.
But when running acme_02.rb
, I got the following error:
$ bundle exec ruby acme_02.rb
/home/sullivan/tmp/acme-client/lib/acme/client/crypto.rb:31:in `public_key': undefined method `public_key' for #<String:0x000000021e30f0> (NoMethodError)
from /home/sullivan/tmp/acme-client/lib/acme/client/crypto.rb:27:in `jwk'
from /home/sullivan/tmp/acme-client/lib/acme/client/crypto.rb:11:in `generate_signed_jws'
from /home/sullivan/tmp/acme-client/lib/acme/client/faraday_middleware.rb:11:in `call'
from /home/sullivan/tmp/acme-client/vendor/bundle/ruby/2.1.0/gems/faraday-0.9.2/lib/faraday/rack_builder.rb:139:in `build_response'
from /home/sullivan/tmp/acme-client/vendor/bundle/ruby/2.1.0/gems/faraday-0.9.2/lib/faraday/connection.rb:377:in `run_request'
from /home/sullivan/tmp/acme-client/vendor/bundle/ruby/2.1.0/gems/faraday-0.9.2/lib/faraday/connection.rb:177:in `post'
from /home/sullivan/tmp/acme-client/lib/acme/client.rb:25:in `register'
from acme_02.rb:16:in `<main>'
I can't find any documentation to separate the challenge process, maybe could you give me a clue?
I'm trying to use Pebble to perform integration tests that don't rely on the Let's Encrypt staging server.
In order to get this to work, I need to either disable SSL verification, or add the Pebble CA. Neither seems to be working.
If I make a Faraday connection directly, though, using identical connection options as what I used for acme-client, I do not get SSL errors (as expected).
Example:
require 'openssl'
private_key = OpenSSL::PKey::RSA.new(4096)
# Local Pebble endpoint
endpoint = "https://0.0.0.0:14000/"
connection_options = { ssl: { verify: false }, request: { open_timeout: 5, timeout: 5 } }
client = Acme::Client.new(private_key: private_key, endpoint: endpoint, connection_options: connection_options)
client.connection.get
# Traceback (most recent call last):
# 1: from (irb):35
# Faraday::SSLError (SSL_connect returned=1 errno=0 state=error: certificate verify failed (unable to get local issuer certificate))
But it works when using a plain old Faraday connection with no middleware:
connection = Faraday.new endpoint, **connection_options
connection.get
#<Faraday::Response:0x00007f84e45c8508 @on_complete_callbacks=[], @env=#<Faraday::Env @method=:get @body="404 page not found\n" @url=#<URI::HTTPS https://0.0.0.0:14000/> @request=#<Faraday::RequestOptions timeout=5, open_timeout=5> @request_headers={"User-Agent"=>"Faraday v0.14.0"} @ssl=#<Faraday::SSLOptions (empty)> @response=#<Faraday::Response:0x00007f84e45c8508 ...> @response_headers={"content-type"=>"text/plain; charset=utf-8", "x-content-type-options"=>"nosniff", "date"=>"Sat, 10 Feb 2018 20:19:37 GMT", "content-length"=>"19", "connection"=>"close"} @status=404 @reason_phrase="Not Found">>
The only difference between the two is the middleware used:
acme-client/lib/acme/client.rb
Lines 100 to 105 in 552f4a1
I tried replicating this with a dummy middleware, which still works fine:
class DummyMiddleware < Faraday::Middleware
def call(env)
@app.call(env)
end
end
connection = Faraday.new endpoint, **connection_options do |configuration|
configuration.use DummyMiddleware
configuration.adapter Faraday.default_adapter
end
connection.get
#<Faraday::Response:0x00007f84e45c8508 @on_complete_callbacks=[], @env=#<Faraday::Env @method=:get @body="404 page not found\n" @url=#<URI::HTTPS https://0.0.0.0:14000/> @request=#<Faraday::RequestOptions timeout=5, open_timeout=5> @request_headers={"User-Agent"=>"Faraday v0.14.0"} @ssl=#<Faraday::SSLOptions (empty)> @response=#<Faraday::Response:0x00007f84e45c8508 ...> @response_headers={"content-type"=>"text/plain; charset=utf-8", "x-content-type-options"=>"nosniff", "date"=>"Sat, 10 Feb 2018 20:19:37 GMT", "content-length"=>"19", "connection"=>"close"} @status=404 @reason_phrase="Not Found">>
My next idea is to try to slowly build up the dummy middleware until it is identical to the middleware used here, but I figured I would gather my thoughts and leave them here first, and see if anyone can even reproduce this.
Hi,
In the past couple of days I keep getting "JWS has invalid anti-replay nonce" and hence no certificate is issued.
Here is the error trace:
/usr/local/rvm/gems/ruby-2.1.5/gems/acme-client-0.2.4/lib/acme/client/faraday_middleware.rb:52:in on_complete': JWS has invalid anti-replay nonce (Acme::Client::Error::BadNonce) from /usr/local/rvm/gems/ruby-2.1.5/gems/acme-client-0.2.4/lib/acme/client/faraday_middleware.rb:12:in
block in call'
from /usr/local/rvm/gems/ruby-2.1.5/gems/faraday-0.9.2/lib/faraday/response.rb:57:in on_complete' from /usr/local/rvm/gems/ruby-2.1.5/gems/acme-client-0.2.4/lib/acme/client/faraday_middleware.rb:12:in
call'
from /usr/local/rvm/gems/ruby-2.1.5/gems/faraday-0.9.2/lib/faraday/rack_builder.rb:139:in build_response' from /usr/local/rvm/gems/ruby-2.1.5/gems/faraday-0.9.2/lib/faraday/connection.rb:377:in
run_request'
from /usr/local/rvm/gems/ruby-2.1.5/gems/faraday-0.9.2/lib/faraday/connection.rb:177:in post' from /usr/local/rvm/gems/ruby-2.1.5/gems/acme-client-0.2.4/lib/acme/client.rb:48:in
new_certificate'
Hi,
I am running into the following errors relating to Faraday when using this client from the CLI.
$ bundle exec ./bin/console
2.2.0 :001 > private_key = OpenSSL::PKey::RSA.new(2048)
=> #OpenSSL::PKey::RSA:0x005648bd221148
2.2.0 :002 > endpoint = 'https://acme-v01.api.letsencrypt.org/directory'
=> "https://acme-v01.api.letsencrypt.org/directory"
2.2.0 :003 > client = Acme::Client.new(private_key: private_key, endpoint: endpoint)
...
2.2.0 :004 > registration = client.register(contact: 'mailto:[email protected]')
NameError: uninitialized constant Acme::Client::Faraday
from acme-client/lib/acme/client.rb:51:in connection' from acme-client/lib/acme/client.rb:23:in
register'
from (irb):4
Note this is after I applied #8.
Hi there,
How would I issue www.domain.com and domain.com?
Thanks,
Relevant backtrace:
gems/acme-client-0.5.1/lib/acme/client/faraday_middleware.rb:87:in `decode_link_headers': undefined method `split' for {"next"=>"https://acme-v01.api.letsencrypt.org/acme/new-cert"}:Hash (NoMethodError)
from gems/acme-client-0.5.1/lib/acme/client/faraday_middleware.rb:29:in `on_complete'
from gems/acme-client-0.5.1/lib/acme/client/faraday_middleware.rb:18:in `block in call'
from gems/faraday-0.11.0/lib/faraday/response.rb:61:in `on_complete'
from gems/acme-client-0.5.1/lib/acme/client/faraday_middleware.rb:18:in `call'
from gems/faraday-0.11.0/lib/faraday/rack_builder.rb:139:in `build_response'
from gems/faraday-0.11.0/lib/faraday/connection.rb:377:in `run_request'
from gems/faraday-0.11.0/lib/faraday/connection.rb:140:in `get'
from gems/acme-client-0.5.1/lib/acme/client.rb:63:in `fetch_authorization'
Looks like it's expecting a string but getting a hash.
Not sure if it matters, but I'm running it in a docker container (alpine 3.4) using ruby 2.4.0.
I can dig in for more details if necessary.
I believe the certificate needs to be listed first within the file, followed by the chain. Currently the certificate is at the end of the file. Creating a full chain certificate currently produces the follow error with Nginx:
nginx: [emerg] SSL_CTX_use_PrivateKey_file("/etc/ssl/booko2/booko.2015.key") failed (SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch)
Using the openssl client we're given details of the root, not the certificate.
Swapping the order of the certificates creates an full chain certificate that Nginx can correctly load.
If that's right, then lib/acme/certificate.rb should be changed from
def fullchain_to_pem
[*x509_chain, x509].map(&:to_pem).join
end
to
def fullchain_to_pem
[x509, *x509_chain].map(&:to_pem).join
end
Hi,
after upgrading ruby to v2.3.0 I see big warning message about circular require:
/home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/json-jwt-1.5.2/lib/json/jose.rb:14: warning: method redefined; discarding old header
/home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/json-jwt-1.5.2/lib/json/jws.rb:57: warning: method redefined; discarding old signature_base_string
/home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/json-jwt-1.5.2/lib/json/jose.rb:14: warning: method redefined; discarding old header
/home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/json-jwt-1.5.2/lib/json/jwe.rb:150: warning: method redefined; discarding old jwe_encrypted_key
/home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/json-jwt-1.5.2/lib/json/jwe.rb:209: warning: method redefined; discarding old authentication_tag
/home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.1/lib/acme/client.rb:1: warning: loading in progress, circular require considered harmful - /home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.1/lib/acme-client.rb
from /home/lgromanowski/.rvm/gems/ruby-2.3.0@global/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:4:in `<main>'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0@global/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:4:in `select'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0@global/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:9:in `block in <main>'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0@global/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:9:in `each'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0@global/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:10:in `block (2 levels) in <main>'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0@global/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:10:in `require'
from /home/lgromanowski/prj/letsencrypt-plugin/test/controllers/application_controller_test.rb:1:in `<top (required)>'
from /home/lgromanowski/prj/letsencrypt-plugin/test/controllers/application_controller_test.rb:1:in `require'
from /home/lgromanowski/prj/letsencrypt-plugin/test/test_helper.rb:9:in `<top (required)>'
from /home/lgromanowski/prj/letsencrypt-plugin/test/test_helper.rb:9:in `require'
from /home/lgromanowski/prj/letsencrypt-plugin/test/dummy/config/environment.rb:2:in `<top (required)>'
from /home/lgromanowski/prj/letsencrypt-plugin/test/dummy/config/environment.rb:2:in `require'
from /home/lgromanowski/prj/letsencrypt-plugin/test/dummy/config/application.rb:4:in `<top (required)>'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/bundler-1.11.2/lib/bundler.rb:99:in `require'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/bundler-1.11.2/lib/bundler/runtime.rb:61:in `require'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/bundler-1.11.2/lib/bundler/runtime.rb:61:in `each'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/bundler-1.11.2/lib/bundler/runtime.rb:72:in `block in require'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/bundler-1.11.2/lib/bundler/runtime.rb:72:in `each'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/bundler-1.11.2/lib/bundler/runtime.rb:77:in `block (2 levels) in require'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/bundler-1.11.2/lib/bundler/runtime.rb:77:in `require'
from /home/lgromanowski/prj/letsencrypt-plugin/lib/letsencrypt_plugin.rb:7:in `<top (required)>'
from /home/lgromanowski/prj/letsencrypt-plugin/lib/letsencrypt_plugin.rb:7:in `require'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.1/lib/acme-client.rb:14:in `<top (required)>'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.1/lib/acme-client.rb:14:in `require'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.1/lib/acme/client.rb:1:in `<top (required)>'
from /home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/acme-client-0.3.1/lib/acme/client.rb:1:in `require'
/home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/activerecord-4.2.6/lib/active_record/fixtures.rb:887: warning: method redefined; discarding old letsencrypt_plugin_challenges
/home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/activerecord-4.2.6/lib/active_record/fixtures.rb:887: warning: previous definition of letsencrypt_plugin_challenges was here
/home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/activerecord-4.2.6/lib/active_record/fixtures.rb:887: warning: method redefined; discarding old letsencrypt_plugin_settings
/home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/activerecord-4.2.6/lib/active_record/fixtures.rb:887: warning: previous definition of letsencrypt_plugin_settings was here
/home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/webmock-1.24.2/lib/webmock/http_lib_adapters/httpclient_adapter.rb:215: warning: shadowing outer local variable - v
/home/lgromanowski/.rvm/gems/ruby-2.3.0/gems/webmock-1.24.2/lib/webmock/http_lib_adapters/httpclient_adapter.rb:233: warning: shadowing outer local variable - v
and after looking into acme-client.rb there is: require 'acme/client'
and then in acme/client.rb: require 'acme-client'
.
dns validation would allow me to validate hosts via dns.
This issue is the following of #48 but with another issue.
I generate validation file and token on acme_01.rb
, without validation:
# We're going to need a private key.
require 'openssl'
private_key = OpenSSL::PKey::RSA.new(2048)
File.write('private_key', private_key)
print private_key
endpoint = 'https://acme-v01.api.letsencrypt.org/'
require 'acme/client'
client = Acme::Client.new(private_key: private_key, endpoint: endpoint)
registration = client.register(contact: 'mailto:[email protected]')
registration.agree_terms
authorization = client.authorize(domain: 'sullivansenechal.com')
challenge = authorization.http01
puts 'Filename: ' + challenge.filename # => ".well-known/acme-challenge/:some_token"
puts 'File content:' + challenge.file_content # => 'string token and JWK thumbprint'
puts 'Content type: ' + challenge.content_type
puts 'Token: ' + challenge.token
File.write('private_token', challenge.token)
FileUtils.mkdir_p( File.join( 'public', File.dirname( challenge.filename ) ) )
File.write( File.join( 'public', challenge.filename), challenge.file_content )
The problem is, when I want to launch acme_02.rb
to validate the file, this one has changed.
I noticed that the challenge need a token, so I'm trying to store it and use it on the second script:
# We're going to need a private key.
require 'openssl'
private_key = OpenSSL::PKey::RSA.new(File.read('private_key'))
print private_key
endpoint = 'https://acme-v01.api.letsencrypt.org/'
require 'acme/client'
client = Acme::Client.new(private_key: private_key, endpoint: endpoint)
authorization = client.authorize(domain: 'sullivansenechal.com')
challenge = authorization.http01(token: File.read('private_token'))
puts 'Filename: ' + challenge.filename # => ".well-known/acme-challenge/:some_token"
puts 'File content:' + challenge.file_content # => 'string token and JWK thumbprint'
puts 'Content type: ' + challenge.content_type
puts 'Token: ' + challenge.token
challenge.request_verification # => true
puts challenge.verify_status # => 'pending'
sleep(1)
puts challenge.verify_status # => 'valid'
# We're going to need a certificate signing request. If not explicitly
# specified, the first name listed becomes the common name.
csr = Acme::Client::CertificateRequest.new(names: %w[sullivansenechal.com www.sullivansenechal.com])
# We can now request a certificate, you can pass anything that returns
# a valid DER encoded CSR when calling to_der on it, for example a
# OpenSSL::X509::Request too.
certificate = client.new_certificate(csr) # => #<Acme::Client::Certificate ....>
# Save the certificate and key
File.write("privkey.pem", certificate.request.private_key.to_pem)
File.write("cert.pem", certificate.to_pem)
File.write("chain.pem", certificate.chain_to_pem)
File.write("fullchain.pem", certificate.fullchain_to_pem)
But I got the following error:
acme_02.rb:14:in `<main>': wrong number of arguments (1 for 0) (ArgumentError)
So I assume challenge = authorization.http01(token: File.read('private_token'))
is not the way to do.
How can I pass the token to the authorization challenge?
Doing (as far as I can tell) exactly what the README tells me to:
$ gem install acme-client
Fetching: securecompare-1.0.0.gem (100%)
Successfully installed securecompare-1.0.0
Fetching: bindata-2.3.1.gem (100%)
Successfully installed bindata-2.3.1
Fetching: url_safe_base64-0.2.2.gem (100%)
Successfully installed url_safe_base64-0.2.2
Fetching: json-jwt-1.6.2.gem (100%)
Successfully installed json-jwt-1.6.2
Fetching: acme-client-0.3.3.gem (100%)
Successfully installed acme-client-0.3.3
5 gems installed
$ irb
>> require 'openssl'
=> true
>> private_key = OpenSSL::PKey::RSA.new(4096)
=> #<OpenSSL::PKey::RSA:0x00000001708fb8>
>> endpoint = 'https://acme-staging.api.letsencrypt.org/'
=> "https://acme-staging.api.letsencrypt.org/"
>> require 'acme-client'
=> true
>> client = Acme::Client.new(private_key: private_key, endpoint: endpoint, connection_options: { request: { open_timeout: 5, timeout: 5 } })
=> #<Acme::Client:0x00000001329230 @connection_options={:request=>{:open_timeout=>5, :timeout=>5}}, @directory_uri=nil, @private_key=#<OpenSSL::PKey::RSA:0x00000001708fb8>, @endpoint="https://acme-staging.api.letsencrypt.org/", @nonces=[], @operation_endpoints={"new-authz"=>"/acme/new-authz", "new-cert"=>"/acme/new-cert", "new-reg"=>"/acme/new-reg", "revoke-cert"=>"/acme/revoke-cert"}>
>> registration = client.register(contact: 'mailto:[email protected]')
Acme::Client::Error::Malformed: algorithm 'none' in JWS header not acceptable
from /home/mpalmer/.gem/ruby/2.1.0/gems/acme-client-0.3.3/lib/acme/client/faraday_middleware.rb:37:in `raise_on_error!'
from /home/mpalmer/.gem/ruby/2.1.0/gems/acme-client-0.3.3/lib/acme/client/faraday_middleware.rb:27:in `on_complete'
from /home/mpalmer/.gem/ruby/2.1.0/gems/acme-client-0.3.3/lib/acme/client/faraday_middleware.rb:12:in `block in call'
from /home/mpalmer/.gem/ruby/2.1.0/gems/faraday-0.9.2/lib/faraday/response.rb:57:in `on_complete'
from /home/mpalmer/.gem/ruby/2.1.0/gems/acme-client-0.3.3/lib/acme/client/faraday_middleware.rb:12:in `call'
from /home/mpalmer/.gem/ruby/2.1.0/gems/faraday-0.9.2/lib/faraday/rack_builder.rb:139:in `build_response'
from /home/mpalmer/.gem/ruby/2.1.0/gems/faraday-0.9.2/lib/faraday/connection.rb:377:in `run_request'
from /home/mpalmer/.gem/ruby/2.1.0/gems/faraday-0.9.2/lib/faraday/connection.rb:177:in `post'
from /home/mpalmer/.gem/ruby/2.1.0/gems/acme-client-0.3.3/lib/acme/client.rb:23:in `register'
from (irb):6
from /usr/bin/irb:11:in `<main>'
I'm working on the assumption that some dependent gem has lost its mind, but I'll file this here in the hope someone else might know more quickly than I can find out.
Hey guys,
Thanks for the great gem, ive implemented into my app and its almost working. Im stuck at the pending state of challenge_verify_status
When i inspect the auth_uri
the acme service also responds with pending
.
Do you have any clueue? Im using the 'https://acme-v01.api.letsencrypt.org/'
btw. thanks guys!!
{
"identifier": {
"type": "dns",
"value": "www.weteling.com"
},
"status": "pending",
"expires": "2017-08-23T11:33:21Z",
"challenges": [
{
"type": "tls-sni-01",
"status": "pending",
"uri": "https://acme-v01.api.letsencrypt.org/acme/challenge/qnBc49KubV28busiCAeqOSnkmvFmwPK0uJGbeOrQ5uo/1763958644",
"token": "hAp4s-VI0SE8ObQbvCV1g7-TgM9K3KJR7n-1L56B7yA"
},
{
"type": "dns-01",
"status": "pending",
"uri": "https://acme-v01.api.letsencrypt.org/acme/challenge/qnBc49KubV28busiCAeqOSnkmvFmwPK0uJGbeOrQ5uo/1763958645",
"token": "cmWruko_gqhrhyUoGOBsKSM5KmTSFk_0jTBicsYIzig"
},
{
"type": "http-01",
"status": "pending",
"uri": "https://acme-v01.api.letsencrypt.org/acme/challenge/qnBc49KubV28busiCAeqOSnkmvFmwPK0uJGbeOrQ5uo/1763958646",
"token": "64kHa2ArDXdUWhqdCN2AIvkNmIUiWfx2brQHZXjzsaM"
}
],
"combinations": [
[
0
],
[
2
],
[
1
]
]
}
Reported in will-in-wi/letsencrypt-webfaction#38
Turns out this was added in letsencrypt/boulder#1902
This was fixed in CertBot by explicitly adding a version number certbot/certbot#2529
I think this'll be fixed by adding csr.version = 2
in Acme::Client::CertificateRequest#generate
. I'm getting a PR ready once I can test that it really fixes the problem.
Hey @unixcharles can I trouble you to release a new version of this gem? We would like to use #53 without having to reference a sha.
Thanks!
A very common task when using this library will be generating a CSR. Ruby's OpenSSL API is rather inconvenient, especially when it comes down to including a subjectAltName
extension.
Should we provide a convenience class to ease CSR generation? What would the interface look like? First thoughts would be to keep it as simple as client.new_certificate(%w(example.org www.example.org), private_key)
, client.new_certificate(Acme.generate_csr(%w(example.org www.example.org), private_key)
or client.new_certificate(Acme::Csr.new(%w(example.org www.example.org), private_key))
. The first hostname would be picked for the CN
.
When setting api server to https://acme-staging.api.letsencrypt.org/, IdenTrustCA.pem is required to be in OPENSSL cert directory.
How to pass (/etc/ssl/certs) through to openssl's ca_path?
Can be done at cli with export SSL_CERT_FILE=....
export SSL_CERT_DIR=/etc/ssl/certs works.
Though prefer to set in ruby.
I'm trying to re-use the challenge from a stored hash. I can re-use it once properly, but any subsequent usage seems to fail with error Unable to update challenge :: Response does not complete challenge
.
Is there some one-time nonce or something encoded into the token or am I doing something really wrong? I'm following the steps from the README tutorial, only change is that I'm storing the challenge hash into MongoDB but that should not really matter as I'm able to re-use the challenge once.
We're using the DNS verification so we need to be able to do this in two steps. Would like to be able to re-use the challenge multiple times since the verification might fail and has to be retried. Now the only alternative seems to be to create the authorization from scratch thus changing the DNS record content also. :(
Hi, thank you for kindly maintenance. If possible, would you please add versions tag to the this repository ?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.