leap_cli 1.8.1 → 1.9

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.
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.