acme-client 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +56 -18
- data/acme-client.gemspec +2 -0
- data/lib/acme/client.rb +11 -0
- data/lib/acme/client/resources/challenges/base.rb +4 -0
- data/lib/acme/client/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 056d35b4c628c93ad60a134ab46044cdbc5f9e38
|
4
|
+
data.tar.gz: d90a2d072631c7f68e9ea783e1a8878bc844314f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8b93b207b8016d1464f9e9f4fa20e7e303554eee08ac01eed069de34db084318dfb440e78d1ef07978bbd91ae385c8eaae3350b18b5f806c8a170d6bb776a0a
|
7
|
+
data.tar.gz: a23f0b2e33d7434abfe4e77bcb51e4d4d867cff629781f820704a484c89c0a6abfab028099542936c539b81c0e11d0abd9a05da4a96485425a91cb1034847ba0
|
data/README.md
CHANGED
@@ -3,18 +3,37 @@
|
|
3
3
|
|
4
4
|
`acme-client` is a client implementation of the [ACME](https://letsencrypt.github.io/acme-spec) protocol in Ruby.
|
5
5
|
|
6
|
-
You can find the
|
6
|
+
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.
|
7
7
|
|
8
|
-
ACME is part of the [Letsencrypt](https://letsencrypt.org/) project,
|
8
|
+
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.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Via Rubygems:
|
13
|
+
|
14
|
+
$ gem install acme-client
|
15
|
+
|
16
|
+
Or add it to a Gemfile:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'acme-client'
|
20
|
+
```
|
9
21
|
|
10
22
|
## Usage
|
11
23
|
|
24
|
+
### Register client
|
25
|
+
|
26
|
+
In order to authenticate our client, we have to create an account for it.
|
27
|
+
|
12
28
|
```ruby
|
13
29
|
# We're going to need a private key.
|
14
30
|
require 'openssl'
|
15
|
-
private_key = OpenSSL::PKey::RSA.new(
|
31
|
+
private_key = OpenSSL::PKey::RSA.new(4096)
|
16
32
|
|
17
33
|
# We need an ACME server to talk to, see github.com/letsencrypt/boulder
|
34
|
+
# WARNING: This endpoint is the production endpoint, which is rate limited and will produce valid certificates.
|
35
|
+
# You should probably use the staging endpoint for all your experimentation:
|
36
|
+
# endpoint = 'https://acme-staging.api.letsencrypt.org/'
|
18
37
|
endpoint = 'https://acme-v01.api.letsencrypt.org/'
|
19
38
|
|
20
39
|
# Initialize the client
|
@@ -24,18 +43,24 @@ client = Acme::Client.new(private_key: private_key, endpoint: endpoint)
|
|
24
43
|
# If the private key is not known to the server, we need to register it for the first time.
|
25
44
|
registration = client.register(contact: 'mailto:contact@example.com')
|
26
45
|
|
27
|
-
# You
|
46
|
+
# 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)
|
28
47
|
registration.agree_terms
|
48
|
+
```
|
49
|
+
|
50
|
+
### Authorize for domain
|
29
51
|
|
30
|
-
|
52
|
+
Before you are able to obtain certificates for your domain, you have to prove that you are in control of it.
|
31
53
|
|
32
|
-
|
54
|
+
```ruby
|
33
55
|
authorization = client.authorize(domain: 'example.org')
|
34
56
|
|
35
|
-
#
|
57
|
+
# This example is using the http-01 challenge type. Other challenges are dns-01 or tls-sni-01.
|
36
58
|
challenge = authorization.http01
|
37
59
|
|
38
|
-
# The http-01 method will require you to
|
60
|
+
# The http-01 method will require you to respond to a HTTP request.
|
61
|
+
|
62
|
+
# You can retrieve the challenge token
|
63
|
+
challenge.token # => "some_token"
|
39
64
|
|
40
65
|
# You can retrieve the expected path for the file.
|
41
66
|
challenge.filename # => ".well-known/acme-challenge/:some_token"
|
@@ -43,38 +68,51 @@ challenge.filename # => ".well-known/acme-challenge/:some_token"
|
|
43
68
|
# You can generate the body of the expected response.
|
44
69
|
challenge.file_content # => 'string token and JWK thumbprint'
|
45
70
|
|
46
|
-
# You
|
71
|
+
# You are not required to send a Content-Type. This method will return the right Content-Type should you decide to include one.
|
47
72
|
challenge.content_type
|
48
73
|
|
49
|
-
# Save the file. We'll create a public directory to serve it from, and we'll
|
74
|
+
# Save the file. We'll create a public directory to serve it from, and inside it we'll create the challenge file.
|
50
75
|
FileUtils.mkdir_p( File.join( 'public', File.dirname( challenge.filename ) ) )
|
51
76
|
|
52
|
-
#
|
77
|
+
# We'll write the content of the file
|
53
78
|
File.write( File.join( 'public', challenge.filename), challenge.file_content )
|
54
79
|
|
55
|
-
#
|
56
|
-
|
80
|
+
# Optionally save the challenge for use at another time (eg: by a background job processor)
|
81
|
+
File.write('challenge', challenge.to_h.to_json)
|
82
|
+
|
83
|
+
# The challenge file can be served with a Ruby webserver.
|
84
|
+
# You can run a webserver in another console for that purpose. You may need to forward ports on your router.
|
85
|
+
#
|
86
|
+
# $ ruby -run -e httpd public -p 8080 --bind-address 0.0.0.0
|
57
87
|
|
88
|
+
# Load a saved challenge. This is only required if you need to reuse a saved challenge as outlined above.
|
89
|
+
challenge = client.challenge_from_hash(JSON.parse(File.read('challenge')))
|
58
90
|
|
59
91
|
# Once you are ready to serve the confirmation request you can proceed.
|
60
92
|
challenge.request_verification # => true
|
61
93
|
challenge.verify_status # => 'pending'
|
62
94
|
|
63
|
-
# Wait a bit for the server to make the request, or
|
95
|
+
# Wait a bit for the server to make the request, or just blink. It should be fast.
|
64
96
|
sleep(1)
|
65
97
|
|
66
98
|
challenge.verify_status # => 'valid'
|
99
|
+
```
|
100
|
+
|
101
|
+
### Obtain a certificate
|
67
102
|
|
103
|
+
Now that your account is authorized for the domain, you should be able to obtain a certificate for it.
|
104
|
+
|
105
|
+
```ruby
|
68
106
|
# We're going to need a certificate signing request. If not explicitly
|
69
107
|
# specified, the first name listed becomes the common name.
|
70
108
|
csr = Acme::Client::CertificateRequest.new(names: %w[example.org www.example.org])
|
71
109
|
|
72
|
-
# We can now request a certificate
|
73
|
-
# a valid DER encoded CSR when calling to_der on it
|
74
|
-
# OpenSSL::X509::Request too.
|
110
|
+
# We can now request a certificate. You can pass anything that returns
|
111
|
+
# a valid DER encoded CSR when calling to_der on it. For example an
|
112
|
+
# OpenSSL::X509::Request should work too.
|
75
113
|
certificate = client.new_certificate(csr) # => #<Acme::Client::Certificate ....>
|
76
114
|
|
77
|
-
# Save the certificate and key
|
115
|
+
# Save the certificate and the private key to files
|
78
116
|
File.write("privkey.pem", certificate.request.private_key.to_pem)
|
79
117
|
File.write("cert.pem", certificate.to_pem)
|
80
118
|
File.write("chain.pem", certificate.chain_to_pem)
|
data/acme-client.gemspec
CHANGED
@@ -15,6 +15,8 @@ Gem::Specification.new do |spec|
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
16
|
spec.require_paths = ['lib']
|
17
17
|
|
18
|
+
spec.required_ruby_version = '>= 2.1.0'
|
19
|
+
|
18
20
|
spec.add_development_dependency 'bundler', '~> 1.6', '>= 1.6.9'
|
19
21
|
spec.add_development_dependency 'rake', '~> 10.0'
|
20
22
|
spec.add_development_dependency 'rspec', '~> 3.3', '>= 3.3.0'
|
data/lib/acme/client.rb
CHANGED
@@ -68,6 +68,17 @@ class Acme::Client
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
+
def challenge_from_hash(attributes)
|
72
|
+
case attributes.fetch('type')
|
73
|
+
when 'http-01'
|
74
|
+
Acme::Client::Resources::Challenges::HTTP01.new(self, attributes)
|
75
|
+
when 'dns-01'
|
76
|
+
Acme::Client::Resources::Challenges::DNS01.new(self, attributes)
|
77
|
+
when 'tls-sni-01'
|
78
|
+
Acme::Client::Resources::Challenges::TLSSNI01.new(self, attributes)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
71
82
|
private
|
72
83
|
|
73
84
|
def fetch_chain(response, limit = 10)
|
data/lib/acme/client/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acme-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charles Barbier
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -191,7 +191,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
191
191
|
requirements:
|
192
192
|
- - ">="
|
193
193
|
- !ruby/object:Gem::Version
|
194
|
-
version:
|
194
|
+
version: 2.1.0
|
195
195
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
196
196
|
requirements:
|
197
197
|
- - ">="
|