Skip to content

Commit

Permalink
Merge pull request #10 from anchordotdev/fix-x509-caching
Browse files Browse the repository at this point in the history
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 expired/expiring 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.
  • Loading branch information
benburkert authored Jul 15, 2024
2 parents e68de35 + d59a34f commit 79a7386
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 0 deletions.
6 changes: 6 additions & 0 deletions lib/puma/acme/structs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions puma-acme.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
39 changes: 39 additions & 0 deletions test/structs_test.rb
Original file line number Diff line number Diff line change
@@ -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: key
)

cert = R509::CertificateAuthority::Signer.selfsign(
csr: csr,
not_before: not_before,
not_after: not_after
)

cert.to_pem
end
end
1 change: 1 addition & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require 'minitest/mock_expectations'
require 'puma/configuration'
require 'puma/events'
require 'r509'
require 'securerandom'
require 'vcr'
require 'webmock'
Expand Down

0 comments on commit 79a7386

Please sign in to comment.