diff options
Diffstat (limited to 'lib/leap_cli/commands')
-rw-r--r-- | lib/leap_cli/commands/cert.rb (renamed from lib/leap_cli/commands/ca.rb) | 68 | ||||
-rw-r--r-- | lib/leap_cli/commands/compile.rb | 2 | ||||
-rw-r--r-- | lib/leap_cli/commands/node.rb | 27 | ||||
-rw-r--r-- | lib/leap_cli/commands/ping.rb | 58 | ||||
-rw-r--r-- | lib/leap_cli/commands/run.rb | 53 | ||||
-rw-r--r-- | lib/leap_cli/commands/test.rb | 2 | ||||
-rw-r--r-- | lib/leap_cli/commands/user.rb | 37 | ||||
-rw-r--r-- | lib/leap_cli/commands/vagrant.rb | 4 | ||||
-rw-r--r-- | lib/leap_cli/commands/vm.rb | 5 |
9 files changed, 217 insertions, 39 deletions
diff --git a/lib/leap_cli/commands/ca.rb b/lib/leap_cli/commands/cert.rb index 3c5fc7d5..68fa9444 100644 --- a/lib/leap_cli/commands/ca.rb +++ b/lib/leap_cli/commands/cert.rb @@ -102,13 +102,13 @@ module LeapCli; module Commands def generate_test_client_cert(prefix=nil) require 'leap_cli/x509' cert = CertificateAuthority::Certificate.new - cert.serial_number.number = cert_serial_number(provider.domain) - cert.subject.common_name = [prefix, random_common_name(provider.domain)].join + cert.serial_number.number = X509.cert_serial_number(provider.domain) + cert.subject.common_name = [prefix, X509.random_common_name(provider.domain)].join cert.not_before = X509.yesterday cert.not_after = X509.yesterday.advance(:years => 1) cert.key_material.generate_key(1024) # just for testing, remember! - cert.parent = client_ca_root - cert.sign! client_test_signing_profile + cert.parent = X509.client_ca_root + cert.sign! X509.client_test_signing_profile yield cert.key_material.private_key.to_pem, cert.to_pem end @@ -281,11 +281,13 @@ module LeapCli; module Commands if status == 'valid' log 'authorized!', color: :green, style: :bold elsif status == 'error' - bail! :error, message + bail! :error, message.inspect elsif status == 'unauthorized' - bail!(:unauthorized, message, color: :yellow, style: :bold) do + bail!(:unauthorized, message.inspect, color: :yellow, style: :bold) do log 'You must first run `leap cert register` to register the account key with letsencrypt.org' end + else + bail!(:error, "unrecognized status: #{status.inspect}, #{message.inspect}") end log :fetching, "new certificate from letsencrypt.org" @@ -335,31 +337,41 @@ module LeapCli; module Commands # This method will bail if any checks fail. # def domain_ready_for_acme!(domain) - begin - uri = URI("https://#{domain}/.well-known/acme-challenge/ok") - options = { - use_ssl: true, - open_timeout: 5, - verify_mode: OpenSSL::SSL::VERIFY_NONE - } - Net::HTTP.start(uri.host, uri.port, options) do |http| - http.request(Net::HTTP::Get.new(uri)) do |response| - if !response.is_a?(Net::HTTPSuccess) - bail!(:error, "Could not GET %s" % uri) do - log "%s %s" % [response.code, response.message] - log "You may need to run `leap deploy`" - end + uri = URI("https://#{domain}/.well-known/acme-challenge/ok") + options = { + use_ssl: true, + open_timeout: 5, + verify_mode: OpenSSL::SSL::VERIFY_NONE + } + http_get(uri, options) + end + + private + + def http_get(uri, options, limit = 10) + raise ArgumentError, "HTTP redirect too deep (#{uri})" if limit == 0 + Net::HTTP.start(uri.host, uri.port, options) do |http| + http.request(Net::HTTP::Get.new(uri)) do |response| + case response + when Net::HTTPSuccess then + return response + when Net::HTTPRedirection then + return http_get(URI(response['location']), options, limit - 1) + else + bail!(:error, "Could not GET %s" % uri) do + log "%s %s" % [response.code, response.message] + log "You may need to run `leap deploy`" end end end - rescue Errno::ETIMEDOUT, Net::OpenTimeout - bail! :error, "Connection attempt timed out: %s" % uri - rescue Interrupt - bail! - rescue StandardError => exc - bail!(:error, "Could not GET %s" % uri) do - log exc.to_s - end + end + rescue Errno::ETIMEDOUT, Net::OpenTimeout + bail! :error, "Connection attempt timed out: %s" % uri + rescue Interrupt + bail! + rescue StandardError => exc + bail!(:error, "Could not GET %s" % uri) do + log exc.to_s end end diff --git a/lib/leap_cli/commands/compile.rb b/lib/leap_cli/commands/compile.rb index 92c879d7..16dff3df 100644 --- a/lib/leap_cli/commands/compile.rb +++ b/lib/leap_cli/commands/compile.rb @@ -155,7 +155,7 @@ module LeapCli buffer = StringIO.new keys = Dir.glob(path([:user_ssh, '*'])) if keys.empty? - bail! "You must have at least one public SSH user key configured in order to proceed. See `leap help add-user`." + bail! "You must have at least one public SSH user key configured in order to proceed. See `leap help user add`." end if file_exists?(path(:monitor_pub_key)) keys << path(:monitor_pub_key) diff --git a/lib/leap_cli/commands/node.rb b/lib/leap_cli/commands/node.rb index 60540de9..9cde15bc 100644 --- a/lib/leap_cli/commands/node.rb +++ b/lib/leap_cli/commands/node.rb @@ -45,6 +45,23 @@ module LeapCli; module Commands do_node_rm(global_options, options, args) end end + + node.desc 'Mark a node as disabled.' + node.arg_name 'NAME' + node.command :disable do |cmd| + cmd.action do |global_options,options,args| + do_node_disable(global_options, options, args) + end + end + + node.desc 'Mark a node as enabled.' + node.arg_name 'NAME' + node.command :enable do |cmd| + cmd.action do |global_options,options,args| + do_node_enable(global_options, options, args) + end + end + end ## @@ -126,4 +143,14 @@ module LeapCli; module Commands remove_node_facts(node.name) end + def do_node_enable(global, options, args) + node = get_node_from_args(args, include_disabled: true) + node.update_json({}, remove: ["enabled"]) + end + + def do_node_disable(global, options, args) + node = get_node_from_args(args, include_disabled: true) + node.update_json("enabled" => false) + end + end; end diff --git a/lib/leap_cli/commands/ping.rb b/lib/leap_cli/commands/ping.rb new file mode 100644 index 00000000..4283d9b3 --- /dev/null +++ b/lib/leap_cli/commands/ping.rb @@ -0,0 +1,58 @@ +module LeapCli; module Commands + + desc "Ping nodes to see if they are alive." + long_desc "Attempts to ping each node in the FILTER set." + arg_name "FILTER" + command :ping do |c| + c.flag 'timeout', :arg_name => "TIMEOUT", + :default_value => 2, :desc => 'Wait at most TIMEOUT seconds.' + c.flag 'count', :arg_name => "COUNT", + :default_value => 2, :desc => 'Ping COUNT times.' + c.action do |global, options, args| + do_ping(global, options, args) + end + end + + private + + def do_ping(global, options, args) + assert_bin!('ping') + + timeout = [options[:timeout].to_i, 1].max + count = [options[:count].to_i, 1].max + nodes = nil + + if args && args.any? + node = manager.disabled_node(args.first) + if node + nodes = Config::ObjectList.new + nodes.add(node.name, node) + end + end + + nodes ||= manager.filter! args + + threads = [] + nodes.each_node do |node| + threads << Thread.new do + cmd = "ping -i 0.2 -n -q -W #{timeout} -c #{count} #{node.ip_address} 2>&1" + log(2, cmd) + output = `#{cmd}` + if $?.success? + last = output.split("\n").last + times = last.split('=').last.strip + min, avg, max, mdev = times.split('/') + log("ping #{min} ms", host: node.name, color: :green) + else + log(:failed, "to ping #{node.ip_address}", host: node.name) + end + end + end + threads.map(&:join) + + log("done") + end + +end; end + + diff --git a/lib/leap_cli/commands/run.rb b/lib/leap_cli/commands/run.rb index cad9b7a0..9149d594 100644 --- a/lib/leap_cli/commands/run.rb +++ b/lib/leap_cli/commands/run.rb @@ -3,13 +3,27 @@ module LeapCli; module Commands desc 'Run a shell command remotely' long_desc "Runs the specified command COMMAND on each node in the FILTER set. " + "For example, `leap run 'uname -a' webapp`" - arg_name 'COMMAND FILTER' command :run do |c| c.switch 'stream', :default => false, :desc => 'If set, stream the output as it arrives. (default: --stream for a single node, --no-stream for multiple nodes)' c.flag 'port', :arg_name => 'SSH_PORT', :desc => 'Override default SSH port used when trying to connect to the server.' - c.action do |global, options, args| - run_shell_command(global, options, args) + + c.desc 'Run an arbitrary shell command.' + c.arg_name 'FILTER', optional: true + c.command :command do |command| + command.action do |global, options, args| + run_shell_command(global, options, args) + end + end + + c.desc 'Generate one or more new invite codes.' + c.arg_name '[COUNT] [ENVIRONMENT]' + c.command :invite do |invite| + invite.action do |global_options,options,args| + run_new_invites(global_options, options, args) + end end + + c.default_command :command end private @@ -27,6 +41,39 @@ module LeapCli; module Commands end end + CMD_NEW_INVITES="cd /srv/leap/webapp; RAILS_ENV=production bundle exec rake \"generate_invites[NUM,USES]\"" + + def run_new_invites(global, options, args) + require 'leap_cli/ssh' + count = 1 + uses = 1 + env = nil + arg1 = args.shift + arg2 = args.shift + if arg1 && arg2 + env = manager.env(arg2) + count = arg1 + elsif arg1 + env = manager.env(arg1) + else + env = manager.env(nil) + end + unless env + bail! "Environment name you specified does not match one that is available. See `leap env ls` for the available names" + end + + env_name = env.name == 'default' ? nil : env.name + webapp_nodes = env.nodes[:environment => env_name][:services => 'webapp'].first + if webapp_nodes.empty? + bail! "Could not find a webapp node for the specified environment" + end + stream_command( + webapp_nodes, + CMD_NEW_INVITES.sub('NUM', count.to_s).sub('USES', uses.to_s), + options + ) + end + def capture_command(nodes, cmd, options) SSH.remote_command(nodes, options) do |ssh, host| output = ssh.capture(cmd, :log_output => false) diff --git a/lib/leap_cli/commands/test.rb b/lib/leap_cli/commands/test.rb index 70eb00fd..e2815aae 100644 --- a/lib/leap_cli/commands/test.rb +++ b/lib/leap_cli/commands/test.rb @@ -35,7 +35,7 @@ module LeapCli; module Commands SSH::remote_command(node, options) do |ssh, host| ssh.stream(test_cmd(options), :raise_error => true, :log_wrap => true) end - rescue LeapCli::SSH::ExecuteError + rescue LeapCli::SSH::TimeoutError, SSHKit::Runner::ExecuteError, SSHKit::Command::Failed if options[:continue] exit_status(1) else diff --git a/lib/leap_cli/commands/user.rb b/lib/leap_cli/commands/user.rb index 1ca92719..7fd5f52d 100644 --- a/lib/leap_cli/commands/user.rb +++ b/lib/leap_cli/commands/user.rb @@ -113,14 +113,42 @@ module LeapCli def do_list_users(global, options, args) require 'leap_cli/ssh' + ssh_keys = {} + Dir.glob("#{ENV['HOME']}/.ssh/*.pub").each do |keyfile| + key = SSH::Key.load(keyfile) + ssh_keys[key.fingerprint] = key if key + end + + ssh_agent_keys = {} + if !`which ssh-add`.empty? + `ssh-add -L`.split("\n").each do |keystring| + key = SSH::Key.load(keystring) + ssh_agent_keys[key.fingerprint] = key if key + end + end + Dir.glob(path([:user_ssh, '*'])).each do |keyfile| username = File.basename(File.dirname(keyfile)) log username, :color => :cyan do log Path.relative_path(keyfile) key = SSH::Key.load(keyfile) - log 'SSH MD5 fingerprint: ' + key.fingerprint(:digest => :md5, :type => :ssh, :encoding => :hex) - log 'SSH SHA256 fingerprint: ' + key.fingerprint(:digest => :sha256, :type => :ssh, :encoding => :base64) - log 'DER MD5 fingerprint: ' + key.fingerprint(:digest => :md5, :type => :der, :encoding => :hex) + if key.nil? + log :warning, "could not read ssh key #{keyfile}" do + log "currently, only these ssh key types are supported: " + SSH::Key::SUPPORTED_TYPES.join(", ") + end + else + log 'SSH MD5 fingerprint: ' + key.fingerprint(:digest => :md5, :type => :ssh, :encoding => :hex) + log 'SSH SHA256 fingerprint: ' + key.fingerprint(:digest => :sha256, :type => :ssh, :encoding => :base64) + log 'DER MD5 fingerprint: ' + key.fingerprint(:digest => :md5, :type => :der, :encoding => :hex) + if ssh_keys[key.fingerprint] + log 'Matches local key: ' + ssh_keys[key.fingerprint].filename, color: :green + if ssh_agent_keys[key.fingerprint] + log 'Matches ssh-agent key: ' + ssh_agent_keys[key.fingerprint].summary(encoding: :base64), color: :green + else + log :error, 'No matching key in the ssh-agent' + end + end + end end end end @@ -154,6 +182,9 @@ module LeapCli end else key_index = 0 + log "Picking the only compatible ssh key: "+ ssh_keys[key_index].filename do + log ssh_keys[key_index].summary + end end return ssh_keys[key_index] diff --git a/lib/leap_cli/commands/vagrant.rb b/lib/leap_cli/commands/vagrant.rb index f8a75b61..78b2fede 100644 --- a/lib/leap_cli/commands/vagrant.rb +++ b/lib/leap_cli/commands/vagrant.rb @@ -132,10 +132,10 @@ module LeapCli; module Commands lines << %[ config.vm.provider "virtualbox" do |v|] lines << %[ v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]] lines << %[ v.name = "#{node.name}"] - lines << %[ v.memory = 1536] + lines << %[ v.memory = 2048] lines << %[ end] lines << %[ config.vm.provider "libvirt" do |v|] - lines << %[ v.memory = 1536] + lines << %[ v.memory = 2048] lines << %[ end] lines << %[ #{leapfile.custom_vagrant_vm_line}] if leapfile.custom_vagrant_vm_line lines << %[ end] diff --git a/lib/leap_cli/commands/vm.rb b/lib/leap_cli/commands/vm.rb index 790774f1..6f97dbce 100644 --- a/lib/leap_cli/commands/vm.rb +++ b/lib/leap_cli/commands/vm.rb @@ -415,7 +415,6 @@ module LeapCli; module Commands config = manager.env.cloud name = nil if options[:mock] - Fog.mock! name = 'mock_aws' config['mock_aws'] = { "api" => "aws", @@ -451,6 +450,10 @@ module LeapCli; module Commands assert! entry['api'] == 'aws', "cloud.json: currently, only 'aws' is supported for `api`." assert! entry['vendor'] == 'aws', "cloud.json: currently, only 'aws' is supported for `vendor`." + LeapCli::Cloud::check_dependencies!(entry) + if options[:mock] + Fog.mock! + end return LeapCli::Cloud.new(name, entry, node) end |