leap_cli 1.8.1 → 1.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/bin/leap +6 -12
  3. data/lib/leap_cli.rb +3 -23
  4. data/lib/leap_cli/bootstrap.rb +36 -12
  5. data/lib/leap_cli/commands/common.rb +88 -46
  6. data/lib/leap_cli/commands/new.rb +24 -17
  7. data/lib/leap_cli/commands/pre.rb +3 -1
  8. data/lib/leap_cli/core_ext/hash.rb +19 -0
  9. data/lib/leap_cli/leapfile.rb +47 -32
  10. data/lib/leap_cli/log.rb +196 -88
  11. data/lib/leap_cli/path.rb +5 -5
  12. data/lib/leap_cli/util.rb +28 -18
  13. data/lib/leap_cli/version.rb +8 -3
  14. data/vendor/acme-client/lib/acme-client.rb +1 -0
  15. data/vendor/acme-client/lib/acme/client.rb +122 -0
  16. data/vendor/acme-client/lib/acme/client/certificate.rb +30 -0
  17. data/vendor/acme-client/lib/acme/client/certificate_request.rb +111 -0
  18. data/vendor/acme-client/lib/acme/client/crypto.rb +98 -0
  19. data/vendor/acme-client/lib/acme/client/error.rb +16 -0
  20. data/vendor/acme-client/lib/acme/client/faraday_middleware.rb +123 -0
  21. data/vendor/acme-client/lib/acme/client/resources.rb +5 -0
  22. data/vendor/acme-client/lib/acme/client/resources/authorization.rb +44 -0
  23. data/vendor/acme-client/lib/acme/client/resources/challenges.rb +6 -0
  24. data/vendor/acme-client/lib/acme/client/resources/challenges/base.rb +43 -0
  25. data/vendor/acme-client/lib/acme/client/resources/challenges/dns01.rb +19 -0
  26. data/vendor/acme-client/lib/acme/client/resources/challenges/http01.rb +18 -0
  27. data/vendor/acme-client/lib/acme/client/resources/challenges/tls_sni01.rb +24 -0
  28. data/vendor/acme-client/lib/acme/client/resources/registration.rb +37 -0
  29. data/vendor/acme-client/lib/acme/client/self_sign_certificate.rb +60 -0
  30. data/vendor/acme-client/lib/acme/client/version.rb +7 -0
  31. data/vendor/base32/lib/base32.rb +67 -0
  32. data/vendor/certificate_authority/lib/certificate_authority.rb +2 -1
  33. data/vendor/certificate_authority/lib/certificate_authority/certificate.rb +4 -4
  34. data/vendor/certificate_authority/lib/certificate_authority/certificate_revocation_list.rb +7 -5
  35. data/vendor/certificate_authority/lib/certificate_authority/core_extensions.rb +46 -0
  36. data/vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb +6 -2
  37. data/vendor/certificate_authority/lib/certificate_authority/extensions.rb +10 -3
  38. data/vendor/certificate_authority/lib/certificate_authority/key_material.rb +11 -9
  39. data/vendor/certificate_authority/lib/certificate_authority/ocsp_handler.rb +3 -3
  40. data/vendor/certificate_authority/lib/certificate_authority/pkcs11_key_material.rb +0 -2
  41. data/vendor/certificate_authority/lib/certificate_authority/serial_number.rb +8 -2
  42. data/vendor/certificate_authority/lib/certificate_authority/validations.rb +31 -0
  43. data/vendor/rsync_command/lib/rsync_command.rb +49 -12
  44. metadata +50 -91
  45. data/lib/leap/platform.rb +0 -90
  46. data/lib/leap_cli/config/environment.rb +0 -180
  47. data/lib/leap_cli/config/filter.rb +0 -178
  48. data/lib/leap_cli/config/manager.rb +0 -419
  49. data/lib/leap_cli/config/node.rb +0 -77
  50. data/lib/leap_cli/config/object.rb +0 -428
  51. data/lib/leap_cli/config/object_list.rb +0 -209
  52. data/lib/leap_cli/config/provider.rb +0 -22
  53. data/lib/leap_cli/config/secrets.rb +0 -87
  54. data/lib/leap_cli/config/sources.rb +0 -11
  55. data/lib/leap_cli/config/tag.rb +0 -25
  56. data/lib/leap_cli/lib_ext/capistrano_connections.rb +0 -16
  57. data/lib/leap_cli/logger.rb +0 -237
  58. data/lib/leap_cli/remote/leap_plugin.rb +0 -192
  59. data/lib/leap_cli/remote/puppet_plugin.rb +0 -26
  60. data/lib/leap_cli/remote/rsync_plugin.rb +0 -35
  61. data/lib/leap_cli/remote/tasks.rb +0 -51
  62. data/lib/leap_cli/ssh_key.rb +0 -195
  63. data/lib/leap_cli/util/remote_command.rb +0 -158
  64. data/lib/leap_cli/util/secret.rb +0 -55
  65. data/lib/leap_cli/util/x509.rb +0 -33
