# frozen_string_literal: true require 'bolt/node/errors' require 'bolt/transport/sudoable' require 'json' require 'shellwords' module Bolt module Transport class SSH < Sudoable def self.options %w[host port user password sudo-password private-key host-key-check connect-timeout tmpdir run-as tty run-as-command proxyjump interpreters] end def self.default_options { 'connect-timeout' => 10, 'host-key-check' => true, 'tty' => false, 'load-config' => true } end def provided_features ['shell'] end def self.validate(options) logger = Logging.logger[self] validate_sudo_options(options, logger) host_key = options['host-key-check'] unless !!host_key == host_key raise Bolt::ValidationError, 'host-key-check option must be a Boolean true or false' end if (key_opt = options['private-key']) unless key_opt.instance_of?(String) || (key_opt.instance_of?(Hash) && key_opt.include?('key-data')) raise Bolt::ValidationError, "private-key option must be the path to a private key file or a hash containing the 'key-data'" end end timeout_value = options['connect-timeout'] unless timeout_value.is_a?(Integer) || timeout_value.nil? error_msg = "connect-timeout value must be an Integer, received #{timeout_value}:#{timeout_value.class}" raise Bolt::ValidationError, error_msg end end def initialize super require 'net/ssh' require 'net/scp' begin require 'net/ssh/krb' rescue LoadError logger.debug("Authentication method 'gssapi-with-mic' (Kerberos) is not available.") end @transport_logger = Logging.logger[Net::SSH] @transport_logger.level = :warn end def with_connection(target) conn = Connection.new(target, @transport_logger) conn.connect yield conn ensure begin conn&.disconnect rescue StandardError => e logger.info("Failed to close connection to #{target.uri} : #{e.message}") end end def connected?(target) with_connection(target) { true } rescue Bolt::Node::ConnectError false end end end end require 'bolt/transport/ssh/connection'