summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn McCrae <john.mccrae@progress.com>2022-03-23 13:17:44 -0700
committerJohn McCrae <john.mccrae@progress.com>2022-03-23 13:17:44 -0700
commit25545606b7990b713dd8072e8cf3403f9a14a62a (patch)
treed1ae9d5c8eb25f2a1cbc2b0cbac30fac518c84fb
parent7621812b7e829fd53ad65430c61cb01a572d623b (diff)
downloadchef-25545606b7990b713dd8072e8cf3403f9a14a62a.tar.gz
updating gemlock files and updating code from feedback
Signed-off-by: John McCrae <john.mccrae@progress.com>
-rw-r--r--Gemfile.lock40
-rw-r--r--lib/chef/client.rb110
-rw-r--r--lib/chef/http/authenticator.rb10
-rw-r--r--spec/unit/client_spec.rb13
-rw-r--r--spec/unit/http/authenticator_spec.rb22
5 files changed, 91 insertions, 104 deletions
diff --git a/Gemfile.lock b/Gemfile.lock
index d937910dfd..175c6de329 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -8,10 +8,10 @@ GIT
GIT
remote: https://github.com/chef/ohai.git
- revision: 996b52b8017f1378e4c29b4faeb9ed90db1e9579
+ revision: db6480f47f37954b9d3ac1fbb7837fbdb474646a
branch: main
specs:
- ohai (18.0.8)
+ ohai (18.0.9)
chef-config (>= 14.12, < 19)
chef-utils (>= 16.0, < 19)
ffi (~> 1.9)
@@ -188,7 +188,7 @@ GEM
chef-zero (>= 14.0)
net-ssh
coderay (1.1.3)
- concurrent-ruby (1.1.9)
+ concurrent-ruby (1.1.10)
corefoundation (0.3.13)
ffi (>= 1.15.0)
crack (0.4.5)
@@ -240,7 +240,7 @@ GEM
domain_name (~> 0.5)
httpclient (2.8.3)
iniparse (1.5.0)
- inspec-core (4.52.9)
+ inspec-core (4.56.17)
addressable (~> 2.4)
chef-telemetry (~> 1.0, >= 1.0.8)
faraday (>= 0.9.0, < 1.5)
@@ -253,7 +253,7 @@ GEM
parallel (~> 1.9)
parslet (>= 1.5, < 2.0)
pry (~> 0.13)
- rspec (>= 3.9, < 3.11)
+ rspec (>= 3.9, <= 3.11)
rspec-its (~> 1.2)
rubyzip (>= 1.2.2, < 3.0)
semverse (~> 3.0)
@@ -263,8 +263,8 @@ GEM
train-core (~> 3.0)
tty-prompt (~> 0.17)
tty-table (~> 0.10)
- inspec-core-bin (4.52.9)
- inspec-core (= 4.52.9)
+ inspec-core-bin (4.56.17)
+ inspec-core (= 4.56.17)
io-wait (0.2.1)
ipaddress (0.8.3)
iso8601 (0.13.0)
@@ -315,7 +315,7 @@ GEM
net-ssh (6.1.0)
netrc (0.11.0)
nori (2.6.0)
- parallel (1.21.0)
+ parallel (1.22.0)
parser (3.1.1.0)
ast (~> 2.4.1)
parslet (1.8.2)
@@ -356,22 +356,22 @@ GEM
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
rexml (3.2.5)
- rspec (3.10.0)
- rspec-core (~> 3.10.0)
- rspec-expectations (~> 3.10.0)
- rspec-mocks (~> 3.10.0)
- rspec-core (3.10.2)
- rspec-support (~> 3.10.0)
- rspec-expectations (3.10.2)
+ rspec (3.11.0)
+ rspec-core (~> 3.11.0)
+ rspec-expectations (~> 3.11.0)
+ rspec-mocks (~> 3.11.0)
+ rspec-core (3.11.0)
+ rspec-support (~> 3.11.0)
+ rspec-expectations (3.11.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.10.0)
+ rspec-support (~> 3.11.0)
rspec-its (1.3.0)
rspec-core (>= 3.0.0)
rspec-expectations (>= 3.0.0)
- rspec-mocks (3.10.3)
+ rspec-mocks (3.11.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.10.0)
- rspec-support (3.10.3)
+ rspec-support (~> 3.11.0)
+ rspec-support (3.11.0)
rubocop (1.25.1)
parallel (~> 1.10)
parser (>= 3.1.0.0)
@@ -401,7 +401,7 @@ GEM
date
timeout (0.2.0)
tomlrb (1.3.0)
- train-core (3.8.7)
+ train-core (3.8.9)
addressable (~> 2.5)
ffi (!= 1.13.0)
json (>= 1.8, < 3.0)
diff --git a/lib/chef/client.rb b/lib/chef/client.rb
index c3b62a60c3..d50b8c6704 100644
--- a/lib/chef/client.rb
+++ b/lib/chef/client.rb
@@ -65,15 +65,7 @@ class Chef
# The main object in a Chef run. Preps a Chef::Node and Chef::RunContext,
# syncs cookbooks if necessary, and triggers convergence.
class Client
- class KeyMigration
- include Singleton
- attr_accessor :key_migrated
- attr_accessor :old_priv_key
- def initialize
- @key_migrated = false
- @old_priv_key = nil
- end
- end
+ CRYPT_EXPORTABLE = 0x00000001
attr_reader :local_context
@@ -661,8 +653,8 @@ class Chef
if result.rassoc("#{cert_name}")
logger.trace("Client key #{config[:client_key]} is present in Certificate Store - skipping registration")
else
- move_key_and_register(cert_name)
- logger.trace("Client key #{config[:client_key]} moved to the Certificate Store - skipping registration")
+ create_new_key_and_register(cert_name)
+ logger.trace("New client keys created in the Certificate Store - skipping registration")
end
events.skipping_registration(client_name, config)
elsif File.exists?(config[:client_key])
@@ -683,7 +675,6 @@ class Chef
raise
end
- #
# In the brave new world of No Certs On Disk, we want to put the pem file into Keychain or the Certstore
# But is it already there?
def check_certstore_for_key(cert_name)
@@ -692,35 +683,47 @@ class Chef
win32certstore.search("#{cert_name}")
end
- def generate_pfx_package(cert_name, date = nil)
- require_relative "mixin/powershell_exec"
- extend Chef::Mixin::PowershellExec
- ::Chef::HTTP::Authenticator.get_cert_password
- powershell_code = <<~EOH
-
- $date = "#{date}"
-
- $certSplat = @{
- Subject = "#{cert_name}"
- KeyExportPolicy = 'Exportable'
- KeyUsage = @('KeyEncipherment','DigitalSignature')
- CertStoreLocation = 'Cert:\\LocalMachine\\My'
- TextExtension = @("2.5.29.37={text}1.3.6.1.5.5.7.3.2,1.3.6.1.5.5.7.3.1")
- };
- if ([string]$date -as [DateTime]){
- $certSplat.add('NotAfter', $date)
- }
-
- New-SelfSignedCertificate @certSplat;
- EOH
- powershell_exec!(powershell_code)
+ def generate_pfx_package(cert_name, date)
+ self.class.generate_pfx_package(cert_name, date)
+ end
+
+ def self.generate_pfx_package(cert_name, date)
+ require "openssl"
+
+ key = OpenSSL::PKey::RSA.new(2048)
+ public_key = key.public_key
+
+ subject = "CN=#{cert_name}"
+
+ cert = OpenSSL::X509::Certificate.new
+ cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
+ cert.not_before = Time.now
+ cert.not_after = Time.parse(date)
+ cert.public_key = public_key
+ cert.serial = 0x0
+ cert.version = 2
+
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ef.subject_certificate = cert
+ ef.issuer_certificate = cert
+ cert.extensions = [
+ ef.create_extension("subjectKeyIdentifier", "hash"),
+ ef.create_extension("keyUsage", "digitalSignature,keyEncipherment", true),
+ ]
+ cert.add_extension(ef.create_ext_from_string("extendedKeyUsage=critical,serverAuth,clientAuth"))
+
+ cert.sign key, OpenSSL::Digest::SHA256.new
+ password = ::Chef::HTTP::Authenticator.get_cert_password
+ pfx = OpenSSL::PKCS12.create(password, subject, key, cert)
+ pfx
end
- def move_key_and_register(cert_name)
+ def create_new_key_and_register(cert_name)
+ require "pry"
require "time" unless defined?(Time)
autoload :URI, "uri"
- KeyMigration.instance.key_migrated = true
+ # KeyMigration.instance.key_migrated = true
node = Chef::Config[:node_name]
d = Time.now
@@ -733,31 +736,29 @@ class Chef
expiration_date: end_date,
}
- generate_pfx_package(cert_name, end_date)
- payload[:public_key] = get_public_key(cert_name)
+ new_pfx = generate_pfx_package(cert_name, end_date)
+ payload[:public_key] = new_pfx.certificate.public_key.to_pem
base_url = "#{Chef::Config[:chef_server_url]}"
client = Chef::ServerAPI.new(base_url, client_name: Chef::Config[:validation_client_name], signing_key_filename: Chef::Config[:validation_key])
client.post(base_url + "/clients", payload)
- KeyMigration.instance.key_migrated = false
+ # KeyMigration.instance.key_migrated = false
Chef::Log.trace("Updated client data: #{client.inspect}")
+ import_pfx_to_store(new_pfx)
+ end
+
+ def import_pfx_to_store(new_pfx)
+ self.class.import_pfx_to_store(new_pfx)
end
- def get_public_key(cert_name)
+ def self.import_pfx_to_store(new_pfx)
password = ::Chef::HTTP::Authenticator.get_cert_password
- require_relative "mixin/powershell_exec"
- extend Chef::Mixin::PowershellExec
- powershell_code = <<~EOH
- $my_pwd = ConvertTo-SecureString -String "#{password}" -Force -AsPlainText;
- $tempfile = $([System.IO.Path]::GetTempPath()) + $([System.IO.Path]::GetRandomFileName());
- $cert = Get-ChildItem -path cert:\\LocalMachine\\My -Recurse | Where-Object { $_.Subject -match "#{cert_name}$" } -ErrorAction Stop;
- Export-PFXCertificate -Cert $cert -Password $my_pwd -FilePath $tempfile;
- return $tempfile;
- EOH
- cert_file = powershell_exec!(powershell_code).result
- path = cert_file[1]
- p12 = OpenSSL::PKCS12.new(File.binread(path), password)
- File.delete(path)
- p12.key.public_to_pem
+ require "win32-certstore"
+ tempfile = Tempfile.new("#{Chef::Config[:node_name]}.pfx")
+ File.open(tempfile, "wb") { |f| f.print new_pfx.to_der }
+
+ store = ::Win32::Certstore.open("MY")
+ store.add_pfx(tempfile, password, CRYPT_EXPORTABLE)
+ tempfile.unlink
end
#
@@ -1024,3 +1025,4 @@ end
require_relative "cookbook_loader"
require_relative "cookbook_version"
require_relative "cookbook/synchronizer"
+
diff --git a/lib/chef/http/authenticator.rb b/lib/chef/http/authenticator.rb
index 641599972a..79e462c5fc 100644
--- a/lib/chef/http/authenticator.rb
+++ b/lib/chef/http/authenticator.rb
@@ -119,6 +119,7 @@ class Chef
#
def self.detect_certificate_key(client_name)
if ChefUtils.windows?
+ require "pry"
check_certstore_for_key(client_name)
else # generic return for Mac and LInux clients
false
@@ -141,9 +142,7 @@ class Chef
def load_signing_key(key_file, raw_key = nil)
results = retrieve_certificate_key(Chef::Config[:node_name])
- if ::Chef::Config[:migrate_key_to_keystore] == true && ::Chef::Client::KeyMigration.instance.key_migrated == true
- @raw_key = IO.read(Chef::Config[:validation_key]).strip
- elsif !!results
+ if !!results
@raw_key = results
elsif key_file == nil? && raw_key == nil?
puts "\nNo key detected\n"
@@ -192,7 +191,8 @@ class Chef
unless @win32registry.key_exists?(new_path)
@win32registry.create_key(new_path, true)
end
- password = SOME_CHARS.sample(1 + rand(SOME_CHARS.count)).join[0...14]
+ size = 14
+ password = SOME_CHARS.sample(size).join
encrypted_pass = encrypt_pfx_pass(password)
values = { name: "PfxPass", type: :string, data: encrypted_pass }
@win32registry.set_value(new_path, values)
@@ -247,7 +247,7 @@ class Chef
powershell_code = <<~CODE
Try {
$my_pwd = ConvertTo-SecureString -String "#{password}" -Force -AsPlainText;
- $cert = Get-ChildItem -path cert:\\LocalMachine\\My -Recurse | Where-Object { $_.Subject -match "#{client_name}$" } -ErrorAction Stop;
+ $cert = Get-ChildItem -path cert:\\LocalMachine\\My -Recurse | Where-Object { $_.Subject -match "chef-#{client_name}$" } -ErrorAction Stop;
$tempfile = [System.IO.Path]::GetTempPath() + "export_pfx.pfx";
Export-PfxCertificate -Cert $cert -Password $my_pwd -FilePath $tempfile;
}
diff --git a/spec/unit/client_spec.rb b/spec/unit/client_spec.rb
index 1b8775e0d8..199ca7e9b6 100644
--- a/spec/unit/client_spec.rb
+++ b/spec/unit/client_spec.rb
@@ -291,6 +291,7 @@ describe Chef::Client, :windows_only do
let(:hostname) { "test" }
let(:my_client) { Chef::Client.new }
let(:cert_name) { "chef-#{hostname}" }
+ let(:node_name) { "#{hostname}" }
let(:end_date) do
d = Time.now
end_date = Time.new(d.year, d.month + 3, d.day, d.hour, d.min, d.sec).utc.iso8601
@@ -298,6 +299,7 @@ describe Chef::Client, :windows_only do
# include_context "client"
before(:each) do
Chef::Config[:migrate_key_to_keystore] = true
+ Chef::Config[:node_name] = node_name
end
after(:each) do
@@ -305,19 +307,14 @@ describe Chef::Client, :windows_only do
end
context "when the client intially boots the first time" do
- it "created a new pfx object" do
- expect(my_client.generate_pfx_package(cert_name, end_date)).to be_truthy
- end
-
- it "verfies that a certificate correctly exists in the Cert Store" do
+ it "verfies that a certificate was correctly created and exists in the Cert Store" do
my_client.generate_pfx_package(cert_name, end_date)
expect(my_client.check_certstore_for_key(cert_name)).not_to be false
end
it "correctly returns a new Publc Key" do
- my_client.generate_pfx_package(cert_name, end_date)
- public_key = my_client.get_public_key(cert_name)
- cert_object = OpenSSL::PKey::RSA.new(public_key)
+ new_pfx = my_client.generate_pfx_package(cert_name, end_date)
+ cert_object = new_pfx.certificate.public_key.to_pem
expect(cert_object.to_s).to match(/PUBLIC KEY/)
end
diff --git a/spec/unit/http/authenticator_spec.rb b/spec/unit/http/authenticator_spec.rb
index 0bb81b8ca5..0ce5448d1a 100644
--- a/spec/unit/http/authenticator_spec.rb
+++ b/spec/unit/http/authenticator_spec.rb
@@ -28,14 +28,15 @@ describe Chef::HTTP::Authenticator, :windows_only do
let(:node_name) { "test" }
let(:passwrd) { "some_insecure_password" }
- before(:each) do
- ::Chef::Config[:node_name] = "test"
+ before do
+ Chef::Config[:node_name] = node_name
cert_name = "chef-#{node_name}"
d = Time.now
end_date = Time.new(d.year, d.month + 3, d.day, d.hour, d.min, d.sec).utc.iso8601
my_client = Chef::Client.new
- my_client.generate_pfx_package(cert_name, end_date)
+ pfx = my_client.generate_pfx_package(cert_name, end_date)
+ my_client.import_pfx_to_store(pfx)
end
after(:each) do
@@ -57,25 +58,16 @@ describe Chef::HTTP::Authenticator, :windows_only do
end
it "retrieves a certificate password from the registry when the hive exists" do
- set_registry_hive
+ class_instance.get_cert_password
expect { class_instance.get_cert_password }.not_to raise_error
end
- it "correctly retrieves a private key from the certstore" do
- cert_name = "chef-#{node_name}"
- expect { class_instance.retrieve_certificate_key(cert_name) }.not_to raise_error
- end
-
it "correctly retrieves a valid certificate in pem format from the certstore" do
require "openssl"
certificate = class_instance.retrieve_certificate_key(node_name)
cert_object = OpenSSL::PKey::RSA.new(certificate)
expect(cert_object.to_s).to match(/BEGIN RSA PRIVATE KEY/)
end
-
- # does retrieving a cert work
- # is the password at least 14 characters
- # is the pem a proper cert object
end
def delete_certificate(cert_name)
@@ -93,10 +85,6 @@ describe Chef::HTTP::Authenticator, :windows_only do
@win32registry.delete_key(path, true)
end
end
-
- def set_registry_hive
- class_instance.get_cert_password
- end
end
describe Chef::HTTP::Authenticator do