@@ -0,0 +1,60 @@
1
+ class Acme::Client::SelfSignCertificate
2
+ attr_reader :private_key, :subject_alt_names, :not_before, :not_after
3
+
4
+ extend Forwardable
5
+ def_delegators :certificate, :to_pem, :to_der
6
+
7
+ def initialize(subject_alt_names:, not_before: default_not_before, not_after: default_not_after, private_key: generate_private_key)
8
+ @private_key = private_key
9
+ @subject_alt_names = subject_alt_names
10
+ @not_before = not_before
11
+ @not_after = not_after
12
+ end
13
+
14
+ def certificate
15
+ @certificate ||= begin
16
+ certificate = generate_certificate
17
+
18
+ extension_factory = generate_extension_factory(certificate)
19
+ subject_alt_name_entry = subject_alt_names.map { |d| "DNS: #{d}" }.join(',')
20
+ subject_alt_name_extension = extension_factory.create_extension('subjectAltName', subject_alt_name_entry)
21
+ certificate.add_extension(subject_alt_name_extension)
22
+
23
+ certificate.sign(private_key, digest)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def generate_private_key
30
+ OpenSSL::PKey::RSA.new(2048)
31
+ end
32
+
33
+ def default_not_before
34
+ Time.now - 3600
35
+ end
36
+
37
+ def default_not_after
38
+ Time.now + 30 * 24 * 3600
39
+ end
40
+
41
+ def digest
42
+ OpenSSL::Digest::SHA256.new
43
+ end
44
+
45
+ def generate_certificate
46
+ certificate = OpenSSL::X509::Certificate.new
47
+ certificate.not_before = not_before
48
+ certificate.not_after = not_after
49
+ certificate.public_key = private_key.public_key
50
+ certificate.version = 2
51
+ certificate
52
+ end
53
+
54
+ def generate_extension_factory(certificate)
55
+ extension_factory = OpenSSL::X509::ExtensionFactory.new
56
+ extension_factory.subject_certificate = certificate
57
+ extension_factory.issuer_certificate = certificate
58
+ extension_factory
59
+ end
60
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Acme
4
+ class Client
5
+ VERSION = '0.4.1'.freeze
6
+ end
7
+ end
@@ -0,0 +1,67 @@
1
+ require 'openssl'
2
+
3
+ # Module for encoding and decoding in Base32 per RFC 3548
4
+ module Base32
5
+ TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.freeze
6
+
7
+ class Chunk
8
+ def initialize(bytes)
9
+ @bytes = bytes
10
+ end
11
+
12
+ def decode
13
+ bytes = @bytes.take_while {|c| c != 61} # strip padding
14
+ n = (bytes.length * 5.0 / 8.0).floor
15
+ p = bytes.length < 8 ? 5 - (n * 8) % 5 : 0
16
+ c = bytes.inject(0) {|m,o| (m << 5) + Base32.table.index(o.chr)} >> p
17
+ (0..n-1).to_a.reverse.collect {|i| ((c >> i * 8) & 0xff).chr}
18
+ end
19
+
20
+ def encode
21
+ n = (@bytes.length * 8.0 / 5.0).ceil
22
+ p = n < 8 ? 5 - (@bytes.length * 8) % 5 : 0
23
+ c = @bytes.inject(0) {|m,o| (m << 8) + o} << p
24
+ [(0..n-1).to_a.reverse.collect {|i| Base32.table[(c >> i * 5) & 0x1f].chr},
25
+ ("=" * (8-n))]
26
+ end
27
+ end
28
+
29
+ def self.chunks(str, size)
30
+ result = []
31
+ bytes = str.bytes
32
+ while bytes.any? do
33
+ result << Chunk.new(bytes.take(size))
34
+ bytes = bytes.drop(size)
35
+ end
36
+ result
37
+ end
38
+
39
+ def self.encode(str)
40
+ chunks(str, 5).collect(&:encode).flatten.join
41
+ end
42
+
43
+ def self.decode(str)
44
+ chunks(str, 8).collect(&:decode).flatten.join
45
+ end
46
+
47
+ def self.random_base32(length=16, padding=true)
48
+ random = ''
49
+ OpenSSL::Random.random_bytes(length).each_byte do |b|
50
+ random << self.table[b % 32]
51
+ end
52
+ padding ? random.ljust((length / 8.0).ceil * 8, '=') : random
53
+ end
54
+
55
+ def self.table=(table)
56
+ raise ArgumentError, "Table must have 32 unique characters" unless self.table_valid?(table)
57
+ @table = table
58
+ end
59
+
60
+ def self.table
61
+ @table || TABLE
62
+ end
63
+
64
+ def self.table_valid?(table)
65
+ table.bytes.to_a.size == 32 && table.bytes.to_a.uniq.size == 32
66
+ end
67
+ end
@@ -2,11 +2,12 @@ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) ||
2
2
 
3
3
  #Exterior requirements
4
4
  require 'openssl'
5
- require 'active_model'
6
5
 
7
6
  #Internal modules
7
+ require 'certificate_authority/core_extensions'
8
8
  require 'certificate_authority/signing_entity'
9
9
  require 'certificate_authority/revocable'
10
+ require 'certificate_authority/validations'
10
11
  require 'certificate_authority/distinguished_name'
11
12
  require 'certificate_authority/serial_number'
12
13
  require 'certificate_authority/key_material'
@@ -1,6 +1,6 @@
1
1
  module CertificateAuthority
2
2
  class Certificate
3
- include ActiveModel::Validations
3
+ include Validations
4
4
  include Revocable
5
5
 
6
6
  attr_accessor :distinguished_name
@@ -15,7 +15,7 @@ module CertificateAuthority
15
15
 
16
16
  attr_accessor :parent
17
17
 
18
- validate do |certificate|
18
+ def validate
19
19
  errors.add :base, "Distinguished name must be valid" unless distinguished_name.valid?
20
20
  errors.add :base, "Key material must be valid" unless key_material.valid?
21
21
  errors.add :base, "Serial number must be valid" unless serial_number.valid?
@@ -32,8 +32,8 @@ module CertificateAuthority
32
32
  self.distinguished_name = DistinguishedName.new
33
33
  self.serial_number = SerialNumber.new
34
34
  self.key_material = MemoryKeyMaterial.new
35
- self.not_before = Time.now
36
- self.not_after = Time.now + 60 * 60 * 24 * 365 # One year
35
+ self.not_before = Date.today.utc
36
+ self.not_after = Date.today.advance(:years => 1).utc
37
37
  self.parent = self
38
38
  self.extensions = load_extensions()
39
39
 
@@ -1,20 +1,22 @@
1
1
  module CertificateAuthority
2
2
  class CertificateRevocationList
3
- include ActiveModel::Validations
3
+ include Validations
4
4
 
5
5
  attr_accessor :certificates
6
6
  attr_accessor :parent
7
7
  attr_accessor :crl_body
8
8
  attr_accessor :next_update
9
+ attr_accessor :last_update_skew_seconds
9
10
 
10
- validate do |crl|
11
- errors.add :next_update, "Next update must be a positive value" if crl.next_update < 0
12
- errors.add :parent, "A parent entity must be set" if crl.parent.nil?
11
+ def validate
12
+ errors.add :next_update, "Next update must be a positive value" if self.next_update < 0
13
+ errors.add :parent, "A parent entity must be set" if self.parent.nil?
13
14
  end
14
15
 
15
16
  def initialize
16
17
  self.certificates = []
17
18
  self.next_update = 60 * 60 * 4 # 4 hour default
19
+ self.last_update_skew_seconds = 0
18
20
  end
19
21
 
20
22
  def <<(revocable)
@@ -54,7 +56,7 @@ module CertificateAuthority
54
56
  end
55
57
 
56
58
  crl.version = 1
57
- crl.last_update = Time.now
59
+ crl.last_update = Time.now - self.last_update_skew_seconds
58
60
  crl.next_update = Time.now + self.next_update
59
61
 
60
62
  signing_cert = OpenSSL::X509::Certificate.new(self.parent.to_pem)
@@ -0,0 +1,46 @@
1
+ #
2
+ # ActiveSupport has these modifications. Now that we don't use ActiveSupport,
3
+ # these are added here as a kindness.
4
+ #
5
+
6
+ require 'date'
7
+
8
+ unless nil.respond_to?(:blank?)
9
+ class NilClass
10
+ def blank?
11
+ true
12
+ end
13
+ end
14
+ end
15
+
16
+ unless String.respond_to?(:blank?)
17
+ class String
18
+ def blank?
19
+ self.empty?
20
+ end
21
+ end
22
+ end
23
+
24
+ class Date
25
+
26
+ def today
27
+ t = Time.now.utc
28
+ Date.new(t.year, t.month, t.day)
29
+ end
30
+
31
+ def utc
32
+ self.to_datetime.to_time.utc
33
+ end
34
+
35
+ unless Date.respond_to?(:advance)
36
+ def advance(options)
37
+ options = options.dup
38
+ d = self
39
+ d = d >> options.delete(:years) * 12 if options[:years]
40
+ d = d >> options.delete(:months) if options[:months]
41
+ d = d + options.delete(:weeks) * 7 if options[:weeks]
42
+ d = d + options.delete(:days) if options[:days]
43
+ d
44
+ end
45
+ end
46
+ end
@@ -1,8 +1,12 @@
1
1
  module CertificateAuthority
2
2
  class DistinguishedName
3
- include ActiveModel::Validations
3
+ include Validations
4
4
 
5
- validates_presence_of :common_name
5
+ def validate
6
+ if self.common_name.nil? || self.common_name.empty?
7
+ errors.add :common_name, 'cannot be blank'
8
+ end
9
+ end
6
10
 
7
11
  attr_accessor :common_name
8
12
  alias :cn :common_name
@@ -31,13 +31,20 @@ module CertificateAuthority
31
31
  OPENSSL_IDENTIFIER = "basicConstraints"
32
32
 
33
33
  include ExtensionAPI
34
- include ActiveModel::Validations
34
+ include Validations
35
35
 
36
36
  attr_accessor :critical
37
37
  attr_accessor :ca
38
38
  attr_accessor :path_len
39
- validates :critical, :inclusion => [true,false]
40
- validates :ca, :inclusion => [true,false]
39
+
40
+ def validate
41
+ unless [true, false].include? self.critical
42
+ errors.add :critical, 'must be true or false'
43
+ end
44
+ unless [true, false].include? self.ca
45
+ errors.add :ca, 'must be true or false'
46
+ end
47
+ end
41
48
 
42
49
  def initialize
43
50
  @critical = false
@@ -38,7 +38,7 @@ module CertificateAuthority
38
38
 
39
39
  class MemoryKeyMaterial
40
40
  include KeyMaterial
41
- include ActiveModel::Validations
41
+ include Validations
42
42
 
43
43
  attr_accessor :keypair
44
44
  attr_accessor :private_key
@@ -47,11 +47,13 @@ module CertificateAuthority
47
47
  def initialize
48
48
  end
49
49
 
50
- validates_each :private_key do |record, attr, value|
51
- record.errors.add :private_key, "cannot be blank" if record.private_key.nil?
52
- end
53
- validates_each :public_key do |record, attr, value|
54
- record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
50
+ def validate
51
+ if private_key.nil?
52
+ errors.add :private_key, "cannot be blank"
53
+ end
54
+ if public_key.nil?
55
+ errors.add :public_key, "cannot be blank"
56
+ end
55
57
  end
56
58
 
57
59
  def is_in_hardware?
@@ -80,10 +82,10 @@ module CertificateAuthority
80
82
 
81
83
  class SigningRequestKeyMaterial
82
84
  include KeyMaterial
83
- include ActiveModel::Validations
85
+ include Validations
84
86
 
85
- validates_each :public_key do |record, attr, value|
86
- record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
87
+ def validate
88
+ errors.add :public_key, "cannot be blank" if public_key.nil?
87
89
  end
88
90
 
89
91
  attr_accessor :public_key
@@ -68,7 +68,7 @@ module CertificateAuthority
68
68
 
69
69
  ## DEPRECATED
70
70
  class OCSPHandler
71
- include ActiveModel::Validations
71
+ include Validations
72
72
 
73
73
  attr_accessor :ocsp_request
74
74
  attr_accessor :certificate_ids
@@ -78,10 +78,10 @@ module CertificateAuthority
78
78
 
79
79
  attr_accessor :ocsp_response_body
80
80
 
81
- validate do |crl|
81
+ def validate
82
82
  errors.add :parent, "A parent entity must be set" if parent.nil?
83
+ all_certificates_available
83
84
  end
84
- validate :all_certificates_available
85
85
 
86
86
  def initialize
87
87
  self.certificates = {}
@@ -1,8 +1,6 @@
1
1
  module CertificateAuthority
2
2
  class Pkcs11KeyMaterial
3
3
  include KeyMaterial
4
- include ActiveModel::Validations
5
- include ActiveModel::Serialization
6
4
 
7
5
  attr_accessor :engine
8
6
  attr_accessor :token_id
@@ -2,12 +2,18 @@ require 'securerandom'
2
2
 
3
3
  module CertificateAuthority
4
4
  class SerialNumber
5
- include ActiveModel::Validations
5
+ include Validations
6
6
  include Revocable
7
7
 
8
8
  attr_accessor :number
9
9
 
10
- validates :number, :presence => true, :numericality => {:greater_than => 0}
10
+ def validate
11
+ if self.number.nil?
12
+ errors.add :number, "must not be empty"
13
+ elsif self.number.to_i <= 0
14
+ errors.add :number, "must be greater than zero"
15
+ end
16
+ end
11
17
 
12
18
  def initialize
13
19
  self.number = SecureRandom.random_number(2**128-1)
@@ -0,0 +1,31 @@
1
+ #
2
+ # This is a super simple replacement for ActiveSupport::Validations
3
+ #
4
+
5
+ module CertificateAuthority
6
+ class Errors < Array
7
+ def add(symbol, msg)
8
+ self.push([symbol, msg])
9
+ end
10
+ def full_messages
11
+ self.map {|i| i[0].to_s + ": " + i[1]}.join("\n")
12
+ end
13
+ end
14
+
15
+ module Validations
16
+ def valid?
17
+ @errors = Errors.new
18
+ validate
19
+ errors.empty?
20
+ end
21
+
22
+ # must be overridden
23
+ def validate
24
+ raise NotImplementedError
25
+ end
26
+
27
+ def errors
28
+ @errors ||= Errors.new
29
+ end
30
+ end
31
+ end
@@ -4,6 +4,44 @@ require "rsync_command/thread_pool"
4
4
 
5
5
  require 'monitor'
6
6
 
7
+ class RsyncRunner
8
+ attr_accessor :logger
9
+ attr_accessor :source, :dest, :flags, :includes, :excludes
10
+ attr_accessor :user, :host
11
+ attr_accessor :chdir, :ssh
12
+ def initialize(rsync_command)
13
+ @logger = nil
14
+ @source = ""
15
+ @dest = ""
16
+ @flags = ""
17
+ @includes = []
18
+ @excludes = []
19
+ @rsync_command = rsync_command
20
+ end
21
+ def log(*args)
22
+ @logger.log(*args)
23
+ end
24
+ def valid?
25
+ !@source.empty? || !@dest.empty?
26
+ end
27
+ def to_hash
28
+ fields = [:flags, :includes, :excludes, :logger, :ssh, :chdir]
29
+ fields.inject({}){|hsh, i|
30
+ hsh[i] = self.send(i); hsh
31
+ }
32
+ end
33
+ def exec
34
+ return unless valid?
35
+ dest = {
36
+ :user => self.user,
37
+ :host => self.host,
38
+ :path => self.dest
39
+ }
40
+ src = self.source
41
+ @rsync_command.exec_rsync(src, dest, self.to_hash)
42
+ end
43
+ end
44
+
7
45
  class RsyncCommand
8
46
  attr_accessor :failures, :logger
9
47
 
@@ -21,15 +59,23 @@ class RsyncCommand
21
59
  def asynchronously(array, &block)
22
60
  pool = ThreadPool.new
23
61
  array.each do |item|
24
- pool.schedule(item, &block)
62
+ pool.schedule(RsyncRunner.new(self), item, &block)
25
63
  end
26
64
  pool.shutdown
27
65
  end
28
66
 
67
+ #
68
+ # returns true if last exec returned a failure
69
+ #
70
+ def failed?
71
+ @failures && @failures.any?
72
+ end
73
+
29
74
  #
30
75
  # runs rsync, recording failures
31
76
  #
32
- def exec(src, dest, options={})
77
+ def exec_rsync(src, dest, options={})
78
+ logger = options[:logger] || @logger
33
79
  @failures.synchronize do
34
80
  @failures.clear
35
81
  end
@@ -37,7 +83,7 @@ class RsyncCommand
37
83
  if options[:chdir]
38
84
  rsync_cmd = "cd '#{options[:chdir]}'; #{rsync_cmd}"
39
85
  end
40
- @logger.debug rsync_cmd if @logger
86
+ logger.debug rsync_cmd if logger
41
87
  ok = system(rsync_cmd)
42
88
  unless ok
43
89
  @failures.synchronize do
@@ -46,13 +92,6 @@ class RsyncCommand
46
92
  end
47
93
  end
48
94
 
49
- #
50
- # returns true if last exec returned a failure
51
- #
52
- def failed?
53
- @failures && @failures.any?
54
- end
55
-
56
95
  #
57
96
  # build rsync command
58
97
  #
@@ -70,8 +109,6 @@ class RsyncCommand
70
109
  "rsync #{flags.compact.join(' ')} #{src} #{dest}"
71
110
  end
72
111
 
73
- private
74
-
75
112
  #
76
113
  # Creates an rsync location if the +address+ is a hash with keys :user, :host, and :path
77
114
  # (each component is optional). If +address+ is a string, we just pass it through.