leap_cli 1.5.6 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/bin/leap +29 -6
  2. data/lib/leap/platform.rb +36 -1
  3. data/lib/leap_cli/commands/ca.rb +97 -20
  4. data/lib/leap_cli/commands/compile.rb +49 -8
  5. data/lib/leap_cli/commands/db.rb +13 -4
  6. data/lib/leap_cli/commands/deploy.rb +138 -29
  7. data/lib/leap_cli/commands/env.rb +76 -0
  8. data/lib/leap_cli/commands/facts.rb +10 -3
  9. data/lib/leap_cli/commands/inspect.rb +2 -2
  10. data/lib/leap_cli/commands/list.rb +10 -10
  11. data/lib/leap_cli/commands/node.rb +7 -132
  12. data/lib/leap_cli/commands/node_init.rb +169 -0
  13. data/lib/leap_cli/commands/pre.rb +4 -27
  14. data/lib/leap_cli/commands/ssh.rb +152 -0
  15. data/lib/leap_cli/commands/test.rb +22 -13
  16. data/lib/leap_cli/commands/user.rb +12 -4
  17. data/lib/leap_cli/commands/vagrant.rb +4 -4
  18. data/lib/leap_cli/config/filter.rb +175 -0
  19. data/lib/leap_cli/config/manager.rb +130 -61
  20. data/lib/leap_cli/config/node.rb +32 -0
  21. data/lib/leap_cli/config/object.rb +69 -44
  22. data/lib/leap_cli/config/object_list.rb +44 -39
  23. data/lib/leap_cli/config/secrets.rb +24 -12
  24. data/lib/leap_cli/config/tag.rb +7 -0
  25. data/lib/{core_ext → leap_cli/core_ext}/boolean.rb +0 -0
  26. data/lib/{core_ext → leap_cli/core_ext}/hash.rb +0 -0
  27. data/lib/{core_ext → leap_cli/core_ext}/json.rb +0 -0
  28. data/lib/{core_ext → leap_cli/core_ext}/nil.rb +0 -0
  29. data/lib/{core_ext → leap_cli/core_ext}/string.rb +0 -0
  30. data/lib/leap_cli/core_ext/yaml.rb +29 -0
  31. data/lib/leap_cli/exceptions.rb +24 -0
  32. data/lib/leap_cli/leapfile.rb +60 -10
  33. data/lib/{lib_ext → leap_cli/lib_ext}/capistrano_connections.rb +0 -0
  34. data/lib/{lib_ext → leap_cli/lib_ext}/gli.rb +0 -0
  35. data/lib/leap_cli/log.rb +1 -1
  36. data/lib/leap_cli/logger.rb +18 -1
  37. data/lib/leap_cli/markdown_document_listener.rb +1 -1
  38. data/lib/leap_cli/override/json.rb +11 -0
  39. data/lib/leap_cli/path.rb +20 -6
  40. data/lib/leap_cli/remote/leap_plugin.rb +2 -2
  41. data/lib/leap_cli/remote/puppet_plugin.rb +1 -1
  42. data/lib/leap_cli/remote/rsync_plugin.rb +1 -1
  43. data/lib/leap_cli/remote/tasks.rb +1 -1
  44. data/lib/leap_cli/ssh_key.rb +63 -1
  45. data/lib/leap_cli/util/remote_command.rb +19 -2
  46. data/lib/leap_cli/util/secret.rb +1 -1
  47. data/lib/leap_cli/util/x509.rb +3 -2
  48. data/lib/leap_cli/util.rb +11 -3
  49. data/lib/leap_cli/version.rb +2 -2
  50. data/lib/leap_cli.rb +24 -14
  51. data/vendor/certificate_authority/lib/certificate_authority/certificate.rb +85 -29
  52. data/vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb +5 -0
  53. data/vendor/certificate_authority/lib/certificate_authority/extensions.rb +406 -41
  54. data/vendor/certificate_authority/lib/certificate_authority/key_material.rb +0 -34
  55. data/vendor/certificate_authority/lib/certificate_authority/serial_number.rb +6 -0
  56. data/vendor/certificate_authority/lib/certificate_authority/signing_request.rb +36 -1
  57. metadata +25 -24
  58. data/lib/leap_cli/commands/shell.rb +0 -89
  59. data/lib/leap_cli/config/macros.rb +0 -430
  60. data/lib/leap_cli/constants.rb +0 -7
  61. data/lib/leap_cli/requirements.rb +0 -19
  62. data/lib/lib_ext/markdown_document_listener.rb +0 -122
@@ -33,7 +33,7 @@ module CertificateAuthority
33
33
  self.serial_number = SerialNumber.new
34
34
  self.key_material = MemoryKeyMaterial.new
35
35
  self.not_before = Time.now
