Skip to content

Instantly share code, notes, and snippets.

@hh
Created October 1, 2015 16:19
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hh/0ebb6103fc259c809055 to your computer and use it in GitHub Desktop.
Save hh/0ebb6103fc259c809055 to your computer and use it in GitHub Desktop.
Winrm over SSL reusing rdp certificate
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
@shotah
Copy link

shotah commented Nov 28, 2016

On line 37 I see an undefined variable $certId. May need to be corrected.

@Chef-Mike
Copy link

Line 40 needs to be updated so it doesn't error:
sc.exe config winrm start=auto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment