knife-windows 1.2.0 → 1.2.1

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -5
  3. data/.travis.yml +23 -23
  4. data/CHANGELOG.md +104 -101
  5. data/DOC_CHANGES.md +14 -14
  6. data/Gemfile +12 -12
  7. data/LICENSE +201 -201
  8. data/README.md +376 -376
  9. data/RELEASE_NOTES.md +34 -34
  10. data/Rakefile +21 -21
  11. data/appveyor.yml +42 -42
  12. data/ci.gemfile +15 -15
  13. data/features/knife_help.feature +20 -20
  14. data/features/support/env.rb +5 -5
  15. data/knife-windows.gemspec +25 -26
  16. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +247 -247
  17. data/lib/chef/knife/bootstrap_windows_base.rb +415 -415
  18. data/lib/chef/knife/bootstrap_windows_ssh.rb +115 -115
  19. data/lib/chef/knife/bootstrap_windows_winrm.rb +95 -95
  20. data/lib/chef/knife/core/windows_bootstrap_context.rb +366 -362
  21. data/lib/chef/knife/knife_windows_base.rb +33 -33
  22. data/lib/chef/knife/windows_cert_generate.rb +155 -155
  23. data/lib/chef/knife/windows_cert_install.rb +68 -68
  24. data/lib/chef/knife/windows_helper.rb +36 -36
  25. data/lib/chef/knife/windows_listener_create.rb +107 -107
  26. data/lib/chef/knife/winrm.rb +122 -122
  27. data/lib/chef/knife/winrm_base.rb +113 -113
  28. data/lib/chef/knife/winrm_knife_base.rb +298 -298
  29. data/lib/chef/knife/winrm_session.rb +86 -86
  30. data/lib/chef/knife/winrm_shared_options.rb +47 -47
  31. data/lib/chef/knife/wsman_endpoint.rb +44 -44
  32. data/lib/chef/knife/wsman_test.rb +117 -95
  33. data/lib/knife-windows/path_helper.rb +234 -234
  34. data/lib/knife-windows/version.rb +6 -6
  35. data/spec/assets/win_template_rendered_with_bootstrap_install_command.txt +217 -217
  36. data/spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt +217 -217
  37. data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +329 -329
  38. data/spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt +329 -329
  39. data/spec/assets/win_template_unrendered.txt +246 -246
  40. data/spec/functional/bootstrap_download_spec.rb +234 -234
  41. data/spec/spec_helper.rb +93 -93
  42. data/spec/unit/knife/bootstrap_options_spec.rb +154 -150
  43. data/spec/unit/knife/bootstrap_template_spec.rb +92 -92
  44. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +295 -295
  45. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +177 -151
  46. data/spec/unit/knife/windows_cert_generate_spec.rb +90 -90
  47. data/spec/unit/knife/windows_cert_install_spec.rb +51 -51
  48. data/spec/unit/knife/windows_listener_create_spec.rb +76 -76
  49. data/spec/unit/knife/winrm_session_spec.rb +64 -64
  50. data/spec/unit/knife/winrm_spec.rb +516 -516
  51. data/spec/unit/knife/wsman_test_spec.rb +201 -178
  52. metadata +2 -16
