Merge pull request #8485 from chrisroberts/communicators/win-ssh

Add winssh communicator
This commit is contained in:
Chris Roberts 2017-04-17 08:21:33 -07:00 committed by GitHub
commit 232a44f46d
8 changed files with 967 additions and 7 deletions

View File

@ -143,7 +143,7 @@ module VagrantPlugins
# If we're already attempting to switch out the SSH key, then
# just return that we're ready (for Machine#guest).
@lock.synchronize do
return true if @inserted_key || !@machine.config.ssh.insert_key
return true if @inserted_key || !machine_config_ssh.insert_key
@inserted_key = true
end
@ -458,9 +458,9 @@ module VagrantPlugins
# Determine the shell to execute. Prefer the explicitly passed in shell
# over the default configured shell. If we are using `sudo` then we
# need to wrap the shell in a `sudo` call.
cmd = @machine.config.ssh.shell
cmd = machine_config_ssh.shell
cmd = shell if shell
cmd = @machine.config.ssh.sudo_command.gsub("%c", cmd) if sudo
cmd = machine_config_ssh.sudo_command.gsub("%c", cmd) if sudo
cmd
end
@ -482,7 +482,7 @@ module VagrantPlugins
# Open the channel so we can execute or command
channel = connection.open_channel do |ch|
if @machine.config.ssh.pty
if machine_config_ssh.pty
ch.request_pty do |ch2, success|
pty = success && command != ""
@ -611,7 +611,7 @@ module VagrantPlugins
begin
keep_alive = nil
if @machine.config.ssh.keep_alive
if machine_config_ssh.keep_alive
# Begin sending keep-alive packets while we wait for the script
# to complete. This avoids connections closing on long-running
# scripts.
@ -687,9 +687,13 @@ module VagrantPlugins
end
def generate_environment_export(env_key, env_value)
template = @machine.config.ssh.export_command_template
template = machine_config_ssh.export_command_template
template.sub("%ENV_KEY%", env_key).sub("%ENV_VALUE%", env_value) + "\n"
end
def machine_config_ssh
@machine.config.ssh
end
end
end
end

View File

