summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn McCrae <john.mccrae@progress.com>2021-06-14 19:07:33 -0700
committerJohn McCrae <john.mccrae@progress.com>2021-06-14 19:07:33 -0700
commitf3e15a72c1141f863239ef9d3fafbeaff0299945 (patch)
tree4f30af56629bb828023c503f8eb043ecfcc16d6c
parent694ffcf0ce40cad71defb6d1654376364c084350 (diff)
downloadchef-jfm/nocerts.tar.gz
chef client is successfully reading from the windows cert store and attempting to connect to the chef serverjfm/nocerts
Signed-off-by: John McCrae <john.mccrae@progress.com>
-rw-r--r--lib/chef/client.rb74
-rw-r--r--lib/chef/http/authenticator.rb88
2 files changed, 149 insertions, 13 deletions
diff --git a/lib/chef/client.rb b/lib/chef/client.rb
index 3a312701d2..dafbfac797 100644
--- a/lib/chef/client.rb
+++ b/lib/chef/client.rb
@@ -20,6 +20,8 @@
require_relative "config"
require_relative "mixin/params_validate"
+require "chef/mixin/powershell_exec"
+require "chef/mixin/shell_out"
require "chef-utils/dsl/default_paths" unless defined?(ChefUtils::DSL::DefaultPaths)
require_relative "log"
require_relative "deprecated"
@@ -65,6 +67,8 @@ class Chef
# syncs cookbooks if necessary, and triggers convergence.
class Client
extend Chef::Mixin::Deprecation
+ include Chef::Mixin::PowershellExec
+ include Chef::Mixin::ShellOut
extend Forwardable
#
@@ -638,13 +642,21 @@ class Chef
# @api private
#
def register(client_name = node_name, config = Chef::Config)
+ puts "\n cli.rb - This is the client name I am using : #{client_name}"
+ puts "\n cli.rb - This is my client key : #{config[:client_key]}"
if !config[:client_key]
events.skipping_registration(client_name, config)
logger.trace("Client key is unspecified - skipping registration")
- elsif File.exists?(config[:client_key])
+ elsif detect_certificate_key(client_name)
+ events.skipping_registration(client_name, config)
+ logger.trace("Client key #{client_name} is present in certificate repository - skipping registration")
+ puts "\n cli.rb - Did I correctly detect a client key in the certstore or keychain : #{detect_certificate_key(client_name)}"
+ puts "\n cli.rb - Hey, I found an appropriate key in the keychain or Certstore!"
+ elsif File.exist?(config[:client_key])
events.skipping_registration(client_name, config)
logger.trace("Client key #{config[:client_key]} is present - skipping registration")
else
+ puts "\n cli.rb - Did I correctly detect a client key in the certstore or keychain : #{detect_certificate_key(client_name)}"
events.registration_start(node_name, config)
logger.info("Client key #{config[:client_key]} is not present - registering")
Chef::ApiClient::Registration.new(node_name, config[:client_key]).run
@@ -660,6 +672,60 @@ class Chef
end
#
+ # Detects if a private key exists in a certificate repository like Keychain (macOS) or Certificate Store (Windows)
+ #
+ # @param client_name - we're using the node name to store and retrieve any keys
+ # Returns true if a key is found, false if not. False will trigger a registration event which will lead to a certificate based key being created
+ #
+ #
+ def detect_certificate_key(client_name)
+ if ChefUtils.windows?
+ check_certstore_for_key(client_name)
+ elsif ChefUtils.macos?
+ puts "Made it to checking the macos key"
+ check_keychain_for_key(client_name)
+ else # generic return for Linux systemss
+ false
+ end
+ end
+
+ def check_certstore_for_key(client_name)
+ powershell_code = <<~CODE
+ $cert = Get-ChildItem -path cert:\\LocalMachine\\My -Recurse -Force | Where-Object { $_.Subject -Match "#{client_name}" } -ErrorAction Stop
+ if ($cert) {
+ return $true
+ }
+ else{
+ return $false
+ }
+ CODE
+ powershell_exec!(powershell_code).result
+ end
+
+ # below we're checking Keychain for the presence of a cert.
+ # We compare the output we get because the code throws an error (127) if the cert isn't present
+ def check_keychain_for_key(client_name)
+ mine = shell_out! <<~EOH
+ getcert() {
+ CERTSTATUS=$(security find-certificate -c #{client_name} >/dev/null)
+ SUB='#{client_name}'
+ if [[ "$CERTSTATUS" == *"$SUB"* ]]; then
+ true
+ else
+ false
+ fi
+ }
+ getcert
+ getcertresult=$?
+ echo $getcertresult
+ EOH
+
+ # The bash script above checks for a cert in Keychain and returns a 127 if not present, 0 if present.
+ output = mine.run_command
+ output == '0' ? true : false
+ end
+
+ #
# Converges all compiled resources.
#
# Fires the converge_start, converge_complete and converge_failed events.
@@ -863,12 +929,6 @@ class Chef
end
def start_profiling
- if Chef::Config[:slow_report]
- require_relative "handler/slow_report"
-
- Chef::Config.report_handlers << Chef::Handler::SlowReport.new(Chef::Config[:slow_report])
- end
-
return unless Chef::Config[:profile_ruby]
profiling_prereqs!
diff --git a/lib/chef/http/authenticator.rb b/lib/chef/http/authenticator.rb
index 8e45423de9..eda722b255 100644
--- a/lib/chef/http/authenticator.rb
+++ b/lib/chef/http/authenticator.rb
@@ -16,20 +16,19 @@
# limitations under the License.
#
+require "chef/mixin/powershell_exec"
require_relative "auth_credentials"
require_relative "../exceptions"
autoload :OpenSSL, "openssl"
-autoload :ChefUtils, "chef-utils"
-
-require "chef/mixin/powershell_exec"
class Chef
class HTTP
class Authenticator
- include Chef::Mixin::PowershellExec
DEFAULT_SERVER_API_VERSION = "2".freeze
+ include Chef::Mixin::PowershellExec
+
attr_reader :signing_key_filename
attr_reader :raw_key
attr_reader :attr_names
@@ -87,10 +86,42 @@ class Chef
@auth_credentials.client_name
end
+ # def load_signing_key(key_file, raw_key = nil)
+ # if !!key_file
+ # @raw_key = IO.read(key_file).strip
+ # elsif !!raw_key
+ # @raw_key = raw_key.strip
+ # else
+ # return nil
+ # end
+ # # Pass in '' as the passphrase to avoid OpenSSL prompting on the TTY if
+ # # given an encrypted key. This also helps if using a single file for
+ # # both the public and private key with ssh-agent mode.
+ # @key = OpenSSL::PKey::RSA.new(@raw_key, "")
+ # rescue SystemCallError, IOError => e
+ # Chef::Log.warn "Failed to read the private key #{key_file}: #{e.inspect}"
+ # raise Chef::Exceptions::PrivateKeyMissing, "I cannot read #{key_file}, which you told me to use to sign requests!"
+ # rescue OpenSSL::PKey::RSAError
+ # msg = "The file #{key_file} or :raw_key option does not contain a correctly formatted private key or the key is encrypted.\n"
+ # msg << "The key file should begin with '-----BEGIN RSA PRIVATE KEY-----' and end with '-----END RSA PRIVATE KEY-----'"
+ # raise Chef::Exceptions::InvalidPrivateKey, msg
+ # end
+
def load_signing_key(key_file, raw_key = nil)
- puts "This is the key file name I am trying to load : #{@signing_key_filename}"
+ results = retrieve_certificate_key(Chef::Config[:node_name])
+ puts "\n"
+ puts " auth.rb - This is the key file name I am trying to load : #{key_file}"
+ puts " auth.rb - Is there a raw_key name? : #{raw_key ? raw_key : false}"
+ puts " auth.rb - Chef Config thinks this is my node name : #{Chef::Config[:node_name]}"
+ puts " auth.rb - Is the Global @node_name available here? : #{@node_Name ? @node_name : false}"
+ puts "\n"
if key_file == nil? && raw_key == nil?
- puts "No key detected"
+ puts "\nNo key detected\n"
+ elsif results != false
+ # results variable holds 2 values - "False" or the contents of a key.
+ @raw_key = results
+ puts "\n"
+ puts "Hey. I think I got a key back! #{results}"
# first time chef-client runs, generate node-name and burn that to the client.rb
# use that node name to create a p12/pfx on the fly
# store that
@@ -131,6 +162,51 @@ class Chef
raise Chef::Exceptions::InvalidPrivateKey, msg
end
+ def retrieve_certificate_key(client_name)
+ if ChefUtils.windows?
+ check_and_retrieve_certstore_for_key(client_name)
+ elsif ChefUtils.macos?
+ check_keychain_for_key(client_name)
+ else # generic return for Linux systemss
+ false
+ end
+ end
+
+ def check_and_retrieve_certstore_for_key(client_name)
+ # This code block assumes a certificate with a subject name like "Chef-<node-name>" is in the \LocalMachine\My store and
+ # that here is a password stored in the registry to be used to export the pfx with.
+
+ require "openssl" unless defined?(OpenSSL)
+
+ powershell_password_code = <<~CODE
+ Try {
+ Get-ItemPropertyValue -Path "HKLM:\\Software\\Progress\\Authenticator" -Name "PfxPass" -ErrorAction Stop;
+ }
+ Catch {
+ return $false
+ }
+ CODE
+ password = powershell_exec!(powershell_password_code).result
+
+ powershell_code = <<~CODE
+ Try {
+ $pfspass = "#{password}"
+ $my_pwd = ConvertTo-SecureString -String $pfspass -Force -AsPlainText;
+ $cert = Get-ChildItem -path cert:\\LocalMachine\\My -Recurse | Where-Object { $_.Subject -match "#{client_name}" } -ErrorAction Stop;
+ $tempfile = [System.IO.Path]::GetTempPath() + "exportpfx.pfx";
+ Export-PfxCertificate -Cert $cert -Password $my_pwd -FilePath $tempfile;
+ }
+ Catch {
+ return $false
+ }
+ CODE
+ myresult = powershell_exec!(powershell_code).result
+
+ pkcs = OpenSSL::PKCS12.new(File.binread(myresult["PSPath"].split("::")[1]), password)
+ ::File.delete(myresult["PSPath"].split("::")[1])
+ return OpenSSL::PKey::RSA.new(pkcs.key.to_pem)
+ end
+
def authentication_headers(method, url, json_body = nil, headers = nil)
request_params = {
http_method: method,