Created
October 1, 2015 16:19
-
-
Save hh/0ebb6103fc259c809055 to your computer and use it in GitHub Desktop.
Winrm over SSL reusing rdp certificate
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'chef/provisioning/aws_driver' | |
require 'byebug' | |
current_dir = File.dirname(__FILE__) # to get relative files... encrypted databag | |
with_chef_server "https://api.chef.io/organizations/#{ENV['CHEF_ORG']}", | |
:client_name => Chef::Config[:node_name], | |
:signing_key_filename => Chef::Config[:client_key] | |
setup_winrm_ssl_user_data = <<EOD | |
<powershell> | |
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, $SourceStoreScope | |
$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 | |
</powershell> | |
EOD | |
# Likely NOT going to work on a hardened image: | |
# winrm set winrm/config/service '@{AllowUnencrypted="true"}' | |
# winrm set winrm/config/service/auth '@{Basic="true"}' | |
# Not going to open http winrm | |
# netsh advfirewall firewall add rule name="WinRM 5985" protocol=TCP dir=in localport=5985 action=allow | |
# The manual way to reset a password on boot, from Thom's code | |
# $admin = [adsi]("WinNT://./administrator, user") | |
# $admin.psbase.invoke("SetPassword", "#{ENV['PASSWORD']}") | |
with_machine_options bootstrap_options: { | |
user_data: setup_winrm_ssl_user_data, | |
instance_type: 'c3.2xlarge', | |
subnet_id: 'subnet-6021b417', | |
security_group_ids: [ | |
'sg-689c5a0c', | |
'sg-6f9c5a0b', | |
'sg-0a9a5c6e' | |
] | |
}, | |
:winrm_transport => { #optional | |
'https' => { #required (valid values: 'http', 'https') | |
#:disable_sspi => false, #optional, (default: false) | |
#:basic_auth_only => false, #optional, (default: false) | |
:no_ssl_peer_verification => true #optional, (default: false) | |
} | |
}, | |
aws_tags: { | |
:created_by => 'chris', | |
:please_destroy => 'true' | |
}, | |
transport_address_location: :private_ip, | |
image_id: 'ami-7bc3e04b' # aws-marketplace/CIS Microsoft Windows Server 2012 R2 Benchmark v1.1.0-26bb465c-ce26-4da9-afb8-040b2f8c9a7f-ami-7a88f312.2 | |
# image_id: 'ami-f16970c1' # manual without renaming of host based on IP | |
machine_name = 'base-2012-hardened-6' | |
m = machine "#{machine_name}" do | |
action :allocate | |
end | |
ruby_block "Security Info on #{machine_name}" do | |
block do | |
# wait for the machine to be in a ready state | |
mr=resources(machine: machine_name).provider_for_action(:ready) | |
mr.load_current_resource | |
machine=mr.action_ready | |
##### during action_ready,we should NOT verify the ssl cert in wait_for_transport | |
# grab a pointer to the chef-provisioning driver | |
# so we can call driver.config and driver.ec2.* | |
driver = node.run_state[:chef_provisioning].drivers.values.first | |
i=driver.ec2.instances[machine.machine_spec.reference['instance_id']] | |
console_lines=i.console_output.lines | |
console_lines.each do |l| | |
Chef::Log.warn l.chomp | |
end | |
machine.machine_spec.reference.pretty_inspect.lines.each do |l| | |
Chef::Log.warn l.chomp | |
end | |
pem = Cheffish.get_private_key(machine.machine_spec.reference['key_name'], | |
driver.config) | |
private_key = OpenSSL::PKey::RSA.new(pem) | |
encrypted_admin_password = driver.wait_for_admin_password(machine.machine_spec) | |
decoded = Base64.decode64(encrypted_admin_password) | |
decrypted_password = private_key.private_decrypt decoded | |
# We connect just to retrieve the ssl certificate and compare it to what we | |
# see in the console logs | |
noverify_peer_context = OpenSSL::SSL::SSLContext.new | |
noverify_peer_context.verify_mode = OpenSSL::SSL::VERIFY_NONE | |
tcp_connection = TCPSocket.new(i.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 | |
ah = Chef::Provisioning::ChefProviderActionHandler.new(mr) | |
machine.machine_spec.reference['winrm_ssl_subject']=winrm_subject | |
machine.machine_spec.reference['winrm_ssl_thumbprint']=winrm_thumbprint | |
machine.machine_spec.reference['winrm_ssl_cert']=winrm_cert.to_pem | |
machine.machine_spec.reference['winrm_encrypted_password']=encrypted_admin_password | |
machine.machine_spec.save(ah) | |
if rdp_subject != winrm_subject or rdp_thumbprint != winrm_thumbprint | |
Chef::Log.fatal "Winrm ssl port certificate differs from rdp" | |
end | |
FileUtils.mkdir_p(Chef::Config.trusted_certs_dir) | |
filename = File.join(Chef::Config.trusted_certs_dir, "#{machine_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(winrm_cert.to_pem) | |
end | |
end | |
# $ openssl s_client -connect 10.113.70.104:5986 -CAfile | |
# .chef/trusted_certs/base-2012-hardened-6.crt < /dev/null 2>/dev/null | | |
# openssl x509 -fingerprint -noout -in /dev/stdin | |
# SHA1 Fingerprint=80:14:A7:E5:F7:31:6F:2E:8D:29:86:6C:5C:F2:CE:34:8F:2F:5B:67 | |
# $ openssl x509 -in .chef/trusted_certs/base-2012-hardened-6.crt -fingerprint -noout | |
# SHA1 Fingerprint=80:14:A7:E5:F7:31:6F:2E:8D:29:86:6C:5C:F2:CE:34:8F:2F:5B:67 | |
# $ knife winrm --winrm-port 5986 --winrm-transport ssl --winrm-password 'FOOBAR' | |
# --ca-trust-file .chef/trusted_certs/base-2012-hardened-6.crt -m 10.113.70.104 hostname | |
# ERROR: Could not establish a secure connection to the server. | |
# Use `knife ssl check` to troubleshoot your SSL configuration. | |
# If your Chef Server uses a self-signed certificate, you can use | |
# `knife ssl fetch` to make knife trust the server's certificates. | |
# Original Exception: OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=error: certificate verify failed | |
Chef::Log.warn "rdesktop -u Administrator -p '#{decrypted_password}' -g 1280x800 #{i.private_ip_address} # fingerprint #{rdp_thumbprint}" | |
Chef::Log.warn "knife winrm --winrm-password '#{decrypted_password}' -m #{i.private_ip_address}" | |
byebug | |
# execution won't work until we configure winrm to actually communicate to the node | |
# The actual connection is made as ::WinRM::WinRMWebService.new(endpoint, type, options) | |
#(byebug) t=::WinRM::WinRMWebService.new(machine.transport.endpoint,machine.transport.type,machine.transport.options) | |
#<WinRM::WinRMWebService:0x0000000c589ec0> | |
#(byebug) machine.transport.options | |
#{:disable_sspi=>false, :basic_auth_only=>false, :no_ssl_peer_verification=>true, :user=>"Administrator", :pass=>"(foo"} | |
# more options: | |
# options[:ca_trust_path] => HTTPClient::SSLConfig#set_trust_ca | |
# https://github.com/chef/chef-provisioning/blob/v1.4.0/lib/chef/provisioning/transport/winrm.rb#L117 | |
# https://github.com/WinRb/WinRM/blob/v1.3.4/lib/winrm/winrm_service.rb#L51 | |
# https://github.com/WinRb/WinRM/blob/v1.3.4/lib/winrm/http/transport.rb#L111 | |
# http://www.rubydoc.info/gems/httpclient/2.1.5.2/HTTPClient/SSLConfig#set_trust_ca-instance_method | |
# trust_ca_file_or_hashed_dir: | |
# a filename of a PEM/DER formatted OpenSSL::X509::Certificate or a 'c-rehash'ed | |
# directory name which stores trusted certificate files. | |
# Chef::Config.trusted_certs_dir | |
# https://github.com/chef/chef/blob/12.4.3/chef-config/lib/chef-config/config.rb#L394 | |
# A directory that contains additional SSL certificates to trust. Any | |
# certificates in this directory will be added to whatever CA bundle ruby | |
# is using. Use this to add self-signed certs for your Chef Server or local | |
# HTTP file servers. | |
# https://github.com/chef/chef/blob/12.4.3/spec/unit/http/ssl_policies_spec.rb#L116 | |
# http_client.cert_store.verify(self_signed_crt) | |
# Chef::Config[:verify_api_cert] = true/false | |
# Chef::HTTP::APISSLPolicy | |
# https://github.com/chef/chef/blob/12.4.3/lib/chef/http/ssl_policies.rb | |
# ^^ all the meat | |
# | |
# set_custom_certs | |
# https://github.com/chef/chef/blob/12.4.3/lib/chef/http/ssl_policies.rb#L74 | |
# add_trusted_cert | |
# https://github.com/chef/chef/blob/12.4.3/lib/chef/http/ssl_policies.rb#L110 | |
# both use: | |
# cert_store[RW] Sets the X509::Store to verify peer certificate. | |
# set_ca_store (:ssl_ca_path) / :ssl_ca_file) => http_client.ca_file/ca_path | |
# https://github.com/chef/chef/blob/12.4.3/lib/chef/http/ssl_policies.rb#L60 | |
# set_custom_certs (loop though trusted_certs_dir and set http_client.cert_store then store.add_cert) | |
# might run into issues precreating objects | |
# https://github.com/chef/chef-provisioning/blob/9da775cd7cf456cd3b0c7777ae49d1ce1335a6f6/lib/chef/provisioning/convergence_strategy/precreate_chef_objects.rb | |
# winrm supports transfer? | |
# https://github.com/WinRb/WinRM/blob/master/lib/winrm/winrm_service.rb#L377 | |
# https://github.com/chef/chef/blob/master/lib/chef/http/basic_client.rb#L164 | |
# https://github.com/chef/chef/blob/master/lib/chef/http/basic_client.rb#L120 | |
# https://github.com/chef/chef/blob/master/lib/chef/http/basic_client.rb#L143 | |
# http_client is a Net::HTTP | |
# http://ruby-doc.org/stdlib-2.2.3/libdoc/net/http/rdoc/Net/HTTP.html | |
# knife bootstraping: | |
# https://github.com/chef/chef/blob/12.4.3/lib/chef/knife/core/bootstrap_context.rb#L85 | |
# @config[:node_ssl_verify_mode] | |
# knife[:ssl_verify_mode] | |
#options[:chunk_size] || 1024 | |
machine.execute_always('dir "cert:\localmachine\Remote Desktop"').stdout.lines.each do |l| | |
Chef::Log.warn l.chomp | |
end | |
end | |
end | |
machine "#{machine_name}" do | |
action :converge | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Line 40 needs to be updated so it doesn't error:
sc.exe config winrm start=auto