Giter Site home page Giter Site logo

acme-client's People

Contributors

0x7466 avatar aablinov avatar ajacques avatar boone avatar dschwar avatar eagletmt avatar jhass avatar jhawthorn avatar kintner avatar louim avatar mabana avatar mastahyeti avatar maxboisvert avatar mjtko avatar msmith avatar nhinds avatar nickmcdonnough avatar nikita-v avatar olleolleolle avatar phillipp avatar porbas avatar programingnotes avatar sawanoboly avatar shaeli avatar sheldonh avatar sorah avatar technion avatar unixcharles avatar weppos avatar will-in-wi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

acme-client's Issues

Two-word errors will never be instantiated

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.

issue when initializing client

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!

`record_name` in DNS01 is invalid for subdomains

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

Two steps validation script

I'm trying to use our library to manage a "two steps" certificate validation:

  1. Challenge file creation (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.

  1. Validate the challenge (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?

Acme::Client::Error::Malformed: Unable to update challenge :: Response does not complete challenge

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.

fullchain_to_pem puts certificates in wrong order?

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

New release

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?

Big warnings about circular require with ruby 2.3.0

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'.

Undefined method exception when getting authorization (in decode_link_headers)

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.

challenge_verify_status stuck at pending

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
    ]
  ]
}

JWS has invalid anti-replay nonce

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:inblock 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:incall'
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:inrun_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:innew_certificate'

Error when register with an ECDSA account key

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.

Renewing certificate

Is there any documentation explaining how to renew a certificate that is about to expire?

Parse error reading JWS

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>'

Undefined challenge_from_hash but it's in read me

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?

Feature request: get/update details of existing registration

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)

README example fails with "algorithm 'none' in JWS header not acceptable"

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.

IDN Domain not working

xn-----xlcrekfobi.xn--p1ai - domain (Russian Cyrilic) not working

Name does not end in a public suffix

CSR generator

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.

Doesn't locate Faraday correctly

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:inregister'
from (irb):4

Note this is after I applied #8.

Keep token for two steps validation

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?

Unspecified dependency on json gem

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.

Is it possible to follow 301 redirects from www to non-www (and vice versa) for verification?

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.

Load error when in gemfile

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)`

Ruby < 2.2.2

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!

Timeout doesn't appear to work

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>'

SSL connection options seemingly being ignored

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:

def connection
@connection ||= Faraday.new(@endpoint, **@connection_options) do |configuration|
configuration.use Acme::Client::FaradayMiddleware, client: self
configuration.adapter Faraday.default_adapter
end
end

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.

Namespace issues

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:

  1. Claim this is a non-issue and that our constants inside Acme besides Client are acceptable use since generally useful to projects implementing the ACME protocol.
  2. Move everything below Acme::Client. Currently I would only consider the rename of Acme::CertificateRequest to Acme::Client::CertificateRequest a breaking change of public API.
  3. Rename the gem into something outside of the Acme namespace, for example acme_client (AcmeClient, AcmeClient::CertificateRequest). This would involve deprecating the currently published gem and publishing a new one.
  4. Ask @smathy for control over the 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?

DIRECTORY_DEFAULT error

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!'

Set Faraday User Agent for requests

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!

Add support for Ruby 1.9

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!

Registration contact

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?

Acme::Client::Error::Unauthorized: Error creating new cert for www subdomain

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.

Q: Re-using challenge from hash multiple times

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. :(

Challenge authorization

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?

ConnectionFailed on new_certificate

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!

Error parsing certificate request

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

Faraday::ConnectionFailed error

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)>'`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.