chef-ssl-client 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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