36
- self.not_after = Time.now + 60 * 60 * 24 * 365 #One year
36
+ self.not_after = Time.now + 60 * 60 * 24 * 365 # One year
37
37
  self.parent = self
38
38
  self.extensions = load_extensions()
39
39
 
@@ -41,12 +41,31 @@ module CertificateAuthority
41
41
 
42
42
  end
43
43
 
44
+ =begin
45
+ def self.from_openssl openssl_cert
46
+ unless openssl_cert.is_a? OpenSSL::X509::Certificate
47
+ raise "Can only construct from an OpenSSL::X509::Certificate"
48
+ end
49
+
50
+ certificate = Certificate.new
51
+ # Only subject, key_material, and body are used for signing
52
+ certificate.distinguished_name = DistinguishedName.from_openssl openssl_cert.subject
53
+ certificate.key_material.public_key = openssl_cert.public_key
54
+ certificate.openssl_body = openssl_cert
55
+ certificate.serial_number.number = openssl_cert.serial.to_i
56
+ certificate.not_before = openssl_cert.not_before
57
+ certificate.not_after = openssl_cert.not_after
58
+ # TODO extensions
59
+ certificate
60
+ end
61
+ =end
62
+
44
63
  def sign!(signing_profile={})
45
64
  raise "Invalid certificate #{self.errors.full_messages}" unless valid?
46
65
  merge_profile_with_extensions(signing_profile)
47
66
 
48
67
  openssl_cert = OpenSSL::X509::Certificate.new
49
- openssl_cert.version = 2
68
+ openssl_cert.version = 2
50
69
  openssl_cert.not_before = self.not_before
51
70
  openssl_cert.not_after = self.not_after
52
71
  openssl_cert.public_key = self.key_material.public_key
@@ -58,7 +77,6 @@ module CertificateAuthority
58
77
 
59
78
  require 'tempfile'
60
79
  t = Tempfile.new("bullshit_conf")
61
- # t = File.new("/tmp/openssl.cnf")
62
80
  ## The config requires a file even though we won't use it
63
81
  openssl_config = OpenSSL::Config.new(t.path)
64
82
 
@@ -85,7 +103,7 @@ module CertificateAuthority
85
103
  self.extensions.keys.sort{|a,b| b<=>a}.each do |k|
86
104
  e = extensions[k]
87
105
  next if e.to_s.nil? or e.to_s == "" ## If the extension returns an empty string we won't include it
88
- ext = factory.create_ext(e.openssl_identifier, e.to_s)
106
+ ext = factory.create_ext(e.openssl_identifier, e.to_s, e.critical)
89
107
  openssl_cert.add_extension(ext)
90
108
  end
91
109
 
@@ -94,9 +112,10 @@ module CertificateAuthority
94
112
  else
95
113
  digest = OpenSSL::Digest::Digest.new(signing_profile["digest"])
96
114
  end
97
- self.openssl_body = openssl_cert.sign(parent.key_material.private_key,digest)
98
- t.close! if t.is_a?(Tempfile)# We can get rid of the ridiculous temp file
99
- self.openssl_body
115
+
116
+ self.openssl_body = openssl_cert.sign(parent.key_material.private_key, digest)
117
+ ensure
118
+ t.close! if t # We can get rid of the ridiculous temp file
100
119
  end
101
120
 
102
121
  def is_signing_entity?
@@ -116,6 +135,34 @@ module CertificateAuthority
116
135
  self.openssl_body.to_pem
117
136
  end
118
137
 
138
+ def to_csr
139
+ csr = SigningRequest.new
140
+ csr.distinguished_name = self.distinguished_name
141
+ csr.key_material = self.key_material
142
+ factory = OpenSSL::X509::ExtensionFactory.new
143
+ exts = []
144
+ self.extensions.keys.each do |k|
145
+ ## Don't copy over key identifiers for CSRs
146
+ next if k == "subjectKeyIdentifier" || k == "authorityKeyIdentifier"
147
+ e = extensions[k]
148
+ ## If the extension returns an empty string we won't include it
149
+ next if e.to_s.nil? or e.to_s == ""
150
+ exts << factory.create_ext(e.openssl_identifier, e.to_s, e.critical)
151
+ end
152
+ attrval = OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence(exts)])
153
+ attrs = [
154
+ OpenSSL::X509::Attribute.new("extReq", attrval),
155
+ OpenSSL::X509::Attribute.new("msExtReq", attrval)
156
+ ]
157
+ csr.attributes = attrs
158
+ csr
159
+ end
160
+
161
+ def self.from_x509_cert(raw_cert)
162
+ openssl_cert = OpenSSL::X509::Certificate.new(raw_cert)
163
+ Certificate.from_openssl(openssl_cert)
164
+ end
165
+
119
166
  def is_root_entity?
120
167
  self.parent == self && is_signing_entity?
121
168
  end
@@ -134,6 +181,16 @@ module CertificateAuthority
134
181
  items = signing_config[k]
