diff options
22 files changed, 1 insertions, 996 deletions
| diff --git a/leap_cli.gemspec b/leap_cli.gemspec index 8a38893..96bf007 100644 --- a/leap_cli.gemspec +++ b/leap_cli.gemspec @@ -52,15 +52,12 @@ spec = Gem::Specification.new do |s|    # network gems    s.add_runtime_dependency('sshkit', '~> 1.11') -  s.add_runtime_dependency('fog-aws', '~> 0.11')    # crypto gems    # s.add_runtime_dependency('gpgme')    # << does not build on debian jessie, so now optional.                                           # also, there is a ruby-gpgme package anyway. -  # acme-client is vendored for now, we need pre-lease version -  # s.add_runtime_dependency('acme-client', '~> 0.4.2') -  s.add_runtime_dependency('faraday', '~> 0.9', '>= 0.9.1') # for acme-client +  s.add_runtime_dependency('acme-client', '~> 0.6')    # misc gems    s.add_runtime_dependency('ya2yaml', '~> 0.31')    # pure ruby yaml, so we can better control output. see https://github.com/afunai/ya2yaml diff --git a/vendor/acme-client/Gemfile b/vendor/acme-client/Gemfile deleted file mode 100644 index e0b10df..0000000 --- a/vendor/acme-client/Gemfile +++ /dev/null @@ -1,12 +0,0 @@ -source 'https://rubygems.org' -gemspec - -group :development, :test do -  gem 'pry' -  gem 'rubocop', '0.36.0' -  gem 'ruby-prof', require: false - -  if Gem::Version.new(RUBY_VERSION) <= Gem::Version.new('2.2.2') -    gem 'activesupport', '~> 4.2.6' -  end -end diff --git a/vendor/acme-client/LICENSE.txt b/vendor/acme-client/LICENSE.txt deleted file mode 100644 index 73b96b4..0000000 --- a/vendor/acme-client/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Charles Barbier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/acme-client/README.md b/vendor/acme-client/README.md deleted file mode 100644 index 2047885..0000000 --- a/vendor/acme-client/README.md +++ /dev/null @@ -1,168 +0,0 @@ -# Acme::Client -[](https://travis-ci.org/unixcharles/acme-client) - -`acme-client` is a client implementation of the [ACME](https://letsencrypt.github.io/acme-spec) protocol in Ruby. - -You can find the ACME reference implementations of the [server](https://github.com/letsencrypt/boulder) in Go and the [client](https://github.com/letsencrypt/letsencrypt) in Python. - -ACME is part of the [Letsencrypt](https://letsencrypt.org/) project, which goal is to provide free SSL/TLS certificates with  automation of the acquiring and renewal process. - -## Installation - -Via Rubygems: - -	$ gem install acme-client - -Or add it to a Gemfile: - -```ruby -gem 'acme-client' -``` - -## Usage - -### Register client - -In order to authenticate our client, we have to create an account for it. - -```ruby -# We're going to need a private key. -require 'openssl' -private_key = OpenSSL::PKey::RSA.new(4096) - -# We need an ACME server to talk to, see github.com/letsencrypt/boulder -# WARNING: This endpoint is the production endpoint, which is rate limited and will produce valid certificates. -# You should probably use the staging endpoint for all your experimentation: -# endpoint = 'https://acme-staging.api.letsencrypt.org/' -endpoint = 'https://acme-v01.api.letsencrypt.org/' - -# Initialize the client -require 'acme-client' -client = Acme::Client.new(private_key: private_key, endpoint: endpoint, connection_options: { request: { open_timeout: 5, timeout: 5 } }) - -# If the private key is not known to the server, we need to register it for the first time. -registration = client.register(contact: 'mailto:contact@example.com') - -# You may need to agree to the terms of service (that's up the to the server to require it or not but boulder does by default) -registration.agree_terms -``` - -### Authorize for domain - -Before you are able to obtain certificates for your domain, you have to prove that you are in control of it. - -```ruby -authorization = client.authorize(domain: 'example.org') - -# If authorization.status returns 'valid' here you can already get a certificate -# and _must not_ try to solve another challenge. -authorization.status # => 'pending' - -# You can can store the authorization's URI to fully recover it and -# any associated challenges via Acme::Client#fetch_authorization. -authorization.uri # => '...' - -# This example is using the http-01 challenge type. Other challenges are dns-01 or tls-sni-01. -challenge = authorization.http01 - -# The http-01 method will require you to respond to a HTTP request. - -# You can retrieve the challenge token -challenge.token # => "some_token" - -# 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 are not required to send a Content-Type. This method will return the right Content-Type should you decide to include one. -challenge.content_type - -# Save the file. We'll create a public directory to serve it from, and inside it we'll create the challenge file. -FileUtils.mkdir_p( File.join( 'public', File.dirname( challenge.filename ) ) ) - -# We'll write the content of the file -File.write( File.join( 'public', challenge.filename), challenge.file_content ) - -# Optionally save the challenge for use at another time (eg: by a background job processor) -File.write('challenge', challenge.to_h.to_json) - -# The challenge file can be served with a Ruby webserver. -# You can run a webserver in another console for that purpose. You may need to forward ports on your router. -# -# $ ruby -run -e httpd public -p 8080 --bind-address 0.0.0.0 - -# 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'))) - -# Once you are ready to serve the confirmation request you can proceed. -challenge.request_verification # => true -challenge.authorization.verify_status # => 'pending' - -# Wait a bit for the server to make the request, or just blink. It should be fast. -sleep(1) - -# Rely on authorization.verify_status more than on challenge.verify_status, -# if the former is 'valid' you can already issue a certificate and the status of -# the challenge is not relevant and in fact may never change from pending. -challenge.authorization.verify_status # => 'valid' -challenge.error # => nil - -# If authorization.verify_status is 'invalid', you can get at the error -# message only through the failed challenge. -authorization.verify_status # => 'invalid' -authorization.http01.error # => {"type" => "...", "detail" => "..."} -``` - -### Obtain a certificate - -Now that your account is authorized for the domain, you should be able to obtain a certificate for it. - -```ruby -# 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 an -# OpenSSL::X509::Request should work too. -certificate = client.new_certificate(csr) # => #<Acme::Client::Certificate ....> - -# Save the certificate and the private key to files -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) - -# Start a webserver, using your shiny new certificate -# ruby -r openssl -r webrick -r 'webrick/https' -e "s = WEBrick::HTTPServer.new( -#   :Port => 8443, -#   :DocumentRoot => Dir.pwd, -#   :SSLEnable => true, -#   :SSLPrivateKey => OpenSSL::PKey::RSA.new( File.read('privkey.pem') ), -#   :SSLCertificate => OpenSSL::X509::Certificate.new( File.read('cert.pem') )); trap('INT') { s.shutdown }; s.start" -``` - -# Not implemented - -- Recovery methods are not implemented. - -# Requirements - -Ruby >= 2.1 - -## Development - -All the tests use VCR to mock the interaction with the server but if you -need to record new interation against the server simply clone boulder and -run it normally with `./start.py`. - -## Pull request? - -Yes. - -## License - -[MIT License](http://opensource.org/licenses/MIT) - diff --git a/vendor/acme-client/acme-client.gemspec b/vendor/acme-client/acme-client.gemspec deleted file mode 100644 index b62d60c..0000000 --- a/vendor/acme-client/acme-client.gemspec +++ /dev/null @@ -1,27 +0,0 @@ -# coding: utf-8 -lib = File.expand_path('../lib', __FILE__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'acme/client/version' - -Gem::Specification.new do |spec| -  spec.name          = 'acme-client' -  spec.version       = Acme::Client::VERSION -  spec.authors       = ['Charles Barbier'] -  spec.email         = ['unixcharles@gmail.com'] -  spec.summary       = 'Client for the ACME protocol.' -  spec.homepage      = 'http://github.com/unixcharles/acme-client' -  spec.license       = 'MIT' - -  spec.files         = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } -  spec.require_paths = ['lib'] - -  spec.required_ruby_version = '>= 2.1.0' - -  spec.add_development_dependency 'bundler', '~> 1.6', '>= 1.6.9' -  spec.add_development_dependency 'rake', '~> 10.0' -  spec.add_development_dependency 'rspec', '~> 3.3', '>= 3.3.0' -  spec.add_development_dependency 'vcr', '~> 2.9', '>= 2.9.3' -  spec.add_development_dependency 'webmock', '~> 1.21', '>= 1.21.0' - -  spec.add_runtime_dependency 'faraday', '~> 0.9', '>= 0.9.1' -end diff --git a/vendor/acme-client/lib/acme-client.rb b/vendor/acme-client/lib/acme-client.rb deleted file mode 100644 index 7cc7a0a..0000000 --- a/vendor/acme-client/lib/acme-client.rb +++ /dev/null @@ -1 +0,0 @@ -require 'acme/client' diff --git a/vendor/acme-client/lib/acme/client.rb b/vendor/acme-client/lib/acme/client.rb deleted file mode 100644 index 801479e..0000000 --- a/vendor/acme-client/lib/acme/client.rb +++ /dev/null @@ -1,122 +0,0 @@ -# frozen_string_literal: true - -require 'faraday' -require 'json' -require 'openssl' -require 'digest' -require 'forwardable' -require 'base64' -require 'time' - -module Acme; end -class Acme::Client; end - -require 'acme/client/version' -require 'acme/client/certificate' -require 'acme/client/certificate_request' -require 'acme/client/self_sign_certificate' -require 'acme/client/crypto' -require 'acme/client/resources' -require 'acme/client/faraday_middleware' -require 'acme/client/error' - -class Acme::Client -  DEFAULT_ENDPOINT = 'http://127.0.0.1:4000'.freeze -  DIRECTORY_DEFAULT = { -    'new-authz' => '/acme/new-authz', -    'new-cert' => '/acme/new-cert', -    'new-reg' => '/acme/new-reg', -    'revoke-cert' => '/acme/revoke-cert' -  }.freeze - -  def initialize(private_key:, endpoint: DEFAULT_ENDPOINT, directory_uri: nil, connection_options: {}) -    @endpoint, @private_key, @directory_uri, @connection_options = endpoint, private_key, directory_uri, connection_options -    @nonces ||= [] -    load_directory! -  end - -  attr_reader :private_key, :nonces, :operation_endpoints - -  def register(contact:) -    payload = { -      resource: 'new-reg', contact: Array(contact) -    } - -    response = connection.post(@operation_endpoints.fetch('new-reg'), payload) -    ::Acme::Client::Resources::Registration.new(self, response) -  end - -  def authorize(domain:) -    payload = { -      resource: 'new-authz', -      identifier: { -        type: 'dns', -        value: domain -      } -    } - -    response = connection.post(@operation_endpoints.fetch('new-authz'), payload) -    ::Acme::Client::Resources::Authorization.new(self, response.headers['Location'], response) -  end - -  def fetch_authorization(uri) -    response = connection.get(uri) -    ::Acme::Client::Resources::Authorization.new(self, uri, response) -  end - -  def new_certificate(csr) -    payload = { -      resource: 'new-cert', -      csr: Base64.urlsafe_encode64(csr.to_der) -    } - -    response = connection.post(@operation_endpoints.fetch('new-cert'), payload) -    ::Acme::Client::Certificate.new(OpenSSL::X509::Certificate.new(response.body), response.headers['location'], fetch_chain(response), csr) -  end - -  def revoke_certificate(certificate) -    payload = { resource: 'revoke-cert', certificate: Base64.urlsafe_encode64(certificate.to_der) } -    endpoint = @operation_endpoints.fetch('revoke-cert') -    response = connection.post(endpoint, payload) -    response.success? -  end - -  def self.revoke_certificate(certificate, *arguments) -    client = new(*arguments) -    client.revoke_certificate(certificate) -  end - -  def connection -    @connection ||= Faraday.new(@endpoint, **@connection_options) do |configuration| -      configuration.use Acme::Client::FaradayMiddleware, client: self -      configuration.adapter Faraday.default_adapter -    end -  end - -  private - -  def fetch_chain(response, limit = 10) -    links = response.headers['link'] -    if limit.zero? || links.nil? || links['up'].nil? -      [] -    else -      issuer = connection.get(links['up']) -      [OpenSSL::X509::Certificate.new(issuer.body), *fetch_chain(issuer, limit - 1)] -    end -  end - -  def load_directory! -    @operation_endpoints = if @directory_uri -      response = connection.get(@directory_uri) -      body = response.body -      { -        'new-reg' => body.fetch('new-reg'), -        'new-authz' => body.fetch('new-authz'), -        'new-cert' => body.fetch('new-cert'), -        'revoke-cert' => body.fetch('revoke-cert'), -      } -    else -      DIRECTORY_DEFAULT -    end -  end -end diff --git a/vendor/acme-client/lib/acme/client/certificate.rb b/vendor/acme-client/lib/acme/client/certificate.rb deleted file mode 100644 index 6c68cc5..0000000 --- a/vendor/acme-client/lib/acme/client/certificate.rb +++ /dev/null @@ -1,30 +0,0 @@ -class Acme::Client::Certificate -  extend Forwardable - -  attr_reader :x509, :x509_chain, :request, :private_key, :url - -  def_delegators :x509, :to_pem, :to_der - -  def initialize(certificate, url, chain, request) -    @x509 = certificate -    @url = url -    @x509_chain = chain -    @request = request -  end - -  def chain_to_pem -    x509_chain.map(&:to_pem).join -  end - -  def x509_fullchain -    [x509, *x509_chain] -  end - -  def fullchain_to_pem -    x509_fullchain.map(&:to_pem).join -  end - -  def common_name -    x509.subject.to_a.find { |name, _, _| name == 'CN' }[1] -  end -end diff --git a/vendor/acme-client/lib/acme/client/certificate_request.rb b/vendor/acme-client/lib/acme/client/certificate_request.rb deleted file mode 100644 index 8eae0c6..0000000 --- a/vendor/acme-client/lib/acme/client/certificate_request.rb +++ /dev/null @@ -1,111 +0,0 @@ -class Acme::Client::CertificateRequest -  extend Forwardable - -  DEFAULT_KEY_LENGTH = 2048 -  DEFAULT_DIGEST = OpenSSL::Digest::SHA256 -  SUBJECT_KEYS = { -    common_name:         'CN', -    country_name:        'C', -    organization_name:   'O', -    organizational_unit: 'OU', -    state_or_province:   'ST', -    locality_name:       'L' -  }.freeze - -  SUBJECT_TYPES = { -    'CN' => OpenSSL::ASN1::UTF8STRING, -    'C'  => OpenSSL::ASN1::UTF8STRING, -    'O'  => OpenSSL::ASN1::UTF8STRING, -    'OU' => OpenSSL::ASN1::UTF8STRING, -    'ST' => OpenSSL::ASN1::UTF8STRING, -    'L'  => OpenSSL::ASN1::UTF8STRING -  }.freeze - -  attr_reader :private_key, :common_name, :names, :subject - -  def_delegators :csr, :to_pem, :to_der - -  def initialize(common_name: nil, names: [], private_key: generate_private_key, subject: {}, digest: DEFAULT_DIGEST.new) -    @digest = digest -    @private_key = private_key -    @subject = normalize_subject(subject) -    @common_name = common_name || @subject[SUBJECT_KEYS[:common_name]] || @subject[:common_name] -    @names = names.to_a.dup -    normalize_names -    @subject[SUBJECT_KEYS[:common_name]] ||= @common_name -    validate_subject -  end - -  def csr -    @csr ||= generate -  end - -  private - -  def generate_private_key -    OpenSSL::PKey::RSA.new(DEFAULT_KEY_LENGTH) -  end - -  def normalize_subject(subject) -    @subject = subject.each_with_object({}) do |(key, value), hash| -      hash[SUBJECT_KEYS.fetch(key, key)] = value.to_s -    end -  end - -  def normalize_names -    if @common_name -      @names.unshift(@common_name) unless @names.include?(@common_name) -    else -      raise ArgumentError, 'No common name and no list of names given' if @names.empty? -      @common_name = @names.first -    end -  end - -  def validate_subject -    validate_subject_attributes -    validate_subject_common_name -  end - -  def validate_subject_attributes -    extra_keys = @subject.keys - SUBJECT_KEYS.keys - SUBJECT_KEYS.values -    return if extra_keys.empty? -    raise ArgumentError, "Unexpected subject attributes given: #{extra_keys.inspect}" -  end - -  def validate_subject_common_name -    return if @common_name == @subject[SUBJECT_KEYS[:common_name]] -    raise ArgumentError, 'Conflicting common name given in arguments and subject' -  end - -  def generate -    OpenSSL::X509::Request.new.tap do |csr| -      csr.public_key = @private_key.public_key -      csr.subject = generate_subject -      csr.version = 2 -      add_extension(csr) -      csr.sign @private_key, @digest -    end -  end - -  def generate_subject -    OpenSSL::X509::Name.new( -      @subject.map {|name, value| -        [name, value, SUBJECT_TYPES[name]] -      } -    ) -  end - -  def add_extension(csr) -    return if @names.size <= 1 - -    extension = OpenSSL::X509::ExtensionFactory.new.create_extension( -      'subjectAltName', @names.map { |name| "DNS:#{name}" }.join(', '), false -    ) -    csr.add_attribute( -      OpenSSL::X509::Attribute.new( -        'extReq', -        OpenSSL::ASN1::Set.new([OpenSSL::ASN1::Sequence.new([extension])]) -      ) -    ) -  end -end diff --git a/vendor/acme-client/lib/acme/client/crypto.rb b/vendor/acme-client/lib/acme/client/crypto.rb deleted file mode 100644 index dfa5cdc..0000000 --- a/vendor/acme-client/lib/acme/client/crypto.rb +++ /dev/null @@ -1,98 +0,0 @@ -class Acme::Client::Crypto -  attr_reader :private_key - -  def initialize(private_key) -    @private_key = private_key -  end - -  def generate_signed_jws(header:, payload:) -    header = { typ: 'JWT', alg: jws_alg, jwk: jwk }.merge(header) - -    encoded_header = urlsafe_base64(header.to_json) -    encoded_payload = urlsafe_base64(payload.to_json) -    signature_data = "#{encoded_header}.#{encoded_payload}" - -    signature = private_key.sign digest, signature_data -    encoded_signature = urlsafe_base64(signature) - -    { -      protected: encoded_header, -      payload: encoded_payload, -      signature: encoded_signature -    }.to_json -  end - -  def thumbprint -    urlsafe_base64 digest.digest(jwk.to_json) -  end - -  def digest -    OpenSSL::Digest::SHA256.new -  end - -  def urlsafe_base64(data) -    Base64.urlsafe_encode64(data).sub(/[\s=]*\z/, '') -  end - -  private - -  def jws_alg -    { 'RSA' => 'RS256', 'EC' => 'ES256' }.fetch(jwk[:kty]) -  end - -  def jwk -    @jwk ||= case private_key -             when OpenSSL::PKey::RSA -               rsa_jwk -             when OpenSSL::PKey::EC -               ec_jwk -             else -               raise ArgumentError, "Can't handle #{private_key} as private key, only OpenSSL::PKey::RSA and OpenSSL::PKey::EC" -    end -  end - -  def rsa_jwk -    { -      e: urlsafe_base64(public_key.e.to_s(2)), -      kty: 'RSA', -      n: urlsafe_base64(public_key.n.to_s(2)) -    } -  end - -  def ec_jwk -    { -      crv: curve_name, -      kty: 'EC', -      x: urlsafe_base64(coordinates[:x].to_s(2)), -      y: urlsafe_base64(coordinates[:y].to_s(2)) -    } -  end - -  def curve_name -    { -      'prime256v1' => 'P-256', -      'secp384r1' => 'P-384', -      'secp521r1' => 'P-521' -    }.fetch(private_key.group.curve_name) { raise ArgumentError, 'Unknown EC curve' } -  end - -  # rubocop:disable Metrics/AbcSize -  def coordinates -    @coordinates ||= begin -      hex = public_key.to_bn.to_s(16) -      data_len = hex.length - 2 -      hex_x = hex[2, data_len / 2] -      hex_y = hex[2 + data_len / 2, data_len / 2] - -      { -        x: OpenSSL::BN.new([hex_x].pack('H*'), 2), -        y: OpenSSL::BN.new([hex_y].pack('H*'), 2) -      } -    end -  end -  # rubocop:enable Metrics/AbcSize - -  def public_key -    @public_key ||= private_key.public_key -  end -end diff --git a/vendor/acme-client/lib/acme/client/error.rb b/vendor/acme-client/lib/acme/client/error.rb deleted file mode 100644 index 2b35623..0000000 --- a/vendor/acme-client/lib/acme/client/error.rb +++ /dev/null @@ -1,16 +0,0 @@ -class Acme::Client::Error < StandardError -  class NotFound < Acme::Client::Error; end -  class BadCSR < Acme::Client::Error; end -  class BadNonce < Acme::Client::Error; end -  class Connection < Acme::Client::Error; end -  class Dnssec < Acme::Client::Error; end -  class Malformed < Acme::Client::Error; end -  class ServerInternal < Acme::Client::Error; end -  class Acme::Tls < Acme::Client::Error; end -  class Unauthorized < Acme::Client::Error; end -  class UnknownHost < Acme::Client::Error; end -  class Timeout < Acme::Client::Error; end -  class RateLimited < Acme::Client::Error; end -  class RejectedIdentifier < Acme::Client::Error; end -  class UnsupportedIdentifier < Acme::Client::Error; end -end diff --git a/vendor/acme-client/lib/acme/client/faraday_middleware.rb b/vendor/acme-client/lib/acme/client/faraday_middleware.rb deleted file mode 100644 index 21e29c9..0000000 --- a/vendor/acme-client/lib/acme/client/faraday_middleware.rb +++ /dev/null @@ -1,123 +0,0 @@ -# frozen_string_literal: true - -class Acme::Client::FaradayMiddleware < Faraday::Middleware -  attr_reader :env, :response, :client - -  repo_url = 'https://github.com/unixcharles/acme-client' -  USER_AGENT = "Acme::Client v#{Acme::Client::VERSION} (#{repo_url})".freeze - -  def initialize(app, client:) -    super(app) -    @client = client -  end - -  def call(env) -    @env = env -    @env[:request_headers]['User-Agent'] = USER_AGENT -    @env.body = crypto.generate_signed_jws(header: { nonce: pop_nonce }, payload: env.body) -    @app.call(env).on_complete { |response_env| on_complete(response_env) } -  rescue Faraday::TimeoutError -    raise Acme::Client::Error::Timeout -  end - -  def on_complete(env) -    @env = env - -    raise_on_not_found! -    store_nonce -    env.body = decode_body -    env.response_headers['Link'] = decode_link_headers - -    return if env.success? - -    raise_on_error! -  end - -  private - -  def raise_on_not_found! -    raise Acme::Client::Error::NotFound, env.url.to_s if env.status == 404 -  end - -  def raise_on_error! -    raise error_class, error_message -  end - -  def error_message -    if env.body.is_a? Hash -      env.body['detail'] -    else -      "Error message: #{env.body}" -    end -  end - -  def error_class -    if error_name && !error_name.empty? && Acme::Client::Error.const_defined?(error_name) -      Object.const_get("Acme::Client::Error::#{error_name}") -    else -      Acme::Client::Error -    end -  end - -  def error_name -    @error_name ||= begin -      return unless env.body.is_a?(Hash) -      return unless env.body.key?('type') - -      env.body['type'].gsub('urn:acme:error:', '').split(/[_-]/).map(&:capitalize).join -    end -  end - -  def decode_body -    content_type = env.response_headers['Content-Type'] - -    if content_type == 'application/json' || content_type == 'application/problem+json' -      JSON.load(env.body) -    else -      env.body -    end -  end - -  LINK_MATCH = /<(.*?)>;rel="([\w-]+)"/ - -  def decode_link_headers -    return unless env.response_headers.key?('Link') -    link_header = env.response_headers['Link'] - -    links = link_header.split(', ').map { |entry| -      _, link, name = *entry.match(LINK_MATCH) -      [name, link] -    } - -    Hash[*links.flatten] -  end - -  def store_nonce -    nonces << env.response_headers['replay-nonce'] -  end - -  def pop_nonce -    if nonces.empty? -      get_nonce -    else -      nonces.pop -    end -  end - -  def get_nonce -    response = Faraday.head(env.url, nil, 'User-Agent' => USER_AGENT) -    response.headers['replay-nonce'] -  end - -  def nonces -    client.nonces -  end - -  def private_key -    client.private_key -  end - -  def crypto -    @crypto ||= Acme::Client::Crypto.new(private_key) -  end -end diff --git a/vendor/acme-client/lib/acme/client/resources.rb b/vendor/acme-client/lib/acme/client/resources.rb deleted file mode 100644 index ad55688..0000000 --- a/vendor/acme-client/lib/acme/client/resources.rb +++ /dev/null @@ -1,5 +0,0 @@ -module Acme::Client::Resources; end - -require 'acme/client/resources/registration' -require 'acme/client/resources/challenges' -require 'acme/client/resources/authorization' diff --git a/vendor/acme-client/lib/acme/client/resources/authorization.rb b/vendor/acme-client/lib/acme/client/resources/authorization.rb deleted file mode 100644 index 9ca2e76..0000000 --- a/vendor/acme-client/lib/acme/client/resources/authorization.rb +++ /dev/null @@ -1,44 +0,0 @@ -class Acme::Client::Resources::Authorization -  HTTP01 = Acme::Client::Resources::Challenges::HTTP01 -  DNS01 = Acme::Client::Resources::Challenges::DNS01 -  TLSSNI01 = Acme::Client::Resources::Challenges::TLSSNI01 - -  attr_reader :client, :uri, :domain, :status, :expires, :http01, :dns01, :tls_sni01 - -  def initialize(client, uri, response) -    @client = client -    @uri = uri -    assign_attributes(response.body) -  end - -  def verify_status -    response = @client.connection.get(@uri) - -    assign_attributes(response.body) -    status -  end - -  private - -  def assign_attributes(body) -    @expires = Time.iso8601(body['expires']) if body.key? 'expires' -    @domain = body['identifier']['value'] -    @status = body['status'] -    assign_challenges(body['challenges']) -  end - -  def assign_challenges(challenges) -    challenges.each do |attributes| -      challenge = case attributes.fetch('type') -                  when 'http-01' -                    @http01 ||= HTTP01.new(self) -                  when 'dns-01' -                    @dns01 ||= DNS01.new(self) -                  when 'tls-sni-01' -                    @tls_sni01 ||= TLSSNI01.new(self) -      end - -      challenge.assign_attributes(attributes) if challenge -    end -  end -end diff --git a/vendor/acme-client/lib/acme/client/resources/challenges.rb b/vendor/acme-client/lib/acme/client/resources/challenges.rb deleted file mode 100644 index ec92d47..0000000 --- a/vendor/acme-client/lib/acme/client/resources/challenges.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Acme::Client::Resources::Challenges; end - -require 'acme/client/resources/challenges/base' -require 'acme/client/resources/challenges/http01' -require 'acme/client/resources/challenges/dns01' -require 'acme/client/resources/challenges/tls_sni01' diff --git a/vendor/acme-client/lib/acme/client/resources/challenges/base.rb b/vendor/acme-client/lib/acme/client/resources/challenges/base.rb deleted file mode 100644 index c78c74e..0000000 --- a/vendor/acme-client/lib/acme/client/resources/challenges/base.rb +++ /dev/null @@ -1,43 +0,0 @@ -class Acme::Client::Resources::Challenges::Base -  attr_reader :authorization, :status, :uri, :token, :error - -  def initialize(authorization) -    @authorization = authorization -  end - -  def client -    authorization.client -  end - -  def verify_status -    authorization.verify_status - -    status -  end - -  def request_verification -    response = client.connection.post(@uri, resource: 'challenge', type: challenge_type, keyAuthorization: authorization_key) -    response.success? -  end - -  def assign_attributes(attributes) -    @status = attributes.fetch('status', 'pending') -    @uri = attributes.fetch('uri') -    @token = attributes.fetch('token') -    @error = attributes['error'] -  end - -  private - -  def challenge_type -    self.class::CHALLENGE_TYPE -  end - -  def authorization_key -    "#{token}.#{crypto.thumbprint}" -  end - -  def crypto -    @crypto ||= Acme::Client::Crypto.new(client.private_key) -  end -end diff --git a/vendor/acme-client/lib/acme/client/resources/challenges/dns01.rb b/vendor/acme-client/lib/acme/client/resources/challenges/dns01.rb deleted file mode 100644 index 543f438..0000000 --- a/vendor/acme-client/lib/acme/client/resources/challenges/dns01.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -class Acme::Client::Resources::Challenges::DNS01 < Acme::Client::Resources::Challenges::Base -  CHALLENGE_TYPE = 'dns-01'.freeze -  RECORD_NAME = '_acme-challenge'.freeze -  RECORD_TYPE = 'TXT'.freeze - -  def record_name -    RECORD_NAME -  end - -  def record_type -    RECORD_TYPE -  end - -  def record_content -    crypto.urlsafe_base64(crypto.digest.digest(authorization_key)) -  end -end diff --git a/vendor/acme-client/lib/acme/client/resources/challenges/http01.rb b/vendor/acme-client/lib/acme/client/resources/challenges/http01.rb deleted file mode 100644 index 4966091..0000000 --- a/vendor/acme-client/lib/acme/client/resources/challenges/http01.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -class Acme::Client::Resources::Challenges::HTTP01 < Acme::Client::Resources::Challenges::Base -  CHALLENGE_TYPE = 'http-01'.freeze -  CONTENT_TYPE = 'text/plain'.freeze - -  def content_type -    CONTENT_TYPE -  end - -  def file_content -    authorization_key -  end - -  def filename -    ".well-known/acme-challenge/#{token}" -  end -end diff --git a/vendor/acme-client/lib/acme/client/resources/challenges/tls_sni01.rb b/vendor/acme-client/lib/acme/client/resources/challenges/tls_sni01.rb deleted file mode 100644 index 8f455f5..0000000 --- a/vendor/acme-client/lib/acme/client/resources/challenges/tls_sni01.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class Acme::Client::Resources::Challenges::TLSSNI01 < Acme::Client::Resources::Challenges::Base -  CHALLENGE_TYPE = 'tls-sni-01'.freeze - -  def hostname -    digest = crypto.digest.hexdigest(authorization_key) -    "#{digest[0..31]}.#{digest[32..64]}.acme.invalid" -  end - -  def certificate -    self_sign_certificate.certificate -  end - -  def private_key -    self_sign_certificate.private_key -  end - -  private - -  def self_sign_certificate -    @self_sign_certificate ||= Acme::Client::SelfSignCertificate.new(subject_alt_names: [hostname]) -  end -end diff --git a/vendor/acme-client/lib/acme/client/resources/registration.rb b/vendor/acme-client/lib/acme/client/resources/registration.rb deleted file mode 100644 index b7a4c11..0000000 --- a/vendor/acme-client/lib/acme/client/resources/registration.rb +++ /dev/null @@ -1,37 +0,0 @@ -class Acme::Client::Resources::Registration -  attr_reader :id, :key, :contact, :uri, :next_uri, :recover_uri, :term_of_service_uri - -  def initialize(client, response) -    @client = client -    @uri = response.headers['location'] -    assign_links(response.headers['Link']) -    assign_attributes(response.body) -  end - -  def get_terms -    return unless @term_of_service_uri - -    @client.connection.get(@term_of_service_uri).body -  end - -  def agree_terms -    return true unless @term_of_service_uri - -    response = @client.connection.post(@uri, resource: 'reg', agreement: @term_of_service_uri) -    response.success? -  end - -  private - -  def assign_links(links) -    @next_uri = links['next'] -    @recover_uri = links['recover'] -    @term_of_service_uri = links['terms-of-service'] -  end - -  def assign_attributes(body) -    @id = body['id'] -    @key = body['key'] -    @contact = body['contact'] -  end -end diff --git a/vendor/acme-client/lib/acme/client/self_sign_certificate.rb b/vendor/acme-client/lib/acme/client/self_sign_certificate.rb deleted file mode 100644 index 2e7d98c..0000000 --- a/vendor/acme-client/lib/acme/client/self_sign_certificate.rb +++ /dev/null @@ -1,60 +0,0 @@ -class Acme::Client::SelfSignCertificate -  attr_reader :private_key, :subject_alt_names, :not_before, :not_after - -  extend Forwardable -  def_delegators :certificate, :to_pem, :to_der - -  def initialize(subject_alt_names:, not_before: default_not_before, not_after: default_not_after, private_key: generate_private_key) -    @private_key = private_key -    @subject_alt_names = subject_alt_names -    @not_before = not_before -    @not_after = not_after -  end - -  def certificate -    @certificate ||= begin -      certificate = generate_certificate - -      extension_factory = generate_extension_factory(certificate) -      subject_alt_name_entry = subject_alt_names.map { |d| "DNS: #{d}" }.join(',') -      subject_alt_name_extension = extension_factory.create_extension('subjectAltName', subject_alt_name_entry) -      certificate.add_extension(subject_alt_name_extension) - -      certificate.sign(private_key, digest) -    end -  end - -  private - -  def generate_private_key -    OpenSSL::PKey::RSA.new(2048) -  end - -  def default_not_before -    Time.now - 3600 -  end - -  def default_not_after -    Time.now + 30 * 24 * 3600 -  end - -  def digest -    OpenSSL::Digest::SHA256.new -  end - -  def generate_certificate -    certificate = OpenSSL::X509::Certificate.new -    certificate.not_before = not_before -    certificate.not_after = not_after -    certificate.public_key = private_key.public_key -    certificate.version = 2 -    certificate -  end - -  def generate_extension_factory(certificate) -    extension_factory = OpenSSL::X509::ExtensionFactory.new -    extension_factory.subject_certificate = certificate -    extension_factory.issuer_certificate = certificate -    extension_factory -  end -end diff --git a/vendor/acme-client/lib/acme/client/version.rb b/vendor/acme-client/lib/acme/client/version.rb deleted file mode 100644 index c989c12..0000000 --- a/vendor/acme-client/lib/acme/client/version.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -module Acme -  class Client -    VERSION = '0.4.1'.freeze -  end -end | 
