chef-ssl-client 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,6 @@
1
+ =NAME
2
+
3
+ "chef-ssl" Client
4
+
5
+
6
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc 'Default: run specs.'
5
+ task :default => :spec
6
+
7
+ desc "Run specs"
8
+ RSpec::Core::RakeTask.new
data/bin/chef-ssl ADDED
@@ -0,0 +1,7 @@
1
+ require "rubygems"
2
+ require "commander/import"
3
+ require "chef-ssl/client"
4
+
5
+ HighLine.colorize_strings
6
+
7
+ load File.expand_path('../lib/chef-ssl/command.rb', File.dirname(__FILE__))
@@ -0,0 +1,69 @@
1
+ module ChefSSL
2
+ class Client
3
+ class CertSaveFailed < StandardError; end
4
+
5
+ class IssuedCertificate
6
+
7
+ DATABAG = "certificates"
8
+
9
+ def initialize(req, cert, ca=nil)
10
+ @ca = ca
11
+ @req = req
12
+ @cert = cert
13
+ end
14
+
15
+ def to_pem
16
+ @cert.to_pem
17
+ end
18
+
19
+ def sha1_fingerprint
20
+ @cert.sha1_fingerprint
21
+ end
22
+
23
+ def subject
24
+ @cert.subject.to_s
25
+ end
26
+
27
+ def issuer
28
+ @cert.issuer.to_s
29
+ end
30
+
31
+ def not_after
32
+ @cert.not_after
33
+ end
34
+
35
+ def save!
36
+ begin
37
+ Spice.create_data_bag(DATABAG)
38
+ rescue Spice::Error::Conflict
39
+ nil
40
+ end
41
+
42
+ data = {
43
+ :name => DATABAG,
44
+ :id => @req.id,
45
+ :dn => @req.subject,
46
+ :ca => @req.ca,
47
+ :csr => @req.to_pem,
48
+ :key => @req.key,
49
+ :type => @req.type,
50
+ :date => Time.now.to_s,
51
+ :host => @req.host,
52
+ :certificate => @cert.to_pem
53
+ }
54
+ unless @ca.nil?
55
+ data[:cacert] = @ca.certificate.to_pem
56
+ end
57
+
58
+ begin
59
+ ret = Spice.create_data_bag_item(DATABAG, data)
60
+ rescue Spice::Error::Conflict
61
+ raise CertSaveFailed.new("Conflict - certificate data bag exists for #{@req.subject}, id #{@req.id}")
62
+ rescue Spice::Error::ClientError => e
63
+ raise CertSaveFailed.new(e.message)
64
+ end
65
+ end
66
+
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,42 @@
1
+ module ChefSSL
2
+ class Client
3
+ class Request
4
+
5
+ attr_reader :host, :csr, :type, :ca, :id, :name, :key, :days
6
+
7
+ def initialize(host, data, csr=nil)
8
+ @host = host
9
+ @csr = csr || EaSSL::SigningRequest.new.load(data['csr'])
10
+ @type = data['type']
11
+ @ca = data['ca']
12
+ @id = data['id']
13
+ @name = data['name']
14
+ @key = data['key']
15
+ @days = data['days'] || (365 * 5)
16
+ end
17
+
18
+ def subject
19
+ @csr.subject.to_s
20
+ end
21
+
22
+ def to_pem
23
+ @csr.to_pem
24
+ end
25
+
26
+ def issue_certificate(cert_text)
27
+ cert = EaSSL::Certificate.new({}).load(cert_text)
28
+ IssuedCertificate.new(self, cert)
29
+ end
30
+
31
+ def self.create(key, type, options)
32
+ name = EaSSL::CertificateName.new(options)
33
+ csr = EaSSL::SigningRequest.new(:name => name, :key => key)
34
+
35
+ data = {
36
+ :type => type
37
+ }
38
+ self.new('localhost', data, csr)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,83 @@
1
+ module ChefSSL
2
+ class Client
3
+ class SigningAuthority
4
+
5
+ def self.load(options)
6
+ ca = EaSSL::CertificateAuthority.load(
7
+ :ca_path => options[:path],
8
+ :ca_password => options[:password]
9
+ )
10
+ self.new(ca)
11
+ end
12
+
13
+ def initialize(ca)
14
+ @ca = ca
15
+ end
16
+
17
+ def dn
18
+ @ca.certificate.subject.to_s
19
+ end
20
+
21
+ def sign(req)
22
+ cert = @ca.create_certificate(req.csr, req.type, req.days)
23
+ IssuedCertificate.new(req, cert, @ca)
24
+ end
25
+
26
+ def self.create(name, path, passphrase)
27
+ config = {
28
+ :ca_dir => path,
29
+ :password => passphrase,
30
+ :ca_rsa_key_length => 1024,
31
+ :ca_cert_days => 3650,
32
+ :name => name
33
+ }
34
+ config[:serial_file] = File.join(config[:ca_dir], 'serial.txt')
35
+ config[:keypair_file] = File.join(config[:ca_dir], 'cakey.pem')
36
+ config[:cert_file] = File.join(config[:ca_dir], 'cacert.pem')
37
+
38
+ Dir.mkdir config[:ca_dir]
39
+ Dir.mkdir File.join(config[:ca_dir], 'private'), 0700
40
+ Dir.mkdir File.join(config[:ca_dir], 'newcerts')
41
+ Dir.mkdir File.join(config[:ca_dir], 'crl')
42
+
43
+ File.open config[:serial_file], 'w' do |f| f << '1' end
44
+
45
+ keypair = OpenSSL::PKey::RSA.new config[:ca_rsa_key_length]
46
+
47
+ cert = OpenSSL::X509::Certificate.new
48
+ cert.subject = cert.issuer = config[:name]
49
+ cert.not_before = Time.now
50
+ cert.not_after = Time.now + config[:ca_cert_days] * 24 * 60 * 60
51
+ cert.public_key = keypair.public_key
52
+ cert.serial = 0x0
53
+ cert.version = 2 # X509v3
54
+
55
+ ef = OpenSSL::X509::ExtensionFactory.new
56
+ ef.subject_certificate = cert
57
+ ef.issuer_certificate = cert
58
+ cert.extensions = [
59
+ ef.create_extension("basicConstraints", "CA:TRUE", true),
60
+ ef.create_extension("nsComment", "Ruby/OpenSSL/chef-ssl Generated Certificate"),
61
+ ef.create_extension("subjectKeyIdentifier", "hash"),
62
+ ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
63
+ ]
64
+ cert.add_extension ef.create_extension("authorityKeyIdentifier",
65
+ "keyid:always,issuer:always")
66
+ cert.sign keypair, OpenSSL::Digest::SHA1.new
67
+
68
+ keypair_export = keypair.export OpenSSL::Cipher::DES.new(:EDE3, :CBC),
69
+ config[:password]
70
+
71
+ File.open config[:keypair_file], "w", 0400 do |fp|
72
+ fp << keypair_export
73
+ end
74
+
75
+ File.open config[:cert_file], "w", 0644 do |f|
76
+ f << cert.to_pem
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+
@@ -0,0 +1,5 @@
1
+ module ChefSSL
2
+ class Client
3
+ VERSION = '0.0.6'
4
+ end
5
+ end
@@ -0,0 +1,64 @@
1
+ require 'spice'
2
+ require 'eassl'
3
+ require 'json'
4
+ require 'openssl'
5
+ require 'digest/sha2'
6
+ require 'active_support/hash_with_indifferent_access'
7
+ require 'chef/config'
8
+ require 'chef/node'
9
+
10
+ require 'chef-ssl/client/version'
11
+ require 'chef-ssl/client/request'
12
+ require 'chef-ssl/client/signing_authority'
13
+ require 'chef-ssl/client/issued_certificate'
14
+
15
+ module ChefSSL
16
+ class Client
17
+
18
+ def initialize
19
+ path = File.expand_path('knife.rb', '~/.chef')
20
+ Chef::Config.from_file(path)
21
+ Spice.reset
22
+ Spice.setup do |s|
23
+ s.server_url = Chef::Config.chef_server_url
24
+ s.client_name = Chef::Config.node_name
25
+ s.client_key = Spice.read_key_file(File.expand_path(Chef::Config.client_key))
26
+ end
27
+ end
28
+
29
+ def self.load_authority(options)
30
+ SigningAuthority.load(
31
+ :path => options[:path],
32
+ :password => options[:password]
33
+ )
34
+ end
35
+
36
+ def ca_search(ca=nil)
37
+ if ca
38
+ nodes = Spice.nodes("csr_outbox_*_ca:#{ca}")
39
+ else
40
+ nodes = Spice.nodes("csr_outbox_*")
41
+ end
42
+ nodes.each do |node|
43
+ node.normal['csr_outbox'].each do |id, data|
44
+ next if data['csr'].nil? # XXX warn, raise?
45
+ yield Request.new(node.name, data)
46
+ end
47
+ end
48
+ end
49
+
50
+ def common_name_search(name)
51
+ name_sha = Digest::SHA256.new << name
52
+ cert_id = name_sha.to_s
53
+ nodes = Spice.nodes("csr_outbox_*_id:#{cert_id}")
54
+ nodes.each do |node|
55
+ node.normal['csr_outbox'].each do |id, data|
56
+ next unless data['id'] == cert_id
57
+ next if data['csr'].nil? # XXX warn, raise?
58
+ yield Request.new(node.name, data)
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,260 @@
1
+ program :name, "chef-ssl"
2
+ program :version, ChefSSL::Client::VERSION
3
+ program :description, "Chef-automated SSL certificate signing tool"
4
+ program :help_formatter, :compact
5
+
6
+ default_command :help
7
+
8
+ command :issue do |c|
9
+ c.syntax = "chef-ssl issue [options]"
10
+ c.description = "Issue an ad hoc certificate"
11
+ c.example "Issue cert for www.venda.com",
12
+ "chef-ssl issue --ca-path ./myCA --dn /CN=foo --type server"
13
+
14
+ c.option "--ca-path=STRING", String, "the path to the new CA"
15
+ c.option "--dn=STRING", String, "the distinguished name for the new certificate"
16
+ c.option "--type=STRING", String, "the type of certificate, client or server"
17
+
18
+ c.action do |args, options|
19
+ raise "CA path is required" unless options.ca_path
20
+ raise "DN is required" unless options.dn
21
+ raise "type is required" unless options.type
22
+
23
+ begin
24
+ dn = OpenSSL::X509::Name.parse(options.dn)
25
+ rescue NoMethodError
26
+ raise "--dn is required and must be a distinguished name"
27
+ rescue TypeError
28
+ raise "--dn is required and must be a distinguished name"
29
+ rescue OpenSSL::X509::NameError => e
30
+ raise "--dn must specify a valid DN: #{e.message}"
31
+ end
32
+
33
+ unless options.type == 'server' || options.type == 'client'
34
+ raise "type must be server or client"
35
+ end
36
+
37
+ authority = ChefSSL::Client.load_authority(
38
+ :password => ask("Enter CA passphrase: ") { |q| q.echo = false },
39
+ :path => options.ca_path
40
+ )
41
+
42
+ key = EaSSL::Key.new
43
+
44
+ h = dn.to_a.reduce({}) { |h, elem| h[elem[0]] = elem[1]; h }
45
+ name = {
46
+ :city => h['L'],
47
+ :state => h['ST'],
48
+ :country => h['C'],
49
+ :department => h['OU'],
50
+ :common_name => h['CN'],
51
+ :organization => h['O'],
52
+ :email => h['emailAddress']
53
+ }
54
+
55
+ req = ChefSSL::Client::Request.create(key, options.type, name)
56
+ cert = authority.sign(req)
57
+
58
+ puts "#{'Key:'.cyan}"
59
+ puts HighLine.color(key.private_key.to_s, :bright_black)
60
+ puts
61
+ puts "#{'Certificate:'.cyan} SHA1 Fingerprint=#{cert.sha1_fingerprint}"
62
+ puts HighLine.color(cert.to_pem, :bright_black)
63
+ end
64
+ end
65
+
66
+ command :makeca do |c|
67
+ c.syntax = "chef-ssl makeca [options]"
68
+ c.description = "Creates a new CA"
69
+ c.example "Upload cert for CSR www.venda.com",
70
+ "chef-ssl makeca --ca-name '/CN=My New CA' --ca-path ./newCA"
71
+
72
+ c.option "--ca-path=STRING", String, "the path to the new CA"
73
+ c.option "--ca-name=STRING", String, "the distinguished name of the new CA"
74
+
75
+ c.action do |args, options|
76
+ begin
77
+ name = OpenSSL::X509::Name.parse(options.ca_name)
78
+ rescue NoMethodError
79
+ raise "--ca-name is required and must be a distinguished name"
80
+ rescue TypeError
81
+ raise "--ca-name is required and must be a distinguished name"
82
+ rescue OpenSSL::X509::NameError => e
83
+ raise "--ca-name must specify a valid DN: #{e.message}"
84
+ end
85
+
86
+ raise "CA path is required" unless options.ca_path
87
+ raise "CA path must not already exist" if Dir.glob(options.ca_path).length > 0
88
+
89
+ puts "#{'New CA DN'.cyan}: #{name.to_s}"
90
+ puts
91
+
92
+ passphrase = ask("Enter new CA passphrase: ") { |q| q.echo = false }
93
+ passphrase2 = ask("Re-enter new CA passphrase: ") { |q| q.echo = false }
94
+ raise "passphrases do not match" unless passphrase == passphrase2
95
+
96
+ print "\n#{'Creating new CA'.cyan}: "
97
+ ChefSSL::Client::SigningAuthority.create(name, options.ca_path, passphrase)
98
+ puts "done"
99
+ end
100
+ end
101
+
102
+ command :search do |c|
103
+ c.syntax = "chef-ssl search [options]"
104
+ c.description = "Searches for outstanding CSRs"
105
+ c.example "Search for all CSRs awaiting signing by CA bob",
106
+ "chef-ssl search --ca-name bob"
107
+
108
+ c.option "--ca-name=STRING", String, "a name of a CA to limit the search by"
109
+
110
+ c.action do |args, options|
111
+ client = ChefSSL::Client.new
112
+
113
+ puts "#{'Search CA'.cyan}: #{options.ca_name}" if options.ca_name
114
+
115
+ client.ca_search(options.ca_name) do |req|
116
+ puts
117
+ puts "#{' Node Hostname'.cyan}: #{req.host}"
118
+ puts "#{' Certificate Type'.cyan}: #{req.type.bold}"
119
+ puts "#{' Certificate DN'.cyan}: #{req.subject.bold}"
120
+ puts "#{' Requested CA'.cyan}: #{req.ca.bold}"
121
+ puts "#{'Requested Validity'.cyan}: #{req.days.to_s.bold} days"
122
+ puts
123
+ puts HighLine.color(req.to_pem, :bright_black)
124
+ end
125
+ end
126
+ end
127
+
128
+ command :sign do |c|
129
+ c.syntax = "chef-ssl sign [options]"
130
+ c.description = "Search for the given CSR by name and provide a signed certificate"
131
+ c.example "Provide signed cert for CSR www.venda.com",
132
+ "chef-ssl --name www.venda.com"
133
+
134
+ c.option "--name=STRING", String, "common name of the CSR to search for"
135
+
136
+ c.action do |args, options|
137
+
138
+ raise "--name is required" unless options.name
139
+
140
+ client = ChefSSL::Client.new
141
+
142
+ puts "#{'Search name'.cyan}: #{options.name}"
143
+
144
+ client.common_name_search(options.name) do |req|
145
+ puts
146
+ puts "#{' Node Hostname'.cyan}: #{req.host}"
147
+ puts "#{' Certificate Type'.cyan}: #{req.type.bold}"
148
+ puts "#{' Certificate DN'.cyan}: #{req.subject.bold}"
149
+ puts "#{' Requested CA'.cyan}: #{req.ca.bold}"
150
+ puts "#{'Requested Validity'.cyan}: #{req.days.to_s.bold} days"
151
+ puts
152
+ puts HighLine.color(req.to_pem, :bright_black)
153
+
154
+ cert = nil
155
+
156
+ HighLine.new.choose do |menu|
157
+ menu.layout = :one_line
158
+ menu.prompt = "Sign this? "
159
+
160
+ menu.choice :yes do
161
+ cert_text = ask("Paste cert text") do |q|
162
+ q.gather = ""
163
+ end
164
+ cert = req.issue_certificate(cert_text.join("\n"))
165
+ end
166
+ menu.choice :no do
167
+ nil
168
+ end
169
+ end
170
+
171
+ if cert
172
+ puts "#{' Signed:'.cyan} SHA1 Fingerprint=#{cert.sha1_fingerprint}"
173
+ puts "#{'Subject:'.cyan} #{cert.subject}"
174
+ puts "#{' Issuer:'.cyan} #{cert.issuer}"
175
+ puts HighLine.color(cert.to_pem, :bright_black)
176
+
177
+ unless cert.subject == req.subject
178
+ puts "#{'WARNING:'.red.bold} #{'Issued certificate DN does not match request DN!'.bold}"
179
+ end
180
+
181
+ HighLine.new.choose do |menu|
182
+ menu.layout = :one_line
183
+ menu.prompt = "Save certificate? "
184
+
185
+ menu.choice :yes do
186
+ begin
187
+ cert.save!
188
+ puts "Saved OK"
189
+ rescue ChefSSL::Client::CertSaveFailed => e
190
+ puts "Error saving: #{e.message}"
191
+ end
192
+ end
193
+ menu.choice :no do
194
+ nil
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ command :autosign do |c|
203
+ c.syntax = "chef-ssl autosign [options]"
204
+ c.description = "Search for CSRs and sign them with the given CA"
205
+ c.example "Sign with 'CA'",
206
+ "chef-ssl --ca-path CA --ca-name autoCA"
207
+
208
+ c.option "--ca-path=STRING", String, "the path to the signing CA"
209
+ c.option "--ca-name=STRING", String, "the name of the signing CA"
210
+
211
+ c.action do |args, options|
212
+
213
+ raise "--ca-path is required" unless options.ca_path
214
+ raise "--ca-name is required" unless options.ca_name
215
+
216
+ authority = ChefSSL::Client.load_authority(
217
+ :password => ask("Enter CA passphrase: ") { |q| q.echo = false },
218
+ :path => options.ca_path
219
+ )
220
+
221
+ client = ChefSSL::Client.new
222
+
223
+ puts "#{'Search CA'.cyan}: #{options.ca_name}"
224
+
225
+ client.ca_search(options.ca_name) do |req|
226
+ puts
227
+ puts "#{' Node Hostname'.cyan}: #{req.host}"
228
+ puts "#{' Certificate Type'.cyan}: #{req.type.bold}"
229
+ puts "#{' Certificate DN'.cyan}: #{req.subject.bold}"
230
+ puts "#{' Requested CA'.cyan}: #{req.ca.bold}"
231
+ puts "#{'Requested Validity'.cyan}: #{req.days.to_s.bold} days"
232
+ puts
233
+ puts HighLine.color(req.to_pem, :bright_black)
234
+
235
+ HighLine.new.choose do |menu|
236
+ menu.layout = :one_line
237
+ menu.prompt = "#{'Sign with'.cyan}: #{HighLine.color(authority.dn, :bold)}\nSign this? "
238
+
239
+ menu.choice :yes do
240
+ cert = authority.sign(req)
241
+ puts
242
+ puts "#{'Signed:'.cyan} SHA1 Fingerprint=#{cert.sha1_fingerprint}"
243
+ puts HighLine.color(cert.to_pem, :bright_black)
244
+ begin
245
+ cert.save!
246
+ puts "Saved OK"
247
+ rescue ChefSSL::Client::CertSaveFailed => e
248
+ puts "Error saving: #{e.message}"
249
+ end
250
+ end
251
+
252
+ menu.choice :no do
253
+ nil
254
+ end
255
+ end
256
+ end
257
+
258
+ puts "All CSRs processed."
259
+ end
260
+ end
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chef-ssl-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Venda
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-23 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: chef
16
+ requirement: &2163253520 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.10.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2163253520
25
+ - !ruby/object:Gem::Dependency
26
+ name: spice
27
+ requirement: &2163252940 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.3
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2163252940
36
+ - !ruby/object:Gem::Dependency
37
+ name: eassl2
38
+ requirement: &2163252120 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 2.0.0
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *2163252120
47
+ - !ruby/object:Gem::Dependency
48
+ name: highline
49
+ requirement: &2163251220 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.6.0
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *2163251220
58
+ - !ruby/object:Gem::Dependency
59
+ name: commander
60
+ requirement: &2163250360 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 4.1.0
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *2163250360
69
+ - !ruby/object:Gem::Dependency
70
+ name: multi_json
71
+ requirement: &2163249800 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: 1.0.0
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: *2163249800
80
+ - !ruby/object:Gem::Dependency
81
+ name: activesupport
82
+ requirement: &2163249120 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: 3.1.0
88
+ type: :runtime
89
+ prerelease: false
90
+ version_requirements: *2163249120
91
+ - !ruby/object:Gem::Dependency
92
+ name: rspec
93
+ requirement: &2163248560 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ~>
97
+ - !ruby/object:Gem::Version
98
+ version: 2.10.0
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *2163248560
102
+ - !ruby/object:Gem::Dependency
103
+ name: flexmock
104
+ requirement: &2163247960 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: 0.9.0
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: *2163247960
113
+ - !ruby/object:Gem::Dependency
114
+ name: simplecov
115
+ requirement: &2163247420 !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ type: :development
122
+ prerelease: false
123
+ version_requirements: *2163247420
124
+ - !ruby/object:Gem::Dependency
125
+ name: rake
126
+ requirement: &2163246840 !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: *2163246840
135
+ description: A command-line client the ssl cookbook's signing requirements
136
+ email: auto-ssl@venda.com
137
+ executables:
138
+ - chef-ssl
139
+ extensions: []
140
+ extra_rdoc_files:
141
+ - README.rdoc
142
+ files:
143
+ - README.rdoc
144
+ - Rakefile
145
+ - bin/chef-ssl
146
+ - lib/chef-ssl/client/issued_certificate.rb
147
+ - lib/chef-ssl/client/request.rb
148
+ - lib/chef-ssl/client/signing_authority.rb
149
+ - lib/chef-ssl/client/version.rb
150
+ - lib/chef-ssl/client.rb
151
+ - lib/chef-ssl/command.rb
152
+ homepage: http://docs.uk.venda.com/vendadocs/TeamAwesome/Project☃/pki
153
+ licenses: []
154
+ post_install_message:
155
+ rdoc_options: []
156
+ require_paths:
157
+ - lib
158
+ required_ruby_version: !ruby/object:Gem::Requirement
159
+ none: false
160
+ requirements:
161
+ - - ! '>='
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ none: false
166
+ requirements:
167
+ - - ! '>='
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ requirements: []
171
+ rubyforge_project:
172
+ rubygems_version: 1.8.6
173
+ signing_key:
174
+ specification_version: 3
175
+ summary: A command-line client the ssl cookbook's signing requirements
176
+ test_files: []
177
+ has_rdoc: true