ipa-4.10.1-4
- Resolves: rhbz#2161284 'ERROR Could not remove /tmp/tmpbkw6hawo.ipabkp' can be seen prior to 'ipa-client-install' command was successful - Resolves: rhbz#2164403 ipa-trust-add with --range-type=ipa-ad-trust-posix fails while creating an ID range - Resolves: rhbz#2162677 RFE: Implement support for PKI certificate and request pruning - Resolves: rhbz#2167312 - Backport latest test fixes in python3-ipatests Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
This commit is contained in:
parent
f7ee6e148d
commit
d5f3f77077
@ -0,0 +1,54 @@
|
|||||||
|
From 894dca12c120f0bfa705307a0609da47326b8fb2 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Date: Thu, 12 Jan 2023 11:26:53 +0100
|
||||||
|
Subject: [PATCH] server install: remove error log about missing bkup file
|
||||||
|
|
||||||
|
The client installer code can be called in 3 different ways:
|
||||||
|
- from ipa-client-install CLI
|
||||||
|
- from ipa-replica-install CLI if the client is not already installed
|
||||||
|
- from ipa-server-install
|
||||||
|
|
||||||
|
In the last case, the client installer is called with
|
||||||
|
options.on_master=True
|
||||||
|
As a result, it's skipping the part that is creating the krb5
|
||||||
|
configuration:
|
||||||
|
if not options.on_master:
|
||||||
|
nolog = tuple()
|
||||||
|
configure_krb5_conf(...)
|
||||||
|
|
||||||
|
The configure_krb5_conf method is the place where the krb5.conf file is
|
||||||
|
backup'ed with the extention ".ipabkp". For a master installation, this
|
||||||
|
code is not called and the ipabkp file does not exist => delete raises
|
||||||
|
an error.
|
||||||
|
|
||||||
|
When delete fails because the file does not exist, no need to log an
|
||||||
|
error message.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9306
|
||||||
|
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
---
|
||||||
|
ipaclient/install/client.py | 7 +++----
|
||||||
|
1 file changed, 3 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
|
||||||
|
index e5d3e8223efa1ebb69a1b7e963c394f9e1f38816..6e7f17d5b69581320866627cb5747a050cb6e32e 100644
|
||||||
|
--- a/ipaclient/install/client.py
|
||||||
|
+++ b/ipaclient/install/client.py
|
||||||
|
@@ -124,10 +124,9 @@ def cleanup(func):
|
||||||
|
os.rmdir(ccache_dir)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
- try:
|
||||||
|
- os.remove(krb_name + ".ipabkp")
|
||||||
|
- except OSError:
|
||||||
|
- logger.error("Could not remove %s.ipabkp", krb_name)
|
||||||
|
+ # During master installation, the .ipabkp file is not created
|
||||||
|
+ # Ignore the delete error if it is "file does not exist"
|
||||||
|
+ remove_file(krb_name + ".ipabkp")
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
118
0004-ipa-tests-Add-LANG-before-kinit-command-to-fix-issue.patch
Normal file
118
0004-ipa-tests-Add-LANG-before-kinit-command-to-fix-issue.patch
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
From 2520a7adff7a49ddcddaaf19f0e586425dc0d878 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Filip Dvorak <fdvorak@redhat.com>
|
||||||
|
Date: Tue, 6 Dec 2022 15:51:27 +0100
|
||||||
|
Subject: [PATCH] ipa tests: Add LANG before kinit command to fix issue with
|
||||||
|
locale settings
|
||||||
|
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||||
|
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Reviewed-By: Michal Polovka <mpolovka@redhat.com>
|
||||||
|
---
|
||||||
|
ipatests/test_integration/test_krbtpolicy.py | 20 ++++++++++----------
|
||||||
|
1 file changed, 10 insertions(+), 10 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_krbtpolicy.py b/ipatests/test_integration/test_krbtpolicy.py
|
||||||
|
index eae16247bdfb195c1d91209cf2d11eac4c25018f..269cfb0a191821c229aaeb5a3eda0181c6e3ae62 100644
|
||||||
|
--- a/ipatests/test_integration/test_krbtpolicy.py
|
||||||
|
+++ b/ipatests/test_integration/test_krbtpolicy.py
|
||||||
|
@@ -23,7 +23,7 @@ PASSWORD = "Secret123"
|
||||||
|
USER1 = "testuser1"
|
||||||
|
USER2 = "testuser2"
|
||||||
|
MAXLIFE = 86400
|
||||||
|
-
|
||||||
|
+LANG_PKG = ["langpacks-en"]
|
||||||
|
|
||||||
|
def maxlife_within_policy(input, maxlife, slush=3600):
|
||||||
|
"""Given klist output of the TGT verify that it is within policy
|
||||||
|
@@ -45,7 +45,6 @@ def maxlife_within_policy(input, maxlife, slush=3600):
|
||||||
|
|
||||||
|
return maxlife >= diff >= maxlife - slush
|
||||||
|
|
||||||
|
-
|
||||||
|
@pytest.fixture
|
||||||
|
def reset_to_default_policy():
|
||||||
|
"""Reset default user authentication and user authentication type"""
|
||||||
|
@@ -70,7 +69,7 @@ def reset_to_default_policy():
|
||||||
|
def kinit_check_life(master, user):
|
||||||
|
"""Acquire a TGT and check if it's within the lifetime window"""
|
||||||
|
master.run_command(["kinit", user], stdin_text=f"{PASSWORD}\n")
|
||||||
|
- result = master.run_command("klist | grep krbtgt")
|
||||||
|
+ result = master.run_command("LANG=en_US.utf-8 klist | grep krbtgt")
|
||||||
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
||||||
|
|
||||||
|
|
||||||
|
@@ -81,6 +80,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def install(cls, mh):
|
||||||
|
+ tasks.install_packages(cls.master, LANG_PKG)
|
||||||
|
tasks.install_master(cls.master)
|
||||||
|
tasks.create_active_user(cls.master, USER1, PASSWORD)
|
||||||
|
tasks.create_active_user(cls.master, USER2, PASSWORD)
|
||||||
|
@@ -100,7 +100,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
|
||||||
|
master.run_command(['kinit', USER1],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command("LANG=en_US.utf-8 klist | grep krbtgt")
|
||||||
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
||||||
|
|
||||||
|
def test_krbtpolicy_password_and_hardended(self):
|
||||||
|
@@ -122,7 +122,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
|
||||||
|
master.run_command(['kinit', USER1],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt')
|
||||||
|
assert maxlife_within_policy(result.stdout_text, 600,
|
||||||
|
slush=600) is True
|
||||||
|
|
||||||
|
@@ -131,7 +131,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
# Verify that the short policy only applies to USER1
|
||||||
|
master.run_command(['kinit', USER2],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt')
|
||||||
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
||||||
|
|
||||||
|
def test_krbtpolicy_hardended(self):
|
||||||
|
@@ -151,7 +151,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
|
||||||
|
master.run_command(['kinit', USER1],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt')
|
||||||
|
assert maxlife_within_policy(result.stdout_text, 1800,
|
||||||
|
slush=1800) is True
|
||||||
|
|
||||||
|
@@ -160,7 +160,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
# Verify that the short policy only applies to USER1
|
||||||
|
master.run_command(['kinit', USER2],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt')
|
||||||
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
||||||
|
|
||||||
|
def test_krbtpolicy_password(self):
|
||||||
|
@@ -173,7 +173,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
|
||||||
|
master.run_command(['kinit', USER2],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt')
|
||||||
|
assert maxlife_within_policy(result.stdout_text, 1200,
|
||||||
|
slush=1200) is True
|
||||||
|
|
||||||
|
@@ -183,7 +183,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
master.run_command(['ipa', 'krbtpolicy-reset', USER2])
|
||||||
|
master.run_command(['kinit', USER2],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt')
|
||||||
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
||||||
|
|
||||||
|
def test_krbtpolicy_otp(self, reset_to_default_policy):
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
48
0005-trust-add-handle-missing-msSFU30MaxGidNumber.patch
Normal file
48
0005-trust-add-handle-missing-msSFU30MaxGidNumber.patch
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
From 97fc368df2db3b559a9def236d3c3e0a12bcdd0a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Date: Mon, 23 Jan 2023 20:28:17 +0100
|
||||||
|
Subject: [PATCH] trust-add: handle missing msSFU30MaxGidNumber
|
||||||
|
|
||||||
|
When ipa trust-add is executed with --range-type ad-trust-posix,
|
||||||
|
the server tries to find the max uidnumber and max gidnumber
|
||||||
|
from AD domain controller.
|
||||||
|
The values are extracted from the entry
|
||||||
|
CN=<domain>,CN=ypservers,CN=ypServ30,CN=RpcServices,CN=System,<AD suffix>
|
||||||
|
in the msSFU30MaxUidNumber and msSFU30MaxGidNumber attributes.
|
||||||
|
|
||||||
|
msSFU30MaxUidNumber is required but not msSFU30MaxGidNumber.
|
||||||
|
In case msSFU30MaxGidNumber is missing, the code is currently assigning
|
||||||
|
a "None" value and later on evaluates the max between this value and
|
||||||
|
msSFU30MaxUidNumber. The max function cannot compare None and a list
|
||||||
|
of string and triggers an exception.
|
||||||
|
|
||||||
|
To avoid the exception, assign [b'0'] to max gid if msSFU30MaxGidNumber
|
||||||
|
is missing. This way, the comparison succeeds and max returns the
|
||||||
|
value from msSFU30MaxUidNumber.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9310
|
||||||
|
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
---
|
||||||
|
ipaserver/plugins/trust.py | 5 ++++-
|
||||||
|
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py
|
||||||
|
index c074f6d6e609476e416c95bcbe607654718ae9ce..79264b8d8a3b15dd4e5d0553e4ce42194b0ae044 100644
|
||||||
|
--- a/ipaserver/plugins/trust.py
|
||||||
|
+++ b/ipaserver/plugins/trust.py
|
||||||
|
@@ -379,7 +379,10 @@ def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options):
|
||||||
|
range_type = u'ipa-ad-trust-posix'
|
||||||
|
|
||||||
|
max_uid = info.get('msSFU30MaxUidNumber')
|
||||||
|
- max_gid = info.get('msSFU30MaxGidNumber', None)
|
||||||
|
+ # if max_gid is missing, assume 0 and the max will
|
||||||
|
+ # be obtained from max_uid. We just checked that
|
||||||
|
+ # msSFU30MaxUidNumber is defined
|
||||||
|
+ max_gid = info.get('msSFU30MaxGidNumber', [b'0'])
|
||||||
|
max_id = int(max(max_uid, max_gid)[0])
|
||||||
|
|
||||||
|
base_id = int(info.get('msSFU30OrderNumber')[0])
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
337
0006-doc-Design-for-certificate-pruning.patch
Normal file
337
0006-doc-Design-for-certificate-pruning.patch
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
From 51b1c22d025bf40e9ef488bb0faf0c8dff303ccd Mon Sep 17 00:00:00 2001
|
||||||
|
From: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Date: Thu, 8 Dec 2022 16:18:07 -0500
|
||||||
|
Subject: [PATCH] doc: Design for certificate pruning
|
||||||
|
|
||||||
|
This describes how the certificate pruning capability of PKI
|
||||||
|
introduced in v11.3.0 will be integrated into IPA, primarily for
|
||||||
|
ACME.
|
||||||
|
|
||||||
|
Related: https://pagure.io/freeipa/issue/9294
|
||||||
|
|
||||||
|
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||||
|
---
|
||||||
|
doc/designs/expired_certificate_pruning.md | 297 +++++++++++++++++++++
|
||||||
|
doc/designs/index.rst | 1 +
|
||||||
|
2 files changed, 298 insertions(+)
|
||||||
|
create mode 100644 doc/designs/expired_certificate_pruning.md
|
||||||
|
|
||||||
|
diff --git a/doc/designs/expired_certificate_pruning.md b/doc/designs/expired_certificate_pruning.md
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..2c10d914020d3c12b6abb028323cd6796ec33e00
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/doc/designs/expired_certificate_pruning.md
|
||||||
|
@@ -0,0 +1,297 @@
|
||||||
|
+# Expired Certificate Pruning
|
||||||
|
+
|
||||||
|
+## Overview
|
||||||
|
+
|
||||||
|
+https://pagure.io/dogtagpki/issue/1750
|
||||||
|
+
|
||||||
|
+When using short-lived certs and regular issuance, the expired certs can build up in the PKI database and cause issues with replication, performance and overall database size.
|
||||||
|
+
|
||||||
|
+PKI has provided a new feature in 11.3.0, pruning, which is a job that can be executed on a schedule or manually to remove expired certificates and requests.
|
||||||
|
+
|
||||||
|
+Random Serial Numbers v3 (RSNv3) is mandatory to enable pruning.
|
||||||
|
+
|
||||||
|
+Both pruning and RSNv3 require PKI 11.3.0 or higher.
|
||||||
|
+
|
||||||
|
+## Use Cases
|
||||||
|
+
|
||||||
|
+ACME certificates in particular are generally short-lived and expired certificates can build up quickly in a dynamic environment. An example is a CI system that requests one or more certificates per run. These will build up infinitely without a way to remove the expired certificates.
|
||||||
|
+
|
||||||
|
+Another case is simply a very long-lived installation. Over time as hosts come and go certificates build up.
|
||||||
|
+
|
||||||
|
+## How to Use
|
||||||
|
+
|
||||||
|
+https://github.com/dogtagpki/pki/wiki/Configuring-CA-Database-Pruning provides a thorough description of the capabilities of the pruning job.
|
||||||
|
+
|
||||||
|
+The default configuration is to remove expired certificates and incomplete requests after 30 days.
|
||||||
|
+
|
||||||
|
+Pruning is disabled by default.
|
||||||
|
+
|
||||||
|
+Configuration is a four-step process:
|
||||||
|
+
|
||||||
|
+1. Configure the expiration thresholds
|
||||||
|
+2. Enable the job
|
||||||
|
+3. Schedule the job
|
||||||
|
+4. Restart the CA
|
||||||
|
+
|
||||||
|
+The job will be scheduled to use the PKI built-in cron-like timer. It is configured nearly identically to `crontab(5)`. On execution it will remove certificates and requests that fall outside the configured thresholds. LDAP search/time limits can be used to control how many are removed at once.
|
||||||
|
+
|
||||||
|
+In addition to the automated schedule it is possible to manually run the pruning job.
|
||||||
|
+
|
||||||
|
+The tool will not restart the CA. It will be left as an exercise for the user, who will be notified as needed.
|
||||||
|
+
|
||||||
|
+### Where to use
|
||||||
|
+
|
||||||
|
+The pruning configuration is not replicated. It should not be necessary to enable this task on all IPA servers, or more than one.
|
||||||
|
+
|
||||||
|
+Running the task simultaneously on multiple servers has a few downsides:
|
||||||
|
+
|
||||||
|
+* Additional stress on the LDAP server searching for expired certificates and requests
|
||||||
|
+* Unnecessary replication load deleting the same entries on multiple servers
|
||||||
|
+
|
||||||
|
+While enabling this on a single server represents a single-point-of-failure there should be no catastrophic consequences other than expired certificates and requests potentially building up. This can be cleared by enabling pruning on a different server. Depending on the size of the backlog this could take a couple of executions to catch up.
|
||||||
|
+
|
||||||
|
+## Design
|
||||||
|
+
|
||||||
|
+There are several operations, most of which act locally and one of which uses the PKI REST API.
|
||||||
|
+
|
||||||
|
+1. Updating the job configuration (enable, thresholds, etc). This will be done by running the `pki-server ca-config-set` command which modifies CS.cfg directly per the PKI wiki. A restart is required.
|
||||||
|
+
|
||||||
|
+2. Retrieving the current configuration for display. The `pki-server ca-config-find` command returns the entire configuration so the results will need to be filtered.
|
||||||
|
+
|
||||||
|
+3. Managing the job. This can be done using the REST API, https://github.com/dogtagpki/pki/wiki/PKI-REST-API . Operations include enabling the job and triggering it to run now.
|
||||||
|
+
|
||||||
|
+Theoretically for operations 1 and 2 we could use existing code to manually update `CS.cfg` and retrieve values. For future-proofing purposes calling `pki-server` is probably the better long-term option given the limited number of times this will be used. Configuration is likely to be one and done.
|
||||||
|
+
|
||||||
|
+There are four values each that can be managed for pruning certificates and requests:
|
||||||
|
+
|
||||||
|
+* expired cert/incomplete request time
|
||||||
|
+* time unit
|
||||||
|
+* LDAP search size limit
|
||||||
|
+* LDAP search time limit
|
||||||
|
+
|
||||||
|
+The first two configure when an expired certificate or incomplete request will be deleted. The unit can be one of: minute, hour, day, year. By default it is 30 days.
|
||||||
|
+
|
||||||
|
+The LDAP limits control how many entries are returned and how long the search can take. By default it is 1000 entries and unlimited time.
|
||||||
|
+
|
||||||
|
+### Configuration settings
|
||||||
|
+
|
||||||
|
+The configuration values will be set by running `pki-server ca-config-set` This will ensure best forward compatibility. The options are case-sensitive and not validated by the CA until restart. The values are not applied until the CA is restarted.
|
||||||
|
+
|
||||||
|
+### Configuring job execution time
|
||||||
|
+
|
||||||
|
+The CA provides a cron-like interface for scheduling jobs. To configure the job to run at midnight on the first of every month the PKI equivalent command-line is:
|
||||||
|
+
|
||||||
|
+```
|
||||||
|
+pki-server ca-config-set jobsScheduler.job.pruning.cron `"0 0 1 * *"`
|
||||||
|
+```
|
||||||
|
+
|
||||||
|
+This will be the default when pruning is enabled. A separate configuration option will be available for fine-tuning execution time.
|
||||||
|
+
|
||||||
|
+The format is defined https://access.redhat.com/documentation/en-us/red_hat_certificate_system/9/html/administration_guide/setting_up_specific_jobs#Frequency_Settings_for_Automated_Jobs
|
||||||
|
+
|
||||||
|
+### REST Authentication and Authorization
|
||||||
|
+
|
||||||
|
+The REST API for pruning is documented at https://github.com/dogtagpki/pki/wiki/PKI-Start-Job-REST-API
|
||||||
|
+
|
||||||
|
+A PKI job can define an owner that can manage the job over the REST API. We will automatically define the owner as `ipara` when pruning is enabled.
|
||||||
|
+
|
||||||
|
+Manually running the job will be done using the PKI REST API. Authentication to this API for our purposes is done at the `/ca/rest/account/login` endpoint. A cookie is returned which will be used in any subsequent calls. The IPA RA agent certificate will be used for authentication and authorization.
|
||||||
|
+
|
||||||
|
+### Commands
|
||||||
|
+
|
||||||
|
+This will be implemented in the ipa-acme-manage command. While strictly not completely ACME-related this is the primary driver for pruning.
|
||||||
|
+
|
||||||
|
+A new verb will be added, pruning, to be used for enabling and configuring pruning.
|
||||||
|
+
|
||||||
|
+### Enabling pruning
|
||||||
|
+
|
||||||
|
+`# ipa-acme-manage pruning --enable=TRUE`
|
||||||
|
+
|
||||||
|
+Enabling the job will call
|
||||||
|
+
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.enabled true`
|
||||||
|
+
|
||||||
|
+This will also set jobsScheduler.job.pruning.cron to `"0 0 1 * *"` if it has not already been set.
|
||||||
|
+
|
||||||
|
+Additionally it will set the job owner to `ipara` with:
|
||||||
|
+
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.owner ipara`
|
||||||
|
+
|
||||||
|
+Disabling the job will call
|
||||||
|
+
|
||||||
|
+`# pki-server ca-config-unset jobsScheduler.job.pruning.enabled`
|
||||||
|
+
|
||||||
|
+### Cron settings
|
||||||
|
+
|
||||||
|
+To modify the cron settings:
|
||||||
|
+
|
||||||
|
+`# ipa-acme-manage pruning --cron="Minute Hour Day_of_month Month_of_year Day_of_week"`
|
||||||
|
+
|
||||||
|
+Validation of the value will be:
|
||||||
|
+* each of the options is an integer
|
||||||
|
+* minute is within 0-59
|
||||||
|
+* hour is within 0-23
|
||||||
|
+* day of month is within 0-31
|
||||||
|
+* month of year is within 1-12
|
||||||
|
+* day of week is within 0-6
|
||||||
|
+
|
||||||
|
+No validation of setting February 31st will be done. That will be left to PKI. Buyer beware.
|
||||||
|
+
|
||||||
|
+### Disabling pruning
|
||||||
|
+
|
||||||
|
+`$ ipa-acme-manage pruning --enable=FALSE`
|
||||||
|
+
|
||||||
|
+This will remove the configuration option for `jobsScheduler.job.pruning.cron` just to be sure it no longer runs.
|
||||||
|
+
|
||||||
|
+### Configuration
|
||||||
|
+
|
||||||
|
+#### Pruning certificates
|
||||||
|
+
|
||||||
|
+`$ ipa-acme-manage pruning --certretention=VALUE --certretentionunit=UNIT`
|
||||||
|
+
|
||||||
|
+will be the equivalent of:
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.certRetentionTime 30`
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.certRetentionUnit day`
|
||||||
|
+
|
||||||
|
+The unit will always be required when modifying the time.
|
||||||
|
+
|
||||||
|
+`$ ipa-acme-manage pruning --certsearchsizelimit=VALUE --certsearchtimelimit=VALUE`
|
||||||
|
+
|
||||||
|
+will be the equivalent of:
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.certSearchSizeLimit 1000`
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.certSearchTimeLimit 0`
|
||||||
|
+
|
||||||
|
+A value of 0 for searchtimelimit is unlimited.
|
||||||
|
+
|
||||||
|
+#### Pruning requests
|
||||||
|
+
|
||||||
|
+`$ ipa-acme-manage pruning --requestretention=VALUE --requestretentionunit=UNIT`
|
||||||
|
+
|
||||||
|
+will be the equivalent of:
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionTime 30`
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionUnit day`
|
||||||
|
+
|
||||||
|
+The unit will always be required when modifying the time.
|
||||||
|
+
|
||||||
|
+`$ ipa-acme-manage pruning --requestsearchsizelimit=VALUE --requestsearchtimelimit=VALUE`
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+will be the equivalent of:
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.requestSearchSizeLimit 1000`
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.requestSearchTimeLimit 0`
|
||||||
|
+
|
||||||
|
+A value of 0 for searchtimelimit is unlimited.
|
||||||
|
+
|
||||||
|
+These options set the client-side limits. The server imposes its own search size and look through limits. This can be tuned for the uid=pkidbuser,ou=people,o=ipaca user via https://access.redhat.com/documentation/en-us/red_hat_directory_server/11/html/administration_guide/ldapsearch-ex-complex-range
|
||||||
|
+
|
||||||
|
+### Showing the Configuration
|
||||||
|
+
|
||||||
|
+To display the current configuration run `pki-server ca-config-find` and filter the results to only those that contain `jobsScheduler.job.pruning`.
|
||||||
|
+
|
||||||
|
+Default values are not included so will need to be set by `ipa-acme-manage` before displaying.
|
||||||
|
+
|
||||||
|
+Output may look something like:
|
||||||
|
+
|
||||||
|
+```console
|
||||||
|
+# ipa-acme-manage pruning --config-show
|
||||||
|
+Enabled: TRUE
|
||||||
|
+Certificate retention time: 30 days
|
||||||
|
+Certificate search size limit: 1000
|
||||||
|
+Certificate search time limit: 0
|
||||||
|
+Request retention time: 30 days
|
||||||
|
+Request search size limit: 1000
|
||||||
|
+Request search time limit: 0
|
||||||
|
+Cron: 0 0 1 * *
|
||||||
|
+```
|
||||||
|
+
|
||||||
|
+## Implementation
|
||||||
|
+
|
||||||
|
+For online REST operations (login, run job) we will use the `ipaserver/plugins/dogtag.py::RestClient` class to manage the requests. This will take care of the authentication cookie, etc.
|
||||||
|
+
|
||||||
|
+The class uses dogtag.https_request() will can take PEM cert and key files as arguments. These will be used for authentication.
|
||||||
|
+
|
||||||
|
+For the non-REST operations (configuration, cron settings) the tool will fork out to pki-server ca-config-set.
|
||||||
|
+
|
||||||
|
+### UI
|
||||||
|
+
|
||||||
|
+This will only be configurable on the command-line.
|
||||||
|
+
|
||||||
|
+### CLI
|
||||||
|
+
|
||||||
|
+Overview of the CLI commands. Example:
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+| Command | Options |
|
||||||
|
+| --- | ----- |
|
||||||
|
+| ipa-acme-manage pruning | --enable=TRUE |
|
||||||
|
+| ipa-acme-manage pruning | --enable=FALSE |
|
||||||
|
+| ipa-acme-manage pruning | --cron=`"0 0 1 * *"` |
|
||||||
|
+| ipa-acme-manage pruning | --certretention=30 --certretentionunit=day |
|
||||||
|
+| ipa-acme-manage pruning | --certsearchsizelimit=1000 --certsearchtimelimit=0 |
|
||||||
|
+| ipa-acme-manage pruning | --requestretention=30 --requestretentionunit=day |
|
||||||
|
+| ipa-acme-manage pruning | --requestsearchsizelimit=1000 --requestsearchtimelimit=0 |
|
||||||
|
+| ipa-acme-manage pruning | --config-show |
|
||||||
|
+
|
||||||
|
+ipa-acme-manage can only be run as root.
|
||||||
|
+
|
||||||
|
+### Configuration
|
||||||
|
+
|
||||||
|
+Configuration changes will be made to /etc/pki/pki-tomcat/ca/CS.cfg
|
||||||
|
+
|
||||||
|
+## Upgrade
|
||||||
|
+
|
||||||
|
+No expected impact on upgrades.
|
||||||
|
+
|
||||||
|
+## Test plan
|
||||||
|
+
|
||||||
|
+Testing will consist of:
|
||||||
|
+
|
||||||
|
+* Use the default configuration
|
||||||
|
+* enabling the pruning job
|
||||||
|
+* issue one or more certificates
|
||||||
|
+* move time forward +1 days after expiration
|
||||||
|
+* manually running the job
|
||||||
|
+* validating that the certificates are removed
|
||||||
|
+
|
||||||
|
+For size/time limit testing, create a large number of certificates/requests and set the search limit to a low value, then ensure that the number of deleted certs is equal to the search limit. Testing timelimit in this way may be less predictable as it may require a massive number of entries to find to timeout on a non-busy server.
|
||||||
|
+
|
||||||
|
+## Troubleshooting and debugging
|
||||||
|
+
|
||||||
|
+The PKI debug log will contain job information.
|
||||||
|
+
|
||||||
|
+```
|
||||||
|
+2022-12-08 21:14:25 [https-jsse-nio-8443-exec-8] INFO: JobService: Starting job pruning
|
||||||
|
+2022-12-08 21:14:25 [https-jsse-nio-8443-exec-8] INFO: JobService: - principal: null
|
||||||
|
+2022-12-08 21:14:51 [https-jsse-nio-8443-exec-10] INFO: JobService: Starting job pruning 2022-12-08 21:14:51 [https-jsse-nio-8443-exec-10] INFO: JobService: - principal: null
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: Authenticating certificate chain:
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - CN=IPA RA,O=EXAMPLE.TEST
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - CN=Certificate Authority,O=EXAMPLE.TEST
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: LDAPSession: Retrieving cn=19072098145751813471503860299601579276,ou=certificateRepository, ou=ca,o=ipaca
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: CertUserDBAuthentication: UID ipara authenticated.
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: User ipara authenticated
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: UGSubsystem: Retrieving user uid=ipara,ou=People,o=ipaca
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: User DN: uid=ipara,ou=people,o=ipaca
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: Roles:
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - Certificate Manager Agents
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - Registration Manager Agents
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - Security Domain Administrators
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - Enterprise ACME Administrators
|
||||||
|
+2022-12-08 21:15:24 [https-jsse-nio-8443-exec-12] INFO: JobService: Starting job pruning
|
||||||
|
+2022-12-08 21:15:24 [https-jsse-nio-8443-exec-12] INFO: JobService: - principal: GenericPrincipal[ipara(Certificate Manager Agents,Enterprise ACME Administrators,Registration Manager Agents,Security Domain Administrators,)]
|
||||||
|
+2022-12-08 21:15:24 [https-jsse-nio-8443-exec-12] INFO: JobsScheduler: Starting job pruning
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: PruningJob: Running pruning job at Thu Dec 08 21:15:24 UTC 2022
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: PruningJob: Pruning certs expired before Tue Nov 08 21:15:24 UTC 2022
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: PruningJob: - filter: (&(x509Cert.notAfter<=1667942124527)(!(x509Cert.notAfter=1667942124527)))
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: LDAPSession: Searching ou=certificateRepository, ou=ca,o=ipaca for (&(notAfter<=20221108211524Z)(!(notAfter=20221108211524Z)))
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: PruningJob: Pruning incomplete requests last modified before Tue Nov 08 21:15:24 UTC 2022
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: PruningJob: - filter: (&(!(requestState=complete))(requestModifyTime<=1667942124527)(!(requestModifyTime=1667942124527)))
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: LDAPSession: Searching ou=ca, ou=requests,o=ipaca for (&(!(requestState=complete))(dateOfModify<=20221108211524Z)(!(dateOfModify=20221108211524Z)))
|
||||||
|
+```
|
||||||
|
diff --git a/doc/designs/index.rst b/doc/designs/index.rst
|
||||||
|
index 570e526fe35d510feeac62a44dd59224289e0506..1d41c0f84f0d7d3d5f184a47e31b4e71a890805d 100644
|
||||||
|
--- a/doc/designs/index.rst
|
||||||
|
+++ b/doc/designs/index.rst
|
||||||
|
@@ -14,6 +14,7 @@ FreeIPA design documentation
|
||||||
|
hsm.md
|
||||||
|
krb-ticket-policy.md
|
||||||
|
extdom-plugin-protocol.md
|
||||||
|
+ expired_certificate_pruning.md
|
||||||
|
expiring-password-notification.md
|
||||||
|
ldap_grace_period.md
|
||||||
|
ldap_pam_passthrough.md
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
684
0007-ipa-acme-manage-add-certificate-request-pruning-mana.patch
Normal file
684
0007-ipa-acme-manage-add-certificate-request-pruning-mana.patch
Normal file
@ -0,0 +1,684 @@
|
|||||||
|
From 9246a8a003b2b0062e07c289cd7cde8fe902b16f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Date: Thu, 12 Jan 2023 15:06:27 -0500
|
||||||
|
Subject: [PATCH] ipa-acme-manage: add certificate/request pruning management
|
||||||
|
|
||||||
|
Configures PKI to remove expired certificates and non-resolved
|
||||||
|
requests on a schedule.
|
||||||
|
|
||||||
|
This is geared towards ACME which can generate a lot of certificates
|
||||||
|
over a short period of time but is general purpose. It lives in
|
||||||
|
ipa-acme-manage because that is the primary reason for including it.
|
||||||
|
|
||||||
|
Random Serial Numbers v3 must be enabled for this to work.
|
||||||
|
|
||||||
|
Enabling pruning enables the job scheduler within CS and sets the
|
||||||
|
job user as the IPA RA user which has full rights to certificates
|
||||||
|
and requests.
|
||||||
|
|
||||||
|
Disabling pruning does not disable the job scheduler because the
|
||||||
|
tool is stateless. Having the scheduler enabled should not be a
|
||||||
|
problem.
|
||||||
|
|
||||||
|
A restart of PKI is required to apply any changes. This tool forks
|
||||||
|
out to pki-server which does direct writes to CS.cfg. It might
|
||||||
|
be easier to use our own tooling for this but this makes the
|
||||||
|
integration tighter so we pick up any improvements in PKI.
|
||||||
|
|
||||||
|
The "cron" setting is quite limited, taking only integer values
|
||||||
|
and *. It does not accept ranges, either - or /.
|
||||||
|
|
||||||
|
No error checking is done in PKI when setting a value, only when
|
||||||
|
attempting to use it, so some rudimentary validation is done.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9294
|
||||||
|
|
||||||
|
Signed-off-by: Rob Crittenden rcritten@redhat.com
|
||||||
|
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
---
|
||||||
|
install/tools/man/ipa-acme-manage.1 | 83 +++++++
|
||||||
|
ipaserver/install/ipa_acme_manage.py | 303 ++++++++++++++++++++++++-
|
||||||
|
ipatests/test_integration/test_acme.py | 158 +++++++++++++
|
||||||
|
3 files changed, 534 insertions(+), 10 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/install/tools/man/ipa-acme-manage.1 b/install/tools/man/ipa-acme-manage.1
|
||||||
|
index e15d25bd0017d8bd71e425fcb633827fa6f67693..e6cec4e4a7fd460c514a72456a2dc9a2e3682ebd 100644
|
||||||
|
--- a/install/tools/man/ipa-acme-manage.1
|
||||||
|
+++ b/install/tools/man/ipa-acme-manage.1
|
||||||
|
@@ -27,6 +27,89 @@ Disable the ACME service on this host.
|
||||||
|
.TP
|
||||||
|
\fBstatus\fR
|
||||||
|
Display the status of the ACME service.
|
||||||
|
+.TP
|
||||||
|
+\fBpruning\fR
|
||||||
|
+Configure certificate and request pruning.
|
||||||
|
+
|
||||||
|
+.SH "PRUNING"
|
||||||
|
+Pruning is a job that runs in the CA that can remove expired
|
||||||
|
+certificates and certificate requests which have not been issued.
|
||||||
|
+This is particularly important when using short-lived certificates
|
||||||
|
+like those issued with the ACME protocol. Pruning requires that
|
||||||
|
+the IPA server be installed with random serial numbers enabled.
|
||||||
|
+
|
||||||
|
+The CA needs to be restarted after modifying the pruning configuration.
|
||||||
|
+
|
||||||
|
+The job is a cron-like task within the CA that is controlled by a
|
||||||
|
+number of options which dictate how long after the certificate or
|
||||||
|
+request is considered no longer valid and removed from the LDAP
|
||||||
|
+database.
|
||||||
|
+
|
||||||
|
+The cron time and date fields are:
|
||||||
|
+.IP
|
||||||
|
+.ta 1.5i
|
||||||
|
+field allowed values
|
||||||
|
+.br
|
||||||
|
+----- --------------
|
||||||
|
+.br
|
||||||
|
+minute 0-59
|
||||||
|
+.br
|
||||||
|
+hour 0-23
|
||||||
|
+.br
|
||||||
|
+day of month 1-31
|
||||||
|
+.br
|
||||||
|
+month 1-12
|
||||||
|
+.br
|
||||||
|
+day of week 0-6 (0 is Sunday)
|
||||||
|
+.br
|
||||||
|
+.PP
|
||||||
|
+
|
||||||
|
+The cron syntax is limited to * or specific numbers. Ranges are not supported.
|
||||||
|
+
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-enable\fR
|
||||||
|
+Enable certificate pruning.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-disable\fR
|
||||||
|
+Disable certificate pruning.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-cron=CRON\fR
|
||||||
|
+Configure the pruning cron job. The syntax is similar to crontab(5) syntax.
|
||||||
|
+For example, "0 0 1 * *" schedules the job to run at 12:00am on the first
|
||||||
|
+day of each month.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-certretention=CERTRETENTION\fR
|
||||||
|
+Certificate retention time. The default is 30.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-certretentionunit=CERTRETENTIONUNIT\fR
|
||||||
|
+Certificate retention units. Valid units are: minute, hour, day, year.
|
||||||
|
+The default is days.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-certsearchsizelimit=CERTSEARCHSIZELIMIT\fR
|
||||||
|
+LDAP search size limit searching for expired certificates. The default is 1000. This is a client-side limit. There may be additional server-side limitations.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-certsearchtimelimit=CERTSEARCHTIMELIMIT\fR
|
||||||
|
+LDAP search time limit searching for expired certificates. The default is 0, no limit. This is a client-side limit. There may be additional server-side limitations.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-requestretention=REQUESTRETENTION\fR
|
||||||
|
+Request retention time. The default is 30.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-requestretentionunit=REQUESTRETENTIONUNIT\fR
|
||||||
|
+Request retention units. Valid units are: minute, hour, day, year.
|
||||||
|
+The default is days.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-requestsearchsizelimit=REQUESTSEARCHSIZELIMIT\fR
|
||||||
|
+LDAP search size limit searching for unfulfilled requests. The default is 1000. There may be additional server-side limitations.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-requestsearchtimelimit=REQUESTSEARCHTIMELIMIT\fR
|
||||||
|
+LDAP search time limit searching for unfulfilled requests. The default is 0, no limit. There may be additional server-side limitations.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-config\-show\fR
|
||||||
|
+Show the current pruning configuration
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-run\fR
|
||||||
|
+Run the pruning job now. The IPA RA certificate is used to authenticate to the PKI REST backend.
|
||||||
|
+
|
||||||
|
|
||||||
|
.SH "EXIT STATUS"
|
||||||
|
0 if the command was successful
|
||||||
|
diff --git a/ipaserver/install/ipa_acme_manage.py b/ipaserver/install/ipa_acme_manage.py
|
||||||
|
index 0474b9f4a051063ac6df41a81877a2af9d4a2096..b7b2111d9edcec2580aa4a485d7a7340146ff065 100644
|
||||||
|
--- a/ipaserver/install/ipa_acme_manage.py
|
||||||
|
+++ b/ipaserver/install/ipa_acme_manage.py
|
||||||
|
@@ -2,7 +2,12 @@
|
||||||
|
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
+
|
||||||
|
import enum
|
||||||
|
+import pki.util
|
||||||
|
+import logging
|
||||||
|
+
|
||||||
|
+from optparse import OptionGroup # pylint: disable=deprecated-module
|
||||||
|
|
||||||
|
from ipalib import api, errors, x509
|
||||||
|
from ipalib import _
|
||||||
|
@@ -10,10 +15,64 @@ from ipalib.facts import is_ipa_configured
|
||||||
|
from ipaplatform.paths import paths
|
||||||
|
from ipapython.admintool import AdminTool
|
||||||
|
from ipapython import cookie, dogtag
|
||||||
|
+from ipapython.ipautil import run
|
||||||
|
+from ipapython.certdb import NSSDatabase, EXTERNAL_CA_TRUST_FLAGS
|
||||||
|
from ipaserver.install import cainstance
|
||||||
|
+from ipaserver.install.ca import lookup_random_serial_number_version
|
||||||
|
|
||||||
|
from ipaserver.plugins.dogtag import RestClient
|
||||||
|
|
||||||
|
+logger = logging.getLogger(__name__)
|
||||||
|
+
|
||||||
|
+default_pruning_options = {
|
||||||
|
+ 'certRetentionTime': '30',
|
||||||
|
+ 'certRetentionUnit': 'day',
|
||||||
|
+ 'certSearchSizeLimit': '1000',
|
||||||
|
+ 'certSearchTimeLimit': '0',
|
||||||
|
+ 'requestRetentionTime': 'day',
|
||||||
|
+ 'requestRetentionUnit': '30',
|
||||||
|
+ 'requestSearchSizeLimit': '1000',
|
||||||
|
+ 'requestSearchTimeLimit': '0',
|
||||||
|
+ 'cron': ''
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+pruning_labels = {
|
||||||
|
+ 'certRetentionTime': 'Certificate Retention Time',
|
||||||
|
+ 'certRetentionUnit': 'Certificate Retention Unit',
|
||||||
|
+ 'certSearchSizeLimit': 'Certificate Search Size Limit',
|
||||||
|
+ 'certSearchTimeLimit': 'Certificate Search Time Limit',
|
||||||
|
+ 'requestRetentionTime': 'Request Retention Time',
|
||||||
|
+ 'requestRetentionUnit': 'Request Retention Unit',
|
||||||
|
+ 'requestSearchSizeLimit': 'Request Search Size Limit',
|
||||||
|
+ 'requestSearchTimeLimit': 'Request Search Time Limit',
|
||||||
|
+ 'cron': 'cron Schedule'
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def validate_range(val, min, max):
|
||||||
|
+ """dogtag appears to have no error checking in the cron
|
||||||
|
+ entry so do some minimum amount of validation. It is
|
||||||
|
+ left as an exercise for the user to do month/day
|
||||||
|
+ validation so requesting Feb 31 will be accepted.
|
||||||
|
+
|
||||||
|
+ Only * and a number within a min/max range are allowed.
|
||||||
|
+ """
|
||||||
|
+ if val == '*':
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ if '-' in val or '/' in val:
|
||||||
|
+ raise ValueError(f"{val} ranges are not supported")
|
||||||
|
+
|
||||||
|
+ try:
|
||||||
|
+ int(val)
|
||||||
|
+ except ValueError:
|
||||||
|
+ # raise a clearer error
|
||||||
|
+ raise ValueError(f"{val} is not a valid integer")
|
||||||
|
+
|
||||||
|
+ if int(val) < min or int(val) > max:
|
||||||
|
+ raise ValueError(f"{val} not within the range {min}-{max}")
|
||||||
|
+
|
||||||
|
+
|
||||||
|
# Manages the FreeIPA ACME service on a per-server basis.
|
||||||
|
#
|
||||||
|
# This program is a stop-gap until the deployment-wide management of
|
||||||
|
@@ -66,32 +125,121 @@ class acme_state(RestClient):
|
||||||
|
status, unused, _unused = self._request('/acme/disable',
|
||||||
|
headers=headers)
|
||||||
|
if status != 200:
|
||||||
|
- raise RuntimeError('Failed to disble ACME')
|
||||||
|
+ raise RuntimeError('Failed to disable ACME')
|
||||||
|
|
||||||
|
|
||||||
|
class Command(enum.Enum):
|
||||||
|
ENABLE = 'enable'
|
||||||
|
DISABLE = 'disable'
|
||||||
|
STATUS = 'status'
|
||||||
|
+ PRUNE = 'pruning'
|
||||||
|
|
||||||
|
|
||||||
|
class IPAACMEManage(AdminTool):
|
||||||
|
command_name = "ipa-acme-manage"
|
||||||
|
- usage = "%prog [enable|disable|status]"
|
||||||
|
+ usage = "%prog [enable|disable|status|pruning]"
|
||||||
|
description = "Manage the IPA ACME service"
|
||||||
|
|
||||||
|
+ @classmethod
|
||||||
|
+ def add_options(cls, parser):
|
||||||
|
+
|
||||||
|
+ group = OptionGroup(parser, 'Pruning')
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--enable", dest="enable", action="store_true",
|
||||||
|
+ default=False, help="Enable certificate pruning")
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--disable", dest="disable", action="store_true",
|
||||||
|
+ default=False, help="Disable certificate pruning")
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--cron", dest="cron", action="store",
|
||||||
|
+ default=None, help="Configure the pruning cron job")
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--certretention", dest="certretention", action="store",
|
||||||
|
+ default=None, help="Certificate retention time", type=int)
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--certretentionunit", dest="certretentionunit", action="store",
|
||||||
|
+ choices=['minute', 'hour', 'day', 'year'],
|
||||||
|
+ default=None, help="Certificate retention units")
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--certsearchsizelimit", dest="certsearchsizelimit",
|
||||||
|
+ action="store",
|
||||||
|
+ default=None, help="LDAP search size limit", type=int)
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--certsearchtimelimit", dest="certsearchtimelimit", action="store",
|
||||||
|
+ default=None, help="LDAP search time limit", type=int)
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--requestretention", dest="requestretention", action="store",
|
||||||
|
+ default=None, help="Request retention time", type=int)
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--requestretentionunit", dest="requestretentionunit",
|
||||||
|
+ choices=['minute', 'hour', 'day', 'year'],
|
||||||
|
+ action="store", default=None, help="Request retention units")
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--requestsearchsizelimit", dest="requestsearchsizelimit",
|
||||||
|
+ action="store",
|
||||||
|
+ default=None, help="LDAP search size limit", type=int)
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--requestsearchtimelimit", dest="requestsearchtimelimit",
|
||||||
|
+ action="store",
|
||||||
|
+ default=None, help="LDAP search time limit", type=int)
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--config-show", dest="config_show", action="store_true",
|
||||||
|
+ default=False, help="Show the current pruning configuration")
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--run", dest="run", action="store_true",
|
||||||
|
+ default=False, help="Run the pruning job now")
|
||||||
|
+ parser.add_option_group(group)
|
||||||
|
+ super(IPAACMEManage, cls).add_options(parser, debug_option=True)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
def validate_options(self):
|
||||||
|
- # needs root now - if/when this program changes to an API
|
||||||
|
- # wrapper we will no longer need root.
|
||||||
|
super(IPAACMEManage, self).validate_options(needs_root=True)
|
||||||
|
|
||||||
|
if len(self.args) < 1:
|
||||||
|
self.option_parser.error(f'missing command argument')
|
||||||
|
- else:
|
||||||
|
- try:
|
||||||
|
- self.command = Command(self.args[0])
|
||||||
|
- except ValueError:
|
||||||
|
- self.option_parser.error(f'unknown command "{self.args[0]}"')
|
||||||
|
+
|
||||||
|
+ if self.args[0] == "pruning":
|
||||||
|
+ if self.options.enable and self.options.disable:
|
||||||
|
+ self.option_parser.error("Cannot both enable and disable")
|
||||||
|
+ elif (
|
||||||
|
+ any(
|
||||||
|
+ [
|
||||||
|
+ self.options.enable,
|
||||||
|
+ self.options.disable,
|
||||||
|
+ self.options.cron,
|
||||||
|
+ self.options.certretention,
|
||||||
|
+ self.options.certretentionunit,
|
||||||
|
+ self.options.requestretention,
|
||||||
|
+ self.options.requestretentionunit,
|
||||||
|
+ self.options.certsearchsizelimit,
|
||||||
|
+ self.options.certsearchtimelimit,
|
||||||
|
+ self.options.requestsearchsizelimit,
|
||||||
|
+ self.options.requestsearchtimelimit,
|
||||||
|
+ ]
|
||||||
|
+ )
|
||||||
|
+ and (self.options.config_show or self.options.run)
|
||||||
|
+ ):
|
||||||
|
+
|
||||||
|
+ self.option_parser.error(
|
||||||
|
+ "Cannot change and show config or run at the same time"
|
||||||
|
+ )
|
||||||
|
+ elif self.options.cron:
|
||||||
|
+ if len(self.options.cron.split()) != 5:
|
||||||
|
+ self.option_parser.error("Invalid format for --cron")
|
||||||
|
+ # dogtag does no validation when setting an option so
|
||||||
|
+ # do the minimum. The dogtag cron is limited compared to
|
||||||
|
+ # crontab(5).
|
||||||
|
+ opt = self.options.cron.split()
|
||||||
|
+ validate_range(opt[0], 0, 59)
|
||||||
|
+ validate_range(opt[1], 0, 23)
|
||||||
|
+ validate_range(opt[2], 1, 31)
|
||||||
|
+ validate_range(opt[3], 1, 12)
|
||||||
|
+ validate_range(opt[4], 0, 6)
|
||||||
|
+
|
||||||
|
+ try:
|
||||||
|
+ self.command = Command(self.args[0])
|
||||||
|
+ except ValueError:
|
||||||
|
+ self.option_parser.error(f'unknown command "{self.args[0]}"')
|
||||||
|
|
||||||
|
def check_san_status(self):
|
||||||
|
"""
|
||||||
|
@@ -100,6 +248,140 @@ class IPAACMEManage(AdminTool):
|
||||||
|
cert = x509.load_certificate_from_file(paths.HTTPD_CERT_FILE)
|
||||||
|
cainstance.check_ipa_ca_san(cert)
|
||||||
|
|
||||||
|
+ def pruning(self):
|
||||||
|
+ def run_pki_server(command, directive, prefix, value=None):
|
||||||
|
+ """Take a set of arguments to append to pki-server"""
|
||||||
|
+ args = [
|
||||||
|
+ 'pki-server', command,
|
||||||
|
+ f'{prefix}.{directive}'
|
||||||
|
+ ]
|
||||||
|
+ if value:
|
||||||
|
+ args.extend([str(value)])
|
||||||
|
+ logger.debug(args)
|
||||||
|
+ result = run(args, raiseonerr=False, capture_output=True,
|
||||||
|
+ capture_error=True)
|
||||||
|
+ if result.returncode != 0:
|
||||||
|
+ raise RuntimeError(result.error_output)
|
||||||
|
+ return result
|
||||||
|
+
|
||||||
|
+ def ca_config_set(directive, value,
|
||||||
|
+ prefix='jobsScheduler.job.pruning'):
|
||||||
|
+ run_pki_server('ca-config-set', directive, prefix, value)
|
||||||
|
+ # ca-config-set always succeeds, even if the option is
|
||||||
|
+ # not supported.
|
||||||
|
+ newvalue = ca_config_show(directive)
|
||||||
|
+ if str(value) != newvalue.strip():
|
||||||
|
+ raise RuntimeError('Updating %s failed' % directive)
|
||||||
|
+
|
||||||
|
+ def ca_config_show(directive):
|
||||||
|
+ result = run_pki_server('ca-config-show', directive,
|
||||||
|
+ prefix='jobsScheduler.job.pruning')
|
||||||
|
+ return result.output.strip()
|
||||||
|
+
|
||||||
|
+ def config_show():
|
||||||
|
+ status = ca_config_show('enabled')
|
||||||
|
+ if status.strip() == 'true':
|
||||||
|
+ print("Status: enabled")
|
||||||
|
+ else:
|
||||||
|
+ print("Status: disabled")
|
||||||
|
+ for option in (
|
||||||
|
+ 'certRetentionTime', 'certRetentionUnit',
|
||||||
|
+ 'certSearchSizeLimit', 'certSearchTimeLimit',
|
||||||
|
+ 'requestRetentionTime', 'requestRetentionUnit',
|
||||||
|
+ 'requestSearchSizeLimit', 'requestSearchTimeLimit',
|
||||||
|
+ 'cron',
|
||||||
|
+ ):
|
||||||
|
+ value = ca_config_show(option)
|
||||||
|
+ if value:
|
||||||
|
+ print("{}: {}".format(pruning_labels[option], value))
|
||||||
|
+ else:
|
||||||
|
+ print("{}: {}".format(pruning_labels[option],
|
||||||
|
+ default_pruning_options[option]))
|
||||||
|
+
|
||||||
|
+ def run_pruning():
|
||||||
|
+ """Run the pruning job manually"""
|
||||||
|
+
|
||||||
|
+ with NSSDatabase() as tmpdb:
|
||||||
|
+ print("Preparing...")
|
||||||
|
+ tmpdb.create_db()
|
||||||
|
+ tmpdb.import_files((paths.RA_AGENT_PEM, paths.RA_AGENT_KEY),
|
||||||
|
+ import_keys=True)
|
||||||
|
+ tmpdb.import_files((paths.IPA_CA_CRT,))
|
||||||
|
+ for nickname, trust_flags in tmpdb.list_certs():
|
||||||
|
+ if trust_flags.has_key:
|
||||||
|
+ ra_nickname = nickname
|
||||||
|
+ continue
|
||||||
|
+ # external is suffucient for our purposes: C,,
|
||||||
|
+ tmpdb.trust_root_cert(nickname, EXTERNAL_CA_TRUST_FLAGS)
|
||||||
|
+ print("Starting job...")
|
||||||
|
+ args = ['pki', '-C', tmpdb.pwd_file, '-d', tmpdb.secdir,
|
||||||
|
+ '-n', ra_nickname,
|
||||||
|
+ 'ca-job-start', 'pruning']
|
||||||
|
+ logger.debug(args)
|
||||||
|
+ run(args, stdin='y')
|
||||||
|
+
|
||||||
|
+ pki_version = pki.util.Version(pki.specification_version())
|
||||||
|
+ if pki_version < pki.util.Version("11.3.0"):
|
||||||
|
+ raise RuntimeError(
|
||||||
|
+ 'Certificate pruning is not supported in PKI version %s'
|
||||||
|
+ % pki_version
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ if lookup_random_serial_number_version(api) == 0:
|
||||||
|
+ raise RuntimeError(
|
||||||
|
+ 'Certificate pruning requires random serial numbers'
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ if self.options.config_show:
|
||||||
|
+ config_show()
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ if self.options.run:
|
||||||
|
+ run_pruning()
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ # Don't play the enable/disable at the same time game
|
||||||
|
+ if self.options.enable:
|
||||||
|
+ ca_config_set('owner', 'ipara')
|
||||||
|
+ ca_config_set('enabled', 'true')
|
||||||
|
+ ca_config_set('enabled', 'true', 'jobsScheduler')
|
||||||
|
+ elif self.options.disable:
|
||||||
|
+ ca_config_set('enabled', 'false')
|
||||||
|
+
|
||||||
|
+ # pki-server ca-config-set can only set one option at a time so
|
||||||
|
+ # loop through all the options and set what is there.
|
||||||
|
+ if self.options.certretention:
|
||||||
|
+ ca_config_set('certRetentionTime',
|
||||||
|
+ self.options.certretention)
|
||||||
|
+ if self.options.certretentionunit:
|
||||||
|
+ ca_config_set('certRetentionUnit',
|
||||||
|
+ self.options.certretentionunit)
|
||||||
|
+ if self.options.certsearchtimelimit:
|
||||||
|
+ ca_config_set('certSearchTimeLimit',
|
||||||
|
+ self.options.certsearchtimelimit)
|
||||||
|
+ if self.options.certsearchsizelimit:
|
||||||
|
+ ca_config_set('certSearchSizeLimit',
|
||||||
|
+ self.options.certsearchsizelimit)
|
||||||
|
+ if self.options.requestretention:
|
||||||
|
+ ca_config_set('requestRetentionTime',
|
||||||
|
+ self.options.requestretention)
|
||||||
|
+ if self.options.requestretentionunit:
|
||||||
|
+ ca_config_set('requestRetentionUnit',
|
||||||
|
+ self.options.requestretentionunit)
|
||||||
|
+ if self.options.requestsearchsizelimit:
|
||||||
|
+ ca_config_set('requestSearchSizeLimit',
|
||||||
|
+ self.options.requestsearchsizelimit)
|
||||||
|
+ if self.options.requestsearchtimelimit:
|
||||||
|
+ ca_config_set('requestSearchTimeLimit',
|
||||||
|
+ self.options.requestsearchtimelimit)
|
||||||
|
+ if self.options.cron:
|
||||||
|
+ ca_config_set('cron', self.options.cron)
|
||||||
|
+
|
||||||
|
+ config_show()
|
||||||
|
+
|
||||||
|
+ print("The CA service must be restarted for changes to take effect")
|
||||||
|
+
|
||||||
|
+
|
||||||
|
def run(self):
|
||||||
|
if not is_ipa_configured():
|
||||||
|
print("IPA is not configured.")
|
||||||
|
@@ -123,7 +405,8 @@ class IPAACMEManage(AdminTool):
|
||||||
|
elif self.command == Command.STATUS:
|
||||||
|
status = "enabled" if dogtag.acme_status() else "disabled"
|
||||||
|
print("ACME is {}".format(status))
|
||||||
|
- return 0
|
||||||
|
+ elif self.command == Command.PRUNE:
|
||||||
|
+ self.pruning()
|
||||||
|
else:
|
||||||
|
raise RuntimeError('programmer error: unhandled enum case')
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py
|
||||||
|
index 15d7543cfb0fa0fcb921166f7cd8f13d0535a41d..93e785d8febd9fa8d7b3ef87ecb3f2eb42ac5da2 100644
|
||||||
|
--- a/ipatests/test_integration/test_acme.py
|
||||||
|
+++ b/ipatests/test_integration/test_acme.py
|
||||||
|
@@ -12,6 +12,9 @@ from ipalib.constants import IPA_CA_RECORD
|
||||||
|
from ipatests.test_integration.base import IntegrationTest
|
||||||
|
from ipatests.pytest_ipa.integration import tasks
|
||||||
|
from ipatests.test_integration.test_caless import CALessBase, ipa_certs_cleanup
|
||||||
|
+from ipatests.test_integration.test_random_serial_numbers import (
|
||||||
|
+ pki_supports_RSNv3
|
||||||
|
+)
|
||||||
|
from ipaplatform.osinfo import osinfo
|
||||||
|
from ipaplatform.paths import paths
|
||||||
|
from ipatests.test_integration.test_external_ca import (
|
||||||
|
@@ -388,6 +391,16 @@ class TestACME(CALessBase):
|
||||||
|
status = check_acme_status(self.replicas[0], 'disabled')
|
||||||
|
assert status == 'disabled'
|
||||||
|
|
||||||
|
+ def test_acme_pruning_no_random_serial(self):
|
||||||
|
+ """This ACME install is configured without random serial
|
||||||
|
+ numbers. Verify that we can't enable pruning on it."""
|
||||||
|
+ self.master.run_command(['ipa-acme-manage', 'enable'])
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning', '--enable'],
|
||||||
|
+ raiseonerr=False)
|
||||||
|
+ assert result.returncode == 1
|
||||||
|
+ assert "requires random serial numbers" in result.stderr_text
|
||||||
|
+
|
||||||
|
@server_install_teardown
|
||||||
|
def test_third_party_certs(self):
|
||||||
|
"""Require ipa-ca SAN on replacement web certificates"""
|
||||||
|
@@ -630,3 +643,148 @@ class TestACMERenew(IntegrationTest):
|
||||||
|
renewed_expiry = cert.not_valid_after
|
||||||
|
|
||||||
|
assert initial_expiry != renewed_expiry
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class TestACMEPrune(IntegrationTest):
|
||||||
|
+ """Validate that ipa-acme-manage configures dogtag for pruning"""
|
||||||
|
+
|
||||||
|
+ random_serial = True
|
||||||
|
+
|
||||||
|
+ @classmethod
|
||||||
|
+ def install(cls, mh):
|
||||||
|
+ if not pki_supports_RSNv3(mh.master):
|
||||||
|
+ raise pytest.skip("RNSv3 not supported")
|
||||||
|
+ tasks.install_master(cls.master, setup_dns=True,
|
||||||
|
+ random_serial=True)
|
||||||
|
+
|
||||||
|
+ @classmethod
|
||||||
|
+ def uninstall(cls, mh):
|
||||||
|
+ if not pki_supports_RSNv3(mh.master):
|
||||||
|
+ raise pytest.skip("RSNv3 not supported")
|
||||||
|
+ super(TestACMEPrune, cls).uninstall(mh)
|
||||||
|
+
|
||||||
|
+ def test_enable_pruning(self):
|
||||||
|
+ if (tasks.get_pki_version(self.master)
|
||||||
|
+ < tasks.parse_version('11.3.0')):
|
||||||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||||||
|
+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||||
|
+ assert "jobsScheduler.job.pruning.enabled=false".encode() in cs_cfg
|
||||||
|
+
|
||||||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable'])
|
||||||
|
+
|
||||||
|
+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||||
|
+ assert "jobsScheduler.enabled=true".encode() in cs_cfg
|
||||||
|
+ assert "jobsScheduler.job.pruning.enabled=true".encode() in cs_cfg
|
||||||
|
+ assert "jobsScheduler.job.pruning.owner=ipara".encode() in cs_cfg
|
||||||
|
+
|
||||||
|
+ def test_pruning_options(self):
|
||||||
|
+ 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',
|
||||||
|
+ '--certretention=60',
|
||||||
|
+ '--certretentionunit=minute',
|
||||||
|
+ '--certsearchsizelimit=2000',
|
||||||
|
+ '--certsearchtimelimit=5',]
|
||||||
|
+ )
|
||||||
|
+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.certRetentionTime=60".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.certRetentionUnit=minute".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.certSearchSizeLimit=2000".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.certSearchTimeLimit=5".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--requestretention=60',
|
||||||
|
+ '--requestretentionunit=minute',
|
||||||
|
+ '--requestresearchsizelimit=2000',
|
||||||
|
+ '--requestsearchtimelimit=5',]
|
||||||
|
+ )
|
||||||
|
+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.requestRetentionTime=60".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.requestRetentionUnit=minute".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.requestSearchSizeLimit=2000".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.requestSearchTimeLimit=5".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--cron="0 23 1 * *',]
|
||||||
|
+ )
|
||||||
|
+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.cron=0 23 1 * *".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_pruning_negative_options(self):
|
||||||
|
+ """Negative option testing for things we directly cover"""
|
||||||
|
+ if (tasks.get_pki_version(self.master)
|
||||||
|
+ < tasks.parse_version('11.3.0')):
|
||||||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||||||
|
+
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--enable', '--disable'],
|
||||||
|
+ raiseonerr=False
|
||||||
|
+ )
|
||||||
|
+ assert result.returncode == 1
|
||||||
|
+ assert "Cannot both enable and disable" in result.stderr_text
|
||||||
|
+
|
||||||
|
+ for cmd in ('--config-show', '--run'):
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ cmd, '--enable'],
|
||||||
|
+ raiseonerr=False
|
||||||
|
+ )
|
||||||
|
+ assert result.returncode == 1
|
||||||
|
+ assert "Cannot change and show config" in result.stderr_text
|
||||||
|
+
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--cron="* *"'],
|
||||||
|
+ raiseonerr=False
|
||||||
|
+ )
|
||||||
|
+ assert result.returncode == 1
|
||||||
|
+ assert "Invalid format format --cron" in result.stderr_text
|
||||||
|
+
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--cron="100 * * * *"'],
|
||||||
|
+ raiseonerr=False
|
||||||
|
+ )
|
||||||
|
+ assert result.returncode == 1
|
||||||
|
+ assert "100 not within the range 0-59" in result.stderr_text
|
||||||
|
+
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--cron="10 1-5 * * *"'],
|
||||||
|
+ raiseonerr=False
|
||||||
|
+ )
|
||||||
|
+ assert result.returncode == 1
|
||||||
|
+ assert "1-5 ranges are not supported" in result.stderr_text
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
138
0008-doc-add-the-run-command-for-manual-job-execution.patch
Normal file
138
0008-doc-add-the-run-command-for-manual-job-execution.patch
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
From f10d1a0f84ed0f16ab4a1469f16ffadb3e79e59e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Date: Fri, 27 Jan 2023 14:05:37 -0500
|
||||||
|
Subject: [PATCH] doc: add the --run command for manual job execution
|
||||||
|
|
||||||
|
A manual method was mentioned with no specificity. Include
|
||||||
|
the --run command. Also update the troubleshooting section
|
||||||
|
to show what failure to restart the CA after configuration
|
||||||
|
looks like.
|
||||||
|
|
||||||
|
Import the IPA CA chain for manual execution.
|
||||||
|
|
||||||
|
Also fix up some $ -> # to indicate root is needed.
|
||||||
|
|
||||||
|
Related: https://pagure.io/freeipa/issue/9294
|
||||||
|
|
||||||
|
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
---
|
||||||
|
doc/designs/expired_certificate_pruning.md | 46 +++++++++++++++-------
|
||||||
|
1 file changed, 32 insertions(+), 14 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/doc/designs/expired_certificate_pruning.md b/doc/designs/expired_certificate_pruning.md
|
||||||
|
index 2c10d914020d3c12b6abb028323cd6796ec33e00..a23e452696ba2a150c4ad5a3e57360ae0a16a338 100644
|
||||||
|
--- a/doc/designs/expired_certificate_pruning.md
|
||||||
|
+++ b/doc/designs/expired_certificate_pruning.md
|
||||||
|
@@ -139,7 +139,7 @@ No validation of setting February 31st will be done. That will be left to PKI. B
|
||||||
|
|
||||||
|
### Disabling pruning
|
||||||
|
|
||||||
|
-`$ ipa-acme-manage pruning --enable=FALSE`
|
||||||
|
+`# ipa-acme-manage pruning --enable=FALSE`
|
||||||
|
|
||||||
|
This will remove the configuration option for `jobsScheduler.job.pruning.cron` just to be sure it no longer runs.
|
||||||
|
|
||||||
|
@@ -147,46 +147,46 @@ This will remove the configuration option for `jobsScheduler.job.pruning.cron` j
|
||||||
|
|
||||||
|
#### Pruning certificates
|
||||||
|
|
||||||
|
-`$ ipa-acme-manage pruning --certretention=VALUE --certretentionunit=UNIT`
|
||||||
|
+`# ipa-acme-manage pruning --certretention=VALUE --certretentionunit=UNIT`
|
||||||
|
|
||||||
|
will be the equivalent of:
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.certRetentionTime 30`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.certRetentionTime 30`
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.certRetentionUnit day`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.certRetentionUnit day`
|
||||||
|
|
||||||
|
The unit will always be required when modifying the time.
|
||||||
|
|
||||||
|
-`$ ipa-acme-manage pruning --certsearchsizelimit=VALUE --certsearchtimelimit=VALUE`
|
||||||
|
+`# ipa-acme-manage pruning --certsearchsizelimit=VALUE --certsearchtimelimit=VALUE`
|
||||||
|
|
||||||
|
will be the equivalent of:
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.certSearchSizeLimit 1000`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.certSearchSizeLimit 1000`
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.certSearchTimeLimit 0`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.certSearchTimeLimit 0`
|
||||||
|
|
||||||
|
A value of 0 for searchtimelimit is unlimited.
|
||||||
|
|
||||||
|
#### Pruning requests
|
||||||
|
|
||||||
|
-`$ ipa-acme-manage pruning --requestretention=VALUE --requestretentionunit=UNIT`
|
||||||
|
+`# ipa-acme-manage pruning --requestretention=VALUE --requestretentionunit=UNIT`
|
||||||
|
|
||||||
|
will be the equivalent of:
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionTime 30`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionTime 30`
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionUnit day`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionUnit day`
|
||||||
|
|
||||||
|
The unit will always be required when modifying the time.
|
||||||
|
|
||||||
|
-`$ ipa-acme-manage pruning --requestsearchsizelimit=VALUE --requestsearchtimelimit=VALUE`
|
||||||
|
+`# ipa-acme-manage pruning --requestsearchsizelimit=VALUE --requestsearchtimelimit=VALUE`
|
||||||
|
|
||||||
|
|
||||||
|
will be the equivalent of:
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.requestSearchSizeLimit 1000`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.requestSearchSizeLimit 1000`
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.requestSearchTimeLimit 0`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.requestSearchTimeLimit 0`
|
||||||
|
|
||||||
|
A value of 0 for searchtimelimit is unlimited.
|
||||||
|
|
||||||
|
@@ -212,10 +212,15 @@ Request search time limit: 0
|
||||||
|
Cron: 0 0 1 * *
|
||||||
|
```
|
||||||
|
|
||||||
|
+### Manual pruning
|
||||||
|
+
|
||||||
|
+`# ipa-acme-manage pruning --run`
|
||||||
|
+
|
||||||
|
+This is useful for testing the configuration or if the user wants to use the system cron or systemd timers for handling automation.
|
||||||
|
+
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
For online REST operations (login, run job) we will use the `ipaserver/plugins/dogtag.py::RestClient` class to manage the requests. This will take care of the authentication cookie, etc.
|
||||||
|
-
|
||||||
|
The class uses dogtag.https_request() will can take PEM cert and key files as arguments. These will be used for authentication.
|
||||||
|
|
||||||
|
For the non-REST operations (configuration, cron settings) the tool will fork out to pki-server ca-config-set.
|
||||||
|
@@ -239,6 +244,7 @@ Overview of the CLI commands. Example:
|
||||||
|
| ipa-acme-manage pruning | --requestretention=30 --requestretentionunit=day |
|
||||||
|
| ipa-acme-manage pruning | --requestsearchsizelimit=1000 --requestsearchtimelimit=0 |
|
||||||
|
| ipa-acme-manage pruning | --config-show |
|
||||||
|
+| ipa-acme-manage pruning | --run |
|
||||||
|
|
||||||
|
ipa-acme-manage can only be run as root.
|
||||||
|
|
||||||
|
@@ -295,3 +301,15 @@ The PKI debug log will contain job information.
|
||||||
|
2022-12-08 21:15:24 [pruning] INFO: PruningJob: - filter: (&(!(requestState=complete))(requestModifyTime<=1667942124527)(!(requestModifyTime=1667942124527)))
|
||||||
|
2022-12-08 21:15:24 [pruning] INFO: LDAPSession: Searching ou=ca, ou=requests,o=ipaca for (&(!(requestState=complete))(dateOfModify<=20221108211524Z)(!(dateOfModify=20221108211524Z)))
|
||||||
|
```
|
||||||
|
+
|
||||||
|
+### Manual execution fails with Forbidden
|
||||||
|
+
|
||||||
|
+If manually running pruning fails with a message like:
|
||||||
|
+
|
||||||
|
+```console
|
||||||
|
+# ipa-acme-manage pruning --run
|
||||||
|
+CalledProcessError(Command ['pki', '-C', '/tmp/tmppyyd3hfq/pwdfile.txt', '-d', '/tmp/tmppyyd3hfq', '-n', 'CN=IPA RA,O=EXAMPLE.TEST', 'ca-job-start', 'pruning'] returned non-zero exit status 255: 'PKIException: Forbidden\n')
|
||||||
|
+The ipa-acme-manage command failed.
|
||||||
|
+```
|
||||||
|
+
|
||||||
|
+You probably forgot to restart the CA after enabling pruning.
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
43
0009-tests-add-wrapper-around-ACME-RSNv3-test.patch
Normal file
43
0009-tests-add-wrapper-around-ACME-RSNv3-test.patch
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
From d24b69981d94fce7b1e1aa4a5c1ab88a123f96b5 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Date: Fri, 3 Feb 2023 10:04:31 -0500
|
||||||
|
Subject: [PATCH] tests: add wrapper around ACME RSNv3 test
|
||||||
|
|
||||||
|
This test is located outside of the TestACMEPrune because
|
||||||
|
it enables RSNv3 while the server installed by TestACME doesn't.
|
||||||
|
|
||||||
|
It still needs a wrapper to enforce a version of PKI that
|
||||||
|
supports pruning because that is checked first in the tool.
|
||||||
|
Re-ordering that wouldn't be a good user experience.
|
||||||
|
|
||||||
|
https://pagure.io/freeipa/issue/9322
|
||||||
|
|
||||||
|
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
---
|
||||||
|
ipatests/test_integration/test_acme.py | 9 ++++++++-
|
||||||
|
1 file changed, 8 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py
|
||||||
|
index 93e785d8febd9fa8d7b3ef87ecb3f2eb42ac5da2..5ceba05976059de69414a79634d98045c3ab68bb 100644
|
||||||
|
--- a/ipatests/test_integration/test_acme.py
|
||||||
|
+++ b/ipatests/test_integration/test_acme.py
|
||||||
|
@@ -393,7 +393,14 @@ class TestACME(CALessBase):
|
||||||
|
|
||||||
|
def test_acme_pruning_no_random_serial(self):
|
||||||
|
"""This ACME install is configured without random serial
|
||||||
|
- numbers. Verify that we can't enable pruning on it."""
|
||||||
|
+ numbers. Verify that we can't enable pruning on it.
|
||||||
|
+
|
||||||
|
+ This test is located here because by default installs
|
||||||
|
+ don't enable RSNv3.
|
||||||
|
+ """
|
||||||
|
+ 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', 'enable'])
|
||||||
|
result = self.master.run_command(
|
||||||
|
['ipa-acme-manage', 'pruning', '--enable'],
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
57
freeipa.spec
57
freeipa.spec
@ -94,8 +94,6 @@
|
|||||||
# Fedora
|
# Fedora
|
||||||
%global package_name freeipa
|
%global package_name freeipa
|
||||||
%global alt_name ipa
|
%global alt_name ipa
|
||||||
# Fix for CVE-2020-28196
|
|
||||||
%global krb5_version 1.18.2-29
|
|
||||||
# 0.7.16: https://github.com/drkjam/netaddr/issues/71
|
# 0.7.16: https://github.com/drkjam/netaddr/issues/71
|
||||||
%global python_netaddr_version 0.7.16
|
%global python_netaddr_version 0.7.16
|
||||||
# Require 4.7.0 which brings Python 3 bindings
|
# Require 4.7.0 which brings Python 3 bindings
|
||||||
@ -111,7 +109,15 @@
|
|||||||
%endif
|
%endif
|
||||||
%global slapi_nis_version 0.56.5
|
%global slapi_nis_version 0.56.5
|
||||||
|
|
||||||
|
%if 0%{?fedora} < 38
|
||||||
|
# Fix for CVE-2020-28196
|
||||||
|
%global krb5_version 1.18.2-29
|
||||||
%global krb5_kdb_version 8.0
|
%global krb5_kdb_version 8.0
|
||||||
|
%else
|
||||||
|
# Fix for CVE-2020-28196
|
||||||
|
%global krb5_version 1.20.1-3
|
||||||
|
%global krb5_kdb_version 9.0
|
||||||
|
%endif
|
||||||
|
|
||||||
# fix for segfault in python3-ldap, https://pagure.io/freeipa/issue/7324
|
# fix for segfault in python3-ldap, https://pagure.io/freeipa/issue/7324
|
||||||
%global python_ldap_version 3.1.0-1
|
%global python_ldap_version 3.1.0-1
|
||||||
@ -217,7 +223,7 @@
|
|||||||
|
|
||||||
Name: %{package_name}
|
Name: %{package_name}
|
||||||
Version: %{IPA_VERSION}
|
Version: %{IPA_VERSION}
|
||||||
Release: 3%{?rc_version:.%rc_version}%{?dist}
|
Release: 4%{?rc_version:.%rc_version}%{?dist}
|
||||||
Summary: The Identity, Policy and Audit system
|
Summary: The Identity, Policy and Audit system
|
||||||
|
|
||||||
License: GPLv3+
|
License: GPLv3+
|
||||||
@ -236,17 +242,25 @@ Source1: https://releases.pagure.org/freeipa/freeipa-%{version}%{?rc_vers
|
|||||||
|
|
||||||
# RHEL spec file only: START
|
# RHEL spec file only: START
|
||||||
%if %{NON_DEVELOPER_BUILD}
|
%if %{NON_DEVELOPER_BUILD}
|
||||||
%if 0%{?rhel} >= 8
|
%if 0%{?rhel} == 8
|
||||||
|
Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch
|
||||||
|
Patch1002: 1002-Revert-freeipa.spec-depend-on-bind-dnssec-utils.patch
|
||||||
|
%endif
|
||||||
|
%if 0%{?rhel} == 9
|
||||||
Patch0001: 0001-updates-fix-memberManager-ACI-to-allow-managers-from.patch
|
Patch0001: 0001-updates-fix-memberManager-ACI-to-allow-managers-from.patch
|
||||||
Patch0002: 0002-Spec-file-ipa-client-depends-on-krb5-pkinit-openssl.patch
|
Patch0002: 0002-Spec-file-ipa-client-depends-on-krb5-pkinit-openssl.patch
|
||||||
|
Patch0003: 0003-server-install-remove-error-log-about-missing-bkup-f.patch
|
||||||
|
Patch0004: 0004-ipa-tests-Add-LANG-before-kinit-command-to-fix-issue.patch
|
||||||
|
Patch0005: 0005-trust-add-handle-missing-msSFU30MaxGidNumber.patch
|
||||||
|
Patch0006: 0006-doc-Design-for-certificate-pruning.patch
|
||||||
|
Patch0007: 0007-ipa-acme-manage-add-certificate-request-pruning-mana.patch
|
||||||
|
Patch0008: 0008-doc-add-the-run-command-for-manual-job-execution.patch
|
||||||
|
Patch0009: 0009-tests-add-wrapper-around-ACME-RSNv3-test.patch
|
||||||
Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch
|
Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch
|
||||||
%endif
|
%endif
|
||||||
%endif
|
%endif
|
||||||
# RHEL spec file only: END
|
# RHEL spec file only: END
|
||||||
|
|
||||||
# For the timestamp trick in patch application
|
|
||||||
BuildRequires: diffstat
|
|
||||||
|
|
||||||
BuildRequires: openldap-devel
|
BuildRequires: openldap-devel
|
||||||
# For KDB DAL version, make explicit dependency so that increase of version
|
# For KDB DAL version, make explicit dependency so that increase of version
|
||||||
# will cause the build to fail due to unsatisfied dependencies.
|
# will cause the build to fail due to unsatisfied dependencies.
|
||||||
@ -383,7 +397,6 @@ BuildRequires: python3-libsss_nss_idmap
|
|||||||
BuildRequires: python3-lxml
|
BuildRequires: python3-lxml
|
||||||
BuildRequires: python3-netaddr >= %{python_netaddr_version}
|
BuildRequires: python3-netaddr >= %{python_netaddr_version}
|
||||||
BuildRequires: python3-netifaces
|
BuildRequires: python3-netifaces
|
||||||
BuildRequires: python3-paste
|
|
||||||
BuildRequires: python3-pki >= %{pki_version}
|
BuildRequires: python3-pki >= %{pki_version}
|
||||||
BuildRequires: python3-polib
|
BuildRequires: python3-polib
|
||||||
BuildRequires: python3-pyasn1
|
BuildRequires: python3-pyasn1
|
||||||
@ -970,22 +983,7 @@ Custom SELinux policy module for FreeIPA
|
|||||||
|
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
# Update timestamps on the files touched by a patch, to avoid non-equal
|
%autosetup -n freeipa-%{version}%{?rc_version} -N -p1
|
||||||
# .pyc/.pyo files across the multilib peers within a build, where "Level"
|
|
||||||
# is the patch prefix option (e.g. -p1)
|
|
||||||
# Taken from specfile for sssd and python-simplejson
|
|
||||||
UpdateTimestamps() {
|
|
||||||
Level=$1
|
|
||||||
PatchFile=$2
|
|
||||||
|
|
||||||
# Locate the affected files:
|
|
||||||
for f in $(diffstat $Level -l $PatchFile); do
|
|
||||||
# Set the files to have the same timestamp as that of the patch:
|
|
||||||
touch -c -r $PatchFile $f
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
%setup -n freeipa-%{version}%{?rc_version} -q
|
|
||||||
|
|
||||||
# To allow proper application patches to the stripped po files, strip originals
|
# To allow proper application patches to the stripped po files, strip originals
|
||||||
pushd po
|
pushd po
|
||||||
@ -995,10 +993,7 @@ for i in *.po ; do
|
|||||||
done
|
done
|
||||||
popd
|
popd
|
||||||
|
|
||||||
for p in %patches ; do
|
%autopatch -p1
|
||||||
%__patch -p1 -i $p
|
|
||||||
UpdateTimestamps -p1 $p
|
|
||||||
done
|
|
||||||
|
|
||||||
%build
|
%build
|
||||||
# PATH is workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1005235
|
# PATH is workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1005235
|
||||||
@ -1748,6 +1743,12 @@ fi
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Mon Feb 06 2023 Florence Blanc-Renaud <flo@redhat.com> - 4.10.1-4
|
||||||
|
- Resolves: rhbz#2161284 'ERROR Could not remove /tmp/tmpbkw6hawo.ipabkp' can be seen prior to 'ipa-client-install' command was successful
|
||||||
|
- Resolves: rhbz#2164403 ipa-trust-add with --range-type=ipa-ad-trust-posix fails while creating an ID range
|
||||||
|
- Resolves: rhbz#2162677 RFE: Implement support for PKI certificate and request pruning
|
||||||
|
- Resolves: rhbz#2167312 - Backport latest test fixes in python3-ipatests
|
||||||
|
|
||||||
* Wed Dec 21 2022 Alexander Bokovoy <abokovoy@redhat.com> - 4.10.1-3
|
* Wed Dec 21 2022 Alexander Bokovoy <abokovoy@redhat.com> - 4.10.1-3
|
||||||
- Rebuild against krb5 1.20.1 ABI
|
- Rebuild against krb5 1.20.1 ABI
|
||||||
- Resolves: rhbz#2155425
|
- Resolves: rhbz#2155425
|
||||||
|
Loading…
Reference in New Issue
Block a user