summaryrefslogtreecommitdiff
path: root/code/environments/production/modules/certregen/spec/integration
diff options
context:
space:
mode:
authorMike Gabriel <mike.gabriel@das-netzwerkteam.de>2022-02-15 21:52:01 +0100
committerMike Gabriel <mike.gabriel@das-netzwerkteam.de>2022-02-15 21:52:01 +0100
commit77301e8a2accf45c1e9cd55b60e9caf720a20155 (patch)
treea63b8bd07590b0f0b3655f851ed3b1f65c5d2241 /code/environments/production/modules/certregen/spec/integration
parent40236de30e742094fa7e8fbaaac34995121f6466 (diff)
downloadpuppet.FWSECK-master.tar.gz
puppet.FWSECK-master.tar.bz2
puppet.FWSECK-master.zip
modules/: Add module certregen.HEADmaster
Diffstat (limited to 'code/environments/production/modules/certregen/spec/integration')
-rw-r--r--code/environments/production/modules/certregen/spec/integration/puppet/face/certregen_spec.rb77
-rw-r--r--code/environments/production/modules/certregen/spec/integration/puppet_x/certregen/ca_spec.rb88
-rw-r--r--code/environments/production/modules/certregen/spec/integration/puppet_x/certregen/certificate_spec.rb92
-rw-r--r--code/environments/production/modules/certregen/spec/integration/puppet_x/crl_spec.rb54
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