knife-windows 1.5.0 → 1.6.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +26 -26
  3. data/CHANGELOG.md +131 -121
  4. data/DOC_CHANGES.md +22 -14
  5. data/Gemfile +14 -13
  6. data/README.md +400 -392
  7. data/RELEASE_NOTES.md +2 -26
  8. data/appveyor.yml +39 -39
  9. data/ci.gemfile +16 -16
  10. data/knife-windows.gemspec +25 -25
  11. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +246 -233
  12. data/lib/chef/knife/bootstrap_windows_base.rb +443 -454
  13. data/lib/chef/knife/bootstrap_windows_ssh.rb +116 -115
  14. data/lib/chef/knife/bootstrap_windows_winrm.rb +102 -95
  15. data/lib/chef/knife/core/windows_bootstrap_context.rb +378 -378
  16. data/lib/chef/knife/knife_windows_base.rb +33 -33
  17. data/lib/chef/knife/windows_cert_generate.rb +155 -155
  18. data/lib/chef/knife/windows_cert_install.rb +68 -68
  19. data/lib/chef/knife/windows_helper.rb +36 -36
  20. data/lib/chef/knife/windows_listener_create.rb +107 -107
  21. data/lib/chef/knife/winrm.rb +122 -122
  22. data/lib/chef/knife/winrm_base.rb +123 -117
  23. data/lib/chef/knife/winrm_knife_base.rb +306 -305
  24. data/lib/chef/knife/winrm_session.rb +97 -91
  25. data/lib/chef/knife/winrm_shared_options.rb +47 -47
  26. data/lib/chef/knife/wsman_endpoint.rb +44 -44
  27. data/lib/chef/knife/wsman_test.rb +118 -118
  28. data/lib/knife-windows/path_helper.rb +234 -234
  29. data/lib/knife-windows/version.rb +6 -6
  30. data/spec/assets/win_template_rendered_with_bootstrap_install_command.txt +223 -223
  31. data/spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt +223 -223
  32. data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +335 -335
  33. data/spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt +335 -335
  34. data/spec/dummy_winrm_connection.rb +21 -0
  35. data/spec/functional/bootstrap_download_spec.rb +236 -241
  36. data/spec/spec_helper.rb +94 -94
  37. data/spec/unit/knife/bootstrap_options_spec.rb +157 -155
  38. data/spec/unit/knife/bootstrap_template_spec.rb +98 -98
  39. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +423 -426
  40. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +177 -177
  41. data/spec/unit/knife/windows_cert_generate_spec.rb +90 -90
  42. data/spec/unit/knife/windows_cert_install_spec.rb +51 -51
  43. data/spec/unit/knife/windows_listener_create_spec.rb +76 -76
  44. data/spec/unit/knife/winrm_session_spec.rb +71 -76
  45. data/spec/unit/knife/winrm_spec.rb +500 -508
  46. data/spec/unit/knife/wsman_test_spec.rb +209 -209
  47. metadata +16 -17
  48. data/spec/dummy_winrm_service.rb +0 -24
