letsencrypt-cli 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 101dcc9fbc8cd59d85926f5fc726c13393306292
4
+ data.tar.gz: b829a1224e1b3f9ae59d656034cf791f958c978b
5
+ SHA512:
6
+ metadata.gz: c267226d7a856b80b6b8979acba3682687444546cdd2fe9adfc08990ad88ba1800ed48360f9148ea6fd2537d3dd6d8374a32018f8e40caa6250f51268ba05c5d
7
+ data.tar.gz: eab63e043424e82abc9e95d639deb339bd61a693983c6cbee4a6e925517b2f2b3d1ec4f0055131d81e2047fe6bdb7cf0c20cffbb1f3cc8a38df51e8f575da278
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.pem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,16 @@
1
+ language: ruby
2
+ sudo: false
3
+ cache: bundler
4
+
5
+ rvm:
6
+ - 2.2
7
+ - 2.1
8
+ # - 2.0
9
+
10
+ # before_script:
11
+ # - "bundle exec rake db:schema:load RAILS_ENV=test"
12
+
13
+ script:
14
+ - bundle exec rspec
15
+ # - ./exe/letsencrypt-cli
16
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in letsencrypt-cli.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 TODO: Write your name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # Letsencrypt::Cli
2
+
3
+ [![Build Status](https://travis-ci.org/zealot128/ruby-letsencrypt-cli.svg?branch=travis)](https://travis-ci.org/zealot128/ruby-letsencrypt-cli)
4
+ [![Gem Version](https://badge.fury.io/rb/letsencrypt-cli.svg)](https://badge.fury.io/rb/letsencrypt-cli)
5
+
6
+ Yet another Letsencrypt client using Ruby.
7
+
8
+ ## Installation
9
+
10
+ * This tool needs Ruby > 2.0 (as the dependency acme needs that).
11
+ * openssl bindings
12
+ * no sudo! (Just access to webserver root .well-known alias)
13
+
14
+ $ gem install letsencrypt-cli
15
+
16
+ ## Usage
17
+
18
+ Specify ``-t`` to use Letsencrypt test server. Without it, all requests are called against the production server, that might have same more strict rate limiting. If you are just toying around, add the -t flag.
19
+
20
+ ```bash
21
+ # show all commands
22
+
23
+ letsencrypt-cli help
24
+
25
+ # show options for an individual command
26
+ letsencrypt-cli help cert
27
+
28
+ # creates account_key.json in current_dir
29
+ letsencrypt-cli register -t myemail@example.com
30
+
31
+
32
+ # authorize one or more domains/subdomains
33
+ letsencrypt-cli authorize -t --webroot-path /var/www/default example.com www.example.com somedir.example.com
34
+
35
+ # experimental: authorize all server_names in /etc/nginx/sites-enabled/*
36
+ letsencrypt-cli authorize_all -t --webroot-path /var/www/default
37
+
38
+
39
+ # create a certificate for before authorized domains.
40
+ # the first domain will be the cn subject. All other are subjectAlternateName
41
+ letsencrypt-cli cert -t example.com www.example.com somdir.example.com
42
+ # will create key.pem fullchain.pem chain.pem and cert.pem
43
+ ```
44
+
45
+
46
+ ## Example integration nginx:
47
+
48
+
49
+ ```nginx
50
+ server {
51
+ listen 80;
52
+ server_name example.com www.example.com somedir.example.com
53
+ location /.well-known/acme-challenge {
54
+ alias /home/letsencrypt/webroot/.well-known/acme-challenge;
55
+ default_type "text/plain";
56
+ try_files $uri =404;
57
+ }
58
+ ```
59
+
60
+ notice the location - alias. Use this dir with ``--webroot-path`` for authorization.
61
+
62
+ Afterwards, use the fullchain.pem and key.pem:
63
+
64
+ ```nginx
65
+ server {
66
+ listen 443 ssl;
67
+ server_name stefanwienert.de www.stefanwienert.de;
68
+ ssl on;
69
+ ssl_certificate_key /path/to/key.pem;
70
+ ssl_certificate /path/to/fullchain.pem;
71
+
72
+ # use the settings from: https://gist.github.com/konklone/6532544
73
+ ```
74
+
75
+ ## Development
76
+
77
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
78
+
79
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
80
+
81
+ ## Contributing
82
+
83
+ 1. Fork it ( https://github.com/zealot128/letsencrypt-cli/fork )
84
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
85
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
86
+ 4. Push to the branch (`git push origin my-new-feature`)
87
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "letsencrypt/cli"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "letsencrypt-cli"
4
+
5
+ Letsencrypt::Cli::App.start
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'letsencrypt/cli/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "letsencrypt-cli"
8
+ spec.version = Letsencrypt::Cli::VERSION
9
+ spec.authors = ["Stefan Wienert"]
10
+ spec.email = ["stwienert@gmail.com"]
11
+
12
+ spec.summary = %q{slim letsencrypt client for quickly authorizing (multiple) domains and issuing certificates}
13
+ spec.homepage = "https://github.com/zealot28/letsencrypt-cli"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = '>= 2.0.0'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_runtime_dependency 'acme-client'
23
+ spec.add_runtime_dependency 'thor'
24
+ spec.add_runtime_dependency 'colorize'
25
+
26
+ spec.add_development_dependency 'pry'
27
+ spec.add_development_dependency 'simplecov'
28
+ spec.add_development_dependency 'vcr', "~> 3.0"
29
+ spec.add_development_dependency 'webmock', "~> 1.22"
30
+ spec.add_development_dependency 'timecop', "~> 0.8"
31
+ spec.add_development_dependency "bundler", "~> 1.7"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ spec.add_development_dependency "rspec", "~> 3.0"
34
+ end
@@ -0,0 +1 @@
1
+ require 'letsencrypt/cli'
@@ -0,0 +1,10 @@
1
+ require "letsencrypt/cli/version"
2
+ require "letsencrypt/cli/acme_wrapper"
3
+ require "letsencrypt/cli/app"
4
+
5
+
6
+ module Letsencrypt
7
+ module Cli
8
+ # Your code goes here...
9
+ end
10
+ end
@@ -0,0 +1,131 @@
1
+ require 'json'
2
+ require 'acme-client'
3
+ require 'pry'
4
+
5
+ class AcmeWrapper
6
+ def initialize(options)
7
+ @options = options
8
+ if !@options[:color]
9
+ String.disable_colorization = true
10
+ end
11
+ end
12
+
13
+ def log(message, severity=:info)
14
+ @logger ||= Logger.new(STDOUT).tap {|logger|
15
+ logger.level = Logger::SEV_LABEL.index(@options[:log_level].upcase)
16
+ logger.formatter = proc do |sev, datetime, progname, msg|
17
+ "#{datetime.to_s.light_black}: #{msg}\n"
18
+ end
19
+ }
20
+ @logger.send(severity, message)
21
+ end
22
+
23
+ def client
24
+ @client ||= Acme::Client.new(private_key: account_key, endpoint: endpoint)
25
+ end
26
+
27
+ def authorize(domain)
28
+ FileUtils.mkdir_p(@options[:webroot_path])
29
+ log "Authorizing #{domain.blue}.."
30
+ authorization = client.authorize(domain: domain)
31
+
32
+ challenge = authorization.http01
33
+
34
+ challenge_file = File.join(@options[:webroot_path], challenge.filename.split('/').last)
35
+ log "Writing challenge to #{challenge_file}", :debug
36
+ File.write(challenge_file, challenge.file_content)
37
+
38
+ challenge.request_verification
39
+
40
+ 5.times do
41
+ log "Checking verification...", :debug
42
+ sleep 1
43
+ break if challenge.verify_status != 'pending'
44
+ end
45
+ if challenge.verify_status == 'valid'
46
+ log "Authorization successful for #{domain.green}"
47
+ File.unlink(challenge_file)
48
+ true
49
+ else
50
+ log "Authorization error for #{domain.red}", :error
51
+ log challenge.error['detail']
52
+ false
53
+ end
54
+ end
55
+
56
+ def cert(domains)
57
+ return if certificate_exists_and_valid?
58
+ csr = OpenSSL::X509::Request.new
59
+ certificate_private_key = find_or_create_pkey(@options[:private_key_path], "private key", @options[:key_length] || 2048)
60
+
61
+ csr.subject = OpenSSL::X509::Name.new([
62
+ # ['C', options[:country], OpenSSL::ASN1::PRINTABLESTRING],
63
+ # ['ST', options[:state], OpenSSL::ASN1::PRINTABLESTRING],
64
+ # ['L', options[:city], OpenSSL::ASN1::PRINTABLESTRING],
65
+ # ['O', options[:organization], OpenSSL::ASN1::UTF8STRING],
66
+ # ['OU', options[:department], OpenSSL::ASN1::UTF8STRING],
67
+ # ['CN', options[:common_name], OpenSSL::ASN1::UTF8STRING],
68
+ # ['emailAddress', options[:email], OpenSSL::ASN1::UTF8STRING]
69
+ ['CN', domains.first, OpenSSL::ASN1::UTF8STRING]
70
+ ])
71
+ if domains.count > 1
72
+ ef = OpenSSL::X509::ExtensionFactory.new
73
+ exts = [ ef.create_extension( "subjectAltName", domains.map{|domain| "DNS:#{domain}"}.join(','), false ) ]
74
+ attrval = OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence(exts)])
75
+ attrs = [
76
+ OpenSSL::X509::Attribute.new('extReq', attrval),
77
+ OpenSSL::X509::Attribute.new('msExtReq', attrval),
78
+ ]
79
+ attrs.each do |attr|
80
+ csr.add_attribute(attr)
81
+ end
82
+ end
83
+ csr.public_key = certificate_private_key.public_key
84
+ csr.sign(certificate_private_key, OpenSSL::Digest::SHA256.new)
85
+ certificate = client.new_certificate(csr)
86
+ File.write(@options[:fullchain_path], certificate.fullchain_to_pem)
87
+ File.write(@options[:chain_path], certificate.chain_to_pem)
88
+ File.write(@options[:certificate_path], certificate.to_pem)
89
+ log "Certificate successfully created to #{@options[:fullchain_path]} #{@options[:chain_path]} and #{@options[:certificate_path]}!".green
90
+ log "Certificate valid until: #{certificate.x509.not_after}"
91
+ end
92
+
93
+ private
94
+
95
+ def certificate_exists_and_valid?
96
+ if File.exists?(@options[:certificate_path])
97
+ cert = OpenSSL::X509::Certificate.new(File.read(@options[:certificate_path]))
98
+ renew_on = cert.not_after.to_date - @options[:days_valid]
99
+ if renew_on > Date.today
100
+ log "Certificate '#{@options[:certificate_path]}' still valid till #{cert.not_after.to_date}.", :warn
101
+ log "Won't renew until #{renew_on} (#{@options[:days_valid]} days before)", :warn
102
+ exit 1
103
+ end
104
+ end
105
+ end
106
+
107
+ def endpoint
108
+ if @options[:test]
109
+ "https://acme-staging.api.letsencrypt.org"
110
+ else
111
+ ""
112
+ end
113
+ end
114
+
115
+
116
+ def account_key
117
+ @account_key ||= find_or_create_pkey(@options[:account_key], "account key", @options[:key_length] || 4096)
118
+ end
119
+
120
+ def find_or_create_pkey(file_path, name, length)
121
+ if File.exists?(file_path)
122
+ log "existing account key found"
123
+ OpenSSL::PKey::RSA.new File.read file_path
124
+ else
125
+ log "creating new private key to #{file_path}..."
126
+ private_key = OpenSSL::PKey::RSA.new(length)
127
+ File.write(file_path, private_key.to_s)
128
+ private_key
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,72 @@
1
+ require 'thor'
2
+ require 'colorize'
3
+ require 'fileutils'
4
+ module Letsencrypt
5
+ module Cli
6
+ class App < Thor
7
+ class_option :account_key, desc: "Path to private key file (will be created if not exists)", aliases: "-a", default: 'account_key.pem'
8
+ class_option :test, desc: "Use staging url of Letsencrypt instead of production server", aliases: "-t", type: :boolean
9
+ class_option :log_level, desc: "Log Level (debug, info, warn, error, fatal)", default: "info"
10
+ class_option :color, desc: "Disable colorize", default: true, type: :boolean
11
+
12
+ desc 'register EMAIL', 'Register account'
13
+ method_option :key_length, desc: "Length of generated private key", type: :numeric, default: 4096
14
+ def register(email)
15
+ if email.nil? || email == ""
16
+ log "no E-Mail specified!", :fatal
17
+ exit 1
18
+ end
19
+ if !email[/.*@.*/]
20
+ log "not an email", :fatal
21
+ exit 1
22
+ end
23
+ registration = wrapper.client.register(contact: "mailto:" + email)
24
+ registration.agree_terms
25
+ wrapper.log "Account created, Terms accepted"
26
+ end
27
+
28
+ desc 'authorize_all', "Verify all server_names in /etc/nginx/sites-enabled/* (needs read access)"
29
+ method_option :webroot_path, desc: "Path to mapped .acme-challenge folder (no subdir)", aliases: '-w', required: true
30
+ def authorize_all
31
+ lines = Dir['/etc/nginx/sites-enabled/*'].map{|file| File.read(file).lines.grep(/^\s*server_name/) }.flatten
32
+ domains = lines.flatten.map{|i| i.strip.split(/[; ]/).drop(1) }.flatten.reject{|i| i.length < 3 }.uniq
33
+ authorize(*domains)
34
+ end
35
+
36
+ desc 'authorize [DOMAINS]', 'Authorize all domains'
37
+ method_option :webroot_path, desc: "Path to mapped .well-known/acme-challenge folder (no subdirs will be created)", aliases: '-w', required: true
38
+ def authorize(*domains)
39
+ rc = 0
40
+ domains.each do |domain|
41
+ if !wrapper.authorize(domain)
42
+ rc = 1
43
+ end
44
+ end
45
+ if rc != 0
46
+ exit rc
47
+ end
48
+ end
49
+
50
+ desc "cert [DOMAINS]", "create certificate and private key pair for domains. The first domain is the main CN domain, the reset will be added as SAN. If the given certificate-path already exists, script will exit non-zero if the certificate is still valid until the given number of days before."
51
+ method_option :private_key_path, desc: "Path to private key. Will be created if non existant", aliases: '-k', default: 'key.pem'
52
+ method_option :key_length, desc: "Length of private key", default: 2048, type: :numeric
53
+ method_option :fullchain_path, desc: "Path to fullchain certificate (Nginx) (will be overwritten if exists!)", aliases: '-f', default: 'fullchain.pem'
54
+ method_option :certificate_path, desc: "Path to certificate (Apache)", aliases: '-c', default: 'cert.pem'
55
+ method_option :chain_path, desc: "Path to chain (Apache)", aliases: '-n', default: 'chain.pem'
56
+ method_option :days_valid, desc: "If the --certificate-path already exists, only create new stuff, if that certificate isn't valid for less than the given number of days", default: 30, type: :numeric
57
+ def cert(*domains)
58
+ if domains.length == 0
59
+ $stderr.puts "no domains given"
60
+ exit 1
61
+ end
62
+ wrapper.cert(domains)
63
+ end
64
+
65
+ private
66
+
67
+ def wrapper
68
+ @wrapper ||= AcmeWrapper.new(options)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,5 @@
1
+ module Letsencrypt
2
+ module Cli
3
+ VERSION = "0.1.0.beta1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,216 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: letsencrypt-cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.beta1
5
+ platform: ruby
6
+ authors:
7
+ - Stefan Wienert
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-12-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: acme-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: colorize
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: vcr
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: webmock
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.22'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.22'
111
+ - !ruby/object:Gem::Dependency
112
+ name: timecop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.8'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.8'
125
+ - !ruby/object:Gem::Dependency
126
+ name: bundler
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.7'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.7'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rake
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '10.0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '10.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rspec
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '3.0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '3.0'
167
+ description:
168
+ email:
169
+ - stwienert@gmail.com
170
+ executables:
171
+ - letsencrypt-cli
172
+ extensions: []
173
+ extra_rdoc_files: []
174
+ files:
175
+ - ".gitignore"
176
+ - ".rspec"
177
+ - ".travis.yml"
178
+ - Gemfile
179
+ - LICENSE.txt
180
+ - README.md
181
+ - Rakefile
182
+ - bin/console
183
+ - bin/setup
184
+ - exe/letsencrypt-cli
185
+ - letsencrypt-cli.gemspec
186
+ - lib/letsencrypt-cli.rb
187
+ - lib/letsencrypt/cli.rb
188
+ - lib/letsencrypt/cli/acme_wrapper.rb
189
+ - lib/letsencrypt/cli/app.rb
190
+ - lib/letsencrypt/cli/version.rb
191
+ homepage: https://github.com/zealot28/letsencrypt-cli
192
+ licenses:
193
+ - MIT
194
+ metadata: {}
195
+ post_install_message:
196
+ rdoc_options: []
197
+ require_paths:
198
+ - lib
199
+ required_ruby_version: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - ">="
202
+ - !ruby/object:Gem::Version
203
+ version: 2.0.0
204
+ required_rubygems_version: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">"
207
+ - !ruby/object:Gem::Version
208
+ version: 1.3.1
209
+ requirements: []
210
+ rubyforge_project:
211
+ rubygems_version: 2.2.2
212
+ signing_key:
213
+ specification_version: 4
214
+ summary: slim letsencrypt client for quickly authorizing (multiple) domains and issuing
215
+ certificates
216
+ test_files: []