diff options
author | Julien Huon <julien@huon.email> | 2019-12-25 15:08:10 +0100 |
---|---|---|
committer | Julien Huon <julien@huon.email> | 2019-12-25 15:08:10 +0100 |
commit | afc883e252f6e158323e55892525fd4f7cb15bcc (patch) | |
tree | 3d68bea83aa44037c9e42ba0bac6050db456a232 | |
parent | f5f618321ff524cfe5c8cafdf3bdaca55485cd9b (diff) | |
download | chef-afc883e252f6e158323e55892525fd4f7cb15bcc.tar.gz |
Add the capability to automatically renew a certificate with x509_certificate resource
Signed-off-by: Julien Huon <julien@huon.email>
-rw-r--r-- | lib/chef/mixin/openssl_helper.rb | 24 | ||||
-rw-r--r-- | lib/chef/resource/openssl_x509_certificate.rb | 8 | ||||
-rw-r--r-- | spec/unit/mixin/openssl_helper_spec.rb | 42 |
3 files changed, 72 insertions, 2 deletions
diff --git a/lib/chef/mixin/openssl_helper.rb b/lib/chef/mixin/openssl_helper.rb index 5a4bd6077a..28388453c5 100644 --- a/lib/chef/mixin/openssl_helper.rb +++ b/lib/chef/mixin/openssl_helper.rb @@ -401,6 +401,30 @@ class Chef crl.sign(ca_private_key, ::OpenSSL::Digest::SHA256.new) crl end + + # Return true if a certificate need to be renewed (or doesn't exist) according to the number + # of days before expiration given + # @param [string] cert_file path of the cert file or cert content + # @param [integer] renew_before_expiry number of days before expiration + # @return [true, false] + def cert_need_renewall?(cert_file, renew_before_expiry) + raise TypeError, 'cert_file must be a String object' unless cert_file.is_a?(String) + raise TypeError, 'renew_before_expiry must be a Integer object' unless renew_before_expiry.is_a?(Integer) + + resp = true + cert_content = ::File.exist?(cert_file) ? File.read(cert_file) : cert_file + begin + cert = OpenSSL::X509::Certificate.new cert_content + rescue ::OpenSSL::X509::CertificateError + return resp + end + + unless cert.not_after <= Time.now + 3600 * 24 * renew_before_expiry + resp = false + end + + resp + end end end end diff --git a/lib/chef/resource/openssl_x509_certificate.rb b/lib/chef/resource/openssl_x509_certificate.rb index 20cf998239..354c8c0dab 100644 --- a/lib/chef/resource/openssl_x509_certificate.rb +++ b/lib/chef/resource/openssl_x509_certificate.rb @@ -109,13 +109,17 @@ class Chef property :ca_key_pass, String, description: "The passphrase for CA private key's passphrase." + property :renew_before_expiry, Integer, + description: "The number of days before the expiry. The certificate will be automaticaly renewed when the value is reached.", + default: 5 + action :create do description "Generate a certificate" - unless ::File.exist? new_resource.path + if cert_need_renewall?(new_resource.path, new_resource.renew_before_expiry) converge_by("Create #{@new_resource}") do file new_resource.path do - action :create_if_missing + action :create owner new_resource.owner unless new_resource.owner.nil? group new_resource.group unless new_resource.group.nil? mode new_resource.mode unless new_resource.mode.nil? diff --git a/spec/unit/mixin/openssl_helper_spec.rb b/spec/unit/mixin/openssl_helper_spec.rb index a6a6fb0e71..716d2cf3d3 100644 --- a/spec/unit/mixin/openssl_helper_spec.rb +++ b/spec/unit/mixin/openssl_helper_spec.rb @@ -854,4 +854,46 @@ describe Chef::Mixin::OpenSSLHelper do end end end + + describe '#cert_need_renewall?' do + require 'tempfile' + + before(:each) do + @certfile = Tempfile.new('certfile') + end + + context 'When the cert file doesnt not exist' do + it 'returns true' do + expect(instance.cert_need_renewall?('/tmp/bad_filename', 3650)).to be_truthy + end + end + + context 'When the cert file does exist, but does not contain a valid Certificate' do + it 'returns true' do + @certfile.write('I_am_not_a_cert_I_am_a_free_man') + @certfile.close + expect(instance.cert_need_renewall?(@certfile.path, 3650)).to be_truthy + end + end + + context 'When the cert file does exist, and does not contain a soon to expire certficitate' do + it 'returns false' do + @certfile.write("-----BEGIN CERTIFICATE-----\nMIIDODCCAiCgAwIBAgIVAPCkjE+wlZ1PgXwgvFgXKzhSpUkvMA0GCSqGSIb3DQEB\nCwUAMA0xCzAJBgNVBAMMAkNBMB4XDTE5MTIyNTEyNTY1NVoXDTI5MTIyMjEyNTY1\nNVowDTELMAkGA1UEAwwCQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC9bDWs5akf85UEMj8kry4DYNuAnaL4GnMs6XukQtp3dso+FgbKprgVogyepnet\nE+GlQq32u/n4y8K228kB6NoCn+c/yP+4QlKUBt0xSzQbSUuAE/5xZoKi/kH1ZsQ/\nuKXN/tIHagApEUGn5zqc8WBvWPliRAqiklwj8WtSw1WRa5eCdaVtln3wKuvPnYR5\n/V4YBHyHNhtlfXJBMtEaXm1rRzJGun+FdcrsCfcIFXp8lWobF+EVP8fRwqFTEtT6\nRXv6RT8sHy53a0KNTm8qnbacfr1MofgUuhzLjOrbIVvXpnRLeOkv8XW5rSH+zgsC\nZFK3bJ3j6UVbFQV4jXwlAWVrAgMBAAGjgY4wgYswDgYDVR0PAQH/BAQDAgGmMA8G\nA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFK4S2PNu6bpjxkJxedNaxfCrwtD4MEkG\nA1UdIwRCMECAFK4S2PNu6bpjxkJxedNaxfCrwtD4oRGkDzANMQswCQYDVQQDDAJD\nQYIVAPCkjE+wlZ1PgXwgvFgXKzhSpUkvMA0GCSqGSIb3DQEBCwUAA4IBAQBGk+u3\n9N3PLWNOwYrqK7fD4yceWnz4UsV9uN1IU5PQTgYBaGyAZvU+VJluZZeDj7QjwbUW\ngISclvW/pSWpUVW3O0sfAM97u+5UMYHz4W5Bgq8CtdOKHgdZHKhzBePhmou11zO0\nZ6uQ7bkh0/REqKO7TFKaMMnakEhFXoDrS1EiB4W69KVXyrBVzVm5LK7uvOAQAeMp\nnEk3Oz+5pmKjSCf1cEd2jzAgDbaVrIvxICPgXAlNrKukmRW/0UHqDDVBfF7PioD2\nptlQFxWIkih6s/clwhsBFBwV1yyCirYfjhzmKPPLZUmx10okudLzaKrRbkPxrzbC\nmKEZoV+Nz2CNrGm5\n-----END CERTIFICATE-----\n") + @certfile.close + expect(instance.cert_need_renewall?(@certfile.path, 5)).to be_falsey + end + end + + context 'When the cert file does exist, and does contain a soon to expire certficitate' do + it 'returns true' do + @certfile.write("-----BEGIN CERTIFICATE-----\nMIIDODCCAiCgAwIBAgIVAPCkjE+wlZ1PgXwgvFgXKzhSpUkvMA0GCSqGSIb3DQEB\nCwUAMA0xCzAJBgNVBAMMAkNBMB4XDTE5MTIyNTEyNTY1NVoXDTI5MTIyMjEyNTY1\nNVowDTELMAkGA1UEAwwCQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC9bDWs5akf85UEMj8kry4DYNuAnaL4GnMs6XukQtp3dso+FgbKprgVogyepnet\nE+GlQq32u/n4y8K228kB6NoCn+c/yP+4QlKUBt0xSzQbSUuAE/5xZoKi/kH1ZsQ/\nuKXN/tIHagApEUGn5zqc8WBvWPliRAqiklwj8WtSw1WRa5eCdaVtln3wKuvPnYR5\n/V4YBHyHNhtlfXJBMtEaXm1rRzJGun+FdcrsCfcIFXp8lWobF+EVP8fRwqFTEtT6\nRXv6RT8sHy53a0KNTm8qnbacfr1MofgUuhzLjOrbIVvXpnRLeOkv8XW5rSH+zgsC\nZFK3bJ3j6UVbFQV4jXwlAWVrAgMBAAGjgY4wgYswDgYDVR0PAQH/BAQDAgGmMA8G\nA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFK4S2PNu6bpjxkJxedNaxfCrwtD4MEkG\nA1UdIwRCMECAFK4S2PNu6bpjxkJxedNaxfCrwtD4oRGkDzANMQswCQYDVQQDDAJD\nQYIVAPCkjE+wlZ1PgXwgvFgXKzhSpUkvMA0GCSqGSIb3DQEBCwUAA4IBAQBGk+u3\n9N3PLWNOwYrqK7fD4yceWnz4UsV9uN1IU5PQTgYBaGyAZvU+VJluZZeDj7QjwbUW\ngISclvW/pSWpUVW3O0sfAM97u+5UMYHz4W5Bgq8CtdOKHgdZHKhzBePhmou11zO0\nZ6uQ7bkh0/REqKO7TFKaMMnakEhFXoDrS1EiB4W69KVXyrBVzVm5LK7uvOAQAeMp\nnEk3Oz+5pmKjSCf1cEd2jzAgDbaVrIvxICPgXAlNrKukmRW/0UHqDDVBfF7PioD2\nptlQFxWIkih6s/clwhsBFBwV1yyCirYfjhzmKPPLZUmx10okudLzaKrRbkPxrzbC\nmKEZoV+Nz2CNrGm5\n-----END CERTIFICATE-----\n") + @certfile.close + expect(instance.cert_need_renewall?(@certfile.path, 3650)).to be_truthy + end + end + + after(:each) do + @certfile.unlink + end + end end |