@@ -1,117 +1,123 @@
1
- #
2
- # 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_transport,
55
- :short => "-t TRANSPORT",
56
- :long => "--winrm-transport TRANSPORT",
57
- :description => "The WinRM transport type. Valid choices are [ssl, plaintext]",
58
- :default => 'plaintext',
59
- :proc => Proc.new { |transport| Chef::Config[:knife][:winrm_port] = '5986' if transport == 'ssl'
60
- Chef::Config[:knife][:winrm_transport] = transport }
61
-
62
- option :winrm_port,
63
- :short => "-p PORT",
64
- :long => "--winrm-port PORT",
65
- :description => "The WinRM port, by default this is '5985' for 'plaintext' and '5986' for 'ssl' winrm transport",
66
- :default => '5985',
67
- :proc => Proc.new { |key| Chef::Config[:knife][:winrm_port] = key }
68
-
69
- option :kerberos_keytab_file,
70
- :short => "-T KEYTAB_FILE",
71
- :long => "--keytab-file KEYTAB_FILE",
72
- :description => "The Kerberos keytab file used for authentication",
73
- :proc => Proc.new { |keytab| Chef::Config[:knife][:kerberos_keytab_file] = keytab }
74
-
75
- option :kerberos_realm,
76
- :short => "-R KERBEROS_REALM",
77
- :long => "--kerberos-realm KERBEROS_REALM",
78
- :description => "The Kerberos realm used for authentication",
79
- :proc => Proc.new { |realm| Chef::Config[:knife][:kerberos_realm] = realm }
80
-
81
- option :kerberos_service,
82
- :short => "-S KERBEROS_SERVICE",
83
- :long => "--kerberos-service KERBEROS_SERVICE",
84
- :description => "The Kerberos service used for authentication",
85
- :proc => Proc.new { |service| Chef::Config[:knife][:kerberos_service] = service }
86
-
87
- option :ca_trust_file,
88
- :short => "-f CA_TRUST_FILE",
89
- :long => "--ca-trust-file CA_TRUST_FILE",
90
- :description => "The Certificate Authority (CA) trust file used for SSL transport",
91
- :proc => Proc.new { |trust| Chef::Config[:knife][:ca_trust_file] = trust }
92
-
93
- option :winrm_ssl_verify_mode,
94
- :long => "--winrm-ssl-verify-mode SSL_VERIFY_MODE",
95
- :description => "The WinRM peer verification mode. Valid choices are [verify_peer, verify_none]",
96
- :default => :verify_peer,
97
- :proc => Proc.new { |verify_mode| verify_mode.to_sym }
98
-
99
- option :ssl_peer_fingerprint,
100
- :long => "--ssl-peer-fingerprint FINGERPRINT",
101
- :description => "ssl Cert Fingerprint to bypass normal cert chain checks"
102
-
103
- option :winrm_authentication_protocol,
104
- :long => "--winrm-authentication-protocol AUTHENTICATION_PROTOCOL",
105
- :description => "The authentication protocol used during WinRM communication. The supported protocols are #{WINRM_AUTH_PROTOCOL_LIST.join(',')}. Default is 'negotiate'.",
106
- :default => "negotiate",
107
- :proc => Proc.new { |protocol| Chef::Config[:knife][:winrm_authentication_protocol] = protocol }
108
-
109
- option :session_timeout,
110
- :long => "--session-timeout Minutes",
111
- :description => "The timeout for the client for the maximum length of the WinRM session",
112
- :default => 30
113
- end
114
- end
115
- end
116
- end
117
- end
1
+ #
2
+ # 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 => "-t 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
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -1,305 +1,306 @@
1
- #
2
- # 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
-
116
- check_for_errors!
117
-
118
- # Knife seems to ignore the return value of this method,
119
- # so we exit to force the process exit code for this
120
- # subcommand if returns is set
121
- exit @exit_code if @exit_code && @exit_code != 0
122
- 0
123
- end
124
-
125
- def relay_winrm_command(command)
126
- Chef::Log.debug(command)
127
- session_results = []
128
- @winrm_sessions.each do |s|
129
- begin
130
- session_results << s.relay_command(command)
131
- rescue WinRM::WinRMHTTPTransportError, WinRM::WinRMAuthorizationError => e
132
- if authorization_error?(e)
133
- if ! config[:suppress_auth_failure]
134
- # Display errors if the caller hasn't opted to retry
135
- ui.error "Failed to authenticate to #{s.host} as #{locate_config_value(:winrm_user)}"
136
- ui.info "Response: #{e.message}"
137
- ui.info get_failed_authentication_hint
138
- raise e
139
- end
140
- @exit_code = 401
141
- else
142
- raise e
143
- end
144
- end
145
- end
146
- session_results
147
- end
148
-
149
- private
150
-
151
- def get_failed_authentication_hint
152
- if @session_opts[:basic_auth_only]
153
- FAILED_BASIC_HINT
154
- else
155
- FAILED_NOT_BASIC_HINT
156
- end
157
- end
158
-
159
- def authorization_error?(exception)
160
- exception.is_a?(WinRM::WinRMAuthorizationError) ||
161
- exception.message =~ /401/
162
- end
163
-
164
- def check_for_errors!
165
- @winrm_sessions.each do |session|
166
- session_exit_code = session.exit_code
167
- unless success_return_codes.include? session_exit_code.to_i
168
- @exit_code = session_exit_code.to_i
169
- ui.error "Failed to execute command on #{session.host} return code #{session_exit_code}"
170
- end
171
- end
172
- end
173
-
174
- def success_return_codes
175
- #Redundant if the CLI options parsing occurs
176
- return [0] unless config[:returns]
177
- return @success_return_codes ||= config[:returns].split(',').collect {|item| item.to_i}
178
- end
179
-
180
- def session_from_list
181
- @list.each do |item|
182
- Chef::Log.debug("Adding #{item}")
183
- @session_opts[:host] = item
184
- create_winrm_session(@session_opts)
185
- end
186
- end
187
-
188
- def create_winrm_session(options={})
189
- session = Chef::Knife::WinrmSession.new(options)
190
- @winrm_sessions ||= []
191
- @winrm_sessions.push(session)
192
- end
193
-
194
- def resolve_session_options
195
- @session_opts = {
196
- user: resolve_winrm_user,
197
- password: locate_config_value(:winrm_password),
198
- port: locate_config_value(:winrm_port),
199
- operation_timeout: resolve_winrm_session_timeout,
200
- basic_auth_only: resolve_winrm_basic_auth,
201
- disable_sspi: resolve_winrm_disable_sspi,
202
- transport: resolve_winrm_transport,
203
- no_ssl_peer_verification: resolve_no_ssl_peer_verification,
204
- ssl_peer_fingerprint: resolve_ssl_peer_fingerprint
205
- }
206
-
207
- if @session_opts[:user] and (not @session_opts[:password])
208
- @session_opts[:password] = Chef::Config[:knife][:winrm_password] = config[:winrm_password] = get_password
209
- end
210
-
211
- if @session_opts[:transport] == :kerberos
212
- @session_opts.merge!(resolve_winrm_kerberos_options)
213
- end
214
-
215
- @session_opts[:ca_trust_path] = locate_config_value(:ca_trust_file) if locate_config_value(:ca_trust_file)
216
- end
217
-
218
- def resolve_winrm_user
219
- user = locate_config_value(:winrm_user)
220
-
221
- # Prefixing with '.\' when using negotiate
222
- # to auth user against local machine domain
223
- if resolve_winrm_basic_auth ||
224
- resolve_winrm_transport == :kerberos ||
225
- user.include?("\\") ||
226
- user.include?("@")
227
- user
228
- else
229
- ".\\#{user}"
230
- end
231
- end
232
-
233
- def resolve_winrm_session_timeout
234
- #30 min (Default) OperationTimeout for long bootstraps fix for KNIFE_WINDOWS-8
235
- locate_config_value(:session_timeout).to_i * 60 if locate_config_value(:session_timeout)
236
- end
237
-
238
- def resolve_winrm_basic_auth
239
- locate_config_value(:winrm_authentication_protocol) == "basic"
240
- end
241
-
242
- def resolve_winrm_kerberos_options
243
- kerberos_opts = {}
244
- kerberos_opts[:keytab] = locate_config_value(:kerberos_keytab_file) if locate_config_value(:kerberos_keytab_file)
245
- kerberos_opts[:realm] = locate_config_value(:kerberos_realm) if locate_config_value(:kerberos_realm)
246
- kerberos_opts[:service] = locate_config_value(:kerberos_service) if locate_config_value(:kerberos_service)
247
- kerberos_opts
248
- end
249
-
250
- def resolve_winrm_transport
251
- transport = locate_config_value(:winrm_transport).to_sym
252
- if config.any? {|k,v| k.to_s =~ /kerberos/ && !v.nil? }
253
- transport = :kerberos
254
- elsif transport != :ssl && negotiate_auth?
255
- transport = :negotiate
256
- end
257
-
258
- transport
259
- end
260
-
261
- def resolve_no_ssl_peer_verification
262
- locate_config_value(:ca_trust_file).nil? && config[:winrm_ssl_verify_mode] == :verify_none && resolve_winrm_transport == :ssl
263
- end
264
-
265
- def resolve_ssl_peer_fingerprint
266
- locate_config_value(:ssl_peer_fingerprint)
267
- end
268
-
269
- def resolve_winrm_disable_sspi
270
- resolve_winrm_transport != :negotiate
271
- end
272
-
273
- def get_password
274
- @password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
275
- end
276
-
277
- def negotiate_auth?
278
- locate_config_value(:winrm_authentication_protocol) == "negotiate"
279
- end
280
-
281
- def warn_no_ssl_peer_verification
282
- if ! @@ssl_warning_given
283
- @@ssl_warning_given = true
284
- ui.warn(<<-WARN)
285
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
286
- SSL validation of HTTPS requests for the WinRM transport is disabled. HTTPS WinRM
287
- connections are still encrypted, but knife is not able to detect forged replies
288
- or spoofing attacks.
289
-
290
- To fix this issue add an entry like this to your knife configuration file:
291
-
292
- ```
293
- # Verify all WinRM HTTPS connections (default, recommended)
294
- knife[:winrm_ssl_verify_mode] = :verify_peer
295
- ```
296
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
297
- WARN
298
- end
299
- end
300
-
301
- end
302
- end
303
- end
304
- end
305
- end
1
+ #
2
+ # 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
+
116
+ check_for_errors!
117
+
118
+ # Knife seems to ignore the return value of this method,
119
+ # so we exit to force the process exit code for this
120
+ # subcommand if returns is set
121
+ exit @exit_code if @exit_code && @exit_code != 0
122
+ 0
123
+ end
124
+
125
+ def relay_winrm_command(command)
126
+ Chef::Log.debug(command)
127
+ session_results = []
128
+ @winrm_sessions.each do |s|
129
+ begin
130
+ session_results << s.relay_command(command)
131
+ rescue WinRM::WinRMHTTPTransportError, WinRM::WinRMAuthorizationError => e
132
+ if authorization_error?(e)
133
+ if ! config[:suppress_auth_failure]
134
+ # Display errors if the caller hasn't opted to retry
135
+ ui.error "Failed to authenticate to #{s.host} as #{locate_config_value(:winrm_user)}"
136
+ ui.info "Response: #{e.message}"
137
+ ui.info get_failed_authentication_hint
138
+ raise e
139
+ end
140
+ @exit_code = 401
141
+ else
142
+ raise e
143
+ end
144
+ end
145
+ end
146
+ session_results
147
+ end
148
+
149
+ private
150
+
151
+ def get_failed_authentication_hint
152
+ if @session_opts[:basic_auth_only]
153
+ FAILED_BASIC_HINT
154
+ else
155
+ FAILED_NOT_BASIC_HINT
156
+ end
157
+ end
158
+
159
+ def authorization_error?(exception)
160
+ exception.is_a?(WinRM::WinRMAuthorizationError) ||
161
+ exception.message =~ /401/
162
+ end
163
+
164
+ def check_for_errors!
165
+ @winrm_sessions.each do |session|
166
+ session_exit_code = session.exit_code
167
+ unless success_return_codes.include? session_exit_code.to_i
168
+ @exit_code = session_exit_code.to_i
169
+ ui.error "Failed to execute command on #{session.host} return code #{session_exit_code}"
170
+ end
171
+ end
172
+ end
173
+
174
+ def success_return_codes
175
+ #Redundant if the CLI options parsing occurs
176
+ return [0] unless config[:returns]
177
+ return @success_return_codes ||= config[:returns].split(',').collect {|item| item.to_i}
178
+ end
179
+
180
+ def session_from_list
181
+ @list.each do |item|
182
+ Chef::Log.debug("Adding #{item}")
183
+ @session_opts[:host] = item
184
+ create_winrm_session(@session_opts)
185
+ end
186
+ end
187
+
188
+ def create_winrm_session(options={})
189
+ session = Chef::Knife::WinrmSession.new(options)
190
+ @winrm_sessions ||= []
191
+ @winrm_sessions.push(session)
192
+ end
193
+
194
+ def resolve_session_options
195
+ @session_opts = {
196
+ user: resolve_winrm_user,
197
+ password: locate_config_value(:winrm_password),
198
+ port: locate_config_value(:winrm_port),
199
+ operation_timeout: resolve_winrm_session_timeout,
200
+ basic_auth_only: resolve_winrm_basic_auth,
201
+ disable_sspi: resolve_winrm_disable_sspi,
202
+ transport: resolve_winrm_transport,
203
+ no_ssl_peer_verification: resolve_no_ssl_peer_verification,
204
+ ssl_peer_fingerprint: resolve_ssl_peer_fingerprint,
205
+ shell: locate_config_value(:winrm_shell)
206
+ }
207
+
208
+ if @session_opts[:user] and (not @session_opts[:password])
209
+ @session_opts[:password] = Chef::Config[:knife][:winrm_password] = config[:winrm_password] = get_password
210
+ end
211
+
212
+ if @session_opts[:transport] == :kerberos
213
+ @session_opts.merge!(resolve_winrm_kerberos_options)
214
+ end
215
+
216
+ @session_opts[:ca_trust_path] = locate_config_value(:ca_trust_file) if locate_config_value(:ca_trust_file)
217
+ end
218
+
219
+ def resolve_winrm_user
220
+ user = locate_config_value(:winrm_user)
221
+
222
+ # Prefixing with '.\' when using negotiate
223
+ # to auth user against local machine domain
224
+ if resolve_winrm_basic_auth ||
225
+ resolve_winrm_transport == :kerberos ||
226
+ user.include?("\\") ||
227
+ user.include?("@")
228
+ user
229
+ else
230
+ ".\\#{user}"
231
+ end
232
+ end
233
+
234
+ def resolve_winrm_session_timeout
235
+ #30 min (Default) OperationTimeout for long bootstraps fix for KNIFE_WINDOWS-8
236
+ locate_config_value(:session_timeout).to_i * 60 if locate_config_value(:session_timeout)
237
+ end
238
+
239
+ def resolve_winrm_basic_auth
240
+ locate_config_value(:winrm_authentication_protocol) == "basic"
241
+ end
242
+
243
+ def resolve_winrm_kerberos_options
244
+ kerberos_opts = {}
245
+ kerberos_opts[:keytab] = locate_config_value(:kerberos_keytab_file) if locate_config_value(:kerberos_keytab_file)
246
+ kerberos_opts[:realm] = locate_config_value(:kerberos_realm) if locate_config_value(:kerberos_realm)
247
+ kerberos_opts[:service] = locate_config_value(:kerberos_service) if locate_config_value(:kerberos_service)
248
+ kerberos_opts
249
+ end
250
+
251
+ def resolve_winrm_transport
252
+ transport = locate_config_value(:winrm_transport).to_sym
253
+ if config.any? {|k,v| k.to_s =~ /kerberos/ && !v.nil? }
254
+ transport = :kerberos
255
+ elsif transport != :ssl && negotiate_auth?
256
+ transport = :negotiate
257
+ end
258
+
259
+ transport
260
+ end
261
+
262
+ def resolve_no_ssl_peer_verification
263
+ locate_config_value(:ca_trust_file).nil? && config[:winrm_ssl_verify_mode] == :verify_none && resolve_winrm_transport == :ssl
264
+ end
265
+
266
+ def resolve_ssl_peer_fingerprint
267
+ locate_config_value(:ssl_peer_fingerprint)
268
+ end
269
+
270
+ def resolve_winrm_disable_sspi
271
+ resolve_winrm_transport != :negotiate
272
+ end
273
+
274
+ def get_password
275
+ @password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
276
+ end
277
+
278
+ def negotiate_auth?
279
+ locate_config_value(:winrm_authentication_protocol) == "negotiate"
280
+ end
281
+
282
+ def warn_no_ssl_peer_verification
283
+ if ! @@ssl_warning_given
284
+ @@ssl_warning_given = true
285
+ ui.warn(<<-WARN)
286
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
287
+ SSL validation of HTTPS requests for the WinRM transport is disabled. HTTPS WinRM
288
+ connections are still encrypted, but knife is not able to detect forged replies
289
+ or spoofing attacks.
290
+
291
+ To fix this issue add an entry like this to your knife configuration file:
292
+
293
+ ```
294
+ # Verify all WinRM HTTPS connections (default, recommended)
295
+ knife[:winrm_ssl_verify_mode] = :verify_peer
296
+ ```
297
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
298
+ WARN
299
+ end
300
+ end
301
+
302
+ end
303
+ end
304
+ end
305
+ end
306
+ end