diff options
author | Mike Gabriel <mike.gabriel@das-netzwerkteam.de> | 2022-02-15 21:52:01 +0100 |
---|---|---|
committer | Mike Gabriel <mike.gabriel@das-netzwerkteam.de> | 2022-02-15 21:52:01 +0100 |
commit | 77301e8a2accf45c1e9cd55b60e9caf720a20155 (patch) | |
tree | a63b8bd07590b0f0b3655f851ed3b1f65c5d2241 /code/environments/production/modules/certregen/spec/integration | |
parent | 40236de30e742094fa7e8fbaaac34995121f6466 (diff) | |
download | puppet.FWSECK-master.tar.gz puppet.FWSECK-master.tar.bz2 puppet.FWSECK-master.zip |
Diffstat (limited to 'code/environments/production/modules/certregen/spec/integration')
4 files changed, 311 insertions, 0 deletions
diff --git a/code/environments/production/modules/certregen/spec/integration/puppet/face/certregen_spec.rb b/code/environments/production/modules/certregen/spec/integration/puppet/face/certregen_spec.rb new file mode 100644 index 0000000..342aa5a --- /dev/null +++ b/code/environments/production/modules/certregen/spec/integration/puppet/face/certregen_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper' +require 'puppet/face/certregen' + +describe Puppet::Face[:certregen, :current] do + before(:each) do + allow(Puppet::SSL::CertificateAuthority).to receive(:instance) { Puppet::SSL::CertificateAuthority.new } + end + + include_context "Initialize CA" + + describe "ca action" do + it "invokes the cacert and crl actions" do + expect(described_class).to receive(:cacert).with(ca_serial: "01") + expect(described_class).to receive(:crl) + described_class.ca(ca_serial: "01") + end + end + + describe "cacert action" do + it "raises an error when the ca_serial option is not provided" do + expect { + described_class.ca + }.to raise_error(RuntimeError, /The serial number of the CA certificate to rotate must be provided/) + end + + it "raises an error when the ca_serial option is not provided" do + expect { + described_class.ca(ca_serial: "02") + }.to raise_error(RuntimeError, /The serial number of the current CA certificate \(01\) does not match the serial number/) + end + + it "backs up the old CA cert and regenerates a new CA cert" do + old_cacert_serial = Puppet::SSL::CertificateAuthority.new.host.certificate.content.serial + described_class.ca(ca_serial: "01") + new_cacert_serial = Puppet::SSL::CertificateAuthority.new.host.certificate.content.serial + expect(old_cacert_serial).to_not eq(new_cacert_serial) + end + + it "returns the new CA certificate" do + returned_cacert = described_class.ca(ca_serial: "01").first + new_cacert = Puppet::SSL::CertificateAuthority.new.host.certificate.content + expect(returned_cacert.content.serial).to eq new_cacert.serial + expect(returned_cacert.content.not_after).to eq new_cacert.not_after + end + end + + describe 'healthcheck action' do + let(:not_before) { Time.now - (60 * 60 * 24 * 365 * 4) } + let(:not_after) { Time.now + (60 * 60 * 24 * 30) } + it 'warns about expiring CA certificates' do + ca = Puppet::SSL::CertificateAuthority.new + cert = backdate_certificate(ca, ca.host.certificate, not_before, not_after) + Puppet::SSL::Certificate.indirection.save(cert) + + allow(PuppetX::Certregen::CA).to receive(:setup).and_return Puppet::SSL::CertificateAuthority.new + healthchecked = described_class.healthcheck + expect(healthchecked.size).to eq(1) + expect(healthchecked.first.digest.to_s).to eq(cert.digest.to_s) + end + + it 'warns about expiring client certificates' do + cert = make_certificate("expiring", not_before, not_after) + Puppet::SSL::Certificate.indirection.save(cert) + + healthchecked = described_class.healthcheck + expect(healthchecked.size).to eq(1) + expect(healthchecked.first.digest.to_s).to eq(cert.digest.to_s) + end + + it 'orders certificates from shortest expiry to longest expiry' do + Puppet::SSL::Certificate.indirection.save(make_certificate("first", not_before, not_after)) + Puppet::SSL::Certificate.indirection.save(make_certificate("last", not_before + 1, not_after + 1)) + + expect(described_class.healthcheck.map(&:name)).to eq %w[first last] + end + end +end diff --git a/code/environments/production/modules/certregen/spec/integration/puppet_x/certregen/ca_spec.rb b/code/environments/production/modules/certregen/spec/integration/puppet_x/certregen/ca_spec.rb new file mode 100644 index 0000000..bb77a7d --- /dev/null +++ b/code/environments/production/modules/certregen/spec/integration/puppet_x/certregen/ca_spec.rb @@ -0,0 +1,88 @@ +require 'spec_helper' +require 'puppet_x/certregen/ca' + +RSpec.describe PuppetX::Certregen::CA do + + include_context "Initialize CA" + + describe "#setup" do + it "errors out when the node is not a CA" do + Puppet[:ca] = false + expect { + described_class.setup + }.to raise_error(RuntimeError, "Unable to set up CA: this node is not a CA server.") + end + + it "errors out when the node does not have a signed CA certificate" do + FileUtils.rm(Puppet[:cacert]) + expect { + described_class.setup + }.to raise_error(RuntimeError, "Unable to set up CA: the CA certificate is not present.") + end + end + + describe '#sign' do + let(:ca) { double('ca') } + + it 'uses the positional argument form when the Puppet version predates 4.6.0' do + stub_const('Puppet::PUPPETVERSION', '4.5.0') + expect(ca).to receive(:sign).with('hello', false, true) + described_class.sign(ca, 'hello', allow_dns_alt_names: false, self_signing_csr: true) + end + + it 'uses the hash argument form when the Puppet version is 4.6.0 or greater' do + stub_const('Puppet::PUPPETVERSION', '4.8.0') + expect(ca).to receive(:sign).with('hello', allow_dns_alt_names: false, self_signing_csr: false) + described_class.sign(ca, 'hello', allow_dns_alt_names: false, self_signing_csr: false) + end + end + + describe '#backup_cacert' do + it 'backs up the CA cert based on the current timestamp' do + now = Time.now + expect(Time).to receive(:now).at_least(:once).and_return now + described_class.backup + backup = File.join(Puppet[:cadir], "ca_crt.#{Time.now.to_i}.pem") + expect(File.read(backup)).to eq(File.read(Puppet[:cacert])) + end + end + + describe '#regenerate_cacert' do + it 'generates a certificate with a different serial number' do + old_serial = Puppet::SSL::CertificateAuthority.new.host.certificate.content.serial + described_class.regenerate(Puppet::SSL::CertificateAuthority.new) + new_serial = Puppet::SSL::Certificate.indirection.find("ca").content.serial + expect(old_serial).to_not eq new_serial + end + + before do + Puppet[:ca_name] = 'bar' + described_class.regenerate(Puppet::SSL::CertificateAuthority.new) + end + + it 'copies the old subject CN to the new certificate' do + new_cacert = Puppet::SSL::Certificate.indirection.find("ca") + expect(new_cacert.content.subject.to_a[0][1]).to eq 'Puppet CA: foo' + end + + it "matches the issuer field with the old CA and new CA" do + new_cacert = Puppet::SSL::Certificate.indirection.find("ca") + expect(new_cacert.content.issuer.to_a[0][1]).to eq 'Puppet CA: foo' + end + + it "matches the Authority Key Identifier field with the old CA and new CA" do + new_cacert = Puppet::SSL::Certificate.indirection.find("ca") + aki = new_cacert.content.extensions.find { |ext| ext.oid == 'authorityKeyIdentifier' } + expect(aki.value).to match(/Puppet CA: foo/) + end + + it 'copies the cacert to the localcacert' do + described_class.regenerate(Puppet::SSL::CertificateAuthority.new) + cacert = Puppet::SSL::Certificate.from_instance( + OpenSSL::X509::Certificate.new(File.read(Puppet[:cacert]))) + localcacert = Puppet::SSL::Certificate.from_instance( + OpenSSL::X509::Certificate.new(File.read(Puppet[:localcacert]))) + expect(cacert.content.serial).to eq localcacert.content.serial + end + end +end diff --git a/code/environments/production/modules/certregen/spec/integration/puppet_x/certregen/certificate_spec.rb b/code/environments/production/modules/certregen/spec/integration/puppet_x/certregen/certificate_spec.rb new file mode 100644 index 0000000..e60a11b --- /dev/null +++ b/code/environments/production/modules/certregen/spec/integration/puppet_x/certregen/certificate_spec.rb @@ -0,0 +1,92 @@ +require 'spec_helper' +require 'puppet_x/certregen/certificate' + +RSpec.describe PuppetX::Certregen::Certificate do + include_context "Initialize CA" + + let(:ok_certificate) do + Puppet::SSL::CertificateAuthority.new.generate("ok") + end + + let(:expired_certificate) do + one_year = 60 * 60 * 24 * 365 + not_before = Time.now - one_year * 6 + not_after = Time.now - one_year + make_certificate("expired", not_before, not_after) + end + + let(:expiring_certificate) do + not_before = Time.now - (60 * 60 * 24 * 365 * 4) + not_after = Time.now + (60 * 60 * 24 * 30) + make_certificate("expiring", not_before, not_after) + end + + let(:short_lived_certificate) do + not_before = Time.now - 86400 + not_after = Time.now + (60 * 5) + make_certificate("expiring", not_before, not_after) + end + + describe "#expiring?" do + it "is false for nodes outside of the expiration window" do + expect(described_class.expiring?(ok_certificate)).to eq(false) + end + + it "is true for newly generated short lived certificates" do + expect(described_class.expiring?(short_lived_certificate)).to eq(false) + end + + it "is true for expired nodes" do + expect(described_class.expiring?(expired_certificate)).to eq(true) + end + + it "is true for nodes within the expiration window" do + expect(described_class.expiring?(expiring_certificate)).to eq(true) + end + end + + describe '#expiry' do + describe "with an expired cert" do + subject { described_class.expiry(expired_certificate) } + it "has a status of expired" do + expect(subject[:status]).to eq :expired + end + + it "includes the not after date" do + expect(subject[:expiration_date]).to eq expired_certificate.content.not_after + end + end + + describe "with an expiring cert" do + subject { described_class.expiry(expiring_certificate) } + + it "has a status of expiring" do + expect(subject[:status]).to eq :expiring + end + + it "includes the not after date" do + expect(subject[:expiration_date]).to eq expiring_certificate.content.not_after + end + + it "includes the time till expiration" do + expect(subject[:expires_in]).to match(/29 days, 23 hours, 59 minutes/) + end + end + + describe "with an ok cert" do + subject { described_class.expiry(ok_certificate) } + + it "has a status of ok" do + expect(subject[:status]).to eq :ok + end + + it "includes the not after date" do + expect(subject[:expiration_date]).to eq ok_certificate.content.not_after + end + + it "includes the time till expiration" do + expect(subject[:expires_in]).to match(/4 years, 364 days, 23 hours, 59 minutes/) + end + end + end +end diff --git a/code/environments/production/modules/certregen/spec/integration/puppet_x/crl_spec.rb b/code/environments/production/modules/certregen/spec/integration/puppet_x/crl_spec.rb new file mode 100644 index 0000000..3d50cfc --- /dev/null +++ b/code/environments/production/modules/certregen/spec/integration/puppet_x/crl_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' +require 'puppet_x/certregen/crl' + +RSpec.describe PuppetX::Certregen::CRL do + include_context "Initialize CA" + + describe '.refresh' do + def normalize_time(t) + t.utc.round + end + + let(:stub_time) { normalize_time(Time.now + 60 * 60 * 24 * 365) } + let(:oldcrl) { @oldcrl } + + before do + @oldcrl = Puppet::SSL::CertificateRevocationList.indirection.find("ca") + allow(Time).to receive(:now).and_return stub_time + described_class.refresh(Puppet::SSL::CertificateAuthority.new) + end + + subject { Puppet::SSL::CertificateRevocationList.indirection.find('ca') } + + it 'updates the lastUpdate field' do + last_update = normalize_time(subject.content.last_update.utc) + expect(last_update).to eq normalize_time(stub_time - 1) + end + + it 'updates the nextUpdate field' do + next_update = normalize_time(subject.content.next_update.utc) + expect(next_update).to eq normalize_time(stub_time + described_class::FIVE_YEARS) + end + + def crl_number(crl) + crl.content.extensions.find { |ext| ext.oid == 'crlNumber' }.value + end + + it "increments the CRL number" do + newcrl = Puppet::SSL::CertificateRevocationList.from_instance( + OpenSSL::X509::CRL.new(File.read(Puppet[:cacrl])), 'ca') + + old_crl_number = crl_number(oldcrl).to_i + new_crl_number = crl_number(newcrl).to_i + expect(new_crl_number).to eq old_crl_number + 1 + end + + it 'copies the cacrl to the hostcrl' do + cacrl = Puppet::SSL::CertificateRevocationList.from_instance( + OpenSSL::X509::CRL.new(File.read(Puppet[:cacrl])), 'ca') + hostcrl = Puppet::SSL::CertificateRevocationList.from_instance( + OpenSSL::X509::CRL.new(File.read(Puppet[:hostcrl])), 'ca') + expect(crl_number(cacrl)).to eq crl_number(hostcrl) + end + end +end |