integration: add test for early renewal from ARI (#10311)

This depends on a pending Pebble pull request and so will fail
integration tests until/unless that lands:
https://github.com/letsencrypt/pebble/pull/501

However, I'd appreciate some eyes on this PR in this regard: is the
interface we're using in Pebble useful and appropriate? If not, we can
adjust the Pebble PR.

Inspired based on conversation on
https://github.com/certbot/certbot/pull/10307, but note that this just
tests the general case; it does not test the "default server differs
from lineage server" case yet; when I try adding that I get some bugs
that may reflect a problem in #10307 I need to fix (or may reflect that
I need to inhibit the `--server` flag rather than trying to override it
late in the command line).
This commit is contained in:
Jacob Hoffman-Andrews
2025-06-06 14:39:10 -07:00
committed by GitHub
parent 95a70e98c2
commit a75057042f
3 changed files with 45 additions and 1 deletions
@@ -1,4 +1,5 @@
"""Module executing integration tests against certbot core.""" """Module executing integration tests against certbot core."""
import json
import os import os
from os.path import exists from os.path import exists
from os.path import join from os.path import join
@@ -310,6 +311,31 @@ def test_graceful_renew_it_is_time(context: IntegrationTestsContext) -> None:
assert_hook_execution(context.hook_probe, 'deploy') assert_hook_execution(context.hook_probe, 'deploy')
def test_renew_when_ari_says_its_time(context: IntegrationTestsContext) -> None:
"""Test graceful renew is done when it is due time."""
certname = context.get_domain('renew')
context.certbot(['-d', certname])
assert_cert_count_for_lineage(context.config_dir, certname, 1)
# Tell Pebble to make ARI look urgent
with open(join(context.config_dir, 'live', certname, 'cert.pem'), 'r') as c:
certificate_pem = c.read()
misc.set_ari_response(certificate_pem, json.dumps({
'suggestedWindow': {
'start': '2020-01-01T00:00:00Z',
'end': '2020-01-01T00:00:00Z'
}
}))
context.certbot(['renew', '--deploy-hook', misc.echo('deploy', context.hook_probe)],
force_renew=False)
assert_cert_count_for_lineage(context.config_dir, certname, 2)
assert_hook_execution(context.hook_probe, 'deploy')
def test_renew_with_changed_private_key_complexity(context: IntegrationTestsContext) -> None: def test_renew_with_changed_private_key_complexity(context: IntegrationTestsContext) -> None:
"""Test proper renew with updated private key complexity.""" """Test proper renew with updated private key complexity."""
certname = context.get_domain('renew') certname = context.get_domain('renew')
@@ -8,6 +8,7 @@ import errno
import functools import functools
import http.server as SimpleHTTPServer import http.server as SimpleHTTPServer
import importlib.resources import importlib.resources
import json
import os import os
import re import re
import shutil import shutil
@@ -318,3 +319,20 @@ def get_acme_issuers() -> List[Certificate]:
issuers.append(load_pem_x509_certificate(request.content, default_backend())) issuers.append(load_pem_x509_certificate(request.content, default_backend()))
return issuers return issuers
def set_ari_response(certificate_pem: str, response_json: str) -> None:
"""POST to an endpoint on the Pebble server setting the ARI response
for the given certificate."""
set_renewal_info_body = json.dumps(
{
'certificate': certificate_pem,
'ariResponse': response_json,
})
_suppress_x509_verification_warnings()
url = PEBBLE_MANAGEMENT_URL + '/set-renewal-info/'
print(f'sending to {url}: {set_renewal_info_body}')
resp = requests.post(url, verify=False, timeout=10, data=set_renewal_info_body)
if resp.status_code != 200:
print(f'setting renewal info: {resp.status_code} {resp.text}')
assert resp.status_code == 200
@@ -15,7 +15,7 @@ import requests
from certbot_integration_tests.utils.constants import DEFAULT_HTTP_01_PORT from certbot_integration_tests.utils.constants import DEFAULT_HTTP_01_PORT
from certbot_integration_tests.utils.constants import MOCK_OCSP_SERVER_PORT from certbot_integration_tests.utils.constants import MOCK_OCSP_SERVER_PORT
PEBBLE_VERSION = 'v2.7.0' PEBBLE_VERSION = 'v2.8.0'
def fetch(workspace: str, http_01_port: int = DEFAULT_HTTP_01_PORT) -> Tuple[str, str, str]: def fetch(workspace: str, http_01_port: int = DEFAULT_HTTP_01_PORT) -> Tuple[str, str, str]: