WinRM SSL support

This commit is contained in:
Max Lincoln 2014-12-11 13:55:49 -05:00
parent 61a792ac9f
commit 234adaae63
8 changed files with 114 additions and 53 deletions

View File

@ -106,11 +106,8 @@ module VagrantPlugins
WinRMShell.new( WinRMShell.new(
winrm_info[:host], winrm_info[:host],
@machine.config.winrm.username, winrm_info[:port],
@machine.config.winrm.password, @machine.config.winrm
port: winrm_info[:port],
timeout_in_seconds: @machine.config.winrm.timeout,
max_tries: @machine.config.winrm.max_tries,
) )
end end

View File

@ -8,25 +8,32 @@ module VagrantPlugins
attr_accessor :guest_port attr_accessor :guest_port
attr_accessor :max_tries attr_accessor :max_tries
attr_accessor :timeout attr_accessor :timeout
attr_accessor :transport
attr_accessor :ssl_peer_verification
def initialize def initialize
@username = UNSET_VALUE @username = UNSET_VALUE
@password = UNSET_VALUE @password = UNSET_VALUE
@host = UNSET_VALUE @host = UNSET_VALUE
@port = UNSET_VALUE @port = UNSET_VALUE
@guest_port = UNSET_VALUE @guest_port = UNSET_VALUE
@max_tries = UNSET_VALUE @max_tries = UNSET_VALUE
@timeout = UNSET_VALUE @timeout = UNSET_VALUE
@transport = UNSET_VALUE
@ssl_peer_verification = UNSET_VALUE
end end
def finalize! def finalize!
@username = "vagrant" if @username == UNSET_VALUE @username = "vagrant" if @username == UNSET_VALUE
@password = "vagrant" if @password == UNSET_VALUE @password = "vagrant" if @password == UNSET_VALUE
@transport = :plaintext if @transport == UNSET_VALUE
@host = nil if @host == UNSET_VALUE @host = nil if @host == UNSET_VALUE
@port = 5985 if @port == UNSET_VALUE is_ssl = @transport == :ssl
@guest_port = 5985 if @guest_port == UNSET_VALUE @port = (is_ssl ? 5986 : 5985) if @port == UNSET_VALUE
@guest_port = (is_ssl ? 5986 : 5985) if @guest_port == UNSET_VALUE
@max_tries = 20 if @max_tries == UNSET_VALUE @max_tries = 20 if @max_tries == UNSET_VALUE
@timeout = 1800 if @timeout == UNSET_VALUE @timeout = 1800 if @timeout == UNSET_VALUE
@ssl_peer_verification = true if @ssl_peer_verification == UNSET_VALUE
end end
def validate(machine) def validate(machine)
@ -38,6 +45,9 @@ module VagrantPlugins
errors << "winrm.guest_port cannot be nil." if @guest_port.nil? errors << "winrm.guest_port cannot be nil." if @guest_port.nil?
errors << "winrm.max_tries cannot be nil." if @max_tries.nil? errors << "winrm.max_tries cannot be nil." if @max_tries.nil?
errors << "winrm.timeout cannot be nil." if @timeout.nil? errors << "winrm.timeout cannot be nil." if @timeout.nil?
unless @ssl_peer_verification == true || @ssl_peer_verification == false
errors << "winrm.ssl_peer_verification must be a boolean."
end
{ "WinRM" => errors } { "WinRM" => errors }
end end

View File

