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
@@ -1,192 +0,0 @@
1
- #
2
- # these methods are made available in capistrano tasks as 'leap.method_name'
3
- # (see RemoteCommand::new_capistrano)
4
- #
5
-
6
- module LeapCli; module Remote; module LeapPlugin
7
-
8
- def required_packages
9
- "puppet rsync lsb-release locales"
10
- end
11
-
12
- def log(*args, &block)
13
- LeapCli::Util::log(*args, &block)
14
- end
15
-
16
- #
17
- # creates directories that are owned by root and 700 permissions
18
- #
19
- def mkdirs(*dirs)
20
- raise ArgumentError.new('illegal dir name') if dirs.grep(/[\' ]/).any?
21
- run dirs.collect{|dir| "mkdir -m 700 -p #{dir}; "}.join
22
- end
23
-
24
- #
25
- # echos "ok" if the node has been initialized and the required packages are installed, bails out otherwise.
26
- #
27
- def assert_initialized
28
- begin
29
- test_initialized_file = "test -f #{Leap::Platform.init_path}"
30
- check_required_packages = "! dpkg-query -W --showformat='${Status}\n' #{required_packages} 2>&1 | grep -q -E '(deinstall|no packages)'"
31
- run "#{test_initialized_file} && #{check_required_packages} && echo ok"
32
- rescue Capistrano::CommandError => exc
33
- LeapCli::Util.bail! do
34
- exc.hosts.each do |host|
35
- node = host.to_s.split('.').first
36
- LeapCli::Util.log :error, "running deploy: node not initialized. Run 'leap node init #{node}'", :host => host
37
- end
38
- end
39
- end
40
- end
41
-
42
- #
43
- # bails out the deploy if the file /etc/leap/no-deploy exists.
44
- # This kind of sucks, because it would be better to skip over nodes that have no-deploy set instead
45
- # halting the entire deploy. As far as I know, with capistrano, there is no way to close one of the
46
- # ssh connections in the pool and make sure it gets no further commands.
47
- #
48
- def check_for_no_deploy
49
- begin
50
- run "test ! -f /etc/leap/no-deploy"
51
- rescue Capistrano::CommandError => exc
52
- LeapCli::Util.bail! do
53
- exc.hosts.each do |host|
54
- LeapCli::Util.log "Can't continue because file /etc/leap/no-deploy exists", :host => host
55
- end
56
- end
57
- end
58
- end
59
-
60
- #
61
- # dumps debugging information
62
- # #
63
- def debug
64
- run "#{Leap::Platform.leap_dir}/bin/debug.sh"
65
- end
66
-
67
- #
68
- # dumps the recent deploy history to the console
69
- #
70
- def history(lines)
71
- command = "(test -s /var/log/leap/deploy-summary.log && tail -n #{lines} /var/log/leap/deploy-summary.log) || (test -s /var/log/leap/deploy-summary.log.1 && tail -n #{lines} /var/log/leap/deploy-summary.log.1) || (echo 'no history')"
72
- run command
73
- end
74
-
75
- #
76
- # This is a hairy ugly hack, exactly the kind of stuff that makes ruby
77
- # dangerous and too much fun for its own good.
78
- #
79
- # In most places, we run remote ssh without a current 'task'. This works fine,
80
- # except that in a few places, the behavior of capistrano ssh is controlled by
81
- # the options of the current task.
82
- #
83
- # We don't want to create an actual current task, because tasks are no fun
84
- # and can't take arguments or return values. So, when we need to configure
85
- # things that can only be configured in a task, we use this handy hack to
86
- # fake the current task.
87
- #
88
- # This is NOT thread safe, but could be made to be so with some extra work.
89
- #
90
- def with_task(name)
91
- task = @config.tasks[name]
92
- @config.class.send(:alias_method, :original_current_task, :current_task)
93
- @config.class.send(:define_method, :current_task, Proc.new(){ task })
94
- begin
95
- yield
96
- ensure
97
- @config.class.send(:remove_method, :current_task)
98
- @config.class.send(:alias_method, :current_task, :original_current_task)
99
- end
100
- end
101
-
102
- #
103
- # similar to run(cmd, &block), but with:
104
- #
105
- # * exit codes
106
- # * stdout and stderr are combined
107
- #
108
- def stream(cmd, &block)
109
- command = '%s 2>&1; echo "exitcode=$?"' % cmd
110
- run(command) do |channel, stream, data|
111
- exitcode = nil
112
- if data =~ /exitcode=(\d+)\n/
113
- exitcode = $1.to_i
114
- data.sub!(/exitcode=(\d+)\n/,'')
115
- end
116
- yield({:host => channel[:host], :data => data, :exitcode => exitcode})
117
- end
118
- end
119
-
120
- #
121
- # like stream, but capture all the output before returning
122
- #
123
- def capture(cmd, &block)
124
- command = '%s 2>&1; echo "exitcode=$?" 2>&1;' % cmd
125
- host_data = {}
126
- run(command) do |channel, stream, data|
127
- host_data[channel[:host]] ||= ""
128
- if data =~ /exitcode=(\d+)\n/
129
- exitcode = $1.to_i
130
- data.sub!(/exitcode=(\d+)\n/,'')
131
- host_data[channel[:host]] += data
132
- yield({:host => channel[:host], :data => host_data[channel[:host]], :exitcode => exitcode})
133
- else
134
- host_data[channel[:host]] += data
135
- end
136
- end
137
- end
138
-
139
- #
140
- # Run a command, with a nice status report and progress indicator.
141
- # Only successful results are returned, errors are printed.
142
- #
143
- # For each successful run on each host, block is yielded with a hash like so:
144
- #
145
- # {:host => 'bluejay', :exitcode => 0, :data => 'shell output'}
146
- #
147
- def run_with_progress(cmd, &block)
148
- ssh_failures = []
149
- exitcode_failures = []
150
- succeeded = []
151
- task = LeapCli.log_level > 1 ? :standard_task : :skip_errors_task
152
- with_task(task) do
153
- log :querying, 'facts' do
154
- progress " "
155
- call_on_failure do |host|
156
- ssh_failures << host
157
- progress 'F'
158
- end
159
- capture(cmd) do |response|
160
- if response[:exitcode] == 0
161
- progress '.'
162
- yield response
163
- else
164
- exitcode_failures << response
165
- progress 'F'
166
- end
167
- end
168
- end
169
- end
170
- puts "done"
171
- if ssh_failures.any?
172
- log :failed, 'to connect to nodes: ' + ssh_failures.join(' ')
173
- end
174
- if exitcode_failures.any?
175
- log :failed, 'to run successfully:' do
176
- exitcode_failures.each do |response|
177
- log "[%s] exit %s - %s" % [response[:host], response[:exitcode], response[:data].strip]
178
- end
179
- end
180
- end
181
- rescue Capistrano::RemoteError => err
182
- log :error, err.to_s
183
- end
184
-
185
- private
186
-
187
- def progress(str='.')
188
- print str
189
- STDOUT.flush
190
- end
191
-
192
- end; end; end
@@ -1,26 +0,0 @@
1
- #
2
- # these methods are made available in capistrano tasks as 'puppet.method_name'
3
- # (see RemoteCommand::new_capistrano)
4
- #
5
-
6
- module LeapCli; module Remote; module PuppetPlugin
7
-
8
- def apply(options)
9
- run "#{Leap::Platform.leap_dir}/bin/puppet_command set_hostname apply #{flagize(options)}"
10
- end
11
-
12
- private
13
-
14
- def flagize(hsh)
15
- hsh.inject([]) {|str, item|
16
- if item[1] === false
17
- str
18
- elsif item[1] === true
19
- str << "--" + item[0].to_s
20
- else
21
- str << "--" + item[0].to_s + " " + item[1].inspect
22
- end
23
- }.join(' ')
24
- end
25
-
26
- end; end; end
@@ -1,35 +0,0 @@
1
- #
2
- # these methods are made available in capistrano tasks as 'rsync.method_name'
3
- # (see RemoteCommand::new_capistrano)
4
- #
5
-
6
- autoload :RsyncCommand, 'rsync_command'
7
-
8
- module LeapCli; module Remote; module RsyncPlugin
9
-
10
- #
11
- # takes a block, yielded a server, that should return a hash with various rsync options.
12
- # supported options include:
13
- #
14
- # {:source => '', :dest => '', :flags => '', :includes => [], :excludes => []}
15
- #
16
- def update
17
- rsync = RsyncCommand.new(:logger => logger)
18
- rsync.asynchronously(find_servers) do |server|
19
- options = yield server
20
- next unless options
21
- remote_user = server.user || fetch(:user, ENV['USER'])
22
- src = options[:source]
23
- dest = {:user => remote_user, :host => server.host, :path => options[:dest]}
24
- options[:ssh] = ssh_options.merge(server.options[:ssh_options]||{})
25
- options[:chdir] ||= Path.provider
26
- rsync.exec(src, dest, options)
27
- end
28
- if rsync.failed?
29
- LeapCli::Util.bail! do
30
- LeapCli::Util.log :failed, "to rsync to #{rsync.failures.map{|f|f[:dest][:host]}.join(' ')}"
31
- end
32
- end
33
- end
34
-
35
- end; end; end
@@ -1,51 +0,0 @@
1
- #
2
- # This file is evaluated just the same as a typical capistrano "deploy.rb"
3
- # For DSL manual, see https://github.com/capistrano/capistrano/wiki
4
- #
5
-
6
- MAX_HOSTS = 10
7
-
8
- task :install_authorized_keys, :max_hosts => MAX_HOSTS do
9
- leap.log :updating, "authorized_keys" do
10
- leap.mkdirs '/root/.ssh'
11
- upload LeapCli::Path.named_path(:authorized_keys), '/root/.ssh/authorized_keys', :mode => '600'
12
- end
13
- end
14
-
15
- #
16
- # for vagrant nodes, we install insecure vagrant key to authorized_keys2, since deploy
17
- # will overwrite authorized_keys.
18
- #
19
- # why force the insecure vagrant key?
20
- # if we don't do this, then first time initialization might fail if the user has many keys
21
- # (ssh will bomb out before it gets to the vagrant key).
22
- # and it really doesn't make sense to ask users to pin the insecure vagrant key in their
23
- # .ssh/config files.
24
- #
25
- task :install_insecure_vagrant_key, :max_hosts => MAX_HOSTS do
26
- leap.log :installing, "insecure vagrant key" do
27
- leap.mkdirs '/root/.ssh'
28
- upload LeapCli::Path.vagrant_ssh_pub_key_file, '/root/.ssh/authorized_keys2', :mode => '600'
29
- end
30
- end
31
-
32
- task :install_prerequisites, :max_hosts => MAX_HOSTS do
33
- bin_dir = File.join(Leap::Platform.leap_dir, 'bin')
34
- node_init_path = File.join(bin_dir, 'node_init')
35
-
36
- leap.log :running, "node_init script" do
37
- leap.mkdirs bin_dir
38
- upload LeapCli::Path.node_init_script, node_init_path, :mode => '500'
39
- run node_init_path
40
- end
41
- end
42
-
43
- #
44
- # just dummies, used to capture task options
45
- #
46
-
47
- task :skip_errors_task, :on_error => :continue, :max_hosts => MAX_HOSTS do
48
- end
49
-
50
- task :standard_task, :max_hosts => MAX_HOSTS do
51
- end
@@ -1,195 +0,0 @@
1
- #
2
- # A wrapper around OpenSSL::PKey::RSA instances to provide a better api for dealing with SSH keys.
3
- #
4
- # cipher 'ssh-ed25519' not supported yet because we are waiting for support in Net::SSH
5
- #
6
-
7
- require 'net/ssh'
8
- require 'forwardable'
9
-
10
- module LeapCli
11
- class SshKey
12
- extend Forwardable
13
-
14
- attr_accessor :filename
15
- attr_accessor :comment
16
-
17
- # supported ssh key types, in order of preference
18
- SUPPORTED_TYPES = ['ssh-rsa', 'ecdsa-sha2-nistp256']
19
- SUPPORTED_TYPES_RE = /(#{SUPPORTED_TYPES.join('|')})/
20
-
21
- ##
22
- ## CLASS METHODS
23
- ##
24
-
25
- def self.load(arg1, arg2=nil)
26
- key = nil
27
- if arg1.is_a? OpenSSL::PKey::RSA
28
- key = SshKey.new arg1
29
- elsif arg1.is_a? String
30
- if arg1 =~ /^ssh-/
31
- type, data = arg1.split(' ')
32
- key = SshKey.new load_from_data(data, type)
33
- elsif File.exists? arg1
34
- key = SshKey.new load_from_file(arg1)
35
- key.filename = arg1
36
- else
37
- key = SshKey.new load_from_data(arg1, arg2)
38
- end
39
- end
40
- return key
41
- rescue StandardError => exc
42
- end
43
-
44
- def self.load_from_file(filename)
45
- public_key = nil
46
- private_key = nil
47
- begin
48
- public_key = Net::SSH::KeyFactory.load_public_key(filename)
49
- rescue NotImplementedError, Net::SSH::Exception, OpenSSL::PKey::PKeyError
50
- begin
51
- private_key = Net::SSH::KeyFactory.load_private_key(filename)
52
- rescue NotImplementedError, Net::SSH::Exception, OpenSSL::PKey::PKeyError
53
- end
54
- end
55
- public_key || private_key
56
- end
57
-
58
- def self.load_from_data(data, type='ssh-rsa')
59
- public_key = nil
60
- private_key = nil
61
- begin
62
- public_key = Net::SSH::KeyFactory.load_data_public_key("#{type} #{data}")
63
- rescue NotImplementedError, Net::SSH::Exception, OpenSSL::PKey::PKeyError
64
- begin
65
- private_key = Net::SSH::KeyFactory.load_data_private_key("#{type} #{data}")
66
- rescue NotImplementedError, Net::SSH::Exception, OpenSSL::PKey::PKeyError
67
- end
68
- end
69
- public_key || private_key
70
- end
71
-
72
- #
73
- # Picks one key out of an array of keys that we think is the "best",
74
- # based on the order of preference in SUPPORTED_TYPES
75
- #
76
- # Currently, this does not take bitsize into account.
77
- #
78
- def self.pick_best_key(keys)
79
- keys.select {|k|
80
- SUPPORTED_TYPES.include?(k.type)
81
- }.sort {|a,b|
82
- SUPPORTED_TYPES.index(a.type) <=> SUPPORTED_TYPES.index(b.type)
83
- }.first
84
- end
85
-
86
- #
87
- # takes a string with one or more ssh keys, one key per line,
88
- # and returns an array of SshKey objects.
89
- #
90
- # the lines should be in one of these formats:
91
- #
92
- # 1. <hostname> <key-type> <key>
93
- # 2. <key-type> <key>
94
- #
95
- def self.parse_keys(string)
96
- keys = []
97
- lines = string.split("\n").grep(/^[^#]/)
98
- lines.each do |line|
99
- if line =~ / #{SshKey::SUPPORTED_TYPES_RE} /
100
- # <hostname> <key-type> <key>
101
- keys << line.split(' ')[1..2]
102
- elsif line =~ /^#{SshKey::SUPPORTED_TYPES_RE} /
103
- # <key-type> <key>
104
- keys << line.split(' ')
105
- end
106
- end
107
- return keys.map{|k| SshKey.load(k[1], k[0])}
108
- end
109
-
110
- #
111
- # takes a string with one or more ssh keys, one key per line,
112
- # and returns a string that specified the ssh key algorithms
113
- # that are supported by the keys, in order of preference.
114
- #
115
- # eg: ecdsa-sha2-nistp256,ssh-rsa,ssh-ed25519
116
- #
117
- def self.supported_host_key_algorithms(string)
118
- if string
119
- self.parse_keys(string).map {|key|
120
- key.type
121
- }.join(',')
122
- else
123
- ""
124
- end
125
- end
126
-
127
- ##
128
- ## INSTANCE METHODS
129
- ##
130
-
131
- public
132
-
133
- def initialize(rsa_key)
134
- @key = rsa_key
135
- end
136
-
137
- def_delegator :@key, :fingerprint, :fingerprint
138
- def_delegator :@key, :public?, :public?
139
- def_delegator :@key, :private?, :private?
140
- def_delegator :@key, :ssh_type, :type
141
- def_delegator :@key, :public_encrypt, :public_encrypt
142
- def_delegator :@key, :public_decrypt, :public_decrypt
143
- def_delegator :@key, :private_encrypt, :private_encrypt
144
- def_delegator :@key, :private_decrypt, :private_decrypt
145
- def_delegator :@key, :params, :params
146
- def_delegator :@key, :to_text, :to_text
147
-
148
- def public_key
149
- SshKey.new(@key.public_key)
150
- end
151
-
152
- def private_key
153
- SshKey.new(@key.private_key)
154
- end
155
-
156
- #
157
- # not sure if this will always work, but is seems to for now.
158
- #
159
- def bits
160
- Net::SSH::Buffer.from(:key, @key).to_s.split("\001\000").last.size * 8
161
- end
162
-
163
- def summary
164
- if self.filename
165
- "%s %s %s (%s)" % [self.type, self.bits, self.fingerprint, File.basename(self.filename)]
166
- else
167
- "%s %s %s" % [self.type, self.bits, self.fingerprint]
168
- end
169
- end
170
-
171
- def to_s
172
- self.type + " " + self.key
173
- end
174
-
175
- def key
176
- [Net::SSH::Buffer.from(:key, @key).to_s].pack("m*").gsub(/\s/, "")
177
- end
178
-
179
- def ==(other_key)
180
- return false if other_key.nil?
181
- return false if self.class != other_key.class
182
- return self.to_text == other_key.to_text
183
- end
184
-
185
- def in_known_hosts?(*identifiers)
186
- identifiers.each do |identifier|
187
- Net::SSH::KnownHosts.search_for(identifier).each do |key|
188
- return true if self == key
189
- end
190
- end
191
- return false
192
- end
193
-
194
- end
195
- end