@ -0,0 +1,160 @@
require File.expand_path("../../ssh/communicator", __FILE__)
module VagrantPlugins
module CommunicatorWinSSH
# This class provides communication with a Windows VM running
# the Windows native port of OpenSSH
class Communicator < VagrantPlugins::CommunicatorSSH::Communicator
def initialize(machine)
super
@logger = Log4r::Logger.new("vagrant::communication::winssh")
end
# Executes the command on an SSH connection within a login shell.
def shell_execute(connection, command, **opts)
opts = {
sudo: false,
shell: nil
}.merge(opts)
sudo = opts[:sudo]
@logger.info("Execute: #{command} (sudo=#{sudo.inspect})")
exit_status = nil
# Open the channel so we can execute or command
channel = connection.open_channel do |ch|
marker_found = false
data_buffer = ''
stderr_marker_found = false
stderr_data_buffer = ''
tfile = Tempfile.new('vagrant-ssh')
remote_ext = machine_config_ssh.shell.to_s == "powershell" ? "ps1" : "bat"
remote_name = "C:\\Windows\\Temp\\#{File.basename(tfile.path)}.#{remote_ext}"
if machine_config_ssh.shell.to_s == "powershell"
base_cmd = "powershell -File #{remote_name}"
tfile.puts <<-SCRIPT.force_encoding('ASCII-8BIT')
Remove-Item #{remote_name}
Write-Host #{CMD_GARBAGE_MARKER}
[Console]::Error.WriteLine("#{CMD_GARBAGE_MARKER}")
#{command}
SCRIPT
else
base_cmd = remote_name
tfile.puts <<-SCRIPT.force_encoding('ASCII-8BIT')
ECHO OFF
ECHO #{CMD_GARBAGE_MARKER}
ECHO #{CMD_GARBAGE_MARKER} 1>&2
#{command}
SCRIPT
end
tfile.close
upload(tfile.path, remote_name)
tfile.delete
base_cmd = shell_cmd(opts.merge(shell: base_cmd))
@logger.debug("Base SSH exec command: #{base_cmd}")
ch.exec(base_cmd) do |ch2, _|
# Setup the channel callbacks so we can get data and exit status
ch2.on_data do |ch3, data|
# Filter out the clear screen command
data = remove_ansi_escape_codes(data)
if !marker_found
data_buffer << data
marker_index = data_buffer.index(CMD_GARBAGE_MARKER)
if marker_index
marker_found = true
data_buffer.slice!(0, marker_index + CMD_GARBAGE_MARKER.size)
data.replace(data_buffer)
data_buffer = nil
end
end
if block_given? && marker_found
yield :stdout, data
end
end
ch2.on_extended_data do |ch3, type, data|
# Filter out the clear screen command
data = remove_ansi_escape_codes(data)
@logger.debug("stderr: #{data}")
if !stderr_marker_found
stderr_data_buffer << data
marker_index = stderr_data_buffer.index(CMD_GARBAGE_MARKER)
if marker_index
marker_found = true
stderr_data_buffer.slice!(0, marker_index + CMD_GARBAGE_MARKER.size)
data.replace(stderr_data_buffer.lstrip)
data_buffer = nil
end
end
if block_given? && marker_found
yield :stderr, data
end
end
ch2.on_request("exit-status") do |ch3, data|
exit_status = data.read_long
@logger.debug("Exit status: #{exit_status}")
# Close the channel, since after the exit status we're
# probably done. This fixes up issues with hanging.
ch.close
end
end
end
begin
keep_alive = nil
if @machine.config.ssh.keep_alive
# Begin sending keep-alive packets while we wait for the script
# to complete. This avoids connections closing on long-running
# scripts.
keep_alive = Thread.new do
loop do
sleep 5
@logger.debug("Sending SSH keep-alive...")
connection.send_global_request("keep-alive@openssh.com")
end
end
end
# Wait for the channel to complete
begin
channel.wait
rescue Errno::ECONNRESET, IOError
@logger.info(
"SSH connection unexpected closed. Assuming reboot or something.")
exit_status = 0
pty = false
rescue Net::SSH::ChannelOpenFailed
raise Vagrant::Errors::SSHChannelOpenFail
rescue Net::SSH::Disconnect
raise Vagrant::Errors::SSHDisconnected
end
ensure
# Kill the keep-alive thread
keep_alive.kill if keep_alive
end
# Return the final exit status
return exit_status
end
def machine_config_ssh
@machine.config.winssh
end
end
end
end

View File

@ -0,0 +1,30 @@
require File.expand_path("../../../kernel_v2/config/ssh", __FILE__)
module VagrantPlugins
module CommunicatorWinSSH
class Config < VagrantPlugins::Kernel_V2::SSHConfig
def finalize!
@shell = "cmd" if @shell == UNSET_VALUE
@sudo_command = "%c" if @sudo_command == UNSET_VALUE
if @export_command_template == UNSET_VALUE
if @shell == "cmd"
@export_command_template = 'set %ENV_KEY%="%ENV_VALUE%"'
else
@export_command_template = '$env:%ENV_KEY%="%ENV_VALUE%"'
end
end
super
end
def to_s
"WINSSH"
end
# Remove configuration options from regular SSH that are
# not used within this communicator
undef :forward_x11
undef :pty
end
end
end

View File

@ -0,0 +1,21 @@
require "vagrant"
module VagrantPlugins
module CommunicatorWinSSH
class Plugin < Vagrant.plugin("2")
name "windows ssh communicator"
description <<-DESC
DESC
communicator("winssh") do
require File.expand_path("../communicator", __FILE__)
Communicator
end
config("winssh") do
require_relative "config"
Config
end
end
end
end

View File

@ -18,8 +18,11 @@ module VagrantPlugins
args = " #{args.join(" ")}"
end
if @machine.config.vm.communicator == :winrm
case @machine.config.vm.communicator
when :winrm
provision_winrm(args)
when :winssh
provision_winssh(args)
else
provision_ssh(args)
end
@ -94,6 +97,59 @@ module VagrantPlugins
end
end
# This is the provision method called if Windows OpenSSH is what is running
# on the remote end, which assumes a non-POSIX-style host.
def provision_winssh(args)
with_script_file do |path|
# Upload the script to the machine
@machine.communicate.tap do |comm|
env = config.env.map{|k,v| comm.generate_environment_export(k, v)}.join
remote_ext = @machine.config.winssh.shell == "powershell" ? "ps1" : "bat"
upload_path = "C:\\Windows\\Temp\\#{File.basename(path)}.#{remote_ext}"
if remote_ext == "ps1"
# Copy powershell_args from configuration
shell_args = config.powershell_args
# For PowerShell scripts bypass the execution policy unless already specified
shell_args += " -ExecutionPolicy Bypass" if config.powershell_args !~ /[-\/]ExecutionPolicy/i
# CLIXML output is kinda useless, especially on non-windows hosts
shell_args += " -OutputFormat Text" if config.powershell_args !~ /[-\/]OutputFormat/i
command = "#{env}\npowershell #{shell_args} #{upload_path}#{args}"
else
command = "#{env}\n#{upload_path}#{args}"
end
# Reset upload path permissions for the current ssh user
info = nil
retryable(on: Vagrant::Errors::SSHNotReady, tries: 3, sleep: 2) do
info = @machine.ssh_info
raise Vagrant::Errors::SSHNotReady if info.nil?
end
comm.upload(path.to_s, upload_path)
if config.name
@machine.ui.detail(I18n.t("vagrant.provisioners.shell.running",
script: "script: #{config.name}"))
elsif config.path
@machine.ui.detail(I18n.t("vagrant.provisioners.shell.running",
script: path.to_s))
else
@machine.ui.detail(I18n.t("vagrant.provisioners.shell.running",
script: "inline script"))
end
# Execute it with sudo
comm.execute(
command,
sudo: config.privileged,
error_key: :ssh_bad_exit_status_muted
) do |type, data|
handle_comm(type, data)
end
end
end
end
# This provisions using WinRM, which assumes a PowerShell
# console on the other side.
def provision_winrm(args)

View File