@ -32,23 +32,21 @@ module VagrantPlugins
] ]
attr_reader :logger attr_reader :logger
attr_reader :username
attr_reader :password
attr_reader :host attr_reader :host
attr_reader :port attr_reader :port
attr_reader :timeout_in_seconds attr_reader :username
attr_reader :max_tries attr_reader :password
attr_reader :config
def initialize(host, username, password, options = {}) def initialize(host, port, config)
@logger = Log4r::Logger.new("vagrant::communication::winrmshell") @logger = Log4r::Logger.new("vagrant::communication::winrmshell")
@logger.debug("initializing WinRMShell") @logger.debug("initializing WinRMShell")
@host = host @host = host
@port = options[:port] || 5985 @port = port
@username = username @username = config.username
@password = password @password = config.password
@timeout_in_seconds = options[:timeout_in_seconds] || 60 @config = config
@max_tries = options[:max_tries] || 20
end end
def powershell(command, &block) def powershell(command, &block)
@ -87,7 +85,7 @@ module VagrantPlugins
end end
def execute_shell_with_retry(command, shell, &block) def execute_shell_with_retry(command, shell, &block)
retryable(tries: @max_tries, on: @@exceptions_to_retry_on, sleep: 10) do retryable(tries: @config.max_tries, on: @@exceptions_to_retry_on, sleep: 10) do
@logger.debug("#{shell} executing:\n#{command}") @logger.debug("#{shell} executing:\n#{command}")
output = session.send(shell, command) do |out, err| output = session.send(shell, command) do |out, err|
block.call(:stdout, out) if block_given? && out block.call(:stdout, out) if block_given? && out
@ -118,10 +116,11 @@ module VagrantPlugins
@logger.info("Attempting to connect to WinRM...") @logger.info("Attempting to connect to WinRM...")
@logger.info(" - Host: #{@host}") @logger.info(" - Host: #{@host}")
@logger.info(" - Port: #{@port}") @logger.info(" - Port: #{@port}")
@logger.info(" - Username: #{@username}") @logger.info(" - Username: #{@config.username}")
@logger.info(" - Transport: #{@config.transport}")
client = ::WinRM::WinRMWebService.new(endpoint, :plaintext, endpoint_options) client = ::WinRM::WinRMWebService.new(endpoint, @config.transport.to_sym, endpoint_options)
client.set_timeout(@timeout_in_seconds) client.set_timeout(@config.timeout)
client.toggle_nori_type_casting(:off) #we don't want coersion of types client.toggle_nori_type_casting(:off) #we don't want coersion of types
client client
end end
@ -131,7 +130,14 @@ module VagrantPlugins
end end
def endpoint def endpoint
"http://#{@host}:#{@port}/wsman" case @config.transport.to_sym
when :ssl
"https://#{@host}:#{@port}/wsman"
when :plaintext
"http://#{@host}:#{@port}/wsman"
else
raise Errors::WinRMInvalidTransport, transport: @config.transport
end
end end
def endpoint_options def endpoint_options
@ -139,8 +145,8 @@ module VagrantPlugins
pass: @password, pass: @password,
host: @host, host: @host,
port: @port, port: @port,
operation_timeout: @timeout_in_seconds, basic_auth_only: true,
basic_auth_only: true } no_ssl_peer_verification: !@config.ssl_peer_verification }
end end
end #WinShell class end #WinShell class
end end

View File

@ -399,10 +399,15 @@ module VagrantPlugins
host_ip: "127.0.0.1", host_ip: "127.0.0.1",
id: "winrm", id: "winrm",
auto_correct: true auto_correct: true
end
end
if !@__networks["forwarded_port-ssh"] network :forwarded_port,
guest: 5986,
host: 55986,
host_ip: "127.0.0.1",
id: "winrm-ssl",
auto_correct: true
end
elsif !@__networks["forwarded_port-ssh"]
network :forwarded_port, network :forwarded_port,
guest: 22, guest: 22,
host: 2222, host: 2222,

View File

@ -101,6 +101,6 @@ describe VagrantPlugins::CommunicatorWinRM::Communicator do
expect(shell).to receive(:download).with("from", "to") expect(shell).to receive(:download).with("from", "to")
subject.download("from", "to") subject.download("from", "to")
end end
end end
end end

View File

@ -1,14 +1,23 @@
require File.expand_path("../../../../base", __FILE__) require File.expand_path("../../../../base", __FILE__)
require Vagrant.source_root.join("plugins/communicators/winrm/shell") require Vagrant.source_root.join("plugins/communicators/winrm/shell")
require Vagrant.source_root.join("plugins/communicators/winrm/config")
describe VagrantPlugins::CommunicatorWinRM::WinRMShell do describe VagrantPlugins::CommunicatorWinRM::WinRMShell do
include_context "unit" include_context "unit"
let(:session) { double("winrm_session") } let(:session) { double("winrm_session") }
let(:port) { config.transport == :ssl ? 5986 : 5985 }
let(:config) {
VagrantPlugins::CommunicatorWinRM::Config.new.tap do |c|
c.username = 'username'
c.password = 'password'
c.finalize!
end
}
subject do subject do
described_class.new('localhost', 'username', 'password').tap do |comm| described_class.new('localhost', port, config).tap do |comm|
allow(comm).to receive(:new_session).and_return(session) allow(comm).to receive(:new_session).and_return(session)
end end
end end
@ -20,7 +29,8 @@ describe VagrantPlugins::CommunicatorWinRM::WinRMShell do
end end
it "should raise auth error when exception message contains 401" do it "should raise auth error when exception message contains 401" do
expect(session).to receive(:powershell).with(/^dir.+/).and_raise( config.winrm.max_tries = 2
expect(session).to receive(:powershell).with(/^dir.+/).twice.and_raise(
StandardError.new("Oh no! a 401 SOAP error!")) StandardError.new("Oh no! a 401 SOAP error!"))
expect { subject.powershell("dir") }.to raise_error( expect { subject.powershell("dir") }.to raise_error(
VagrantPlugins::CommunicatorWinRM::Errors::AuthError) VagrantPlugins::CommunicatorWinRM::Errors::AuthError)
@ -42,8 +52,22 @@ describe VagrantPlugins::CommunicatorWinRM::WinRMShell do
end end
describe ".endpoint" do describe ".endpoint" do
it "should create winrm endpoint address" do context 'when transport is :ssl' do
expect(subject.send(:endpoint)).to eq("http://localhost:5985/wsman") let(:config) {
VagrantPlugins::CommunicatorWinRM::Config.new.tap do |c|
c.transport = :ssl
c.finalize!
end
}
it "should create winrm endpoint address using https" do
expect(subject.send(:endpoint)).to eq("https://localhost:5986/wsman")
end
end
context "when transport is :plaintext" do
it "should create winrm endpoint address using http" do
expect(subject.send(:endpoint)).to eq("http://localhost:5985/wsman")
end
end end
end end
@ -51,7 +75,7 @@ describe VagrantPlugins::CommunicatorWinRM::WinRMShell do
it "should create endpoint options" do it "should create endpoint options" do
expect(subject.send(:endpoint_options)).to eq( expect(subject.send(:endpoint_options)).to eq(
{ user: "username", pass: "password", host: "localhost", port: 5985, { user: "username", pass: "password", host: "localhost", port: 5985,
operation_timeout: 60, basic_auth_only: true }) basic_auth_only: true, no_ssl_peer_verification: false })
end end
end end

