lecli 0.2.2 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +13 -0
- data/.travis.yml +5 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +9 -1
- data/README.md +71 -17
- data/Rakefile +3 -3
- data/bin/console +4 -4
- data/exe/lecli +36 -41
- data/lecli.gemspec +17 -18
- data/lecli_diagram.png +0 -0
- data/lib/lecli.rb +3 -2
- data/lib/lecli/certificate_builder.rb +114 -63
- data/lib/lecli/version.rb +1 -1
- metadata +26 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da9e05e9c2100f24c17b18b0102812c9a1ef4de5
|
4
|
+
data.tar.gz: 0ded86f70fab0543811022364e58f83b30114df7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94b3f4c61d97431798588137ff64b0a48fba6f906e07396e1908428af9e46f0c883f950d759de42f451b8f51c19eb21100e3d02a8a2698b5ed32fcc8505734af
|
7
|
+
data.tar.gz: 2b9669dded2a38d7bfceb0b6b5f9bffcf806908ed7b4d303815f2d4407734c203a9234a8a7d2f7c0678acefe87492a16beec8445d9499e27dfd418a5c582c6f6
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
3
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
4
4
|
|
5
5
|
# Specify your gem's dependencies in lecli.gemspec
|
6
6
|
gemspec
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
lecli (0.2.
|
4
|
+
lecli (0.2.4)
|
5
5
|
acme-client (~> 2.0.0)
|
6
6
|
thor (~> 0.20.0)
|
7
7
|
|
@@ -11,8 +11,10 @@ GEM
|
|
11
11
|
acme-client (2.0.0)
|
12
12
|
faraday (~> 0.9, >= 0.9.1)
|
13
13
|
diff-lcs (1.3)
|
14
|
+
docile (1.3.1)
|
14
15
|
faraday (0.15.2)
|
15
16
|
multipart-post (>= 1.2, < 3)
|
17
|
+
json (2.1.0)
|
16
18
|
multipart-post (2.0.0)
|
17
19
|
rake (10.5.0)
|
18
20
|
rspec (3.7.0)
|
@@ -28,6 +30,11 @@ GEM
|
|
28
30
|
diff-lcs (>= 1.2.0, < 2.0)
|
29
31
|
rspec-support (~> 3.7.0)
|
30
32
|
rspec-support (3.7.1)
|
33
|
+
simplecov (0.16.1)
|
34
|
+
docile (~> 1.1)
|
35
|
+
json (>= 1.8, < 3)
|
36
|
+
simplecov-html (~> 0.10.0)
|
37
|
+
simplecov-html (0.10.2)
|
31
38
|
thor (0.20.0)
|
32
39
|
|
33
40
|
PLATFORMS
|
@@ -38,6 +45,7 @@ DEPENDENCIES
|
|
38
45
|
lecli!
|
39
46
|
rake (~> 10.0)
|
40
47
|
rspec (~> 3.0)
|
48
|
+
simplecov (~> 0.16.1)
|
41
49
|
|
42
50
|
BUNDLED WITH
|
43
51
|
1.16.0
|
data/README.md
CHANGED
@@ -1,38 +1,92 @@
|
|
1
|
-
#
|
1
|
+
# lecli
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
lecli is a gem that provides a CLI to generate Let's Encrypt certificates. It wraps around the [ACME protocol Client gem](https://github.com/unixcharles/acme-client). It pairs well with cron jobs and the [whenever gem](https://github.com/javan/whenever) for a tighter grip on automation/scripting customization.
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
9
|
-
|
7
|
+
$ gem install lecli
|
8
|
+
|
9
|
+
## Getting started
|
10
|
+
|
11
|
+
The CLI will use the Let's Encrypt staging endpoint unless explicitly passed with the `--production` flag. All other configuration data is managed by a config file - `.lecli.yml`. To help understand the available options you can run the following in your terminal and a sample YAML file will be generated for you
|
10
12
|
|
11
|
-
```
|
12
|
-
|
13
|
+
```
|
14
|
+
$ lecli yaml
|
13
15
|
```
|
14
16
|
|
15
|
-
|
17
|
+
Now let's see what's inside
|
16
18
|
|
17
|
-
|
19
|
+
### `lecli.yml`
|
18
20
|
|
19
|
-
|
21
|
+
```
|
22
|
+
---
|
23
|
+
domains:
|
24
|
+
- example.com
|
25
|
+
common_name: Let's Encrypt
|
26
|
+
account_email: test@account.com
|
27
|
+
request_key: request.pem
|
28
|
+
certificate_key: certificate.pem
|
29
|
+
challenges_relative_path: challenges
|
30
|
+
success_callback_script: deploy.sh
|
31
|
+
```
|
20
32
|
|
21
|
-
|
33
|
+
Most entries are optional, except those that specify the domains you are requesting and "identity fields". Meaning that at least **domains** (list of entries), **common_name** and **account_email** should always appear in order to perform a valid request.
|
22
34
|
|
23
|
-
|
35
|
+
### The flow
|
24
36
|
|
25
|
-
|
37
|
+
From the two available types of validation requests only HTTP (and not DNS) is supported [yet](#contributing). This means you'll need to serve a token (lecli will create them) behind each domain in the **list of domain addresses** requested, on a certain **port**.
|
26
38
|
|
27
|
-
|
39
|
+
The tokens are written to a single **challenges_relative_path** and need to be served behind each domain you are requesting, i.e. `example.com/.well-known/acme-challenge/#{token_filename}`. If requesting multiple domains at once you will need additional setup to route from each domain requested to where the tokens are persisted. When working with a single domain, for example, you can just make this relative path write the tokens on `/usr/share/nginx/html/.well-known/acme-challenge/` if working with an nginx server.
|
40
|
+
|
41
|
+
![alt text](https://github.com/fdoxyz/lecli/blob/master/lecli_diagram.png)
|
42
|
+
|
43
|
+
After Let's Encrypt is able to access both tokens on the list of domain addresses requested the certificates can be issued. The resulting certificate will be identified by the **email** and under the **common_name** provided. The name of the `.pem` files can be customized with **request_key** and **certificate_key**.
|
44
|
+
|
45
|
+
Optionally you can specify a script filename with **success_callback_script**. This script will function as a "callback hook" and it will be run after successfully exporting the domains' certificate.
|
46
|
+
|
47
|
+
In this section you've read about all `lecli.yml` options available (keywords in **bold**). Now, if you've made sure you: (1) Customized the options in this file to create the desired certificate, and (2) made sure the **success_callback_script** path is available for a public internet. You are now ready to kick off the validation process by executing the following on your terminal
|
48
|
+
|
49
|
+
```
|
50
|
+
lecli generate
|
51
|
+
```
|
28
52
|
|
29
|
-
|
53
|
+
### Making use of the result Certificates
|
30
54
|
|
31
|
-
|
55
|
+
A simple example `nginx.conf` excerpt to make use of the result certificates could be the following
|
56
|
+
|
57
|
+
```
|
58
|
+
server {
|
59
|
+
listen 443 ssl;
|
60
|
+
server_name example.com;
|
61
|
+
|
62
|
+
ssl_certificate /etc/nginx/ssl/request.pem;
|
63
|
+
ssl_certificate_key /etc/nginx/ssl/certificate.pem;
|
64
|
+
|
65
|
+
...
|
66
|
+
}
|
67
|
+
```
|
68
|
+
|
69
|
+
You can script a server restart if needed, or any other setup that you require to make use of the newly created certificates. Just make sure to point the **success_callback_script** path in your config file so the CLI can automatically execute it if the request result was success.
|
70
|
+
|
71
|
+
If you pair the CLI with a cron-job (specially using the [whenever](https://github.com/javan/whenever) gem) you've essentially put together a Let's Encrypt bot and can now leverage scripting for complex deployments. Your certificates will be renewed periodically. When using **whenever** you'll have lecli CLI in your crontab as easy as:
|
72
|
+
|
73
|
+
```
|
74
|
+
every :month, at: '4am' do
|
75
|
+
command "lecli --production -f /path/to/config/file.yml"
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
Be sure to run `lecli help` for more details.
|
80
|
+
|
81
|
+
## Development
|
82
|
+
|
83
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`.
|
32
84
|
|
33
85
|
## Contributing
|
34
86
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
87
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/fdoxyz/lecli.
|
88
|
+
|
89
|
+
Please include tests if new features are added and make sure rubocop styling guide is met.
|
36
90
|
|
37
91
|
## License
|
38
92
|
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'lecli'
|
5
5
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
8
8
|
|
9
9
|
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require
|
10
|
+
# require 'pry'
|
11
11
|
# Pry.start
|
12
12
|
|
13
|
-
require
|
13
|
+
require 'irb'
|
14
14
|
IRB.start(__FILE__)
|
data/exe/lecli
CHANGED
@@ -1,60 +1,55 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require 'thor'
|
4
|
+
require 'yaml'
|
5
|
+
require 'lecli'
|
6
6
|
|
7
|
+
# Class in charge of the CLI functionallity powered by Thor
|
7
8
|
class LECLIRunner < Thor
|
8
|
-
|
9
|
-
YAML_FILENAME = ".lecli.yml"
|
10
|
-
|
11
|
-
desc "version", "Prints out the gem version"
|
9
|
+
desc 'version', 'Prints out the gem version'
|
12
10
|
def version
|
13
|
-
puts
|
11
|
+
puts LECLI::VERSION
|
14
12
|
end
|
15
13
|
|
16
|
-
desc
|
14
|
+
desc 'yaml', 'Generates the options file with defaults to customize'
|
17
15
|
option :override,
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
type: :boolean,
|
17
|
+
aliases: [:o],
|
18
|
+
desc: 'Overrides the existing options file the defaults.'
|
21
19
|
def yaml
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
puts YAML_FILENAME
|
26
|
-
else
|
27
|
-
puts "ERROR: #{YAML_FILENAME} file already exists. Try `lecli help yaml`"
|
28
|
-
end
|
20
|
+
LECLI::CertificateBuilder.persist_defaults_file(
|
21
|
+
override: options[:override]
|
22
|
+
)
|
29
23
|
end
|
30
24
|
|
31
|
-
desc
|
25
|
+
desc 'generate', 'Requests and outputs Let\'s Encrypt SSL Certificates'
|
32
26
|
option :production,
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
type: :boolean,
|
28
|
+
aliases: [:p],
|
29
|
+
desc: 'Use Let\'s Encrypt production API endpoint.'
|
30
|
+
option :config_file,
|
31
|
+
default: '.lecli.yml',
|
32
|
+
aliases: [:f],
|
33
|
+
desc: 'Specify the path of the configuration file.'
|
36
34
|
def generate
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
rescue
|
44
|
-
puts "ERROR: Invalid format for file #{YAML_FILENAME}"
|
45
|
-
return 1
|
35
|
+
puts "LOL: #{options[:config_file]}"
|
36
|
+
return
|
37
|
+
opts = LECLI::CertificateBuilder.load_options(config_file: config_file)
|
38
|
+
if opts.nil? # Bail if options can't be loaded properly
|
39
|
+
puts 'Unable to locate .lecli.yml file. Try `lecli help generate`'
|
40
|
+
return
|
46
41
|
end
|
47
42
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
puts "Certificates generated successfully!"
|
52
|
-
|
53
|
-
script_path = hash["success_callback_script"]
|
54
|
-
if File.file?(script_path)
|
55
|
-
puts "Execution attempt of success callback script `#{script_path}`"
|
56
|
-
`./#{script_path}`
|
43
|
+
builder = LECLI::CertificateBuilder.new do |b|
|
44
|
+
b.production = options[:production]
|
57
45
|
end
|
46
|
+
builder.generate_certs(opts)
|
47
|
+
puts 'Certificates generated successfully!'
|
48
|
+
|
49
|
+
script_path = File.expand_path(opts['success_callback_script'])
|
50
|
+
return if File.file?(script_path)
|
51
|
+
puts "Executing now success callback script `#{script_path}`..."
|
52
|
+
`./#{script_path}`
|
58
53
|
end
|
59
54
|
end
|
60
55
|
|
data/lecli.gemspec
CHANGED
@@ -1,31 +1,30 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
3
|
+
require 'lecli/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
6
|
+
spec.name = 'lecli'
|
8
7
|
spec.version = LECLI::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
8
|
+
spec.authors = ['Fernando Valverde Arredondo']
|
9
|
+
spec.email = ['fdov88@gmail.com']
|
11
10
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
15
|
-
spec.license =
|
11
|
+
spec.summary = 'CLI to generate Let\'s Encrypt certificates'
|
12
|
+
spec.description = 'CLI to generate Let\'s Encrypt certificates'
|
13
|
+
spec.homepage = 'https://github.com/fdoxyz/lecli'
|
14
|
+
spec.license = 'MIT'
|
16
15
|
|
17
16
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
17
|
f.match(%r{^(test|spec|features)/})
|
19
18
|
end
|
20
|
-
spec.bindir =
|
19
|
+
spec.bindir = 'exe'
|
21
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
-
spec.require_paths = [
|
23
|
-
|
24
|
-
spec.add_dependency "thor", "~> 0.20.0"
|
25
|
-
spec.add_dependency "acme-client", "~> 2.0.0"
|
21
|
+
spec.require_paths = ['lib']
|
26
22
|
|
27
|
-
spec.
|
28
|
-
spec.
|
29
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
23
|
+
spec.add_dependency 'acme-client', '~> 2.0.0'
|
24
|
+
spec.add_dependency 'thor', '~> 0.20.0'
|
30
25
|
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
29
|
+
spec.add_development_dependency 'simplecov', '~> 0.16.1'
|
31
30
|
end
|
data/lecli_diagram.png
ADDED
Binary file
|
data/lib/lecli.rb
CHANGED
@@ -1,92 +1,143 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require 'thor'
|
2
|
+
require 'acme-client'
|
3
|
+
require 'uri'
|
4
|
+
require 'fileutils'
|
5
5
|
|
6
6
|
module LECLI
|
7
|
+
# Helper class to generate certs and access the default options
|
7
8
|
class CertificateBuilder
|
9
|
+
attr_accessor :production
|
8
10
|
|
9
|
-
|
11
|
+
YAML_FILENAME = '.lecli.yml'.freeze
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@challenges = []
|
15
|
+
@production = false
|
16
|
+
|
17
|
+
# Pass a block to edit the new object for prod/staging or other options
|
18
|
+
yield self if block_given?
|
19
|
+
|
20
|
+
prod_url = 'https://acme-v02.api.letsencrypt.org/directory'
|
21
|
+
staging_url = 'https://acme-staging-v02.api.letsencrypt.org/directory'
|
22
|
+
@endpoint = @production ? prod_url : staging_url
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.default_options
|
10
26
|
{
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
"success_callback_script" => "deploy.sh"
|
27
|
+
'domains' => ['example.com'],
|
28
|
+
'common_name' => 'Let\'s Encrypt',
|
29
|
+
'account_email' => 'test@account.com',
|
30
|
+
'request_key' => 'request.pem',
|
31
|
+
'certificate_key' => 'certificate.pem',
|
32
|
+
'challenges_relative_path' => 'challenges',
|
33
|
+
'success_callback_script' => 'deploy.sh'
|
19
34
|
}
|
20
35
|
end
|
21
36
|
|
22
|
-
def self.
|
23
|
-
|
24
|
-
|
37
|
+
def self.load_options(config_file:)
|
38
|
+
opts = LECLI::CertificateBuilder.default_options
|
39
|
+
opts.merge(YAML.load_file(config_file)) if File.file?(config_file)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.persist_defaults_file(override:)
|
43
|
+
opts = LECLI::CertificateBuilder.default_options
|
44
|
+
if !File.file?(YAML_FILENAME) || override
|
45
|
+
File.write(YAML_FILENAME, opts.to_yaml)
|
46
|
+
puts YAML_FILENAME
|
25
47
|
else
|
26
|
-
|
48
|
+
puts "#{YAML_FILENAME} already exists. Try `lecli help yaml`"
|
27
49
|
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def generate_certs(options)
|
53
|
+
request_challenges(options: options)
|
54
|
+
sleep(3) # We are unaware of challenge hosting, better give them some time
|
28
55
|
|
29
|
-
|
30
|
-
|
56
|
+
request_challenge_validation
|
57
|
+
request_key = finalize_order(
|
58
|
+
domains: options['domains'],
|
59
|
+
title: options['common_name']
|
60
|
+
)
|
61
|
+
|
62
|
+
write_certificate(
|
63
|
+
cert: @order.certificate, relative_path: options['certificate_key']
|
64
|
+
)
|
65
|
+
write_certificate(
|
66
|
+
cert: request_key, relative_path: options['request_key']
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def request_challenges(options:)
|
73
|
+
create_order(email: options['account_email'], domains: options['domains'])
|
74
|
+
setup_challenges_dir(relative_path: options['challenges_relative_path'])
|
75
|
+
persist_challenge_tokens
|
76
|
+
end
|
77
|
+
|
78
|
+
def write_certificate(cert:, relative_path:)
|
79
|
+
full_path = File.expand_path(relative_path)
|
80
|
+
File.write(full_path, cert)
|
81
|
+
end
|
82
|
+
|
83
|
+
def finalize_order(domains:, title:)
|
84
|
+
request_key = OpenSSL::PKey::RSA.new(4096)
|
85
|
+
csr = Acme::Client::CertificateRequest.new(
|
86
|
+
private_key: request_key,
|
87
|
+
names: domains.values,
|
88
|
+
subject: { common_name: title }
|
89
|
+
)
|
90
|
+
@order.finalize(csr: csr)
|
91
|
+
sleep(1) while @order.status == 'processing'
|
92
|
+
request_key
|
93
|
+
end
|
94
|
+
|
95
|
+
def create_order(email:, domains:)
|
96
|
+
pkey = OpenSSL::PKey::RSA.new(4096)
|
97
|
+
client = Acme::Client.new(private_key: pkey, directory: @endpoint)
|
31
98
|
client.new_account(
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
order = client.new_order(identifiers:
|
36
|
-
|
37
|
-
# Setup if necessary & clear challenges directory
|
38
|
-
challenges_dir = File.expand_path(options["challenges_relative_path"])
|
39
|
-
FileUtils.mkdir_p(challenges_dir)
|
40
|
-
FileUtils.rm(Dir[File.join(challenges_dir, "*")])
|
41
|
-
|
42
|
-
challenges = []
|
43
|
-
order.authorizations.each do |authorization|
|
44
|
-
challenge = authorization.http
|
45
|
-
token_path = File.join(challenges_dir, challenge.token)
|
46
|
-
File.write(token_path, challenge.file_content)
|
47
|
-
challenges << challenge
|
48
|
-
end
|
99
|
+
contact: "mailto:#{email}",
|
100
|
+
terms_of_service_agreed: true
|
101
|
+
)
|
102
|
+
@order = client.new_order(identifiers: domains)
|
103
|
+
end
|
49
104
|
|
50
|
-
|
105
|
+
def setup_challenges_dir(relative_path:)
|
106
|
+
@challenges_dir = File.expand_path(relative_path)
|
107
|
+
FileUtils.mkdir_p(@challenges_dir)
|
108
|
+
FileUtils.rm(Dir[File.join(@challenges_dir, '*')])
|
109
|
+
end
|
51
110
|
|
111
|
+
def request_challenge_validation
|
52
112
|
wait_time = 5
|
53
113
|
pending = true
|
54
114
|
while pending
|
55
|
-
challenges.each do |challenge|
|
115
|
+
@challenges.each do |challenge|
|
56
116
|
begin
|
57
117
|
challenge.request_validation
|
58
118
|
rescue Acme::Client::Error::Malformed
|
59
|
-
print
|
119
|
+
print '.'
|
60
120
|
end
|
61
121
|
end
|
62
122
|
|
63
|
-
status = challenges.map(&:status)
|
64
|
-
pending = status.include?(
|
65
|
-
|
66
|
-
if pending
|
67
|
-
puts "At least one challenge still pending, waiting #{wait_time}s ..."
|
68
|
-
sleep(wait_time)
|
123
|
+
status = @challenges.map(&:status)
|
124
|
+
pending = status.include?('pending')
|
69
125
|
|
70
|
-
|
71
|
-
|
72
|
-
|
126
|
+
next unless pending
|
127
|
+
puts "At least one challenge still pending, waiting #{wait_time}s ..."
|
128
|
+
sleep(wait_time)
|
129
|
+
wait_time *= 2 if wait_time < 640 # Gradually increment retry max ~10min
|
73
130
|
end
|
74
|
-
puts
|
75
|
-
|
76
|
-
request_key = OpenSSL::PKey::RSA.new(4096)
|
77
|
-
csr = Acme::Client::CertificateRequest.new(
|
78
|
-
private_key: request_key,
|
79
|
-
names: domains.values,
|
80
|
-
subject: { common_name: options["common_name"] }
|
81
|
-
)
|
82
|
-
order.finalize(csr: csr)
|
83
|
-
sleep(1) while order.status == "processing"
|
84
|
-
|
85
|
-
certificate_path = File.expand_path(options["certificate_key"])
|
86
|
-
File.write(certificate_path, order.certificate)
|
87
|
-
request_path = File.expand_path(options["request_key"])
|
88
|
-
File.write(request_path, request_key)
|
131
|
+
puts 'Challenges are all valid now!'
|
89
132
|
end
|
90
133
|
|
134
|
+
def persist_challenge_tokens
|
135
|
+
@order.authorizations.each do |authorization|
|
136
|
+
challenge = authorization.http
|
137
|
+
token_path = File.join(@challenges_dir, challenge.token)
|
138
|
+
File.write(token_path, challenge.file_content)
|
139
|
+
@challenges << challenge
|
140
|
+
end
|
141
|
+
end
|
91
142
|
end
|
92
143
|
end
|
data/lib/lecli/version.rb
CHANGED
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lecli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fernando Valverde Arredondo
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-07-
|
11
|
+
date: 2018-07-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: acme-client
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 2.0.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 2.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: thor
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 0.20.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 0.20.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,7 +80,21 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '3.0'
|
83
|
-
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.16.1
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.16.1
|
97
|
+
description: CLI to generate Let's Encrypt certificates
|
84
98
|
email:
|
85
99
|
- fdov88@gmail.com
|
86
100
|
executables:
|
@@ -90,6 +104,7 @@ extra_rdoc_files: []
|
|
90
104
|
files:
|
91
105
|
- ".gitignore"
|
92
106
|
- ".rspec"
|
107
|
+
- ".rubocop.yml"
|
93
108
|
- ".travis.yml"
|
94
109
|
- Gemfile
|
95
110
|
- Gemfile.lock
|
@@ -100,6 +115,7 @@ files:
|
|
100
115
|
- bin/setup
|
101
116
|
- exe/lecli
|
102
117
|
- lecli.gemspec
|
118
|
+
- lecli_diagram.png
|
103
119
|
- lib/lecli.rb
|
104
120
|
- lib/lecli/certificate_builder.rb
|
105
121
|
- lib/lecli/version.rb
|
@@ -126,5 +142,5 @@ rubyforge_project:
|
|
126
142
|
rubygems_version: 2.6.14
|
127
143
|
signing_key:
|
128
144
|
specification_version: 4
|
129
|
-
summary: Let's Encrypt
|
145
|
+
summary: CLI to generate Let's Encrypt certificates
|
130
146
|
test_files: []
|