135
182
  items.keys.each do |profile_item_key|
136
183
  if extension.respond_to?("#{profile_item_key}=".to_sym)
184
+ if k == 'subjectAltName' && profile_item_key == 'emails'
185
+ items[profile_item_key].map do |email|
186
+ if email == 'email:copy'
187
+ fail "no email address provided for subject: #{subject.to_x509_name}" unless subject.email_address
188
+ "email:#{subject.email_address}"
189
+ else
190
+ email
191
+ end
192
+ end
193
+ end
137
194
  extension.send("#{profile_item_key}=".to_sym, items[profile_item_key] )
138
195
  else
139
196
  p "Tried applying '#{profile_item_key}' to #{extension.class} but it doesn't respond!"
@@ -142,30 +199,25 @@ module CertificateAuthority
142
199
  end
143
200
  end
144
201
 
202
+ # Enumeration of the extensions. Not the worst option since
203
+ # the likelihood of these needing to be updated is low at best.
204
+ EXTENSIONS = [
205
+ CertificateAuthority::Extensions::BasicConstraints,
206
+ CertificateAuthority::Extensions::CrlDistributionPoints,
207
+ CertificateAuthority::Extensions::SubjectKeyIdentifier,
208
+ CertificateAuthority::Extensions::AuthorityKeyIdentifier,
209
+ CertificateAuthority::Extensions::AuthorityInfoAccess,
210
+ CertificateAuthority::Extensions::KeyUsage,
211
+ CertificateAuthority::Extensions::ExtendedKeyUsage,
212
+ CertificateAuthority::Extensions::SubjectAlternativeName,
213
+ CertificateAuthority::Extensions::CertificatePolicies
214
+ ]
215
+
145
216
  def load_extensions
146
217
  extension_hash = {}
147
218
 
148
- temp_extensions = []
149
- basic_constraints = CertificateAuthority::Extensions::BasicConstraints.new
150
- temp_extensions << basic_constraints
151
- crl_distribution_points = CertificateAuthority::Extensions::CrlDistributionPoints.new
152
- temp_extensions << crl_distribution_points
153
- subject_key_identifier = CertificateAuthority::Extensions::SubjectKeyIdentifier.new
154
- temp_extensions << subject_key_identifier
155
- authority_key_identifier = CertificateAuthority::Extensions::AuthorityKeyIdentifier.new
156
- temp_extensions << authority_key_identifier
157
- authority_info_access = CertificateAuthority::Extensions::AuthorityInfoAccess.new
158
- temp_extensions << authority_info_access
159
- key_usage = CertificateAuthority::Extensions::KeyUsage.new
160
- temp_extensions << key_usage
161
- extended_key_usage = CertificateAuthority::Extensions::ExtendedKeyUsage.new
162
- temp_extensions << extended_key_usage
163
- subject_alternative_name = CertificateAuthority::Extensions::SubjectAlternativeName.new
164
- temp_extensions << subject_alternative_name
165
- certificate_policies = CertificateAuthority::Extensions::CertificatePolicies.new
166
- temp_extensions << certificate_policies
167
-
168
- temp_extensions.each do |extension|
219
+ EXTENSIONS.each do |klass|
220
+ extension = klass.new
169
221
  extension_hash[extension.openssl_identifier] = extension
170
222
  end
171
223
 
@@ -192,7 +244,11 @@ module CertificateAuthority
192
244
  certificate.serial_number.number = openssl_cert.serial.to_i
193
245
  certificate.not_before = openssl_cert.not_before
194
246
  certificate.not_after = openssl_cert.not_after
195
- # TODO extensions
247
+ EXTENSIONS.each do |klass|
248
+ _,v,c = (openssl_cert.extensions.detect { |e| e.to_a.first == klass::OPENSSL_IDENTIFIER } || []).to_a
249
+ certificate.extensions[klass::OPENSSL_IDENTIFIER] = klass.parse(v, c) if v
250
+ end
251
+
196
252
  certificate
197
253
  end
198
254
 
@@ -32,11 +32,16 @@ module CertificateAuthority
32
32
  alias :emailAddress :email_address
33
33
  alias :emailAddress= :email_address=
34
34
 
35
+ attr_accessor :serial_number
36
+ alias :serialNumber :serial_number
37
+ alias :serialNumber= :serial_number=
38
+
35
39
  def to_x509_name
36
40
  raise "Invalid Distinguished Name" unless valid?
37
41
 
38
42
  # NB: the capitalization in the strings counts
39
43
  name = OpenSSL::X509::Name.new
44
+ name.add_entry("serialNumber", serial_number) unless serial_number.blank?
40
45
  name.add_entry("C", country) unless country.blank?
41
46
  name.add_entry("ST", state) unless state.blank?
42
47
  name.add_entry("L", locality) unless locality.blank?