-
Notifications
You must be signed in to change notification settings - Fork 0
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
Let's discuss getting winrm ssl to talk to self-signed certs #2
base: damaster
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
#require 'byebug' | ||
require 'pry' | ||
require 'pry-byebug' | ||
require 'chef/mixin/shell_out' | ||
require 'chef/mixin/deep_merge' | ||
require 'chef/provisioning/driver' | ||
|
@@ -583,7 +586,7 @@ def ready_machine(action_handler, machine_spec, machine_options) | |
end | ||
wait_until_ready_machine(action_handler, machine_spec, instance) | ||
end | ||
|
||
1 # byebug | ||
wait_for_transport(action_handler, machine_spec, machine_options) | ||
machine_for(machine_spec, machine_options, instance) | ||
end | ||
|
@@ -713,7 +716,7 @@ def machine_for(machine_spec, machine_options, instance = nil) | |
raise "Instance for node #{machine_spec.name} has not been created!" | ||
end | ||
|
||
if machine_spec.reference['is_windows'] | ||
if machine_spec.reference['is_windows']< | ||
Chef::Provisioning::Machine::WindowsMachine.new(machine_spec, transport_for(machine_spec, machine_options, instance), convergence_strategy_for(machine_spec, machine_options)) | ||
else | ||
Chef::Provisioning::Machine::UnixMachine.new(machine_spec, transport_for(machine_spec, machine_options, instance), convergence_strategy_for(machine_spec, machine_options)) | ||
|
@@ -893,25 +896,133 @@ def default_ami_for_region(region, criteria = {}) | |
|
||
def create_winrm_transport(machine_spec, machine_options, instance) | ||
remote_host = determine_remote_host(machine_spec, instance) | ||
username = machine_spec.reference[:winrm_username] || 'Administrator' | ||
# default to http for now, should upgrade to https when knife support self-signed | ||
transport_type = machine_options[:winrm_transport] || 'http' | ||
type = case transport_type | ||
when 'http' | ||
:plaintext | ||
when 'https' | ||
:ssl | ||
end | ||
if machine_spec.reference[:winrm_port] | ||
port = machine_spec.reference[:winrm_port] | ||
else #default port | ||
port = case transport_type | ||
when 'http' | ||
'5985' | ||
when 'https' | ||
'5986' | ||
end | ||
end | ||
endpoint = "#{transport_type}://#{remote_host}:#{port}/wsman" | ||
|
||
port = machine_spec.reference['winrm_port'] || 5985 | ||
endpoint = "http://#{remote_host}:#{port}/wsman" | ||
type = :plaintext | ||
pem_bytes = get_private_key(instance.key_name) | ||
encrypted_admin_password = wait_for_admin_password(machine_spec) | ||
if machine_options[:password] | ||
password = machine_options[:password] | ||
else # pull from ec2 and store in reference | ||
if machine_spec.reference[:winrm_encrypted_password] | ||
decoded = Base64.decode64(machine_spec.reference[:winrm_encrypted_password]) | ||
else | ||
encrypted_admin_password = wait_for_admin_password(machine_spec) | ||
machine_spec.reference[:winrm_encrypted_password]=encrypted_admin_password | ||
# ^^^ should be saved | ||
decoded = Base64.decode64(encrypted_admin_password) | ||
end | ||
# decrypt so we can utilize | ||
private_key = OpenSSL::PKey::RSA.new(get_private_key(instance.key_name)) | ||
password = private_key.private_decrypt decoded | ||
end | ||
|
||
decoded = Base64.decode64(encrypted_admin_password) | ||
private_key = OpenSSL::PKey::RSA.new(pem_bytes) | ||
decrypted_password = private_key.private_decrypt decoded | ||
disable_sspi = machine_options[:winrm_disable_sspi] || false # default to Negotiate | ||
basic_auth_only = machine_options[:winrm_basic_auth_only] || false # disallow Basic auth by default | ||
no_ssl_peer_verification = machine_options[:winrm_no_ssl_peer_verification] || false #disallow MITM potential by default | ||
|
||
winrm_options = { | ||
:user => machine_spec.reference['winrm_username'] || 'Administrator', | ||
:pass => decrypted_password, | ||
:disable_sspi => true, | ||
:basic_auth_only => true | ||
user: username, | ||
pass: password, | ||
disable_sspi: disable_sspi, | ||
basic_auth_only: basic_auth_only, | ||
no_ssl_peer_verification: no_ssl_peer_verification, | ||
} | ||
|
||
if no_ssl_peer_verification or type != :ssl | ||
# we won't verify certs | ||
elsif machine_spec.reference[:winrm_ssl_cert] | ||
# we have stored the cert | ||
else | ||
# we need to retrieve the cert and verify it by connecting just to | ||
# retrieve the ssl certificate and compare it to what we see in the | ||
# console logs | ||
console_lines = Base64.decode64(instance.console_output.data.output).lines | ||
noverify_peer_context = OpenSSL::SSL::SSLContext.new | ||
noverify_peer_context.verify_mode = OpenSSL::SSL::VERIFY_NONE | ||
tcp_connection = TCPSocket.new(instance.private_ip_address, '5986') | ||
shady_ssl_connection = OpenSSL::SSL::SSLSocket.new(tcp_connection, noverify_peer_context) | ||
shady_ssl_connection.connect | ||
shady_ssl_connection.peer_cert_chain | ||
winrm_cert = shady_ssl_connection.peer_cert_chain.first | ||
|
||
rdp_thumbprint = console_lines.grep( | ||
/RDPCERTIFICATE-THUMBPRINT/)[-1].split(': ').last.chomp | ||
rdp_subject = console_lines.grep( | ||
/RDPCERTIFICATE-SUBJECTNAME/)[-1].split(': ').last.chomp | ||
winrm_subject = winrm_cert.subject.to_s.split('=').last.upcase | ||
winrm_thumbprint=OpenSSL::Digest::SHA1.new(winrm_cert.to_der).to_s.upcase | ||
|
||
if rdp_subject != winrm_subject or rdp_thumbprint != winrm_thumbprint | ||
Chef::Log.fatal "Winrm ssl port certificate differs from rdp console logs" | ||
end | ||
machine_spec.reference[:winrm_ssl_subject]=winrm_subject | ||
machine_spec.reference[:winrm_ssl_thumbprint]=winrm_thumbprint | ||
machine_spec.reference[:winrm_ssl_cert]=winrm_cert.to_pem | ||
end | ||
|
||
# If we have a cert, use it for verification | ||
if machine_spec.reference[:winrm_ssl_cert] | ||
FileUtils.mkdir_p(Chef::Config.trusted_certs_dir) | ||
filename = File.join(Chef::Config.trusted_certs_dir, "#{machine_spec.name}.crt") | ||
if File.exists?(filename) | ||
Chef::Log.warn("Existing cert for #{winrm_subject} in #{filename}") | ||
else | ||
Chef::Log.warn("Adding certificate for #{winrm_subject} in #{filename}") | ||
File.open(filename, File::CREAT|File::TRUNC|File::RDWR, 0644) do |f| | ||
f.print(machine_spec.reference[:winrm_ssl_cert]) | ||
end | ||
end | ||
winrm_options[:ca_trust_path] = filename | ||
end | ||
Chef::Provisioning::Transport::WinRM.new("#{endpoint}", type, winrm_options, {}) | ||
binding.pry | ||
# in order for ssl to work, we need to tell ssl that it's ok that ssl_subject doesn't | ||
# match the ip | ||
#[19] pry(#<Chef::Provisioning::AWSDriver::Driver>)> endpoint.split('/')[2].split(':').first | ||
# => "10.113.70.104" | ||
# [20] pry(#<Chef::Provisioning::AWSDriver::Driver>)> machine_spec.reference[:winrm_ssl_subject] | ||
# => "IP-0A714668" | ||
# | ||
# [1] pry(#<Chef::Provisioning::AWSDriver::Driver>)> Chef::Provisioning::Transport::WinRM.new("#{endpoint}", type, winrm_options, {}).execute('hostname') | ||
# OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=error: certificate verify failed | ||
# from /home/hh/.rvm/gems/ruby-2.2.0/gems/httpclient-2.6.0.1/lib/httpclient/session.rb:307:in `connect' | ||
# [2] pry(#<Chef::Provisioning::AWSDriver::Driver>)> wtf! | ||
# Exception: OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=error: certificate verify failed | ||
# -- | ||
# 0: /home/hh/.rvm/gems/ruby-2.2.0/gems/httpclient-2.6.0.1/lib/httpclient/session.rb:307:in `connect' | ||
# 1: /home/hh/.rvm/gems/ruby-2.2.0/gems/httpclient-2.6.0.1/lib/httpclient/session.rb:307:in `ssl_connect' | ||
# 2: /home/hh/.rvm/gems/ruby-2.2.0/gems/httpclient-2.6.0.1/lib/httpclient/session.rb:755:in `block in connect' | ||
# 3: /home/hh/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/timeout.rb:89:in `block in timeout' | ||
# 4: /home/hh/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/timeout.rb:99:in `call' | ||
# 5: /home/hh/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/timeout.rb:99:in `timeout' | ||
# 6: /home/hh/.rvm/rubies/ruby-2.2.0/lib/ruby/2.2.0/timeout.rb:125:in `timeout' | ||
# 7: /home/hh/.rvm/gems/ruby-2.2.0/gems/httpclient-2.6.0.1/lib/httpclient/session.rb:746:in `connect' | ||
# 8: /home/hh/.rvm/gems/ruby-2.2.0/gems/httpclient-2.6.0.1/lib/httpclient/session.rb:612:in `query' | ||
# 9: /home/hh/.rvm/gems/ruby-2.2.0/gems/httpclient-2.6.0.1/lib/httpclient/session.rb:164:in `query' | ||
# [3] pry(#<Chef::Provisioning::AWSDriver::Driver>)> winrm_options | ||
# => {:user=>"Administrator", | ||
# :pass=>"(xntd8f=-HNnuJ3", | ||
# :disable_sspi=>false, | ||
# :basic_auth_only=>false, | ||
# :no_ssl_peer_verification=>false, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you want There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mwrock I'm trying to duplicate the utility of ssh where we verify the fingerprint of the connection to a particular ip (and verify it). See http://www.rackspace.com/knowledge_center/article/rackspace-cloud-essentials-checking-a-server%E2%80%99s-ssh-host-fingerprint-with-the-web-console I'm looking for a way to explicitly pass self-signed ssl certificates per https connection. The only reason it's failing right now is the Certificate CN doesn't match the ip we attempt to connect to. If we aren't able to actually verify self-signed ssl certs and bind them to a particular winrm target it kinda makes self-signed certs useless unless we regenerate them to match their IP or a dns name that reaches them... which is not easy to do in ec2 user_data. I'm currently using the following: winrm quickconfig -q
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="300"}'
winrm set winrm/config '@{MaxTimeoutms="1800000"}'
netsh advfirewall firewall add rule name="WinRM 5986" protocol=TCP dir=in localport=5986 action=allow
$SourceStoreScope = 'LocalMachine'
$SourceStorename = 'Remote Desktop'
$SourceStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $SourceStorename, $SourceStoreScop\
e
$SourceStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)
$cert = $SourceStore.Certificates | Where-Object -FilterScript {
$_.subject -like '*'
}
$DestStoreScope = 'LocalMachine'
$DestStoreName = 'My'
$DestStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $DestStoreName, $DestStoreScope
$DestStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$DestStore.Add($cert)
$SourceStore.Close()
$DestStore.Close()
winrm create winrm/config/listener?Address=*+Transport=HTTPS `@`{Hostname=`"($certId)`"`;CertificateThumbprint=`"($cert.Thumbprint)`"`}
net stop winrm
sc config winrm start=auto
net start winrm |
||
# :ca_trust_path=>"/home/hh/stratalux/lifelock/misc/provisioning/.chef/trusted_certs/base-2012-hardened-6.crt"} | ||
end | ||
|
||
def wait_for_admin_password(machine_spec) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@adamedx this is where I could use some winrm magic... @mwrock ?