knife-winops 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rspec +3 -0
- data/.travis.yml +30 -0
- data/CHANGELOG.md +147 -0
- data/DOC_CHANGES.md +22 -0
- data/Gemfile +13 -0
- data/LICENSE +201 -0
- data/README.md +430 -0
- data/RELEASE_NOTES.md +17 -0
- data/Rakefile +21 -0
- data/appveyor.yml +36 -0
- data/ci.gemfile +15 -0
- data/knife-winops.gemspec +26 -0
- data/lib/chef/knife/bootstrap/Chef_bootstrap.erb +44 -0
- data/lib/chef/knife/bootstrap/bootstrap.ps1 +134 -0
- data/lib/chef/knife/bootstrap/tail.cmd +15 -0
- data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +302 -0
- data/lib/chef/knife/bootstrap_windows_base.rb +473 -0
- data/lib/chef/knife/bootstrap_windows_ssh.rb +115 -0
- data/lib/chef/knife/bootstrap_windows_winrm.rb +102 -0
- data/lib/chef/knife/core/windows_bootstrap_context.rb +356 -0
- data/lib/chef/knife/knife_windows_base.rb +33 -0
- data/lib/chef/knife/windows_cert_generate.rb +155 -0
- data/lib/chef/knife/windows_cert_install.rb +68 -0
- data/lib/chef/knife/windows_helper.rb +36 -0
- data/lib/chef/knife/windows_listener_create.rb +107 -0
- data/lib/chef/knife/winrm.rb +127 -0
- data/lib/chef/knife/winrm_base.rb +128 -0
- data/lib/chef/knife/winrm_knife_base.rb +315 -0
- data/lib/chef/knife/winrm_session.rb +101 -0
- data/lib/chef/knife/winrm_shared_options.rb +54 -0
- data/lib/chef/knife/wsman_endpoint.rb +44 -0
- data/lib/chef/knife/wsman_test.rb +118 -0
- data/lib/knife-winops/path_helper.rb +242 -0
- data/lib/knife-winops/version.rb +6 -0
- data/spec/assets/fake_trusted_certs/excluded.txt +2 -0
- data/spec/assets/fake_trusted_certs/github.pem +42 -0
- data/spec/assets/fake_trusted_certs/google.crt +41 -0
- data/spec/assets/win_fake_trusted_cert_script.txt +89 -0
- data/spec/dummy_winrm_connection.rb +21 -0
- data/spec/functional/bootstrap_download_spec.rb +229 -0
- data/spec/spec_helper.rb +93 -0
- data/spec/unit/knife/bootstrap_options_spec.rb +164 -0
- data/spec/unit/knife/bootstrap_template_spec.rb +98 -0
- data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +410 -0
- data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +292 -0
- data/spec/unit/knife/windows_cert_generate_spec.rb +90 -0
- data/spec/unit/knife/windows_cert_install_spec.rb +51 -0
- data/spec/unit/knife/windows_listener_create_spec.rb +76 -0
- data/spec/unit/knife/winrm_session_spec.rb +101 -0
- data/spec/unit/knife/winrm_spec.rb +494 -0
- data/spec/unit/knife/wsman_test_spec.rb +209 -0
- metadata +157 -0
@@ -0,0 +1,128 @@
|
|
1
|
+
#
|
2
|
+
# Original knife-windows author:: Seth Chisamore (<schisamo@chef.io>)
|
3
|
+
# Copyright:: Copyright (c) 2011-2016 Chef Software, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'chef/knife'
|
20
|
+
require 'chef/encrypted_data_bag_item'
|
21
|
+
require 'kconv'
|
22
|
+
|
23
|
+
class Chef
|
24
|
+
class Knife
|
25
|
+
module WinrmBase
|
26
|
+
|
27
|
+
# It includes supported WinRM authentication protocol.
|
28
|
+
WINRM_AUTH_PROTOCOL_LIST ||= %w{basic negotiate kerberos}
|
29
|
+
|
30
|
+
# :nodoc:
|
31
|
+
# Would prefer to do this in a rational way, but can't be done b/c of
|
32
|
+
# Mixlib::CLI's design :(
|
33
|
+
def self.included(includer)
|
34
|
+
includer.class_eval do
|
35
|
+
|
36
|
+
deps do
|
37
|
+
require 'readline'
|
38
|
+
require 'chef/json_compat'
|
39
|
+
end
|
40
|
+
|
41
|
+
option :winrm_user,
|
42
|
+
:short => "-x USERNAME",
|
43
|
+
:long => "--winrm-user USERNAME",
|
44
|
+
:description => "The WinRM username",
|
45
|
+
:default => "Administrator",
|
46
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:winrm_user] = key }
|
47
|
+
|
48
|
+
option :winrm_password,
|
49
|
+
:short => "-P PASSWORD",
|
50
|
+
:long => "--winrm-password PASSWORD",
|
51
|
+
:description => "The WinRM password",
|
52
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:winrm_password] = key }
|
53
|
+
|
54
|
+
option :winrm_shell,
|
55
|
+
:long => "--winrm-shell SHELL",
|
56
|
+
:description => "The WinRM shell type. Valid choices are [cmd, powershell, elevated]. 'elevated' runs powershell in a scheduled task",
|
57
|
+
:default => :cmd,
|
58
|
+
:proc => Proc.new { |shell| shell.to_sym }
|
59
|
+
|
60
|
+
option :winrm_transport,
|
61
|
+
:short => "-w TRANSPORT",
|
62
|
+
:long => "--winrm-transport TRANSPORT",
|
63
|
+
:description => "The WinRM transport type. Valid choices are [ssl, plaintext]",
|
64
|
+
:default => 'plaintext',
|
65
|
+
:proc => Proc.new { |transport| Chef::Config[:knife][:winrm_port] = '5986' if transport == 'ssl'
|
66
|
+
Chef::Config[:knife][:winrm_transport] = transport }
|
67
|
+
|
68
|
+
option :winrm_port,
|
69
|
+
:short => "-p PORT",
|
70
|
+
:long => "--winrm-port PORT",
|
71
|
+
:description => "The WinRM port, by default this is '5985' for 'plaintext' and '5986' for 'ssl' winrm transport",
|
72
|
+
:default => '5985',
|
73
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:winrm_port] = key }
|
74
|
+
|
75
|
+
option :kerberos_keytab_file,
|
76
|
+
:short => "-T KEYTAB_FILE",
|
77
|
+
:long => "--keytab-file KEYTAB_FILE",
|
78
|
+
:description => "The Kerberos keytab file used for authentication",
|
79
|
+
:proc => Proc.new { |keytab| Chef::Config[:knife][:kerberos_keytab_file] = keytab }
|
80
|
+
|
81
|
+
option :kerberos_realm,
|
82
|
+
:short => "-R KERBEROS_REALM",
|
83
|
+
:long => "--kerberos-realm KERBEROS_REALM",
|
84
|
+
:description => "The Kerberos realm used for authentication",
|
85
|
+
:proc => Proc.new { |realm| Chef::Config[:knife][:kerberos_realm] = realm }
|
86
|
+
|
87
|
+
option :kerberos_service,
|
88
|
+
:short => "-S KERBEROS_SERVICE",
|
89
|
+
:long => "--kerberos-service KERBEROS_SERVICE",
|
90
|
+
:description => "The Kerberos service used for authentication",
|
91
|
+
:proc => Proc.new { |service| Chef::Config[:knife][:kerberos_service] = service }
|
92
|
+
|
93
|
+
option :ca_trust_file,
|
94
|
+
:short => "-f CA_TRUST_FILE",
|
95
|
+
:long => "--ca-trust-file CA_TRUST_FILE",
|
96
|
+
:description => "The Certificate Authority (CA) trust file used for SSL transport",
|
97
|
+
:proc => Proc.new { |trust| Chef::Config[:knife][:ca_trust_file] = trust }
|
98
|
+
|
99
|
+
option :winrm_ssl_verify_mode,
|
100
|
+
:long => "--winrm-ssl-verify-mode SSL_VERIFY_MODE",
|
101
|
+
:description => "The WinRM peer verification mode. Valid choices are [verify_peer, verify_none]",
|
102
|
+
:default => :verify_peer,
|
103
|
+
:proc => Proc.new { |verify_mode| verify_mode.to_sym }
|
104
|
+
|
105
|
+
option :ssl_peer_fingerprint,
|
106
|
+
:long => "--ssl-peer-fingerprint FINGERPRINT",
|
107
|
+
:description => "ssl Cert Fingerprint to bypass normal cert chain checks"
|
108
|
+
|
109
|
+
option :winrm_authentication_protocol,
|
110
|
+
:long => "--winrm-authentication-protocol AUTHENTICATION_PROTOCOL",
|
111
|
+
:description => "The authentication protocol used during WinRM communication. The supported protocols are #{WINRM_AUTH_PROTOCOL_LIST.join(',')}. Default is 'negotiate'.",
|
112
|
+
:default => "negotiate",
|
113
|
+
:proc => Proc.new { |protocol| Chef::Config[:knife][:winrm_authentication_protocol] = protocol }
|
114
|
+
|
115
|
+
option :session_timeout,
|
116
|
+
:long => "--session-timeout Minutes",
|
117
|
+
:description => "The timeout for the client for the maximum length of the WinRM session",
|
118
|
+
:default => 30
|
119
|
+
|
120
|
+
option :winrm_codepage,
|
121
|
+
:long => "--winrm-codepage Codepage",
|
122
|
+
:description => "The codepage to use for the winrm cmd shell",
|
123
|
+
:default => 65001
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,315 @@
|
|
1
|
+
#
|
2
|
+
# Original knife-windows author:: Steven Murawski (<smurawski@chef.io)
|
3
|
+
# Copyright:: Copyright (c) 2015-2016 Chef Software, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
|
20
|
+
require 'chef/knife'
|
21
|
+
require 'chef/knife/winrm_base'
|
22
|
+
require 'chef/knife/winrm_shared_options'
|
23
|
+
require 'chef/knife/knife_windows_base'
|
24
|
+
|
25
|
+
class Chef
|
26
|
+
class Knife
|
27
|
+
module WinrmCommandSharedFunctions
|
28
|
+
|
29
|
+
FAILED_BASIC_HINT ||= "Hint: Please check winrm configuration 'winrm get winrm/config/service' AllowUnencrypted flag on remote server."
|
30
|
+
FAILED_NOT_BASIC_HINT ||= <<-eos.gsub /^\s+/, ""
|
31
|
+
Hint: Make sure to prefix domain usernames with the correct domain name.
|
32
|
+
Hint: Local user names should be prefixed with computer name or IP address.
|
33
|
+
EXAMPLE: my_domain\\user_namer
|
34
|
+
eos
|
35
|
+
|
36
|
+
def self.included(includer)
|
37
|
+
includer.class_eval do
|
38
|
+
|
39
|
+
@@ssl_warning_given = false
|
40
|
+
|
41
|
+
include Chef::Knife::WinrmBase
|
42
|
+
include Chef::Knife::WinrmSharedOptions
|
43
|
+
include Chef::Knife::KnifeWindowsBase
|
44
|
+
|
45
|
+
def validate_winrm_options!
|
46
|
+
winrm_auth_protocol = locate_config_value(:winrm_authentication_protocol)
|
47
|
+
|
48
|
+
if ! Chef::Knife::WinrmBase::WINRM_AUTH_PROTOCOL_LIST.include?(winrm_auth_protocol)
|
49
|
+
ui.error "Invalid value '#{winrm_auth_protocol}' for --winrm-authentication-protocol option."
|
50
|
+
ui.info "Valid values are #{Chef::Knife::WinrmBase::WINRM_AUTH_PROTOCOL_LIST.join(",")}."
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
|
54
|
+
warn_no_ssl_peer_verification if resolve_no_ssl_peer_verification
|
55
|
+
end
|
56
|
+
|
57
|
+
#Overrides Chef::Knife#configure_session, as that code is tied to the SSH implementation
|
58
|
+
#Tracked by Issue # 3042 / https://github.com/chef/chef/issues/3042
|
59
|
+
def configure_session
|
60
|
+
validate_winrm_options!
|
61
|
+
resolve_session_options
|
62
|
+
resolve_target_nodes
|
63
|
+
session_from_list
|
64
|
+
end
|
65
|
+
|
66
|
+
def resolve_target_nodes
|
67
|
+
@list = case config[:manual]
|
68
|
+
when true
|
69
|
+
@name_args[0].split(" ")
|
70
|
+
when false
|
71
|
+
r = Array.new
|
72
|
+
q = Chef::Search::Query.new
|
73
|
+
@action_nodes = q.search(:node, @name_args[0])[0]
|
74
|
+
@action_nodes.each do |item|
|
75
|
+
i = extract_nested_value(item, config[:attribute])
|
76
|
+
r.push(i) unless i.nil?
|
77
|
+
end
|
78
|
+
r
|
79
|
+
end
|
80
|
+
|
81
|
+
if @list.length == 0
|
82
|
+
if @action_nodes.length == 0
|
83
|
+
ui.fatal("No nodes returned from search!")
|
84
|
+
else
|
85
|
+
ui.fatal("#{@action_nodes.length} #{@action_nodes.length > 1 ? "nodes":"node"} found, " +
|
86
|
+
"but does not have the required attribute (#{config[:attribute]}) to establish the connection. " +
|
87
|
+
"Try setting another attribute to open the connection using --attribute.")
|
88
|
+
end
|
89
|
+
exit 10
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# TODO: Copied from Knife::Core:GenericPresenter. Should be extracted
|
94
|
+
def extract_nested_value(data, nested_value_spec)
|
95
|
+
nested_value_spec.split(".").each do |attr|
|
96
|
+
if data.nil?
|
97
|
+
nil # don't get no method error on nil
|
98
|
+
elsif data.respond_to?(attr.to_sym)
|
99
|
+
data = data.send(attr.to_sym)
|
100
|
+
elsif data.respond_to?(:[])
|
101
|
+
data = data[attr]
|
102
|
+
else
|
103
|
+
data = begin
|
104
|
+
data.send(attr.to_sym)
|
105
|
+
rescue NoMethodError
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
( !data.kind_of?(Array) && data.respond_to?(:to_hash) ) ? data.to_hash : data
|
111
|
+
end
|
112
|
+
|
113
|
+
def run_command(command = '')
|
114
|
+
relay_winrm_command(command)
|
115
|
+
check_for_errors!
|
116
|
+
@exit_code
|
117
|
+
end
|
118
|
+
|
119
|
+
def relay_winrm_command(command)
|
120
|
+
Chef::Log.debug(command)
|
121
|
+
@session_results = []
|
122
|
+
|
123
|
+
queue = Queue.new
|
124
|
+
@winrm_sessions.each { |s| queue << s }
|
125
|
+
# These nils will kill the Threads once no more sessions are left
|
126
|
+
locate_config_value(:concurrency).times { queue << nil }
|
127
|
+
|
128
|
+
threads = []
|
129
|
+
locate_config_value(:concurrency).times do
|
130
|
+
threads << Thread.new do
|
131
|
+
while session = queue.pop
|
132
|
+
run_command_in_thread(session, command)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
threads.map(&:join)
|
137
|
+
@session_results
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def run_command_in_thread(s, command)
|
143
|
+
@session_results << s.relay_command(command)
|
144
|
+
rescue WinRM::WinRMHTTPTransportError, WinRM::WinRMAuthorizationError => e
|
145
|
+
if authorization_error?(e)
|
146
|
+
if ! config[:suppress_auth_failure]
|
147
|
+
# Display errors if the caller hasn't opted to retry
|
148
|
+
ui.error "Failed to authenticate to #{s.host} as #{locate_config_value(:winrm_user)}"
|
149
|
+
ui.info "Response: #{e.message}"
|
150
|
+
ui.info get_failed_authentication_hint
|
151
|
+
raise e
|
152
|
+
end
|
153
|
+
else
|
154
|
+
raise e
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def get_failed_authentication_hint
|
159
|
+
if @session_opts[:basic_auth_only]
|
160
|
+
FAILED_BASIC_HINT
|
161
|
+
else
|
162
|
+
FAILED_NOT_BASIC_HINT
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def authorization_error?(exception)
|
167
|
+
exception.is_a?(WinRM::WinRMAuthorizationError) ||
|
168
|
+
exception.message =~ /401/
|
169
|
+
end
|
170
|
+
|
171
|
+
def check_for_errors!
|
172
|
+
@exit_code ||= 0
|
173
|
+
@winrm_sessions.each do |session|
|
174
|
+
session_exit_code = session.exit_code
|
175
|
+
unless success_return_codes.include? session_exit_code.to_i
|
176
|
+
@exit_code = [@exit_code, session_exit_code.to_i].max
|
177
|
+
ui.error "Failed to execute command on #{session.host} return code #{session_exit_code}"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def success_return_codes
|
183
|
+
#Redundant if the CLI options parsing occurs
|
184
|
+
return [0] unless config[:returns]
|
185
|
+
return @success_return_codes ||= config[:returns].split(',').collect {|item| item.to_i}
|
186
|
+
end
|
187
|
+
|
188
|
+
def session_from_list
|
189
|
+
@list.each do |item|
|
190
|
+
Chef::Log.debug("Adding #{item}")
|
191
|
+
@session_opts[:host] = item
|
192
|
+
create_winrm_session(@session_opts)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def create_winrm_session(options={})
|
197
|
+
session = Chef::Knife::WinrmSession.new(options)
|
198
|
+
@winrm_sessions ||= []
|
199
|
+
@winrm_sessions.push(session)
|
200
|
+
end
|
201
|
+
|
202
|
+
def resolve_session_options
|
203
|
+
@session_opts = {
|
204
|
+
user: resolve_winrm_user,
|
205
|
+
password: locate_config_value(:winrm_password),
|
206
|
+
port: locate_config_value(:winrm_port),
|
207
|
+
operation_timeout: resolve_winrm_session_timeout,
|
208
|
+
basic_auth_only: resolve_winrm_basic_auth,
|
209
|
+
disable_sspi: resolve_winrm_disable_sspi,
|
210
|
+
transport: resolve_winrm_transport,
|
211
|
+
no_ssl_peer_verification: resolve_no_ssl_peer_verification,
|
212
|
+
ssl_peer_fingerprint: resolve_ssl_peer_fingerprint,
|
213
|
+
shell: locate_config_value(:winrm_shell),
|
214
|
+
codepage: locate_config_value(:winrm_codepage)
|
215
|
+
}
|
216
|
+
|
217
|
+
if @session_opts[:user] and (not @session_opts[:password])
|
218
|
+
@session_opts[:password] = Chef::Config[:knife][:winrm_password] = config[:winrm_password] = get_password
|
219
|
+
end
|
220
|
+
|
221
|
+
if @session_opts[:transport] == :kerberos
|
222
|
+
@session_opts.merge!(resolve_winrm_kerberos_options)
|
223
|
+
end
|
224
|
+
|
225
|
+
@session_opts[:ca_trust_path] = locate_config_value(:ca_trust_file) if locate_config_value(:ca_trust_file)
|
226
|
+
end
|
227
|
+
|
228
|
+
def resolve_winrm_user
|
229
|
+
user = locate_config_value(:winrm_user)
|
230
|
+
|
231
|
+
# Prefixing with '.\' when using negotiate
|
232
|
+
# to auth user against local machine domain
|
233
|
+
if resolve_winrm_basic_auth ||
|
234
|
+
resolve_winrm_transport == :kerberos ||
|
235
|
+
user.include?("\\") ||
|
236
|
+
user.include?("@")
|
237
|
+
user
|
238
|
+
else
|
239
|
+
".\\#{user}"
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def resolve_winrm_session_timeout
|
244
|
+
#30 min (Default) OperationTimeout for long bootstraps fix for KNIFE_WINDOWS-8
|
245
|
+
locate_config_value(:session_timeout).to_i * 60 if locate_config_value(:session_timeout)
|
246
|
+
end
|
247
|
+
|
248
|
+
def resolve_winrm_basic_auth
|
249
|
+
locate_config_value(:winrm_authentication_protocol) == "basic"
|
250
|
+
end
|
251
|
+
|
252
|
+
def resolve_winrm_kerberos_options
|
253
|
+
kerberos_opts = {}
|
254
|
+
kerberos_opts[:keytab] = locate_config_value(:kerberos_keytab_file) if locate_config_value(:kerberos_keytab_file)
|
255
|
+
kerberos_opts[:realm] = locate_config_value(:kerberos_realm) if locate_config_value(:kerberos_realm)
|
256
|
+
kerberos_opts[:service] = locate_config_value(:kerberos_service) if locate_config_value(:kerberos_service)
|
257
|
+
kerberos_opts
|
258
|
+
end
|
259
|
+
|
260
|
+
def resolve_winrm_transport
|
261
|
+
transport = locate_config_value(:winrm_transport).to_sym
|
262
|
+
if config.any? {|k,v| k.to_s =~ /kerberos/ && !v.nil? }
|
263
|
+
transport = :kerberos
|
264
|
+
elsif transport != :ssl && negotiate_auth?
|
265
|
+
transport = :negotiate
|
266
|
+
end
|
267
|
+
|
268
|
+
transport
|
269
|
+
end
|
270
|
+
|
271
|
+
def resolve_no_ssl_peer_verification
|
272
|
+
locate_config_value(:ca_trust_file).nil? && config[:winrm_ssl_verify_mode] == :verify_none && resolve_winrm_transport == :ssl
|
273
|
+
end
|
274
|
+
|
275
|
+
def resolve_ssl_peer_fingerprint
|
276
|
+
locate_config_value(:ssl_peer_fingerprint)
|
277
|
+
end
|
278
|
+
|
279
|
+
def resolve_winrm_disable_sspi
|
280
|
+
resolve_winrm_transport != :negotiate
|
281
|
+
end
|
282
|
+
|
283
|
+
def get_password
|
284
|
+
@password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
|
285
|
+
end
|
286
|
+
|
287
|
+
def negotiate_auth?
|
288
|
+
locate_config_value(:winrm_authentication_protocol) == "negotiate"
|
289
|
+
end
|
290
|
+
|
291
|
+
def warn_no_ssl_peer_verification
|
292
|
+
if ! @@ssl_warning_given
|
293
|
+
@@ssl_warning_given = true
|
294
|
+
ui.warn(<<-WARN)
|
295
|
+
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
296
|
+
SSL validation of HTTPS requests for the WinRM transport is disabled. HTTPS WinRM
|
297
|
+
connections are still encrypted, but knife is not able to detect forged replies
|
298
|
+
or spoofing attacks.
|
299
|
+
|
300
|
+
To fix this issue add an entry like this to your knife configuration file:
|
301
|
+
|
302
|
+
```
|
303
|
+
# Verify all WinRM HTTPS connections (default, recommended)
|
304
|
+
knife[:winrm_ssl_verify_mode] = :verify_peer
|
305
|
+
```
|
306
|
+
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
307
|
+
WARN
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
#
|
2
|
+
# Original knife-windows author:: Steven Murawski <smurawski@chef.io>
|
3
|
+
# Copyright:: Copyright (c) 2015-2016 Chef Software, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'chef/application'
|
20
|
+
require 'winrm'
|
21
|
+
require 'winrm-elevated'
|
22
|
+
|
23
|
+
class Chef
|
24
|
+
class Knife
|
25
|
+
class WinrmSession
|
26
|
+
attr_reader :host, :endpoint, :port, :output, :error, :exit_code
|
27
|
+
|
28
|
+
def initialize(options)
|
29
|
+
configure_proxy
|
30
|
+
|
31
|
+
@host = options[:host]
|
32
|
+
@port = options[:port]
|
33
|
+
@user = options[:user]
|
34
|
+
@shell_args = [ options[:shell] ]
|
35
|
+
@shell_args << { codepage: options[:codepage] } if options[:shell] == :cmd
|
36
|
+
url = "#{options[:host]}:#{options[:port]}/wsman"
|
37
|
+
scheme = options[:transport] == :ssl ? 'https' : 'http'
|
38
|
+
@endpoint = "#{scheme}://#{url}"
|
39
|
+
|
40
|
+
opts = Hash.new
|
41
|
+
opts = {
|
42
|
+
user: @user,
|
43
|
+
password: options[:password],
|
44
|
+
basic_auth_only: options[:basic_auth_only],
|
45
|
+
disable_sspi: options[:disable_sspi],
|
46
|
+
no_ssl_peer_verification: options[:no_ssl_peer_verification],
|
47
|
+
ssl_peer_fingerprint: options[:ssl_peer_fingerprint],
|
48
|
+
endpoint: endpoint,
|
49
|
+
transport: options[:transport]
|
50
|
+
}
|
51
|
+
options[:transport] == :kerberos ? opts.merge!({:service => options[:service], :realm => options[:realm]}) : opts.merge!({:ca_trust_path => options[:ca_trust_path]})
|
52
|
+
opts[:operation_timeout] = options[:operation_timeout] if options[:operation_timeout]
|
53
|
+
Chef::Log.debug("WinRM::WinRMWebService options: #{opts}")
|
54
|
+
Chef::Log.debug("Endpoint: #{endpoint}")
|
55
|
+
Chef::Log.debug("Transport: #{options[:transport]}")
|
56
|
+
|
57
|
+
@winrm_session = WinRM::Connection.new(opts)
|
58
|
+
@winrm_session.logger = Chef::Log
|
59
|
+
|
60
|
+
transport = @winrm_session.send(:transport)
|
61
|
+
http_client = transport.instance_variable_get(:@httpcli)
|
62
|
+
Chef::HTTP::DefaultSSLPolicy.new(http_client.ssl_config).set_custom_certs
|
63
|
+
end
|
64
|
+
|
65
|
+
def relay_command(command)
|
66
|
+
session_result = WinRM::Output.new
|
67
|
+
@winrm_session.shell(*@shell_args) do |shell|
|
68
|
+
shell.username = @user.split("\\").last if shell.respond_to?(:username)
|
69
|
+
session_result = shell.run(command) do |stdout, stderr|
|
70
|
+
print_data(@host, stdout) if stdout
|
71
|
+
print_data(@host, stderr, :red) if stderr
|
72
|
+
end
|
73
|
+
end
|
74
|
+
@exit_code = session_result.exitcode
|
75
|
+
session_result
|
76
|
+
rescue WinRM::WinRMHTTPTransportError, WinRM::WinRMAuthorizationError => e
|
77
|
+
@exit_code = 401
|
78
|
+
raise e
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def print_data(host, data, color = :cyan)
|
84
|
+
if data =~ /\n/
|
85
|
+
data.split(/\n/).each { |d| print_data(host, d, color) }
|
86
|
+
elsif !data.nil?
|
87
|
+
print Chef::Knife::Winrm.ui.color(host, color)
|
88
|
+
puts " #{data}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def configure_proxy
|
93
|
+
if Chef::Config.respond_to?(:export_proxies)
|
94
|
+
Chef::Config.export_proxies
|
95
|
+
else
|
96
|
+
Chef::Application.new.configure_proxy_environment_variables
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|