From a0c888b7cd73b0596735e1d482ae6ffd2ea67eaa Mon Sep 17 00:00:00 2001 From: Ben Burkert Date: Wed, 26 Jun 2024 14:35:33 +0200 Subject: [PATCH 1/2] clear memoized x509 cert on Cert#cert_pem assignment When a Puma::Acme::Cert is renewed, the new cert is assigned by calling the cert_pem= method. The instance memoizes the parsed OpenSSL x509 object, but was not taking changes to the cert_pem into account. This was causing renewed certs to keep using the old expir(ed/ing) cert instead of the replacement. The pstore library is capable of marshaling & unmarshaling the @x509 member, so this inconsistency is present in instances loaded from the pstore cache. --- lib/puma/acme/structs.rb | 6 ++++++ puma-acme.gemspec | 1 + test/structs_test.rb | 39 +++++++++++++++++++++++++++++++++++++++ test/test_helper.rb | 1 + 4 files changed, 47 insertions(+) create mode 100644 test/structs_test.rb diff --git a/lib/puma/acme/structs.rb b/lib/puma/acme/structs.rb index ef87af2..7c143c0 100644 --- a/lib/puma/acme/structs.rb +++ b/lib/puma/acme/structs.rb @@ -59,6 +59,12 @@ def initialize(identifiers: nil, **kwargs) super(identifiers: identifiers, **kwargs) end + def cert_pem=(pem) + @x509 = nil + + self[:cert_pem] = pem + end + def key [:cert, algorithm, identifiers&.map(&:key)] end diff --git a/puma-acme.gemspec b/puma-acme.gemspec index 2e0dfef..4fd2fbd 100644 --- a/puma-acme.gemspec +++ b/puma-acme.gemspec @@ -29,6 +29,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'http.rb', '~> 0.12' s.add_development_dependency 'minitest', '~> 5.14' s.add_development_dependency 'minitest-mock_expectations', '~> 1.2' + s.add_development_dependency 'r509', '~> 1.0' s.add_development_dependency 'rake', '~> 13.0' s.add_development_dependency 'vcr', '~> 6.1' s.add_development_dependency 'webmock', '~> 3.19' diff --git a/test/structs_test.rb b/test/structs_test.rb new file mode 100644 index 0000000..2124ba0 --- /dev/null +++ b/test/structs_test.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require_relative './test_helper' + +class CertsTest < Minitest::Test + def test_changing_pem_changes_cert_properties + key = R509::PrivateKey.new(type: 'RSA', bit_length: 2048) + expired_at = Time.now.utc.to_i - 3600 + expired_pem = cert_pem(key, 'expired.test', not_before: expired_at - 3600, not_after: expired_at) + + cert = Puma::Acme::Cert.new(algorithm: :rsa, identifiers: %w[expired.test], cert_pem: expired_pem) + assert cert.expired? + + + unexpired_at = Time.now.utc.to_i + unexpired_pem = cert_pem(key, 'unexpired.test', not_before: unexpired_at - 3600, not_after: unexpired_at + 3600) + + cert.cert_pem = unexpired_pem + assert !cert.expired? + end + + protected + + def cert_pem(key, common_name, not_before:, not_after:) + csr = R509::CSR.new( + subject: [['CN', common_name]], + message_digest: 'SHA256', + key: + ) + + cert = R509::CertificateAuthority::Signer.selfsign( + csr:, + not_before:, + not_after: + ) + + cert.to_pem + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 6abb068..f6d6dff 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -11,6 +11,7 @@ require 'minitest/mock_expectations' require 'puma/configuration' require 'puma/events' +require 'r509' require 'securerandom' require 'vcr' require 'webmock' From d59a34f2535d9cda663943f487a2620013030438 Mon Sep 17 00:00:00 2001 From: Ben Burkert Date: Thu, 27 Jun 2024 11:11:54 -0400 Subject: [PATCH 2/2] drop ruby 3.1 syntax --- test/structs_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/structs_test.rb b/test/structs_test.rb index 2124ba0..2ec1cd0 100644 --- a/test/structs_test.rb +++ b/test/structs_test.rb @@ -25,13 +25,13 @@ def cert_pem(key, common_name, not_before:, not_after:) csr = R509::CSR.new( subject: [['CN', common_name]], message_digest: 'SHA256', - key: + key: key ) cert = R509::CertificateAuthority::Signer.selfsign( - csr:, - not_before:, - not_after: + csr: csr, + not_before: not_before, + not_after: not_after ) cert.to_pem