@@ -1,113 +1,113 @@
1
- #
2
- # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
- # Copyright:: Copyright (c) 2011 Opscode, 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 :winrm_authentication_protocol,
100
- :long => "--winrm-authentication-protocol AUTHENTICATION_PROTOCOL",
101
- :description => "The authentication protocol used during WinRM communication. The supported protocols are #{WINRM_AUTH_PROTOCOL_LIST.join(',')}. Default is 'negotiate'.",
102
- :default => "negotiate",
103
- :proc => Proc.new { |protocol| Chef::Config[:knife][:winrm_authentication_protocol] = protocol }
104
-
105
- option :session_timeout,
106
- :long => "--session-timeout Minutes",
107
- :description => "The timeout for the client for the maximum length of the WinRM session",
108
- :default => 30
109
- end
110
- end
111
- end
112
- end
113
- end
1
+ #
2
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
+ # Copyright:: Copyright (c) 2011 Opscode, 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 :winrm_authentication_protocol,
100
+ :long => "--winrm-authentication-protocol AUTHENTICATION_PROTOCOL",
101
+ :description => "The authentication protocol used during WinRM communication. The supported protocols are #{WINRM_AUTH_PROTOCOL_LIST.join(',')}. Default is 'negotiate'.",
102
+ :default => "negotiate",
103
+ :proc => Proc.new { |protocol| Chef::Config[:knife][:winrm_authentication_protocol] = protocol }
104
+
105
+ option :session_timeout,
106
+ :long => "--session-timeout Minutes",
107
+ :description => "The timeout for the client for the maximum length of the WinRM session",
108
+ :default => 30
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -1,298 +1,298 @@
1
- #
2
- # Author:: Steven Murawski (<smurawski@chef.io)
3
- # Copyright:: Copyright (c) 2015 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
- @winrm_sessions.each do |s|
128
- begin
129
- s.relay_command(command)
130
- rescue WinRM::WinRMHTTPTransportError, WinRM::WinRMAuthorizationError => e
131
- if authorization_error?(e)
132
- if ! config[:suppress_auth_failure]
133
- # Display errors if the caller hasn't opted to retry
134
- ui.error "Failed to authenticate to #{s.host} as #{locate_config_value(:winrm_user)}"
135
- ui.info "Response: #{e.message}"
136
- ui.info get_failed_authentication_hint
137
- raise e
138
- end
139
- @exit_code = 401
140
- else
141
- raise e
142
- end
143
- end
144
- end
145
- end
146
-
147
- private
148
-
149
- def get_failed_authentication_hint
150
- if @session_opts[:basic_auth_only]
151
- FAILED_BASIC_HINT
152
- else
153
- FAILED_NOT_BASIC_HINT
154
- end
155
- end
156
-
157
- def authorization_error?(exception)
158
- exception.is_a?(WinRM::WinRMAuthorizationError) ||
159
- exception.message =~ /401/
160
- end
161
-
162
- def check_for_errors!
163
- @winrm_sessions.each do |session|
164
- session_exit_code = session.exit_code
165
- unless success_return_codes.include? session_exit_code.to_i
166
- @exit_code = session_exit_code.to_i
167
- ui.error "Failed to execute command on #{session.host} return code #{session_exit_code}"
168
- end
169
- end
170
- end
171
-
172
- def success_return_codes
173
- #Redundant if the CLI options parsing occurs
174
- return [0] unless config[:returns]
175
- return @success_return_codes ||= config[:returns].split(',').collect {|item| item.to_i}
176
- end
177
-
178
- def session_from_list
179
- @list.each do |item|
180
- Chef::Log.debug("Adding #{item}")
181
- @session_opts[:host] = item
182
- create_winrm_session(@session_opts)
183
- end
184
- end
185
-
186
- def create_winrm_session(options={})
187
- session = Chef::Knife::WinrmSession.new(options)
188
- @winrm_sessions ||= []
189
- @winrm_sessions.push(session)
190
- end
191
-
192
- def resolve_session_options
193
- @session_opts = {
194
- user: resolve_winrm_user,
195
- password: locate_config_value(:winrm_password),
196
- port: locate_config_value(:winrm_port),
197
- operation_timeout: resolve_winrm_session_timeout,
198
- basic_auth_only: resolve_winrm_basic_auth,
199
- disable_sspi: resolve_winrm_disable_sspi,
200
- transport: resolve_winrm_transport,
201
- no_ssl_peer_verification: resolve_no_ssl_peer_verification
202
- }
203
-
204
- if @session_opts[:user] and (not @session_opts[:password])
205
- @session_opts[:password] = Chef::Config[:knife][:winrm_password] = config[:winrm_password] = get_password
206
- end
207
-
208
- if @session_opts[:transport] == :kerberos
209
- @session_opts.merge!(resolve_winrm_kerberos_options)
210
- end
211
-
212
- @session_opts[:ca_trust_path] = locate_config_value(:ca_trust_file) if locate_config_value(:ca_trust_file)
213
- end
214
-
215
- def resolve_winrm_user
216
- user = locate_config_value(:winrm_user)
217
-
218
- # Prefixing with '.\' when using negotiate
219
- # to auth user against local machine domain
220
- if resolve_winrm_basic_auth ||
221
- resolve_winrm_transport == :kerberos ||
222
- user.include?("\\") ||
223
- user.include?("@")
224
- user
225
- else
226
- ".\\#{user}"
227
- end
228
- end
229
-
230
- def resolve_winrm_session_timeout
231
- #30 min (Default) OperationTimeout for long bootstraps fix for KNIFE_WINDOWS-8
232
- locate_config_value(:session_timeout).to_i * 60 if locate_config_value(:session_timeout)
233
- end
234
-
235
- def resolve_winrm_basic_auth
236
- locate_config_value(:winrm_authentication_protocol) == "basic"
237
- end
238
-
239
- def resolve_winrm_kerberos_options
240
- kerberos_opts = {}
241
- kerberos_opts[:keytab] = locate_config_value(:kerberos_keytab_file) if locate_config_value(:kerberos_keytab_file)
242
- kerberos_opts[:realm] = locate_config_value(:kerberos_realm) if locate_config_value(:kerberos_realm)
243
- kerberos_opts[:service] = locate_config_value(:kerberos_service) if locate_config_value(:kerberos_service)
244
- kerberos_opts
245
- end
246
-
247
- def resolve_winrm_transport
248
- transport = locate_config_value(:winrm_transport).to_sym
249
- if config.any? {|k,v| k.to_s =~ /kerberos/ && !v.nil? }
250
- transport = :kerberos
251
- elsif transport != :ssl && negotiate_auth?
252
- transport = :negotiate
253
- end
254
-
255
- transport
256
- end
257
-
258
- def resolve_no_ssl_peer_verification
259
- locate_config_value(:ca_trust_file).nil? && config[:winrm_ssl_verify_mode] == :verify_none && resolve_winrm_transport == :ssl
260
- end
261
-
262
- def resolve_winrm_disable_sspi
263
- resolve_winrm_transport != :negotiate
264
- end
265
-
266
- def get_password
267
- @password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
268
- end
269
-
270
- def negotiate_auth?
271
- locate_config_value(:winrm_authentication_protocol) == "negotiate"
272
- end
273
-
274
- def warn_no_ssl_peer_verification
275
- if ! @@ssl_warning_given
276
- @@ssl_warning_given = true
277
- ui.warn(<<-WARN)
278
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
279
- SSL validation of HTTPS requests for the WinRM transport is disabled. HTTPS WinRM
280
- connections are still encrypted, but knife is not able to detect forged replies
281
- or spoofing attacks.
282
-
283
- To fix this issue add an entry like this to your knife configuration file:
284
-
285
- ```
286
- # Verify all WinRM HTTPS connections (default, recommended)
287
- knife[:winrm_ssl_verify_mode] = :verify_peer
288
- ```
289
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
290
- WARN
291
- end
292
- end
293
-
294
- end
295
- end
296
- end
297
- end
298
- end
1
+ #
2
+ # Author:: Steven Murawski (<smurawski@chef.io)
3
+ # Copyright:: Copyright (c) 2015 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
+ @winrm_sessions.each do |s|
128
+ begin
129
+ s.relay_command(command)
130
+ rescue WinRM::WinRMHTTPTransportError, WinRM::WinRMAuthorizationError => e
131
+ if authorization_error?(e)
132
+ if ! config[:suppress_auth_failure]
133
+ # Display errors if the caller hasn't opted to retry
134
+ ui.error "Failed to authenticate to #{s.host} as #{locate_config_value(:winrm_user)}"
135
+ ui.info "Response: #{e.message}"
136
+ ui.info get_failed_authentication_hint
137
+ raise e
138
+ end
139
+ @exit_code = 401
140
+ else
141
+ raise e
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ private
148
+
149
+ def get_failed_authentication_hint
150
+ if @session_opts[:basic_auth_only]
151
+ FAILED_BASIC_HINT
152
+ else
153
+ FAILED_NOT_BASIC_HINT
154
+ end
155
+ end
156
+
157
+ def authorization_error?(exception)
158
+ exception.is_a?(WinRM::WinRMAuthorizationError) ||
159
+ exception.message =~ /401/
160
+ end
161
+
162
+ def check_for_errors!
163
+ @winrm_sessions.each do |session|
164
+ session_exit_code = session.exit_code
165
+ unless success_return_codes.include? session_exit_code.to_i
166
+ @exit_code = session_exit_code.to_i
167
+ ui.error "Failed to execute command on #{session.host} return code #{session_exit_code}"
168
+ end
169
+ end
170
+ end
171
+
172
+ def success_return_codes
173
+ #Redundant if the CLI options parsing occurs
174
+ return [0] unless config[:returns]
175
+ return @success_return_codes ||= config[:returns].split(',').collect {|item| item.to_i}
176
+ end
177
+
178
+ def session_from_list
179
+ @list.each do |item|
180
+ Chef::Log.debug("Adding #{item}")
181
+ @session_opts[:host] = item
182
+ create_winrm_session(@session_opts)
183
+ end
184
+ end
185
+
186
+ def create_winrm_session(options={})
187
+ session = Chef::Knife::WinrmSession.new(options)
188
+ @winrm_sessions ||= []
189
+ @winrm_sessions.push(session)
190
+ end
191
+
192
+ def resolve_session_options
193
+ @session_opts = {
194
+ user: resolve_winrm_user,
195
+ password: locate_config_value(:winrm_password),
196
+ port: locate_config_value(:winrm_port),
197
+ operation_timeout: resolve_winrm_session_timeout,
198
+ basic_auth_only: resolve_winrm_basic_auth,
199
+ disable_sspi: resolve_winrm_disable_sspi,
200
+ transport: resolve_winrm_transport,
201
+ no_ssl_peer_verification: resolve_no_ssl_peer_verification
202
+ }
203
+
204
+ if @session_opts[:user] and (not @session_opts[:password])
205
+ @session_opts[:password] = Chef::Config[:knife][:winrm_password] = config[:winrm_password] = get_password
206
+ end
207
+
208
+ if @session_opts[:transport] == :kerberos
209
+ @session_opts.merge!(resolve_winrm_kerberos_options)
210
+ end
211
+
212
+ @session_opts[:ca_trust_path] = locate_config_value(:ca_trust_file) if locate_config_value(:ca_trust_file)
213
+ end
214
+
215
+ def resolve_winrm_user
216
+ user = locate_config_value(:winrm_user)
217
+
218
+ # Prefixing with '.\' when using negotiate
219
+ # to auth user against local machine domain
220
+ if resolve_winrm_basic_auth ||
221
+ resolve_winrm_transport == :kerberos ||
222
+ user.include?("\\") ||
223
+ user.include?("@")
224
+ user
225
+ else
226
+ ".\\#{user}"
227
+ end
228
+ end
229
+
230
+ def resolve_winrm_session_timeout
231
+ #30 min (Default) OperationTimeout for long bootstraps fix for KNIFE_WINDOWS-8
232
+ locate_config_value(:session_timeout).to_i * 60 if locate_config_value(:session_timeout)
233
+ end
234
+
235
+ def resolve_winrm_basic_auth
236
+ locate_config_value(:winrm_authentication_protocol) == "basic"
237
+ end
238
+
239
+ def resolve_winrm_kerberos_options
240
+ kerberos_opts = {}
241
+ kerberos_opts[:keytab] = locate_config_value(:kerberos_keytab_file) if locate_config_value(:kerberos_keytab_file)
242
+ kerberos_opts[:realm] = locate_config_value(:kerberos_realm) if locate_config_value(:kerberos_realm)
243
+ kerberos_opts[:service] = locate_config_value(:kerberos_service) if locate_config_value(:kerberos_service)
244
+ kerberos_opts
245
+ end
246
+
247
+ def resolve_winrm_transport
248
+ transport = locate_config_value(:winrm_transport).to_sym
249
+ if config.any? {|k,v| k.to_s =~ /kerberos/ && !v.nil? }
250
+ transport = :kerberos
251
+ elsif transport != :ssl && negotiate_auth?
252
+ transport = :negotiate
253
+ end
254
+
255
+ transport
256
+ end
257
+
258
+ def resolve_no_ssl_peer_verification
259
+ locate_config_value(:ca_trust_file).nil? && config[:winrm_ssl_verify_mode] == :verify_none && resolve_winrm_transport == :ssl
260
+ end
261
+
262
+ def resolve_winrm_disable_sspi
263
+ resolve_winrm_transport != :negotiate
264
+ end
265
+
266
+ def get_password
267
+ @password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
268
+ end
269
+
270
+ def negotiate_auth?
271
+ locate_config_value(:winrm_authentication_protocol) == "negotiate"
272
+ end
273
+
274
+ def warn_no_ssl_peer_verification
275
+ if ! @@ssl_warning_given
276
+ @@ssl_warning_given = true
277
+ ui.warn(<<-WARN)
278
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
279
+ SSL validation of HTTPS requests for the WinRM transport is disabled. HTTPS WinRM
280
+ connections are still encrypted, but knife is not able to detect forged replies
281
+ or spoofing attacks.
282
+
283
+ To fix this issue add an entry like this to your knife configuration file:
284
+
285
+ ```
286
+ # Verify all WinRM HTTPS connections (default, recommended)
287
+ knife[:winrm_ssl_verify_mode] = :verify_peer
288
+ ```
289
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
290
+ WARN
291
+ end
292
+ end
293
+
294
+ end
295
+ end
296
+ end
297
+ end
298
+ end