446 lines
17 KiB
Diff
446 lines
17 KiB
Diff
|
From 0f77b359e241fc4055fb8d785e18f96338451ebf Mon Sep 17 00:00:00 2001
|
||
|
From: Mohammad Rizwan <myusuf@redhat.com>
|
||
|
Date: Mon, 6 Feb 2023 15:31:27 +0530
|
||
|
Subject: [PATCH] ipatests: tests for certificate pruning
|
||
|
|
||
|
1. Test to prune the expired certificate by manual run
|
||
|
2. Test to prune expired certificate by cron job
|
||
|
3. Test to prune expired certificate with retention unit option
|
||
|
4. Test to prune expired certificate with search size limit option
|
||
|
5. Test to check config-show command shows set param
|
||
|
6. Test prune command shows proper status after disabling the pruning
|
||
|
|
||
|
related: https://pagure.io/freeipa/issue/9294
|
||
|
|
||
|
Signed-off-by: Mohammad Rizwan <myusuf@redhat.com>
|
||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||
|
---
|
||
|
ipatests/test_integration/test_acme.py | 306 +++++++++++++++++++++----
|
||
|
1 file changed, 260 insertions(+), 46 deletions(-)
|
||
|
|
||
|
diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py
|
||
|
index 5ceba05976059de69414a79634d98045c3ab68bb..1334be52f4530dd8b2a4207744146cd0eb5477a3 100644
|
||
|
--- a/ipatests/test_integration/test_acme.py
|
||
|
+++ b/ipatests/test_integration/test_acme.py
|
||
|
@@ -122,21 +122,23 @@ def certbot_register(host, acme_server):
|
||
|
)
|
||
|
|
||
|
|
||
|
-def certbot_standalone_cert(host, acme_server):
|
||
|
+def certbot_standalone_cert(host, acme_server, no_of_cert=1):
|
||
|
"""method to issue a certbot's certonly standalone cert"""
|
||
|
# Get a cert from ACME service using HTTP challenge and Certbot's
|
||
|
# standalone HTTP server mode
|
||
|
host.run_command(['systemctl', 'stop', 'httpd'])
|
||
|
- host.run_command(
|
||
|
- [
|
||
|
- 'certbot',
|
||
|
- '--server', acme_server,
|
||
|
- 'certonly',
|
||
|
- '--domain', host.hostname,
|
||
|
- '--standalone',
|
||
|
- '--key-type', 'rsa',
|
||
|
- ]
|
||
|
- )
|
||
|
+ for _i in range(0, no_of_cert):
|
||
|
+ host.run_command(
|
||
|
+ [
|
||
|
+ 'certbot',
|
||
|
+ '--server', acme_server,
|
||
|
+ 'certonly',
|
||
|
+ '--domain', host.hostname,
|
||
|
+ '--standalone',
|
||
|
+ '--key-type', 'rsa',
|
||
|
+ '--force-renewal'
|
||
|
+ ]
|
||
|
+ )
|
||
|
|
||
|
|
||
|
class TestACME(CALessBase):
|
||
|
@@ -573,43 +575,41 @@ class TestACMEwithExternalCA(TestACME):
|
||
|
tasks.install_replica(cls.master, cls.replicas[0])
|
||
|
|
||
|
|
||
|
-class TestACMERenew(IntegrationTest):
|
||
|
-
|
||
|
- num_clients = 1
|
||
|
+@pytest.fixture
|
||
|
+def issue_and_expire_acme_cert():
|
||
|
+ """Fixture to expire cert by moving date past expiry of acme cert"""
|
||
|
+ hosts = []
|
||
|
|
||
|
- @classmethod
|
||
|
- def install(cls, mh):
|
||
|
-
|
||
|
- # install packages before client install in case of IPA DNS problems
|
||
|
- cls.acme_server = prepare_acme_client(cls.master, cls.clients[0])
|
||
|
+ def _issue_and_expire_acme_cert(
|
||
|
+ master, client,
|
||
|
+ acme_server_url, no_of_cert=1
|
||
|
+ ):
|
||
|
|
||
|
- tasks.install_master(cls.master, setup_dns=True)
|
||
|
- tasks.install_client(cls.master, cls.clients[0])
|
||
|
+ hosts.append(master)
|
||
|
+ hosts.append(client)
|
||
|
|
||
|
- @pytest.fixture
|
||
|
- def issue_and_expire_cert(self):
|
||
|
- """Fixture to expire cert by moving date past expiry of acme cert"""
|
||
|
# enable the ACME service on master
|
||
|
- self.master.run_command(['ipa-acme-manage', 'enable'])
|
||
|
+ master.run_command(['ipa-acme-manage', 'enable'])
|
||
|
|
||
|
# register the account with certbot
|
||
|
- certbot_register(self.clients[0], self.acme_server)
|
||
|
+ certbot_register(client, acme_server_url)
|
||
|
|
||
|
# request a standalone acme cert
|
||
|
- certbot_standalone_cert(self.clients[0], self.acme_server)
|
||
|
+ certbot_standalone_cert(client, acme_server_url, no_of_cert)
|
||
|
|
||
|
# move system date to expire acme cert
|
||
|
- for host in self.clients[0], self.master:
|
||
|
+ for host in hosts:
|
||
|
tasks.kdestroy_all(host)
|
||
|
tasks.move_date(host, 'stop', '+90days')
|
||
|
|
||
|
+ time.sleep(10)
|
||
|
tasks.get_kdcinfo(host)
|
||
|
# Note raiseonerr=False:
|
||
|
# the assert is located after kdcinfo retrieval.
|
||
|
- result = host.run_command(
|
||
|
+ result = master.run_command(
|
||
|
"KRB5_TRACE=/dev/stdout kinit admin",
|
||
|
stdin_text='{0}\n{0}\n{0}\n'.format(
|
||
|
- self.clients[0].config.admin_password
|
||
|
+ master.config.admin_password
|
||
|
),
|
||
|
raiseonerr=False
|
||
|
)
|
||
|
@@ -618,16 +618,28 @@ class TestACMERenew(IntegrationTest):
|
||
|
tasks.get_kdcinfo(host)
|
||
|
assert result.returncode == 0
|
||
|
|
||
|
- yield
|
||
|
+ yield _issue_and_expire_acme_cert
|
||
|
|
||
|
- # move back date
|
||
|
- for host in self.clients[0], self.master:
|
||
|
- tasks.kdestroy_all(host)
|
||
|
- tasks.move_date(host, 'start', '-90days')
|
||
|
- tasks.kinit_admin(host)
|
||
|
+ # move back date
|
||
|
+ for host in hosts:
|
||
|
+ tasks.move_date(host, 'start', '-90days')
|
||
|
+
|
||
|
+
|
||
|
+class TestACMERenew(IntegrationTest):
|
||
|
+
|
||
|
+ num_clients = 1
|
||
|
+
|
||
|
+ @classmethod
|
||
|
+ def install(cls, mh):
|
||
|
+
|
||
|
+ # install packages before client install in case of IPA DNS problems
|
||
|
+ cls.acme_server = prepare_acme_client(cls.master, cls.clients[0])
|
||
|
+
|
||
|
+ tasks.install_master(cls.master, setup_dns=True)
|
||
|
+ tasks.install_client(cls.master, cls.clients[0])
|
||
|
|
||
|
@pytest.mark.skipif(skip_certbot_tests, reason='certbot not available')
|
||
|
- def test_renew(self, issue_and_expire_cert):
|
||
|
+ def test_renew(self, issue_and_expire_acme_cert):
|
||
|
"""Test if ACME renews the issued cert with cerbot
|
||
|
|
||
|
This test is to check if ACME certificate renews upon
|
||
|
@@ -635,6 +647,8 @@ class TestACMERenew(IntegrationTest):
|
||
|
|
||
|
related: https://pagure.io/freeipa/issue/4751
|
||
|
"""
|
||
|
+ issue_and_expire_acme_cert(
|
||
|
+ self.master, self.clients[0], self.acme_server)
|
||
|
data = self.clients[0].get_file_contents(
|
||
|
f'/etc/letsencrypt/live/{self.clients[0].hostname}/cert.pem'
|
||
|
)
|
||
|
@@ -656,6 +670,7 @@ class TestACMEPrune(IntegrationTest):
|
||
|
"""Validate that ipa-acme-manage configures dogtag for pruning"""
|
||
|
|
||
|
random_serial = True
|
||
|
+ num_clients = 1
|
||
|
|
||
|
@classmethod
|
||
|
def install(cls, mh):
|
||
|
@@ -663,6 +678,8 @@ class TestACMEPrune(IntegrationTest):
|
||
|
raise pytest.skip("RNSv3 not supported")
|
||
|
tasks.install_master(cls.master, setup_dns=True,
|
||
|
random_serial=True)
|
||
|
+ cls.acme_server = prepare_acme_client(cls.master, cls.clients[0])
|
||
|
+ tasks.install_client(cls.master, cls.clients[0])
|
||
|
|
||
|
@classmethod
|
||
|
def uninstall(cls, mh):
|
||
|
@@ -718,7 +735,7 @@ class TestACMEPrune(IntegrationTest):
|
||
|
['ipa-acme-manage', 'pruning',
|
||
|
'--requestretention=60',
|
||
|
'--requestretentionunit=minute',
|
||
|
- '--requestresearchsizelimit=2000',
|
||
|
+ '--requestsearchsizelimit=2000',
|
||
|
'--requestsearchtimelimit=5',]
|
||
|
)
|
||
|
cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||
|
@@ -741,7 +758,7 @@ class TestACMEPrune(IntegrationTest):
|
||
|
|
||
|
self.master.run_command(
|
||
|
['ipa-acme-manage', 'pruning',
|
||
|
- '--cron="0 23 1 * *',]
|
||
|
+ '--cron=0 23 1 * *',]
|
||
|
)
|
||
|
cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||
|
assert (
|
||
|
@@ -760,7 +777,7 @@ class TestACMEPrune(IntegrationTest):
|
||
|
'--enable', '--disable'],
|
||
|
raiseonerr=False
|
||
|
)
|
||
|
- assert result.returncode == 1
|
||
|
+ assert result.returncode == 2
|
||
|
assert "Cannot both enable and disable" in result.stderr_text
|
||
|
|
||
|
for cmd in ('--config-show', '--run'):
|
||
|
@@ -769,20 +786,20 @@ class TestACMEPrune(IntegrationTest):
|
||
|
cmd, '--enable'],
|
||
|
raiseonerr=False
|
||
|
)
|
||
|
- assert result.returncode == 1
|
||
|
+ assert result.returncode == 2
|
||
|
assert "Cannot change and show config" in result.stderr_text
|
||
|
|
||
|
result = self.master.run_command(
|
||
|
['ipa-acme-manage', 'pruning',
|
||
|
- '--cron="* *"'],
|
||
|
+ '--cron=* *'],
|
||
|
raiseonerr=False
|
||
|
)
|
||
|
- assert result.returncode == 1
|
||
|
- assert "Invalid format format --cron" in result.stderr_text
|
||
|
+ assert result.returncode == 2
|
||
|
+ assert "Invalid format for --cron" in result.stderr_text
|
||
|
|
||
|
result = self.master.run_command(
|
||
|
['ipa-acme-manage', 'pruning',
|
||
|
- '--cron="100 * * * *"'],
|
||
|
+ '--cron=100 * * * *'],
|
||
|
raiseonerr=False
|
||
|
)
|
||
|
assert result.returncode == 1
|
||
|
@@ -790,8 +807,205 @@ class TestACMEPrune(IntegrationTest):
|
||
|
|
||
|
result = self.master.run_command(
|
||
|
['ipa-acme-manage', 'pruning',
|
||
|
- '--cron="10 1-5 * * *"'],
|
||
|
+ '--cron=10 1-5 * * *'],
|
||
|
raiseonerr=False
|
||
|
)
|
||
|
assert result.returncode == 1
|
||
|
assert "1-5 ranges are not supported" in result.stderr_text
|
||
|
+
|
||
|
+ def test_prune_cert_manual(self, issue_and_expire_acme_cert):
|
||
|
+ """Test to prune expired certificate by manual run"""
|
||
|
+ if (tasks.get_pki_version(self.master)
|
||
|
+ < tasks.parse_version('11.3.0')):
|
||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||
|
+
|
||
|
+ issue_and_expire_acme_cert(
|
||
|
+ self.master, self.clients[0], self.acme_server)
|
||
|
+
|
||
|
+ # check that the certificate issued for the client
|
||
|
+ result = self.master.run_command(
|
||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname]
|
||
|
+ )
|
||
|
+ assert f'CN={self.clients[0].hostname}' in result.stdout_text
|
||
|
+
|
||
|
+ # run prune command manually
|
||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable'])
|
||
|
+ self.master.run_command(['ipactl', 'restart'])
|
||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--run'])
|
||
|
+ # wait for cert to get prune
|
||
|
+ time.sleep(50)
|
||
|
+
|
||
|
+ # check if client cert is removed
|
||
|
+ result = self.master.run_command(
|
||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname],
|
||
|
+ raiseonerr=False
|
||
|
+ )
|
||
|
+ assert f'CN={self.clients[0].hostname}' not in result.stdout_text
|
||
|
+
|
||
|
+ def test_prune_cert_cron(self, issue_and_expire_acme_cert):
|
||
|
+ """Test to prune expired certificate by cron job"""
|
||
|
+ if (tasks.get_pki_version(self.master)
|
||
|
+ < tasks.parse_version('11.3.0')):
|
||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||
|
+
|
||
|
+ issue_and_expire_acme_cert(
|
||
|
+ self.master, self.clients[0], self.acme_server)
|
||
|
+
|
||
|
+ # check that the certificate issued for the client
|
||
|
+ result = self.master.run_command(
|
||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname]
|
||
|
+ )
|
||
|
+ assert f'CN={self.clients[0].hostname}' in result.stdout_text
|
||
|
+
|
||
|
+ # enable pruning
|
||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable'])
|
||
|
+
|
||
|
+ # cron would be set to run the next minute
|
||
|
+ cron_minute = self.master.run_command(
|
||
|
+ [
|
||
|
+ "python3",
|
||
|
+ "-c",
|
||
|
+ (
|
||
|
+ "from datetime import datetime; "
|
||
|
+ "print(int(datetime.now().strftime('%M')) + 5)"
|
||
|
+ ),
|
||
|
+ ]
|
||
|
+ ).stdout_text.strip()
|
||
|
+ self.master.run_command(
|
||
|
+ ['ipa-acme-manage', 'pruning',
|
||
|
+ f'--cron={cron_minute} * * * *']
|
||
|
+ )
|
||
|
+ self.master.run_command(['ipactl', 'restart'])
|
||
|
+ # wait for 5 minutes to cron to execute and 20 sec for just in case
|
||
|
+ time.sleep(320)
|
||
|
+
|
||
|
+ # check if client cert is removed
|
||
|
+ result = self.master.run_command(
|
||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname],
|
||
|
+ raiseonerr=False
|
||
|
+ )
|
||
|
+ assert f'CN={self.clients[0].hostname}' not in result.stdout_text
|
||
|
+
|
||
|
+ def test_prune_cert_retention_unit(self, issue_and_expire_acme_cert):
|
||
|
+ """Test to prune expired certificate with retention unit option"""
|
||
|
+ if (tasks.get_pki_version(self.master)
|
||
|
+ < tasks.parse_version('11.3.0')):
|
||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||
|
+ issue_and_expire_acme_cert(
|
||
|
+ self.master, self.clients[0], self.acme_server)
|
||
|
+
|
||
|
+ # check that the certificate issued for the client
|
||
|
+ result = self.master.run_command(
|
||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname]
|
||
|
+ )
|
||
|
+ assert f'CN={self.clients[0].hostname}' in result.stdout_text
|
||
|
+
|
||
|
+ # enable pruning
|
||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable'])
|
||
|
+
|
||
|
+ # certretention set to 5 min
|
||
|
+ self.master.run_command(
|
||
|
+ ['ipa-acme-manage', 'pruning',
|
||
|
+ '--certretention=5', '--certretentionunit=minute']
|
||
|
+ )
|
||
|
+ self.master.run_command(['ipactl', 'restart'])
|
||
|
+
|
||
|
+ # wait for 5 min and check if expired cert is removed
|
||
|
+ time.sleep(310)
|
||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--run'])
|
||
|
+ result = self.master.run_command(
|
||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname],
|
||
|
+ raiseonerr=False
|
||
|
+ )
|
||
|
+ assert f'CN={self.clients[0].hostname}' not in result.stdout_text
|
||
|
+
|
||
|
+ def test_prune_cert_search_size_limit(self, issue_and_expire_acme_cert):
|
||
|
+ """Test to prune expired certificate with search size limit option"""
|
||
|
+ if (tasks.get_pki_version(self.master)
|
||
|
+ < tasks.parse_version('11.3.0')):
|
||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||
|
+ no_of_cert = 10
|
||
|
+ search_size_limit = 5
|
||
|
+ issue_and_expire_acme_cert(
|
||
|
+ self.master, self.clients[0], self.acme_server, no_of_cert)
|
||
|
+
|
||
|
+ # check that the certificate issued for the client
|
||
|
+ result = self.master.run_command(
|
||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname]
|
||
|
+ )
|
||
|
+ assert f'CN={self.clients[0].hostname}' in result.stdout_text
|
||
|
+ assert f'Number of entries returned {no_of_cert}'
|
||
|
+
|
||
|
+ # enable pruning
|
||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable'])
|
||
|
+
|
||
|
+ # certretention set to 5 min
|
||
|
+ self.master.run_command(
|
||
|
+ ['ipa-acme-manage', 'pruning',
|
||
|
+ f'--certsearchsizelimit={search_size_limit}',
|
||
|
+ '--certsearchtimelimit=100']
|
||
|
+ )
|
||
|
+ self.master.run_command(['ipactl', 'restart'])
|
||
|
+
|
||
|
+ # prune the certificates
|
||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--run'])
|
||
|
+
|
||
|
+ # check if 5 expired cert is removed
|
||
|
+ result = self.master.run_command(
|
||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname]
|
||
|
+ )
|
||
|
+ assert f'Number of entries returned {no_of_cert - search_size_limit}'
|
||
|
+
|
||
|
+ def test_prune_config_show(self, issue_and_expire_acme_cert):
|
||
|
+ """Test to check config-show command shows set param"""
|
||
|
+ if (tasks.get_pki_version(self.master)
|
||
|
+ < tasks.parse_version('11.3.0')):
|
||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||
|
+
|
||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable'])
|
||
|
+ self.master.run_command(
|
||
|
+ ['ipa-acme-manage', 'pruning',
|
||
|
+ '--cron=0 0 1 * *']
|
||
|
+ )
|
||
|
+ self.master.run_command(
|
||
|
+ ['ipa-acme-manage', 'pruning',
|
||
|
+ '--certretention=30', '--certretentionunit=day']
|
||
|
+ )
|
||
|
+ self.master.run_command(
|
||
|
+ ['ipa-acme-manage', 'pruning',
|
||
|
+ '--certsearchsizelimit=1000', '--certsearchtimelimit=0']
|
||
|
+ )
|
||
|
+ self.master.run_command(
|
||
|
+ ['ipa-acme-manage', 'pruning',
|
||
|
+ '--requestretention=30', '--requestretentionunit=day']
|
||
|
+ )
|
||
|
+ self.master.run_command(
|
||
|
+ ['ipa-acme-manage', 'pruning',
|
||
|
+ '--requestsearchsizelimit=1000', '--requestsearchtimelimit=0']
|
||
|
+ )
|
||
|
+ result = self.master.run_command(
|
||
|
+ ['ipa-acme-manage', 'pruning', '--config-show']
|
||
|
+ )
|
||
|
+ assert 'Status: enabled' in result.stdout_text
|
||
|
+ assert 'Certificate Retention Time: 30' in result.stdout_text
|
||
|
+ assert 'Certificate Retention Unit: day' in result.stdout_text
|
||
|
+ assert 'Certificate Search Size Limit: 1000' in result.stdout_text
|
||
|
+ assert 'Certificate Search Time Limit: 100' in result.stdout_text
|
||
|
+ assert 'Request Retention Time: 30' in result.stdout_text
|
||
|
+ assert 'Request Retention Unit: day' in result.stdout_text
|
||
|
+ assert 'Request Search Size Limit' in result.stdout_text
|
||
|
+ assert 'Request Search Time Limit: 100' in result.stdout_text
|
||
|
+ assert 'cron Schedule: 0 0 1 * *' in result.stdout_text
|
||
|
+
|
||
|
+ def test_prune_disable(self, issue_and_expire_acme_cert):
|
||
|
+ """Test prune command throw error after disabling the pruning"""
|
||
|
+ if (tasks.get_pki_version(self.master)
|
||
|
+ < tasks.parse_version('11.3.0')):
|
||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||
|
+
|
||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--disable'])
|
||
|
+ result = self.master.run_command(
|
||
|
+ ['ipa-acme-manage', 'pruning',
|
||
|
+ '--cron=0 0 1 * *']
|
||
|
+ )
|
||
|
+ assert 'Status: disabled' in result.stdout_text
|
||
|
--
|
||
|
2.39.1
|
||
|
|