Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WinRM certificate auth #520

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ if ENV["GEMFILE_MOD"]
puts "GEMFILE_MOD: #{ENV["GEMFILE_MOD"]}"
instance_eval(ENV["GEMFILE_MOD"])
else
gem "chef", git: "https://github.com/chef/chef", branch: "main"
gem "ohai", git: "https://github.com/chef/ohai", branch: "main"
gem "knife"
gem "chef", "~> 17"
gem "ohai", "~> 17"
gem "knife", "~> 17"
end

group :test do
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@ transport as follows using the `-t` (or `--winrm-transport`) option with the
knife winrm -m web1.cloudapp.net -t ssl -x "proddomain\webuser" -P "super_secret_password" -f ~/mycert.crt
knife winrm -m db1.cloudapp.net -t ssl -x "localadmin" -P "super_secret_password" ~/mycert.crt

Client certificates can be used for authentication in lieu of username/password credentials:

knife winrm -m web1.cloudapp.net -t ssl --winrm-authentication-protocol cert --winrm-client-cert ~/myclient.crt --winrm-client-key ~/myclient.key -f ~/mycert.crt

### Troubleshooting authentication

Unencrypted traffic with Basic authentication should only be used for low level wire protocol debugging. The configuration for plain text connectivity to
Expand All @@ -323,7 +327,7 @@ authentication; an account local to the remote system must be used.

### Platform WinRM authentication support

`knife-windows` supports `Kerberos`, `Negotiate`, and `Basic` authentication
`knife-windows` supports `Kerberos`, `Negotiate`, `Certificate`, and `Basic` authentication
for WinRM communication.

The following table shows the authentication protocols that can be used with
Expand Down
10 changes: 9 additions & 1 deletion lib/chef/knife/helpers/winrm_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Knife
module WinrmBase

# It includes supported WinRM authentication protocol.
WINRM_AUTH_PROTOCOL_LIST ||= %w{basic negotiate kerberos}.freeze
WINRM_AUTH_PROTOCOL_LIST ||= %w{basic negotiate kerberos cert}.freeze

# :nodoc:
# Would prefer to do this in a rational way, but can't be done b/c of
Expand Down Expand Up @@ -86,6 +86,14 @@ def self.included(includer)
long: "--ca-trust-file CA_TRUST_FILE",
description: "The Certificate Authority (CA) trust file used for SSL transport"

option :winrm_client_cert,
long: "--winrm-client-cert CERT_FILE",
description: "Certificate to use when --winrm-authentication-protocol is set to 'cert'"

option :winrm_client_key,
long: "--winrm-client-key KEY_FILE",
description: "Certificate to use when --winrm-authentication-protocol is set to 'cert'"

option :winrm_ssl_verify_mode,
long: "--winrm-ssl-verify-mode SSL_VERIFY_MODE",
description: "The WinRM peer verification mode. Valid choices are [verify_peer, verify_none]",
Expand Down
21 changes: 17 additions & 4 deletions lib/chef/knife/helpers/winrm_knife_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def extract_nested_value(data, nested_value_spec)

def run_command(command = "")
relay_winrm_command(command)
@exit_code = 0
check_for_errors!
@exit_code
end
Expand Down Expand Up @@ -204,8 +205,6 @@ def resolve_session_options
config[:winrm_port] ||= ( config[:winrm_transport] == "ssl" ) ? "5986" : "5985"

@session_opts = {
user: resolve_winrm_user,
password: config[:winrm_password],
port: config[:winrm_port],
operation_timeout: resolve_winrm_session_timeout,
basic_auth_only: resolve_winrm_basic_auth,
Expand All @@ -217,8 +216,18 @@ def resolve_session_options
codepage: config[:winrm_codepage],
}

if @session_opts[:user] && (not @session_opts[:password])
@session_opts[:password] = config[:winrm_password] = get_password
if cert_auth?
@session_opts[:winrm_client_cert] = config[:winrm_client_cert] if config[:winrm_client_cert]
@session_opts[:winrm_client_key] = config[:winrm_client_key] if config[:winrm_client_key]
@session_opts[:transport] = :ssl
@session_opts.delete(:user)
@session_opts.delete(:password)
else
@session_opts[:user] = resolve_winrm_user
@session_opts[:password] = config[:winrm_password]
if @session_opts[:user] && (not @session_opts[:password])
@session_opts[:password] = config[:winrm_password] = get_password
end
end

if @session_opts[:transport] == :kerberos
Expand Down Expand Up @@ -287,6 +296,10 @@ def get_password
@password ||= ui.ask("Enter your password: ", echo: false)
end

def cert_auth?
config[:winrm_authentication_protocol] == "cert"
end

def negotiate_auth?
config[:winrm_authentication_protocol] == "negotiate"
end
Expand Down
21 changes: 17 additions & 4 deletions lib/chef/knife/helpers/winrm_session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,18 @@ def initialize(options)
@user = options[:user]
@shell_args = [ options[:shell] ]
@shell_args << { codepage: options[:codepage] } if options[:shell] == :cmd
url = "#{options[:host]}:#{options[:port]}/wsman"
scheme = options[:transport] == :ssl ? "https" : "http"
if options[:transport] == :ssl
scheme = "https"
@port ||= 5986
else
scheme = "http"
@port ||= 5985
end
url = "#{@host}:#{@port}/wsman"
@endpoint = "#{scheme}://#{url}"

opts = {}
opts = {
user: @user,
password: options[:password],
basic_auth_only: options[:basic_auth_only],
disable_sspi: options[:disable_sspi],
no_ssl_peer_verification: options[:no_ssl_peer_verification],
Expand All @@ -50,6 +54,15 @@ def initialize(options)
}
options[:transport] == :kerberos ? opts.merge!({ service: options[:service], realm: options[:realm] }) : opts.merge!({ ca_trust_path: options[:ca_trust_path] })
opts[:operation_timeout] = options[:operation_timeout] if options[:operation_timeout]
if options[:winrm_client_cert] && options[:winrm_client_key]
opts[:client_cert] = options[:winrm_client_cert]
opts[:client_key] = options[:winrm_client_key]
opts.delete(:user)
opts.delete(:password)
else
opts[:user] = @user
opts[:password] = options[:password]
end
Chef::Log.debug("WinRM::WinRMWebService options: #{opts}")
Chef::Log.debug("Endpoint: #{endpoint}")
Chef::Log.debug("Transport: #{options[:transport]}")
Expand Down