From 95c97a7ba7d212e5f7c763173ea15411678081ad Mon Sep 17 00:00:00 2001 From: Scott Poore Date: Thu, 25 Jun 2026 16:20:20 -0500 Subject: [PATCH 1/4] AD role: change Enrollment Agent name Changing the default Certificate Template name for the Enrollment Agent template from EnrollmentAgent4 to EnrollmentAgent_IDMTEST. --- sssd_test_framework/roles/ad.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sssd_test_framework/roles/ad.py b/sssd_test_framework/roles/ad.py index 64f2c336..2a8aafc5 100644 --- a/sssd_test_framework/roles/ad.py +++ b/sssd_test_framework/roles/ad.py @@ -2659,7 +2659,7 @@ def _setup_enrollment_agent(self) -> None: FriendlyName = "Enrollment Agent" [RequestAttributes] - CertificateTemplate = "EnrollmentAgent2" + CertificateTemplate = "EnrollmentAgent_IDMTEST" "@ Set-Content -Path "{inf_path}" -Value $infContent """) From 24e1b302d68e737297a9f95262b57c7419d75a5f Mon Sep 17 00:00:00 2001 From: Scott Poore Date: Fri, 26 Jun 2026 14:55:59 -0500 Subject: [PATCH 2/4] topology_controller: update join_domain for ad Updates for better AD support including with smart card tests that require a pkinit_anchor for the krb5 config. Also backing up krb5.conf and krb5.keytab to make setup/teardown a little cleaner. Skipping krb5.conf removal for AD tests so there is a functional version for use. --- sssd_test_framework/topology_controllers.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/sssd_test_framework/topology_controllers.py b/sssd_test_framework/topology_controllers.py index b02fdb45..f9124b63 100644 --- a/sssd_test_framework/topology_controllers.py +++ b/sssd_test_framework/topology_controllers.py @@ -2,6 +2,7 @@ import re import tempfile +import textwrap from pytest_mh import BackupTopologyController from pytest_mh.conn import ProcessResult @@ -66,10 +67,14 @@ def join_domain(self, client: ClientHost, provider: IPAHost | ADHost | SambaHost """ Helper method for joining domains """ + client.fs.backup("/etc/krb5.conf") + client.fs.backup("/etc/krb5.keytab") self.logger.info(f"Enrolling {client.hostname} into {provider.domain}") # Remove any existing Kerberos configuration and keytab - client.fs.rm("/etc/krb5.conf") + # Skip removing krb5.conf when provider is AD + if not isinstance(provider, (ADHost)): + client.fs.rm("/etc/krb5.conf") client.fs.rm("/etc/krb5.keytab") # First check if joined. If so, leave. @@ -100,6 +105,18 @@ def join_domain(self, client: ClientHost, provider: IPAHost | ADHost | SambaHost client.conn.exec(["realm", "leave", "--unattended", provider.domain], input=provider.adminpw) client.conn.exec(["realm", "join", provider.domain], input=provider.adminpw) + if isinstance(provider, (ADHost)): + ad_krb5_content = textwrap.dedent(f"""\ + [realms] + {provider.realm} = {{ + default_domain = {provider.domain} + pkinit_anchors = FILE:/etc/sssd/pki/sssd_auth_ca_db.pem + pkinit_eku_checking = kpServerAuth + pkinit_kdc_hostname = {provider.hostname} + }} + """) + client.fs.write("/etc/krb5.conf.d/ad_pkinit", ad_krb5_content) + class ClientTopologyController(ProvisionedBackupTopologyController): """ From 3ba2f166421c26f369e616a49cf4b72b92bb748d Mon Sep 17 00:00:00 2001 From: Scott Poore Date: Fri, 26 Jun 2026 15:01:19 -0500 Subject: [PATCH 3/4] Re-Enable AD CA along with containers update Re-enabling AD CA role support for smart card testing to align with sssd-ci-containers change to enable this funtionality. Depends-on: https://github.com/SSSD/sssd-ci-containers/pull/174 --- sssd_test_framework/roles/ad.py | 106 +++++++++++++++----------------- 1 file changed, 50 insertions(+), 56 deletions(-) diff --git a/sssd_test_framework/roles/ad.py b/sssd_test_framework/roles/ad.py index 2a8aafc5..5228328b 100644 --- a/sssd_test_framework/roles/ad.py +++ b/sssd_test_framework/roles/ad.py @@ -183,21 +183,18 @@ def test_example_autofs(client: Client, ad: AD, nfs: NFS): } """ - # - # Disabling CA functionality until sssd-ci-container updated - # - # self._ca = ADCertificateAuthority(self.host) - # """ - # AD Certificate Authority server management. - # - # Provides certificate operations: - # - Request certificates using templates - # - Request smartcard certificates with Enrollment Agent - # - Revoke certificates with configurable reasons - # - Manage certificate holds - # - Export certificates as PFX - # - Retrieve certificate and template details - # """ + self._ca = ADCertificateAuthority(self.host) + """ + AD Certificate Authority server management. + + Provides certificate operations: + - Request certificates using templates + - Request smartcard certificates with Enrollment Agent + - Revoke certificates with configurable reasons + - Manage certificate holds + - Export certificates as PFX + - Retrieve certificate and template details + """ @property def password_policy(self) -> ADPasswordPolicy: @@ -217,47 +214,44 @@ def test_example(client: Client, ad: AD): """ return self._password_policy - # - # Disabling CA functionality until sssd-ci-container updated - # - # @property - # def ca(self) -> ADCertificateAuthority: - # """ - # AD Certificate Authority management. - # - # Provides certificate operations: - # - # - Request certificates using templates - # - Request smartcard certificates with Enrollment Agent - # - Revoke certificates with configurable reasons - # - Manage certificate holds - # - Export certificates as PFX - # - Retrieve certificate and template details - # - # .. code-block:: python - # :caption: Example usage - # - # @pytest.mark.topology(KnownTopology.AD) - # def test_example(client: Client, ad: AD): - # # Request smartcard certificate - # cert, key, csr = ad.ca.request( - # template="SmartcardLogon", - # subject="CN=testuser" - # ) - # - # # Get certificate details - # cert_details = ad.ca.get(cert) - # - # # Place certificate on hold (temporary revocation) - # ad.ca.revoke_hold(cert) - # - # # Remove hold (restore certificate) - # ad.ca.revoke_hold_remove(cert) - # - # # Permanently revoke certificate - # ad.ca.revoke(cert, reason="key_compromise") - # """ - # return self._ca + @property + def ca(self) -> ADCertificateAuthority: + """ + AD Certificate Authority management. + + Provides certificate operations: + + - Request certificates using templates + - Request smartcard certificates with Enrollment Agent + - Revoke certificates with configurable reasons + - Manage certificate holds + - Export certificates as PFX + - Retrieve certificate and template details + + .. code-block:: python + :caption: Example usage + + @pytest.mark.topology(KnownTopology.AD) + def test_example(client: Client, ad: AD): + # Request smartcard certificate + cert, key, csr = ad.ca.request( + template="SmartcardLogon", + subject="CN=testuser" + ) + + # Get certificate details + cert_details = ad.ca.get(cert) + + # Place certificate on hold (temporary revocation) + ad.ca.revoke_hold(cert) + + # Remove hold (restore certificate) + ad.ca.revoke_hold_remove(cert) + + # Permanently revoke certificate + ad.ca.revoke(cert, reason="key_compromise") + """ + return self._ca @property def naming_context(self) -> str: From abecbd1c1adc007bd01367a0a2c46e60ff5b7acc Mon Sep 17 00:00:00 2001 From: Scott Poore Date: Mon, 20 Jul 2026 08:42:18 -0500 Subject: [PATCH 4/4] AD Role: openssl password and ca name fixes In some environments, the stdin password pipe for an openssl command in the ad.ca.request() method fails with invalid password. Removing the pipe and setting it directly from the command line. The AD CA is not always configured with the name forced to ad-RootCA. Changing to use _get_ca_config() to determine the CA name to use. --- sssd_test_framework/roles/ad.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/sssd_test_framework/roles/ad.py b/sssd_test_framework/roles/ad.py index 5228328b..597ebcdf 100644 --- a/sssd_test_framework/roles/ad.py +++ b/sssd_test_framework/roles/ad.py @@ -2824,7 +2824,7 @@ def request( self.export_pfx(cert_path, pfx_path) self.host.conn.run( - f'"Secret123" | & openssl pkcs12 -in {pfx_path} -nocerts -nodes -out {extracted_key_path} -passin stdin' + f"openssl pkcs12 -in {pfx_path} -nocerts -nodes -out {extracted_key_path} -passin pass:Secret123" ) self.host.conn.run(f"openssl rsa -in {extracted_key_path} -out {key_path}") @@ -3068,24 +3068,25 @@ def get_ca_cert(self) -> str: :rtype: str :raises RuntimeError: If CA certificate cannot be retrieved. """ + ca_name = self._get_ca_config().split("\\", 1)[1].strip('"') result = self.host.conn.run( - textwrap.dedent("""\ - $ca = Get-ChildItem -Path Cert:\\LocalMachine\\Root | Where-Object { - $_.Subject -like '*CN=ad-RootCA*' -and $_.Issuer -eq $_.Subject - } | Select-Object -First 1 - if ($ca) { + textwrap.dedent(f"""\ + $ca = Get-ChildItem -Path Cert:\\LocalMachine\\Root | Where-Object {{ + $_.Subject -like '*CN={ca_name}*' -and $_.Issuer -eq $_.Subject + }} | Select-Object -First 1 + if ($ca) {{ [System.Convert]::ToBase64String($ca.Export('Cert')) - } else { - $ca = Get-ChildItem -Path Cert:\\LocalMachine\\My | Where-Object { - $_.Subject -like '*CN=ad-RootCA*' - } | Select-Object -First 1 - if ($ca) { + }} else {{ + $ca = Get-ChildItem -Path Cert:\\LocalMachine\\My | Where-Object {{ + $_.Subject -like '*CN={ca_name}*' + }} | Select-Object -First 1 + if ($ca) {{ [System.Convert]::ToBase64String($ca.Export('Cert')) - } else { + }} else {{ Write-Error "CA certificate not found" exit 1 - } - } + }} + }} """), raise_on_error=False, )