@ -0,0 +1,525 @@
require File.expand_path("../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/communicators/winssh/communicator")
describe VagrantPlugins::CommunicatorWinSSH::Communicator do
include_context "unit"
let(:export_command_template){ 'export %ENV_KEY%="%ENV_VALUE%"' }
let(:ssh) do
double("ssh",
timeout: 1,
host: nil,
port: 5986,
guest_port: 5986,
keep_alive: false
)
end
# SSH configuration information mock
let(:winssh) do
double("winssh",
insert_key: false,
export_command_template: export_command_template,
shell: 'cmd'
)
end
# Configuration mock
let(:config) { double("config", winssh: winssh, ssh: ssh) }
# Provider mock
let(:provider) { double("provider") }
# UI mock
let(:ui) { double("ui") }
# Machine mock built with previously defined
let(:machine) do
double("machine",
config: config,
provider: provider,
ui: ui
)
end
# Subject instance to test
let(:communicator){ @communicator ||= described_class.new(machine) }
# Underlying net-ssh connection mock
let(:connection) { double("connection") }
# Base net-ssh connection channel mock
let(:channel) { double("channel") }
# net-ssh connection channel mock for running commands
let(:command_channel) { double("command_channel") }
# Default exit data for commands run
let(:exit_data) { double("exit_data", read_long: 0) }
# Marker used for flagging start of output
let(:command_garbage_marker) { communicator.class.const_get(:CMD_GARBAGE_MARKER) }
# Start marker output when PTY is enabled
let(:pty_delim_start) { communicator.class.const_get(:PTY_DELIM_START) }
# End marker output when PTY is enabled
let(:pty_delim_end) { communicator.class.const_get(:PTY_DELIM_END) }
# Command output returned on stdout
let(:command_stdout_data) { '' }
# Command output returned on stderr
let(:command_stderr_data) { '' }
# Mock for net-ssh scp
let(:scp) { double("scp") }
# Stub file to match commands
let(:ssh_cmd_file){ double("ssh_cmd_file", path: "/dev/null/path") }
# Setup for commands using the net-ssh connection. This can be reused where needed
# by providing to `before`
connection_setup = proc do
allow(connection).to receive(:logger)
allow(connection).to receive(:closed?).and_return false
allow(connection).to receive(:open_channel).
and_yield(channel).and_return(channel)
allow(channel).to receive(:wait).and_return true
allow(channel).to receive(:close)
allow(command_channel).to receive(:send_data)
allow(command_channel).to receive(:eof!)
allow(command_channel).to receive(:on_data).
and_yield(nil, command_stdout_data)
allow(command_channel).to receive(:on_extended_data).
and_yield(nil, nil, command_stderr_data)
allow(machine).to receive(:ssh_info).and_return(host: '10.1.2.3', port: 22)
allow(channel).to receive(:[]=).with(any_args).and_return(true)
allow(channel).to receive(:on_close)
allow(channel).to receive(:on_data)
allow(channel).to receive(:on_extended_data)
allow(channel).to receive(:on_request)
allow(channel).to receive(:on_process)
allow(channel).to receive(:exec).with(anything).
and_yield(command_channel, '').and_return channel
expect(command_channel).to receive(:on_request).with('exit-status').
and_yield(nil, exit_data)
# Return mocked net-ssh connection during setup
allow(communicator).to receive(:retryable).and_return(connection)
allow(Tempfile).to receive(:new).with(/vagrant-ssh/).and_return(ssh_cmd_file)
allow(ssh_cmd_file).to receive(:puts)
allow(ssh_cmd_file).to receive(:close)
allow(ssh_cmd_file).to receive(:delete)
allow(scp).to receive(:upload!)
allow(communicator).to receive(:scp_connect).and_return(true)
end
describe ".wait_for_ready" do
before(&connection_setup)
context "with no static config (default scenario)" do
before do
allow(ui).to receive(:detail)
end
context "when ssh_info requires a multiple tries before it is ready" do
before do
expect(machine).to receive(:ssh_info).
and_return(nil).ordered
expect(machine).to receive(:ssh_info).
and_return(host: '10.1.2.3', port: 22).ordered
end
it "retries ssh_info until ready" do
# retries are every 0.5 so buffer the timeout just a hair over
expect(communicator.wait_for_ready(0.6)).to eq(true)
end
end
end
end
describe ".ready?" do
before(&connection_setup)
it "returns true if shell test is successful" do
expect(communicator.ready?).to be_true
end
context "with an invalid shell test" do
before do
expect(exit_data).to receive(:read_long).and_return 1
end
it "returns raises SSHInvalidShell error" do
expect{ communicator.ready? }.to raise_error Vagrant::Errors::SSHInvalidShell
end
end
end
describe ".execute" do
before(&connection_setup)
it "runs valid command and returns successful status code" do
expect(ssh_cmd_file).to receive(:puts).with(/dir/)
expect(communicator.execute("dir")).to eq(0)
end
it "prepends UUID output to command for garbage removal" do
expect(ssh_cmd_file).to receive(:puts).
with(/ECHO OFF\nECHO #{command_garbage_marker}\nECHO #{command_garbage_marker}.*/)
expect(communicator.execute("dir")).to eq(0)
end
context "with command returning an error" do
let(:exit_data) { double("exit_data", read_long: 1) }
it "raises error when exit-code is non-zero" do
expect(ssh_cmd_file).to receive(:puts).with(/dir/)
expect{ communicator.execute("dir") }.to raise_error(Vagrant::Errors::VagrantError)
end
it "returns exit-code when exit-code is non-zero and error check is disabled" do
expect(ssh_cmd_file).to receive(:puts).with(/dir/)
expect(communicator.execute("dir", error_check: false)).to eq(1)
end
end
context "with garbage content prepended to command output" do
let(:command_stdout_data) do
"Line of garbage\nMore garbage\n#{command_garbage_marker}Dir1\nDir2\n"
end
it "removes any garbage output prepended to command output" do
stdout = ''
expect(
communicator.execute("dir") do |type, data|
if type == :stdout
stdout << data
end
end
).to eq(0)
expect(stdout).to eq("Dir1\nDir2\n")
end
end
context "with garbage content prepended to command stderr output" do
let(:command_stderr_data) do
"Line of garbage\nMore garbage\n#{command_garbage_marker}Dir1\nDir2\n"
end
it "removes any garbage output prepended to command stderr output" do
stderr = ''
expect(
communicator.execute("dir") do |type, data|
if type == :stderr
stderr << data
end
end
).to eq(0)
expect(stderr).to eq("Dir1\nDir2\n")
end
end
end
describe ".test" do
before(&connection_setup)
context "with exit code as zero" do
it "returns true" do
expect(communicator.test("dir")).to be_true
end
end
context "with exit code as non-zero" do
before do
expect(exit_data).to receive(:read_long).and_return 1
end
it "returns false" do
expect(communicator.test("false.exe")).to be_false
end
end
end
describe ".upload" do
before do
expect(communicator).to receive(:scp_connect).and_yield(scp)
end
it "uploads a directory if local path is a directory" do
Dir.mktmpdir('vagrant-test') do |dir|
expect(scp).to receive(:upload!).with(dir, 'C:\destination', recursive: true)
communicator.upload(dir, 'C:\destination')
end
end
it "uploads a file if local path is a file" do
file = Tempfile.new('vagrant-test')
begin
expect(scp).to receive(:upload!).with(instance_of(File), 'C:\destination\file')
communicator.upload(file.path, 'C:\destination\file')
ensure
file.delete
end
end
it "raises custom error on permission errors" do
file = Tempfile.new('vagrant-test')
begin
expect(scp).to receive(:upload!).with(instance_of(File), 'C:\destination\file').
and_raise("Permission denied")
expect{ communicator.upload(file.path, 'C:\destination\file') }.to(
raise_error(Vagrant::Errors::SCPPermissionDenied)
)
ensure
file.delete
end
end
it "does not raise custom error on non-permission errors" do
file = Tempfile.new('vagrant-test')
begin
expect(scp).to receive(:upload!).with(instance_of(File), 'C:\destination\file').
and_raise("Some other error")
expect{ communicator.upload(file.path, 'C:\destination\file') }.to raise_error(RuntimeError)
ensure
file.delete
end
end
end
describe ".download" do
before do
expect(communicator).to receive(:scp_connect).and_yield(scp)
end
it "calls scp to download file" do
expect(scp).to receive(:download!).with('/path/from', 'C:\path\to')
communicator.download('/path/from', 'C:\path\to')
end
end
describe ".connect" do
it "cannot be called directly" do
expect{ communicator.connect }.to raise_error(NoMethodError)
end
context "with default configuration" do
before do
expect(machine).to receive(:ssh_info).and_return(
host: nil,
port: nil,
private_key_path: nil,
username: nil,
password: nil,
keys_only: true,
paranoid: false
)
end
it "has keys_only enabled" do
expect(Net::SSH).to receive(:start).with(
nil, nil, hash_including(
keys_only: true
)
).and_return(true)
communicator.send(:connect)
end
it "has paranoid disabled" do
expect(Net::SSH).to receive(:start).with(
nil, nil, hash_including(
paranoid: false
)
).and_return(true)
communicator.send(:connect)
end
it "does not include any private key paths" do
expect(Net::SSH).to receive(:start).with(
nil, nil, hash_excluding(
keys: anything
)
).and_return(true)
communicator.send(:connect)
end
it "includes `none` and `hostbased` auth methods" do
expect(Net::SSH).to receive(:start).with(
nil, nil, hash_including(
auth_methods: ["none", "hostbased"]
)
).and_return(true)
communicator.send(:connect)
end
end
context "with keys_only disabled and paranoid enabled" do
before do
expect(machine).to receive(:ssh_info).and_return(
host: nil,
port: nil,
private_key_path: nil,
username: nil,
password: nil,
keys_only: false,
paranoid: true
)
end
it "has keys_only enabled" do
expect(Net::SSH).to receive(:start).with(
nil, nil, hash_including(
keys_only: false
)
).and_return(true)
communicator.send(:connect)
end
it "has paranoid disabled" do
expect(Net::SSH).to receive(:start).with(
nil, nil, hash_including(
paranoid: true
)
).and_return(true)
communicator.send(:connect)
end
end
context "with host and port configured" do
before do
expect(machine).to receive(:ssh_info).and_return(
host: '127.0.0.1',
port: 2222,
private_key_path: nil,
username: nil,
password: nil,
keys_only: true,
paranoid: false
)
end
it "specifies configured host" do
expect(Net::SSH).to receive(:start).with("127.0.0.1", anything, anything)
communicator.send(:connect)
end
it "has port defined" do
expect(Net::SSH).to receive(:start).with("127.0.0.1", anything, hash_including(port: 2222))
communicator.send(:connect)
end
end
context "with private_key_path configured" do
before do
expect(machine).to receive(:ssh_info).and_return(
host: '127.0.0.1',
port: 2222,
private_key_path: ['/priv/key/path'],
username: nil,
password: nil,
keys_only: true,
paranoid: false
)
end
it "includes private key paths" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
keys: ["/priv/key/path"]
)
).and_return(true)
communicator.send(:connect)
end
it "includes `publickey` auth method" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
auth_methods: ["none", "hostbased", "publickey"]
)
).and_return(true)
communicator.send(:connect)
end
end
context "with username and password configured" do
before do
expect(machine).to receive(:ssh_info).and_return(
host: '127.0.0.1',
port: 2222,
private_key_path: nil,
username: 'vagrant',
password: 'vagrant',
keys_only: true,
paranoid: false
)
end
it "has username defined" do
expect(Net::SSH).to receive(:start).with(anything, 'vagrant', anything).and_return(true)
communicator.send(:connect)
end
it "has password defined" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
password: 'vagrant'
)
).and_return(true)
communicator.send(:connect)
end
it "includes `password` auth method" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
auth_methods: ["none", "hostbased", "password"]
)
).and_return(true)
communicator.send(:connect)
end
end
context "with password and private_key_path configured" do
before do
expect(machine).to receive(:ssh_info).and_return(
host: '127.0.0.1',
port: 2222,
private_key_path: ['/priv/key/path'],
username: 'vagrant',
password: 'vagrant',
keys_only: true,
paranoid: false
)
end
it "has password defined" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
password: 'vagrant'
)
).and_return(true)
communicator.send(:connect)
end
it "includes private key paths" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
keys: ["/priv/key/path"]
)
).and_return(true)
communicator.send(:connect)
end
it "includes `publickey` and `password` auth methods" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
auth_methods: ["none", "hostbased", "publickey", "password"]
)
).and_return(true)
communicator.send(:connect)
end
end
end
describe ".generate_environment_export" do
it "should generate bourne shell compatible export" do
communicator.send(:generate_environment_export, "TEST", "value").should eq("export TEST=\"value\"\n")
end
context "with custom template defined" do
let(:export_command_template){ "setenv %ENV_KEY% %ENV_VALUE%" }
it "should generate custom export based on template" do
communicator.send(:generate_environment_export, "TEST", "value").should eq("setenv TEST value\n")
end
end
end
end

