knife-windows 1.1.0 → 1.1.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 +20 -20
  4. data/CHANGELOG.md +87 -83
  5. data/DOC_CHANGES.md +20 -20
  6. data/Gemfile +12 -12
  7. data/LICENSE +201 -201
  8. data/README.md +396 -396
  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 +28 -28
  16. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +247 -247
  17. data/lib/chef/knife/bootstrap_windows_base.rb +407 -401
  18. data/lib/chef/knife/bootstrap_windows_ssh.rb +110 -110
  19. data/lib/chef/knife/bootstrap_windows_winrm.rb +95 -102
  20. data/lib/chef/knife/core/windows_bootstrap_context.rb +362 -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 -212
  27. data/lib/chef/knife/winrm_base.rb +118 -118
  28. data/lib/chef/knife/winrm_knife_base.rb +309 -218
  29. data/lib/chef/knife/winrm_session.rb +82 -82
  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 +95 -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 -233
  41. data/spec/spec_helper.rb +88 -88
  42. data/spec/unit/knife/bootstrap_options_spec.rb +148 -146
  43. data/spec/unit/knife/bootstrap_template_spec.rb +92 -92
  44. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +259 -243
  45. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +151 -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 +73 -73
  50. data/spec/unit/knife/winrm_spec.rb +551 -504
  51. data/spec/unit/knife/wsman_test_spec.rb +178 -175
  52. metadata +3 -23
