This works by now calling the `:ssh` action on the provider. This action is allowed to do whatever it pleases, but should at some point probably call the `SSHExec` built-in middleware. The `SSHExec` built-in middleware was added. This uses the information returned by `Machine#ssh_info` and uses the `Vagrant::Util::SSH` helper to exec into the remote machine. The provider should do any work upfront in verifying that the machine is ready to be SSHed into.
120 lines
4.6 KiB
Ruby
120 lines
4.6 KiB
Ruby
require "log4r"
|
|
|
|
require "vagrant/util/file_mode"
|
|
require "vagrant/util/platform"
|
|
require "vagrant/util/safe_exec"
|
|
|
|
module Vagrant
|
|
module Util
|
|
# This is a class that has helpers on it for dealing with SSH. These
|
|
# helpers don't depend on any part of Vagrant except what is given
|
|
# via the parameters.
|
|
class SSH
|
|
LOGGER = Log4r::Logger.new("vagrant::util::ssh")
|
|
|
|
# Checks that the permissions for a private key are valid, and fixes
|
|
# them if possible. SSH requires that permissions on the private key
|
|
# are 0600 on POSIX based systems. This will make a best effort to
|
|
# fix these permissions if they are not properly set.
|
|
#
|
|
# @param [Pathname] key_path The path to the private key.
|
|
def self.check_key_permissions(key_path)
|
|
# Don't do anything if we're on Windows, since Windows doesn't worry
|
|
# about key permissions.
|
|
return if Platform.windows?
|
|
|
|
LOGGER.debug("Checking key permissions: #{key_path}")
|
|
stat = key_path.stat
|
|
|
|
if stat.owned? && FileMode.from_octal(stat.mode) != "600"
|
|
LOGGER.info("Attempting to correct key permissions to 0600")
|
|
key_path.chmod(0600)
|
|
|
|
# Re-stat the file to get the new mode, and verify it worked
|
|
stat = key_path.stat
|
|
if FileMode.from_octal(stat.mode) != "600"
|
|
raise Errors::SSHKeyBadPermissions, :key_path => key_path
|
|
end
|
|
end
|
|
rescue Errno::EPERM
|
|
# This shouldn't happen since we verify we own the file, but
|
|
# it is possible in theory, so we raise an error.
|
|
raise Errors::SSHKeyBadPermissions, :key_path => key_path
|
|
end
|
|
|
|
# Halts the running of this process and replaces it with a full-fledged
|
|
# SSH shell into a remote machine.
|
|
#
|
|
# Note: This method NEVER returns. The process ends after this.
|
|
#
|
|
# @param [Hash] ssh_info This is the SSH information. For the keys
|
|
# required please see the documentation of {Machine#ssh_info}.
|
|
# @param [Hash] opts These are additional options that are supported
|
|
# by exec.
|
|
def self.exec(ssh_info, opts={})
|
|
# If we're running Windows, raise an exception since we currently
|
|
# still don't support exec-ing into SSH. In the future this should
|
|
# certainly be possible if we can detect we're in an environment that
|
|
# supports it.
|
|
if Platform.windows?
|
|
raise Errors::SSHUnavailableWindows,
|
|
:host => ssh_info[:host],
|
|
:port => ssh_info[:port],
|
|
:username => ssh_info[:username],
|
|
:key_path => ssh_info[:private_key_path]
|
|
end
|
|
|
|
# Verify that we have SSH available on the system.
|
|
raise Errors::SSHUnavailable if !Kernel.system("which ssh > /dev/null 2>&1")
|
|
|
|
# If plain mode is enabled then we don't do any authentication (we don't
|
|
# set a user or an identity file)
|
|
plain_mode = opts[:plain_mode]
|
|
|
|
options = {}
|
|
options[:host] = ssh_info[:host]
|
|
options[:port] = ssh_info[:port]
|
|
options[:username] = ssh_info[:username]
|
|
options[:private_key_path] = ssh_info[:private_key_path]
|
|
|
|
# Command line options
|
|
command_options = [
|
|
"-p", options[:port].to_s,
|
|
"-o", "LogLevel=FATAL",
|
|
"-o", "StrictHostKeyChecking=no",
|
|
"-o", "UserKnownHostsFile=/dev/null"]
|
|
|
|
# Configurables
|
|
command_options += ["-o", "ForwardAgent=yes"] if ssh_info[:forward_agent]
|
|
command_options.concat(opts[:extra_args]) if opts[:extra_args]
|
|
|
|
# Solaris/OpenSolaris/Illumos uses SunSSH which doesn't support the
|
|
# IdentitiesOnly option. Also, we don't enable it in plain mode so
|
|
# that SSH properly searches our identities and tries to do it itself.
|
|
if !Platform.solaris? && !plain_mode
|
|
command_options += ["-o", "IdentitiesOnly=yes"]
|
|
end
|
|
|
|
# If we're not in plain mode, attach the private key path.
|
|
command_options += ["-i", options[:private_key_path].to_s] if !plain_mode
|
|
|
|
if ssh_info[:forward_x11]
|
|
# Both are required so that no warnings are shown regarding X11
|
|
command_options += [
|
|
"-o", "ForwardX11=yes",
|
|
"-o", "ForwardX11Trusted=yes"]
|
|
end
|
|
|
|
# Build up the host string for connecting
|
|
host_string = options[:host]
|
|
host_string = "#{options[:username]}@#{host_string}" if !plain_mode
|
|
command_options << host_string
|
|
|
|
# Invoke SSH with all our options
|
|
LOGGER.info("Invoking SSH: #{command_options.inspect}")
|
|
SafeExec.exec("ssh", *command_options)
|
|
end
|
|
end
|
|
end
|
|
end
|