View File

@ -0,0 +1,163 @@
---
layout: "docs"
page_title: "config.winssh - Vagrantfile"
sidebar_current: "vagrantfile-winssh"
description: |-
The settings within "config.winssh" relate to configuring how Vagrant
will access your machine over Windows OpenSSH. As with most Vagrant settings, the
defaults are typically fine, but you can fine tune whatever you would like.
---
# WinSSH
The WinSSH communicator is built specifically for the Windows native
port of OpenSSH. It does not rely on a POSIX-like environment which
removes the requirement of extra software installation (like cygwin)
for proper functionality.
_NOTE: The Windows native port of OpenSSH is still considered
"pre-release" and is non-production ready._
For more information, see the [Win32-OpenSSH project page](https://github.com/PowerShell/Win32-OpenSSH/).
# WinSSH Settings
The WinSSH communicator uses the same connection configuration options
as the SSH communicator. These settings provide the information for the
communicator to establish a connection to the VM.
**Config namespace: `config.ssh`**
The settings within `config.ssh` relate to configuring how Vagrant
will access your machine over SSH. As with most Vagrant settings, the
defaults are typically fine, but you can fine tune whatever you would like.
## Available Settings
`config.ssh.username` - This sets the username that Vagrant will SSH
as by default. Providers are free to override this if they detect a more
appropriate user. By default this is "vagrant," since that is what most
public boxes are made as.
<hr>
`config.ssh.password` - This sets a password that Vagrant will use to
authenticate the SSH user. Note that Vagrant recommends you use key-based
authentication rather than a password (see `private_key_path`) below. If
you use a password, Vagrant will automatically insert a keypair if
`insert_key` is true.
<hr>
`config.ssh.host` - The hostname or IP to SSH into. By default this is
empty, because the provider usually figures this out for you.
<hr>
`config.ssh.port` - The port to SSH into. By default this is port 22.
<hr>
`config.ssh.guest_port` - The port on the guest that SSH is running on. This
is used by some providers to detect forwarded ports for SSH. For example, if
this is set to 22 (the default), and Vagrant detects a forwarded port to
port 22 on the guest from port 4567 on the host, Vagrant will attempt
to use port 4567 to talk to the guest if there is no other option.
<hr>
`config.ssh.private_key_path` - The path to the private key to use to
SSH into the guest machine. By default this is the insecure private key
that ships with Vagrant, since that is what public boxes use. If you make
your own custom box with a custom SSH key, this should point to that
private key.
You can also specify multiple private keys by setting this to be an array.
This is useful, for example, if you use the default private key to bootstrap
the machine, but replace it with perhaps a more secure key later.
<hr>
`config.ssh.insert_key` - If `true`, Vagrant will automatically insert
a keypair to use for SSH, replacing Vagrant's default insecure key
inside the machine if detected. By default, this is true.
This only has an effect if you do not already use private keys for
authentication or if you are relying on the default insecure key.
If you do not have to care about security in your project and want to
keep using the default insecure key, set this to `false`.
<hr>
`config.ssh.keys_only` - Only use Vagrant-provided SSH private keys (do not use
any keys stored in ssh-agent). The default value is `true`.`
<hr>
`config.ssh.paranoid` - Perform strict host-key verification. The default value
is `false`.
# WinSSH Settings
The configuration options below are specific to the WinSSH communicator.
**Config namespace: `config.winssh`**
## Available Settings
`config.winssh.forward_agent` - If `true`, agent forwarding over SSH
connections is enabled. Defaults to false.
<hr>
`config.winssh.forward_env` - An array of host environment variables to forward to
the guest. If you are familiar with OpenSSH, this corresponds to the `SendEnv`
parameter.
```ruby
config.winssh.forward_env = ["CUSTOM_VAR"]
```
<hr>
`config.winssh.proxy_command` - A command-line command to execute that receives
the data to send to SSH on stdin. This can be used to proxy the SSH connection.
`%h` in the command is replaced with the host and `%p` is replaced with
the port.
<hr>
`config.winssh.keep_alive` If `true`, this setting SSH will send keep-alive packets
every 5 seconds by default to keep connections alive.
<hr>
`config.winssh.shell` - The shell to use when executing SSH commands from
Vagrant. By default this is `cmd`. Valid values are `"cmd"` or `"powershell"`.
Note that this has no effect on the shell you get when you run `vagrant ssh`.
This configuration option only affects the shell to use when executing commands
internally in Vagrant.
<hr>
`config.winssh.export_command_template` - The template used to generate
exported environment variables in the active session. This can be useful
when using a Bourne incompatible shell like C shell. The template supports
two variables which are replaced with the desired environment variable key and
environment variable value: `%ENV_KEY%` and `%ENV_VALUE%`. The default template
for a `cmd` configured shell is:
```ruby
config.winssh.export_command_template = 'set %ENV_KEY%="%ENV_VALUE%"'
```
The default template for a `powershell` configured shell is:
```ruby
config.winssh.export_command_template = '$env:%ENV_KEY%="%ENV_VALUE%"'
```
`config.winssh.sudo_command` - The command to use when executing a command
with `sudo`. This defaults to `%c` (assumes vagrant user is an administator
and needs no escalation). The `%c` will be replaced by the command that is
being executed.

View File

@ -69,6 +69,7 @@
<li<%= sidebar_current("vagrantfile-machine") %>><a href="/docs/vagrantfile/machine_settings.html"><tt>config.vm</tt></a></li>
<li<%= sidebar_current("vagrantfile-ssh") %>><a href="/docs/vagrantfile/ssh_settings.html"><tt>config.ssh</tt></a></li>
<li<%= sidebar_current("vagrantfile-winrm") %>><a href="/docs/vagrantfile/winrm_settings.html"><tt>config.winrm</tt></a></li>
<li<%= sidebar_current("vagrantfile-winssh") %>><a href="/docs/vagrantfile/winssh_settings.html"><tt>config.winssh</tt></a></li>
<li<%= sidebar_current("vagrantfile-vagrant") %>><a href="/docs/vagrantfile/vagrant_settings.html"><tt>config.vagrant</tt></a></li>
</ul>
</li>