@@ -1,118 +1,118 @@
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 :identity_file,
70
- :short => "-i IDENTITY_FILE",
71
- :long => "--identity-file IDENTITY_FILE",
72
- :description => "The SSH identity file used for authentication"
73
-
74
- option :kerberos_keytab_file,
75
- :short => "-T KEYTAB_FILE",
76
- :long => "--keytab-file KEYTAB_FILE",
77
- :description => "The Kerberos keytab file used for authentication",
78
- :proc => Proc.new { |keytab| Chef::Config[:knife][:kerberos_keytab_file] = keytab }
79
-
80
- option :kerberos_realm,
81
- :short => "-R KERBEROS_REALM",
82
- :long => "--kerberos-realm KERBEROS_REALM",
83
- :description => "The Kerberos realm used for authentication",
84
- :proc => Proc.new { |realm| Chef::Config[:knife][:kerberos_realm] = realm }
85
-
86
- option :kerberos_service,
87
- :short => "-S KERBEROS_SERVICE",
88
- :long => "--kerberos-service KERBEROS_SERVICE",
89
- :description => "The Kerberos service used for authentication",
90
- :proc => Proc.new { |service| Chef::Config[:knife][:kerberos_service] = service }
91
-
92
- option :ca_trust_file,
93
- :short => "-f CA_TRUST_FILE",
94
- :long => "--ca-trust-file CA_TRUST_FILE",
95
- :description => "The Certificate Authority (CA) trust file used for SSL transport",
96
- :proc => Proc.new { |trust| Chef::Config[:knife][:ca_trust_file] = trust }
97
-
98
- option :winrm_ssl_verify_mode,
99
- :long => "--winrm-ssl-verify-mode SSL_VERIFY_MODE",
100
- :description => "The WinRM peer verification mode. Valid choices are [verify_peer, verify_none]",
101
- :default => :verify_peer,
102
- :proc => Proc.new { |verify_mode| verify_mode.to_sym }
103
-
104
- option :winrm_authentication_protocol,
105
- :long => "--winrm-authentication-protocol AUTHENTICATION_PROTOCOL",
106
- :description => "The authentication protocol used during WinRM communication. The supported protocols are #{WINRM_AUTH_PROTOCOL_LIST.join(',')}. Default is 'negotiate'.",
107
- :default => "negotiate",
108
- :proc => Proc.new { |protocol| Chef::Config[:knife][:winrm_authentication_protocol] = protocol }
109
-
110
- option :session_timeout,
111
- :long => "--session-timeout Minutes",
112
- :description => "The timeout for the client for the maximum length of the WinRM session",
113
- :default => 30
114
- end
115
- end
116
- end
117
- end
118
- 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 :identity_file,
70
+ :short => "-i IDENTITY_FILE",
71
+ :long => "--identity-file IDENTITY_FILE",
72
+ :description => "The SSH identity file used for authentication"
73
+
74
+ option :kerberos_keytab_file,
75
+ :short => "-T KEYTAB_FILE",
76
+ :long => "--keytab-file KEYTAB_FILE",
77
+ :description => "The Kerberos keytab file used for authentication",
78
+ :proc => Proc.new { |keytab| Chef::Config[:knife][:kerberos_keytab_file] = keytab }
79
+
80
+ option :kerberos_realm,
81
+ :short => "-R KERBEROS_REALM",
82
+ :long => "--kerberos-realm KERBEROS_REALM",
83
+ :description => "The Kerberos realm used for authentication",
84
+ :proc => Proc.new { |realm| Chef::Config[:knife][:kerberos_realm] = realm }
85
+
86
+ option :kerberos_service,
87
+ :short => "-S KERBEROS_SERVICE",
88
+ :long => "--kerberos-service KERBEROS_SERVICE",
89
+ :description => "The Kerberos service used for authentication",
90
+ :proc => Proc.new { |service| Chef::Config[:knife][:kerberos_service] = service }
91
+
92
+ option :ca_trust_file,
93
+ :short => "-f CA_TRUST_FILE",
94
+ :long => "--ca-trust-file CA_TRUST_FILE",
95
+ :description => "The Certificate Authority (CA) trust file used for SSL transport",
96
+ :proc => Proc.new { |trust| Chef::Config[:knife][:ca_trust_file] = trust }
97
+
98
+ option :winrm_ssl_verify_mode,
99
+ :long => "--winrm-ssl-verify-mode SSL_VERIFY_MODE",
100
+ :description => "The WinRM peer verification mode. Valid choices are [verify_peer, verify_none]",
101
+ :default => :verify_peer,
102
+ :proc => Proc.new { |verify_mode| verify_mode.to_sym }
103
+
104
+ option :winrm_authentication_protocol,
105
+ :long => "--winrm-authentication-protocol AUTHENTICATION_PROTOCOL",
106
+ :description => "The authentication protocol used during WinRM communication. The supported protocols are #{WINRM_AUTH_PROTOCOL_LIST.join(',')}. Default is 'negotiate'.",
107
+ :default => "negotiate",
108
+ :proc => Proc.new { |protocol| Chef::Config[:knife][:winrm_authentication_protocol] = protocol }
109
+
110
+ option :session_timeout,
111
+ :long => "--session-timeout Minutes",
112
+ :description => "The timeout for the client for the maximum length of the WinRM session",
113
+ :default => 30
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -1,218 +1,309 @@
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
- def self.included(includer)
29
- includer.class_eval do
30
-
31
- @@ssl_warning_given = false
32
-
33
- include Chef::Knife::WinrmBase
34
- include Chef::Knife::WinrmSharedOptions
35
- include Chef::Knife::KnifeWindowsBase
36
-
37
- def validate_winrm_options!
38
- winrm_auth_protocol = locate_config_value(:winrm_authentication_protocol)
39
-
40
- if ! Chef::Knife::WinrmBase::WINRM_AUTH_PROTOCOL_LIST.include?(winrm_auth_protocol)
41
- ui.error "Invalid value '#{winrm_auth_protocol}' for --winrm-authentication-protocol option."
42
- ui.info "Valid values are #{Chef::Knife::WinrmBase::WINRM_AUTH_PROTOCOL_LIST.join(",")}."
43
- exit 1
44
- end
45
-
46
- if negotiate_auth? && !Chef::Platform.windows? && !(locate_config_value(:winrm_transport) == 'ssl')
47
- ui.warn <<-eos.gsub /^\s+/, ""
48
- You are using '--winrm-authentication-protocol negotiate' with
49
- '--winrm-transport plaintext' on a non-Windows system which results in
50
- unencrypted traffic. To avoid this warning and secure communication,
51
- use '--winrm-transport ssl' instead of the plaintext transport,
52
- or execute this command from a Windows system which enables encrypted
53
- communication over plaintext with the negotiate authentication protocol.
54
- eos
55
- end
56
-
57
- warn_no_ssl_peer_verification if resolve_no_ssl_peer_verification
58
- end
59
-
60
- #Overrides Chef::Knife#configure_session, as that code is tied to the SSH implementation
61
- #Tracked by Issue # 3042 / https://github.com/chef/chef/issues/3042
62
- def configure_session
63
- validate_winrm_options!
64
- resolve_session_options
65
- resolve_target_nodes
66
- session_from_list
67
- end
68
-
69
- def resolve_target_nodes
70
- @list = case config[:manual]
71
- when true
72
- @name_args[0].split(" ")
73
- when false
74
- r = Array.new
75
- q = Chef::Search::Query.new
76
- @action_nodes = q.search(:node, @name_args[0])[0]
77
- @action_nodes.each do |item|
78
- i = extract_nested_value(item, config[:attribute])
79
- r.push(i) unless i.nil?
80
- end
81
- r
82
- end
83
-
84
- if @list.length == 0
85
- if @action_nodes.length == 0
86
- ui.fatal("No nodes returned from search!")
87
- else
88
- ui.fatal("#{@action_nodes.length} #{@action_nodes.length > 1 ? "nodes":"node"} found, " +
89
- "but does not have the required attribute (#{config[:attribute]}) to establish the connection. " +
90
- "Try setting another attribute to open the connection using --attribute.")
91
- end
92
- exit 10
93
- end
94
- end
95
-
96
- def validate_password
97
- if @session_opts[:user] and (not @session_opts[:password])
98
- @session_opts[:password] = Chef::Config[:knife][:winrm_password] = config[:winrm_password] = get_password
99
- end
100
- end
101
-
102
- private
103
-
104
- def session_from_list
105
- @list.each do |item|
106
- Chef::Log.debug("Adding #{item}")
107
- @session_opts[:host] = item
108
- create_winrm_session(@session_opts)
109
- end
110
- end
111
-
112
- def create_winrm_session(options={})
113
- session = Chef::Knife::WinrmSession.new(options)
114
- @winrm_sessions ||= []
115
- @winrm_sessions.push(session)
116
- end
117
-
118
- def resolve_session_options
119
- @session_opts = {
120
- user: resolve_winrm_user,
121
- password: locate_config_value(:winrm_password),
122
- port: locate_config_value(:winrm_port),
123
- operation_timeout: resolve_winrm_session_timeout,
124
- basic_auth_only: resolve_winrm_basic_auth,
125
- disable_sspi: resolve_winrm_disable_sspi,
126
- transport: resolve_winrm_transport,
127
- no_ssl_peer_verification: resolve_no_ssl_peer_verification
128
- }
129
- if @session_opts[:transport] == :kerberos
130
- @session_opts.merge!(resolve_winrm_kerberos_options)
131
- end
132
- @session_opts[:ca_trust_path] = locate_config_value(:ca_trust_file) if locate_config_value(:ca_trust_file)
133
- end
134
-
135
- def resolve_winrm_user
136
- user = locate_config_value(:winrm_user)
137
-
138
- # Prefixing with '.\' when using negotiate
139
- # to auth user against local machine domain
140
- if resolve_winrm_basic_auth ||
141
- resolve_winrm_transport == :kerberos ||
142
- user.include?("\\") ||
143
- user.include?("@")
144
- user
145
- else
146
- ".\\#{user}"
147
- end
148
- end
149
-
150
- def resolve_winrm_session_timeout
151
- #30 min (Default) OperationTimeout for long bootstraps fix for KNIFE_WINDOWS-8
152
- locate_config_value(:session_timeout).to_i * 60 if locate_config_value(:session_timeout)
153
- end
154
-
155
- def resolve_winrm_basic_auth
156
- locate_config_value(:winrm_authentication_protocol) == "basic"
157
- end
158
-
159
- def resolve_winrm_kerberos_options
160
- kerberos_opts = {}
161
- kerberos_opts[:keytab] = locate_config_value(:kerberos_keytab_file) if locate_config_value(:kerberos_keytab_file)
162
- kerberos_opts[:realm] = locate_config_value(:kerberos_realm) if locate_config_value(:kerberos_realm)
163
- kerberos_opts[:service] = locate_config_value(:kerberos_service) if locate_config_value(:kerberos_service)
164
- kerberos_opts
165
- end
166
-
167
- def resolve_winrm_transport
168
- transport = locate_config_value(:winrm_transport).to_sym
169
- if config.any? {|k,v| k.to_s =~ /kerberos/ && !v.nil? }
170
- transport = :kerberos
171
- elsif Chef::Platform.windows? && transport != :ssl && negotiate_auth?
172
- transport = :sspinegotiate
173
- end
174
-
175
- transport
176
- end
177
-
178
- def resolve_no_ssl_peer_verification
179
- locate_config_value(:ca_trust_file).nil? && config[:winrm_ssl_verify_mode] == :verify_none && resolve_winrm_transport == :ssl
180
- end
181
-
182
- def resolve_winrm_disable_sspi
183
- !Chef::Platform.windows? || resolve_winrm_transport == :ssl || !negotiate_auth?
184
- end
185
-
186
- def get_password
187
- @password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
188
- end
189
-
190
- def negotiate_auth?
191
- locate_config_value(:winrm_authentication_protocol) == "negotiate"
192
- end
193
-
194
- def warn_no_ssl_peer_verification
195
- if ! @@ssl_warning_given
196
- @@ssl_warning_given = true
197
- ui.warn(<<-WARN)
198
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
199
- SSL validation of HTTPS requests for the WinRM transport is disabled. HTTPS WinRM
200
- connections are still encrypted, but knife is not able to detect forged replies
201
- or spoofing attacks.
202
-
203
- To fix this issue add an entry like this to your knife configuration file:
204
-
205
- ```
206
- # Verify all WinRM HTTPS connections (default, recommended)
207
- knife[:winrm_ssl_verify_mode] = :verify_peer
208
- ```
209
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
210
- WARN
211
- end
212
- end
213
-
214
- end
215
- end
216
- end
217
- end
218
- 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
+ if negotiate_auth? && !Chef::Platform.windows? && !(locate_config_value(:winrm_transport) == 'ssl')
55
+ ui.warn <<-eos.gsub /^\s+/, ""
56
+ You are using '--winrm-authentication-protocol negotiate' with
57
+ '--winrm-transport plaintext' on a non-Windows system which results in
58
+ unencrypted traffic. To avoid this warning and secure communication,
59
+ use '--winrm-transport ssl' instead of the plaintext transport,
60
+ or execute this command from a Windows system which enables encrypted
61
+ communication over plaintext with the negotiate authentication protocol.
62
+ eos
63
+ end
64
+
65
+ warn_no_ssl_peer_verification if resolve_no_ssl_peer_verification
66
+ end
67
+
68
+ #Overrides Chef::Knife#configure_session, as that code is tied to the SSH implementation
69
+ #Tracked by Issue # 3042 / https://github.com/chef/chef/issues/3042
70
+ def configure_session
71
+ validate_winrm_options!
72
+ resolve_session_options
73
+ resolve_target_nodes
74
+ session_from_list
75
+ end
76
+
77
+ def resolve_target_nodes
78
+ @list = case config[:manual]
79
+ when true
80
+ @name_args[0].split(" ")
81
+ when false
82
+ r = Array.new
83
+ q = Chef::Search::Query.new
84
+ @action_nodes = q.search(:node, @name_args[0])[0]
85
+ @action_nodes.each do |item|
86
+ i = extract_nested_value(item, config[:attribute])
87
+ r.push(i) unless i.nil?
88
+ end
89
+ r
90
+ end
91
+
92
+ if @list.length == 0
93
+ if @action_nodes.length == 0
94
+ ui.fatal("No nodes returned from search!")
95
+ else
96
+ ui.fatal("#{@action_nodes.length} #{@action_nodes.length > 1 ? "nodes":"node"} found, " +
97
+ "but does not have the required attribute (#{config[:attribute]}) to establish the connection. " +
98
+ "Try setting another attribute to open the connection using --attribute.")
99
+ end
100
+ exit 10
101
+ end
102
+ end
103
+
104
+ # TODO: Copied from Knife::Core:GenericPresenter. Should be extracted
105
+ def extract_nested_value(data, nested_value_spec)
106
+ nested_value_spec.split(".").each do |attr|
107
+ if data.nil?
108
+ nil # don't get no method error on nil
109
+ elsif data.respond_to?(attr.to_sym)
110
+ data = data.send(attr.to_sym)
111
+ elsif data.respond_to?(:[])
112
+ data = data[attr]
113
+ else
114
+ data = begin
115
+ data.send(attr.to_sym)
116
+ rescue NoMethodError
117
+ nil
118
+ end
119
+ end
120
+ end
121
+ ( !data.kind_of?(Array) && data.respond_to?(:to_hash) ) ? data.to_hash : data
122
+ end
123
+
124
+ def run_command(command = '')
125
+ relay_winrm_command(command)
126
+
127
+ check_for_errors!
128
+
129
+ # Knife seems to ignore the return value of this method,
130
+ # so we exit to force the process exit code for this
131
+ # subcommand if returns is set
132
+ exit @exit_code if @exit_code && @exit_code != 0
133
+ 0
134
+ end
135
+
136
+ def relay_winrm_command(command)
137
+ Chef::Log.debug(command)
138
+ @winrm_sessions.each do |s|
139
+ begin
140
+ s.relay_command(command)
141
+ rescue WinRM::WinRMHTTPTransportError, WinRM::WinRMAuthorizationError => e
142
+ if authorization_error?(e)
143
+ if ! config[:suppress_auth_failure]
144
+ # Display errors if the caller hasn't opted to retry
145
+ ui.error "Failed to authenticate to #{s.host} as #{locate_config_value(:winrm_user)}"
146
+ ui.info "Response: #{e.message}"
147
+ ui.info get_failed_authentication_hint
148
+ raise e
149
+ end
150
+ @exit_code = 401
151
+ else
152
+ raise e
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ private
159
+
160
+ def get_failed_authentication_hint
161
+ if @session_opts[:basic_auth_only]
162
+ FAILED_BASIC_HINT
163
+ else
164
+ FAILED_NOT_BASIC_HINT
165
+ end
166
+ end
167
+
168
+ def authorization_error?(exception)
169
+ exception.is_a?(WinRM::WinRMAuthorizationError) ||
170
+ exception.message =~ /401/
171
+ end
172
+
173
+ def check_for_errors!
174
+ @winrm_sessions.each do |session|
175
+ session_exit_code = session.exit_code
176
+ unless success_return_codes.include? session_exit_code.to_i
177
+ @exit_code = session_exit_code.to_i
178
+ ui.error "Failed to execute command on #{session.host} return code #{session_exit_code}"
179
+ end
180
+ end
181
+ end
182
+
183
+ def success_return_codes
184
+ #Redundant if the CLI options parsing occurs
185
+ return [0] unless config[:returns]
186
+ return @success_return_codes ||= config[:returns].split(',').collect {|item| item.to_i}
187
+ end
188
+
189
+ def session_from_list
190
+ @list.each do |item|
191
+ Chef::Log.debug("Adding #{item}")
192
+ @session_opts[:host] = item
193
+ create_winrm_session(@session_opts)
194
+ end
195
+ end
196
+
197
+ def create_winrm_session(options={})
198
+ session = Chef::Knife::WinrmSession.new(options)
199
+ @winrm_sessions ||= []
200
+ @winrm_sessions.push(session)
201
+ end
202
+
203
+ def resolve_session_options
204
+ @session_opts = {
205
+ user: resolve_winrm_user,
206
+ password: locate_config_value(:winrm_password),
207
+ port: locate_config_value(:winrm_port),
208
+ operation_timeout: resolve_winrm_session_timeout,
209
+ basic_auth_only: resolve_winrm_basic_auth,
210
+ disable_sspi: resolve_winrm_disable_sspi,
211
+ transport: resolve_winrm_transport,
212
+ no_ssl_peer_verification: resolve_no_ssl_peer_verification
213
+ }
214
+
215
+ if @session_opts[:user] and (not @session_opts[:password])
216
+ @session_opts[:password] = Chef::Config[:knife][:winrm_password] = config[:winrm_password] = get_password
217
+ end
218
+
219
+ if @session_opts[:transport] == :kerberos
220
+ @session_opts.merge!(resolve_winrm_kerberos_options)
221
+ end
222
+
223
+ @session_opts[:ca_trust_path] = locate_config_value(:ca_trust_file) if locate_config_value(:ca_trust_file)
224
+ end
225
+
226
+ def resolve_winrm_user
227
+ user = locate_config_value(:winrm_user)
228
+
229
+ # Prefixing with '.\' when using negotiate
230
+ # to auth user against local machine domain
231
+ if resolve_winrm_basic_auth ||
232
+ resolve_winrm_transport == :kerberos ||
233
+ user.include?("\\") ||
234
+ user.include?("@")
235
+ user
236
+ else
237
+ ".\\#{user}"
238
+ end
239
+ end
240
+
241
+ def resolve_winrm_session_timeout
242
+ #30 min (Default) OperationTimeout for long bootstraps fix for KNIFE_WINDOWS-8
243
+ locate_config_value(:session_timeout).to_i * 60 if locate_config_value(:session_timeout)
244
+ end
245
+
246
+ def resolve_winrm_basic_auth
247
+ locate_config_value(:winrm_authentication_protocol) == "basic"
248
+ end
249
+
250
+ def resolve_winrm_kerberos_options
251
+ kerberos_opts = {}
252
+ kerberos_opts[:keytab] = locate_config_value(:kerberos_keytab_file) if locate_config_value(:kerberos_keytab_file)
253
+ kerberos_opts[:realm] = locate_config_value(:kerberos_realm) if locate_config_value(:kerberos_realm)
254
+ kerberos_opts[:service] = locate_config_value(:kerberos_service) if locate_config_value(:kerberos_service)
255
+ kerberos_opts
256
+ end
257
+
258
+ def resolve_winrm_transport
259
+ transport = locate_config_value(:winrm_transport).to_sym
260
+ if config.any? {|k,v| k.to_s =~ /kerberos/ && !v.nil? }
261
+ transport = :kerberos
262
+ elsif Chef::Platform.windows? && transport != :ssl && negotiate_auth?
263
+ transport = :sspinegotiate
264
+ end
265
+
266
+ transport
267
+ end
268
+
269
+ def resolve_no_ssl_peer_verification
270
+ locate_config_value(:ca_trust_file).nil? && config[:winrm_ssl_verify_mode] == :verify_none && resolve_winrm_transport == :ssl
271
+ end
272
+
273
+ def resolve_winrm_disable_sspi
274
+ !Chef::Platform.windows? || resolve_winrm_transport == :ssl || !negotiate_auth?
275
+ end
276
+
277
+ def get_password
278
+ @password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
279
+ end
280
+
281
+ def negotiate_auth?
282
+ locate_config_value(:winrm_authentication_protocol) == "negotiate"
283
+ end
284
+
285
+ def warn_no_ssl_peer_verification
286
+ if ! @@ssl_warning_given
287
+ @@ssl_warning_given = true
288
+ ui.warn(<<-WARN)
289
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
290
+ SSL validation of HTTPS requests for the WinRM transport is disabled. HTTPS WinRM
291
+ connections are still encrypted, but knife is not able to detect forged replies
292
+ or spoofing attacks.
293
+
294
+ To fix this issue add an entry like this to your knife configuration file:
295
+
296
+ ```
297
+ # Verify all WinRM HTTPS connections (default, recommended)
298
+ knife[:winrm_ssl_verify_mode] = :verify_peer
299
+ ```
300
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
301
+ WARN
302
+ end
303
+ end
304
+
305
+ end
306
+ end
307
+ end
308
+ end
309
+ end