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