lib/chef/knife/winrm_knife_base.rb in knife-windows-1.0.0.rc.1 vs lib/chef/knife/winrm_knife_base.rb in knife-windows-1.0.0.rc.2
- old
+ new
@@ -1,201 +1,218 @@
-#
-# Author:: Steven Murawski (<smurawski@chef.io)
-# Copyright:: Copyright (c) 2015 Chef Software, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-
-require 'chef/knife'
-require 'chef/knife/winrm_base'
-require 'chef/knife/winrm_shared_options'
-
-class Chef
- class Knife
- module WinrmCommandSharedFunctions
- def self.included(includer)
- includer.class_eval do
-
- @@ssl_warning_given = false
-
- include Chef::Knife::WinrmBase
- include Chef::Knife::WinrmSharedOptions
-
- #Overrides Chef::Knife#configure_session, as that code is tied to the SSH implementation
- #Tracked by Issue # 3042 / https://github.com/chef/chef/issues/3042
- def configure_session
- resolve_session_options
- resolve_target_nodes
- session_from_list
- end
-
- def resolve_target_nodes
- @list = case config[:manual]
- when true
- @name_args[0].split(" ")
- when false
- r = Array.new
- q = Chef::Search::Query.new
- @action_nodes = q.search(:node, @name_args[0])[0]
- @action_nodes.each do |item|
- i = extract_nested_value(item, config[:attribute])
- r.push(i) unless i.nil?
- end
- r
- end
- if @list.length == 0
- if @action_nodes.length == 0
- ui.fatal("No nodes returned from search!")
- else
- ui.fatal("#{@action_nodes.length} #{@action_nodes.length > 1 ? "nodes":"node"} found, " +
- "but does not have the required attribute (#{config[:attribute]}) to establish the connection. " +
- "Try setting another attribute to open the connection using --attribute.")
- end
- exit 10
- end
- end
-
- def validate_password
- if @session_opts[:user] and (not @session_opts[:password])
- @session_opts[:password] = Chef::Config[:knife][:winrm_password] = config[:winrm_password] = get_password
- end
- end
-
- private
-
- def session_from_list
- @list.each do |item|
- Chef::Log.debug("Adding #{item}")
- @session_opts[:host] = item
- create_winrm_session(@session_opts)
- end
- end
-
- def create_winrm_session(options={})
- session = Chef::Knife::WinrmSession.new(options)
- @winrm_sessions ||= []
- @winrm_sessions.push(session)
- end
-
- def resolve_session_options
- resolve_winrm_basic_options
- resolve_winrm_auth_settings
- resolve_winrm_kerberos_options
- resolve_winrm_transport_options
- resolve_winrm_ssl_options
- end
-
- def resolve_winrm_basic_options
- @session_opts = {}
- @session_opts[:user] = locate_config_value(:winrm_user)
- @session_opts[:password] = locate_config_value(:winrm_password)
- @session_opts[:port] = locate_config_value(:winrm_port)
-
- #30 min (Default) OperationTimeout for long bootstraps fix for KNIFE_WINDOWS-8
- @session_opts[:operation_timeout] = locate_config_value(:session_timeout).to_i * 60 if locate_config_value(:session_timeout)
- end
-
- def resolve_winrm_kerberos_options
- if config.keys.any? {|k| k.to_s =~ /kerberos/ }
- @session_opts[:transport] = :kerberos
- @session_opts[:keytab] = locate_config_value(:kerberos_keytab_file) if locate_config_value(:kerberos_keytab_file)
- @session_opts[:realm] = locate_config_value(:kerberos_realm) if locate_config_value(:kerberos_realm)
- @session_opts[:service] = locate_config_value(:kerberos_service) if locate_config_value(:kerberos_service)
- end
- end
-
- def resolve_winrm_transport_options
- @session_opts[:disable_sspi] = true
- @session_opts[:transport] = locate_config_value(:winrm_transport).to_sym unless @session_opts[:transport] == :kerberos
- if negotiate_auth? && @session_opts[:transport] == :ssl
- Chef::Log.debug("Trying WinRM communication with negotiate authentication and :ssl transport")
- elsif use_windows_native_auth?
- load_windows_specific_gems
- @session_opts[:transport] = :sspinegotiate
- @session_opts[:disable_sspi] = false
- elsif negotiate_auth? && !Chef::Platform.windows?
- ui.warn "\nUsing '--winrm-authentication-protocol negotiate' with '--winrm-transport plaintext' is only supported when this tool is invoked from a Windows system."
- ui.warn "Switch to either '--winrm-transport ssl' or '--winrm-authentication-protocol basic'."
- exit 1
- end
- end
-
- def resolve_winrm_ssl_options
- @session_opts[:ca_trust_path] = locate_config_value(:ca_trust_file) if locate_config_value(:ca_trust_file)
- @session_opts[:no_ssl_peer_verification] = no_ssl_peer_verification?(@session_opts[:ca_trust_path])
- warn_no_ssl_peer_verification if @session_opts[:no_ssl_peer_verification]
- end
-
- def resolve_winrm_auth_settings
- winrm_auth_protocol = locate_config_value(:winrm_authentication_protocol)
- if ! Chef::Knife::WinrmBase::WINRM_AUTH_PROTOCOL_LIST.include?(winrm_auth_protocol)
- ui.error "Invalid value '#{winrm_auth_protocol}' for --winrm-authentication-protocol option."
- ui.info "Valid values are #{Chef::Knife::WinrmBase::WINRM_AUTH_PROTOCOL_LIST.join(",")}."
- exit 1
- end
-
- if winrm_auth_protocol == "basic"
- @session_opts[:basic_auth_only] = true
- else
- @session_opts[:basic_auth_only] = false
- end
- end
-
- def no_ssl_peer_verification?(ca_trust_path)
- ca_trust_path.nil? && (config[:winrm_ssl_verify_mode] == :verify_none)
- end
-
- def use_windows_native_auth?
- Chef::Platform.windows? && @session_opts[:transport] != :ssl && negotiate_auth?
- end
-
- def load_windows_specific_gems
- require 'winrm-s'
- Chef::Log.debug("Applied 'winrm-s' monkey patch and trying WinRM communication with 'sspinegotiate'")
- end
-
- def get_password
- @password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
- end
-
- # returns true if winrm_authentication_protocol is 'negotiate'
- def negotiate_auth?
- locate_config_value(:winrm_authentication_protocol) == "negotiate"
- end
-
- def warn_no_ssl_peer_verification
- if ! @@ssl_warning_given
- @@ssl_warning_given = true
- ui.warn(<<-WARN)
-* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-SSL validation of HTTPS requests for the WinRM transport is disabled. HTTPS WinRM
-connections are still encrypted, but knife is not able to detect forged replies
-or spoofing attacks.
-
-To fix this issue add an entry like this to your knife configuration file:
-
-```
- # Verify all WinRM HTTPS connections (default, recommended)
- knife[:winrm_ssl_verify_mode] = :verify_peer
-```
-* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-WARN
- end
- end
-
- end
- end
- end
- end
-end
+#
+# Author:: Steven Murawski (<smurawski@chef.io)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+require 'chef/knife'
+require 'chef/knife/winrm_base'
+require 'chef/knife/winrm_shared_options'
+require 'chef/knife/knife_windows_base'
+
+class Chef
+ class Knife
+ module WinrmCommandSharedFunctions
+ def self.included(includer)
+ includer.class_eval do
+
+ @@ssl_warning_given = false
+
+ include Chef::Knife::WinrmBase
+ include Chef::Knife::WinrmSharedOptions
+ include Chef::Knife::KnifeWindowsBase
+
+ def validate_options!
+ winrm_auth_protocol = locate_config_value(:winrm_authentication_protocol)
+
+ if ! Chef::Knife::WinrmBase::WINRM_AUTH_PROTOCOL_LIST.include?(winrm_auth_protocol)
+ ui.error "Invalid value '#{winrm_auth_protocol}' for --winrm-authentication-protocol option."
+ ui.info "Valid values are #{Chef::Knife::WinrmBase::WINRM_AUTH_PROTOCOL_LIST.join(",")}."
+ exit 1
+ end
+
+ if negotiate_auth? && !Chef::Platform.windows? && !(locate_config_value(:winrm_transport) == 'ssl')
+ ui.warn <<-eos.gsub /^\s+/, ""
+ You are using '--winrm-authentication-protocol negotiate' with
+ '--winrm-transport plaintext' on a non-Windows system which results in
+ unencrypted traffic. To avoid this warning and secure communication,
+ use '--winrm-transport ssl' instead of the plaintext transport,
+ or execute this command from a Windows system which enables encrypted
+ communication over plaintext with the negotiate authentication protocol.
+ eos
+ end
+
+ warn_no_ssl_peer_verification if resolve_no_ssl_peer_verification
+ end
+
+ #Overrides Chef::Knife#configure_session, as that code is tied to the SSH implementation
+ #Tracked by Issue # 3042 / https://github.com/chef/chef/issues/3042
+ def configure_session
+ validate_options!
+ resolve_session_options
+ resolve_target_nodes
+ session_from_list
+ end
+
+ def resolve_target_nodes
+ @list = case config[:manual]
+ when true
+ @name_args[0].split(" ")
+ when false
+ r = Array.new
+ q = Chef::Search::Query.new
+ @action_nodes = q.search(:node, @name_args[0])[0]
+ @action_nodes.each do |item|
+ i = extract_nested_value(item, config[:attribute])
+ r.push(i) unless i.nil?
+ end
+ r
+ end
+
+ if @list.length == 0
+ if @action_nodes.length == 0
+ ui.fatal("No nodes returned from search!")
+ else
+ ui.fatal("#{@action_nodes.length} #{@action_nodes.length > 1 ? "nodes":"node"} found, " +
+ "but does not have the required attribute (#{config[:attribute]}) to establish the connection. " +
+ "Try setting another attribute to open the connection using --attribute.")
+ end
+ exit 10
+ end
+ end
+
+ def validate_password
+ if @session_opts[:user] and (not @session_opts[:password])
+ @session_opts[:password] = Chef::Config[:knife][:winrm_password] = config[:winrm_password] = get_password
+ end
+ end
+
+ private
+
+ def session_from_list
+ @list.each do |item|
+ Chef::Log.debug("Adding #{item}")
+ @session_opts[:host] = item
+ create_winrm_session(@session_opts)
+ end
+ end
+
+ def create_winrm_session(options={})
+ session = Chef::Knife::WinrmSession.new(options)
+ @winrm_sessions ||= []
+ @winrm_sessions.push(session)
+ end
+
+ def resolve_session_options
+ @session_opts = {
+ user: resolve_winrm_user,
+ password: locate_config_value(:winrm_password),
+ port: locate_config_value(:winrm_port),
+ operation_timeout: resolve_winrm_session_timeout,
+ basic_auth_only: resolve_winrm_basic_auth,
+ disable_sspi: resolve_winrm_disable_sspi,
+ transport: resolve_winrm_transport,
+ no_ssl_peer_verification: resolve_no_ssl_peer_verification
+ }
+ if @session_opts[:transport] == :kerberos
+ @session_opts.merge!(resolve_winrm_kerberos_options)
+ end
+ @session_opts[:ca_trust_path] = locate_config_value(:ca_trust_file) if locate_config_value(:ca_trust_file)
+ end
+
+ def resolve_winrm_user
+ user = locate_config_value(:winrm_user)
+
+ # Prefixing with '.\' when using negotiate
+ # to auth user against local machine domain
+ if resolve_winrm_basic_auth ||
+ resolve_winrm_transport == :kerberos ||
+ user.include?("\\") ||
+ user.include?("@")
+ user
+ else
+ ".\\#{user}"
+ end
+ end
+
+ def resolve_winrm_session_timeout
+ #30 min (Default) OperationTimeout for long bootstraps fix for KNIFE_WINDOWS-8
+ locate_config_value(:session_timeout).to_i * 60 if locate_config_value(:session_timeout)
+ end
+
+ def resolve_winrm_basic_auth
+ locate_config_value(:winrm_authentication_protocol) == "basic"
+ end
+
+ def resolve_winrm_kerberos_options
+ kerberos_opts = {}
+ kerberos_opts[:keytab] = locate_config_value(:kerberos_keytab_file) if locate_config_value(:kerberos_keytab_file)
+ kerberos_opts[:realm] = locate_config_value(:kerberos_realm) if locate_config_value(:kerberos_realm)
+ kerberos_opts[:service] = locate_config_value(:kerberos_service) if locate_config_value(:kerberos_service)
+ kerberos_opts
+ end
+
+ def resolve_winrm_transport
+ transport = locate_config_value(:winrm_transport).to_sym
+ if config.any? {|k,v| k.to_s =~ /kerberos/ && !v.nil? }
+ transport = :kerberos
+ elsif Chef::Platform.windows? && transport != :ssl && negotiate_auth?
+ transport = :sspinegotiate
+ end
+
+ transport
+ end
+
+ def resolve_no_ssl_peer_verification
+ locate_config_value(:ca_trust_file).nil? && config[:winrm_ssl_verify_mode] == :verify_none && resolve_winrm_transport == :ssl
+ end
+
+ def resolve_winrm_disable_sspi
+ !Chef::Platform.windows? || resolve_winrm_transport == :ssl || !negotiate_auth?
+ end
+
+ def get_password
+ @password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
+ end
+
+ def negotiate_auth?
+ locate_config_value(:winrm_authentication_protocol) == "negotiate"
+ end
+
+ def warn_no_ssl_peer_verification
+ if ! @@ssl_warning_given
+ @@ssl_warning_given = true
+ ui.warn(<<-WARN)
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+SSL validation of HTTPS requests for the WinRM transport is disabled. HTTPS WinRM
+connections are still encrypted, but knife is not able to detect forged replies
+or spoofing attacks.
+
+To fix this issue add an entry like this to your knife configuration file:
+
+```
+ # Verify all WinRM HTTPS connections (default, recommended)
+ knife[:winrm_ssl_verify_mode] = :verify_peer
+```
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+WARN
+ end
+ end
+
+ end
+ end
+ end
+ end
+end