View File

@ -23,6 +23,13 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
end end
end end
def find_network(name)
network_definitions = subject.networks.map do |n|
n[1]
end
network_definitions.find {|n| n[:id] == name}
end
before do before do
env = double("env") env = double("env")
env.stub(root_path: nil) env.stub(root_path: nil)
@ -183,6 +190,7 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
subject.finalize! subject.finalize!
n = subject.networks n = subject.networks
expect(n.length).to eq(2) expect(n.length).to eq(2)
expect(n[0][0]).to eq(:forwarded_port) expect(n[0][0]).to eq(:forwarded_port)
expect(n[0][1][:guest]).to eq(5985) expect(n[0][1][:guest]).to eq(5985)
expect(n[0][1][:host]).to eq(55985) expect(n[0][1][:host]).to eq(55985)
@ -190,9 +198,10 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
expect(n[0][1][:id]).to eq("winrm") expect(n[0][1][:id]).to eq("winrm")
expect(n[1][0]).to eq(:forwarded_port) expect(n[1][0]).to eq(:forwarded_port)
expect(n[1][1][:guest]).to eq(22) expect(n[1][1][:guest]).to eq(5986)
expect(n[1][1][:host]).to eq(2222) expect(n[1][1][:host]).to eq(55986)
expect(n[1][1][:id]).to eq("ssh") expect(n[1][1][:host_ip]).to eq("127.0.0.1")
expect(n[1][1][:id]).to eq("winrm-ssl")
end end
it "allows overriding SSH" do it "allows overriding SSH" do
@ -214,12 +223,22 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
guest: 22, host: 14100, id: "winrm" guest: 22, host: 14100, id: "winrm"
subject.finalize! subject.finalize!
n = subject.networks winrm_network = find_network 'winrm'
expect(n.length).to eq(2) expect(winrm_network[:guest]).to eq(22)
expect(n[0][0]).to eq(:forwarded_port) expect(winrm_network[:host]).to eq(14100)
expect(n[0][1][:guest]).to eq(22) expect(winrm_network[:id]).to eq("winrm")
expect(n[0][1][:host]).to eq(14100) end
expect(n[0][1][:id]).to eq("winrm")
it "allows overriding WinRM SSL" do
subject.communicator = :winrmssl
subject.network "forwarded_port",
guest: 22, host: 14100, id: "winrmssl"
subject.finalize!
winrmssl_network = find_network 'winrmssl'
expect(winrmssl_network[:guest]).to eq(22)
expect(winrmssl_network[:host]).to eq(14100)
expect(winrmssl_network[:id]).to eq("winrmssl")
end end
it "turns all forwarded port ports to ints" do it "turns all forwarded port ports to ints" do

View File

@ -28,7 +28,7 @@ Gem::Specification.new do |s|
s.add_dependency "rb-kqueue", "~> 0.2.0" s.add_dependency "rb-kqueue", "~> 0.2.0"
s.add_dependency "rest-client", ">= 1.6.0", "< 2.0" s.add_dependency "rest-client", ">= 1.6.0", "< 2.0"
s.add_dependency "wdm", "~> 0.1.0" s.add_dependency "wdm", "~> 0.1.0"
s.add_dependency "winrm", "~> 1.1.3" s.add_dependency "winrm", "= 1.3.0.dev.2"
# We lock this down to avoid compilation issues. # We lock this down to avoid compilation issues.
s.add_dependency "nokogiri", "= 1.6.3.1" s.add_dependency "nokogiri", "= 1.6.3.1"