From 26feecae026581e39a43f001faff59e81a92c03d Mon Sep 17 00:00:00 2001 From: Lenka Doudova Date: Wed, 18 Feb 2026 14:33:49 +0100 Subject: [PATCH] Issue 7236 - Fix GSSAPI tests (#7237) * Issue 7236 - Fix GSSAPI tests Description: Fix for failing GSSAPI tests Add GSSAPI_ACK variable to pytest workflow for proper execution in Github CI Relates: #7236 Author: Lenka Doudova Reviewer: Barbora Simonova, Viktor Ashirov --- .github/workflows/lmdbpytest.yml | 2 +- .github/workflows/pytest.yml | 2 +- .../tests/suites/gssapi/simple_gssapi_test.py | 2 + .../suites/gssapi_repl/gssapi_repl_test.py | 43 +++++--------- src/lib389/lib389/topologies.py | 57 +++++++++++++++++++ 5 files changed, 76 insertions(+), 30 deletions(-) diff --git a/.github/workflows/lmdbpytest.yml b/.github/workflows/lmdbpytest.yml index 2d0a122bf..376090bf6 100644 --- a/.github/workflows/lmdbpytest.yml +++ b/.github/workflows/lmdbpytest.yml @@ -120,7 +120,7 @@ jobs: sudo docker exec $CID sh -c "systemctl enable --now cockpit.socket" sudo docker exec $CID sh -c "mkdir -p /workspace/assets/cores && chmod 777 /workspace{,/assets{,/cores}}" sudo docker exec $CID sh -c "echo '/workspace/assets/cores/core.%e.%P' > /proc/sys/kernel/core_pattern" - sudo docker exec -e WEBUI=1 -e NSSLAPD_DB_LIB=mdb -e DEBUG=pw:api -e PASSWD="${PASSWD}" $CID py.test --suppress-no-test-exit-code -m "not flaky" --junit-xml=pytest.xml --html=pytest.html --browser=firefox --browser=chromium -v dirsrvtests/tests/suites/${{ matrix.suite }} + sudo docker exec -e WEBUI=1 -e NSSLAPD_DB_LIB=mdb -e DEBUG=pw:api -e PASSWD="${PASSWD}" -e GSSAPI_ACK=1 $CID py.test --suppress-no-test-exit-code -m "not flaky" --junit-xml=pytest.xml --html=pytest.html --browser=firefox --browser=chromium -v dirsrvtests/tests/suites/${{ matrix.suite }} - name: Make the results file readable by all if: always() diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 8a543be85..a51553656 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -125,7 +125,7 @@ jobs: echo "Tests skipped because read-only Berkeley Database is installed." > pytest.html echo "'Tests skipped because read-only Berkeley Database is installed.'" > pytest.xml else - sudo docker exec -e WEBUI=1 -e NSSLAPD_DB_LIB=bdb -e DEBUG=pw:api -e PASSWD="${PASSWD}" $CID py.test --suppress-no-test-exit-code -m "not flaky" --junit-xml=pytest.xml --html=pytest.html --browser=firefox --browser=chromium -v dirsrvtests/tests/suites/${{ matrix.suite }} + sudo docker exec -e WEBUI=1 -e NSSLAPD_DB_LIB=bdb -e DEBUG=pw:api -e PASSWD="${PASSWD}" -e GSSAPI_ACK=1 $CID py.test --suppress-no-test-exit-code -m "not flaky" --junit-xml=pytest.xml --html=pytest.html --browser=firefox --browser=chromium -v dirsrvtests/tests/suites/${{ matrix.suite }} fi - name: Make the results file readable by all diff --git a/dirsrvtests/tests/suites/gssapi/simple_gssapi_test.py b/dirsrvtests/tests/suites/gssapi/simple_gssapi_test.py index be6f68a9a..e48de3491 100644 --- a/dirsrvtests/tests/suites/gssapi/simple_gssapi_test.py +++ b/dirsrvtests/tests/suites/gssapi/simple_gssapi_test.py @@ -34,6 +34,8 @@ def testuser(topology_st_gssapi): }) # Give them a krb princ user.create_keytab() + # Make krb5 config readable by everyone for the tests to work + os.chmod(user._instance.realm.krb5confrealm, 0o644) return user @gssapi_ack diff --git a/dirsrvtests/tests/suites/gssapi_repl/gssapi_repl_test.py b/dirsrvtests/tests/suites/gssapi_repl/gssapi_repl_test.py index 402684aab..fa7fc9c24 100644 --- a/dirsrvtests/tests/suites/gssapi_repl/gssapi_repl_test.py +++ b/dirsrvtests/tests/suites/gssapi_repl/gssapi_repl_test.py @@ -10,7 +10,7 @@ import pytest from lib389.tasks import * from lib389.utils import * from lib389.agreement import * -from lib389.topologies import topology_m2 +from lib389.topologies import topology_m2_gssapi, gssapi_ack pytestmark = pytest.mark.tier2 @@ -69,25 +69,8 @@ def _allow_machine_account(inst, name): (ldap.MOD_REPLACE, 'nsDS5ReplicaBindDN', f"uid={name},ou=Machines,{DEFAULT_SUFFIX}".encode('utf-8')) ]) -def _verify_etc_hosts(): - #Check if /etc/hosts is compatible with the test - NEEDED_HOSTS = ( ('ldapkdc.example.com', '127.0.0.1'), - ('ldapkdc1.example.com', '127.0.1.1'), - ('ldapkdc2.example.com', '127.0.2.1')) - found_hosts = {} - with open('/etc/hosts','r') as f: - for l in f: - s = l.split() - if len(s) < 2: - continue - for nh in NEEDED_HOSTS: - if (s[0] == nh[1] and s[1] == nh[0]): - found_hosts[s[1]] = True - return len(found_hosts) == len(NEEDED_HOSTS) - -@pytest.mark.skipif(not _verify_etc_hosts(), reason="/etc/hosts does not contains the needed hosts.") -@pytest.mark.skipif(True, reason="Test disabled because it requires specific kerberos requirement (server principal, keytab, etc ...") -def test_gssapi_repl(topology_m2): +@gssapi_ack +def test_gssapi_repl(topology_m2_gssapi): """Test gssapi authenticated replication agreement of two suppliers using KDC :id: 552850aa-afc3-473e-9c39-aae802b46f11 @@ -112,8 +95,8 @@ def test_gssapi_repl(topology_m2): 6. Test User should be created on M1 and M2 both 7. Test User should be created on M1 and M2 both """ - supplier1 = topology_m2.ms["supplier1"] - supplier2 = topology_m2.ms["supplier2"] + supplier1 = topology_m2_gssapi.ms["supplier1"] + supplier2 = topology_m2_gssapi.ms["supplier2"] # Create the locations on each supplier for the other to bind to. _create_machine_ou(supplier1) @@ -134,10 +117,9 @@ def test_gssapi_repl(topology_m2): # Creating agreement from supplier 1 to supplier 2 # Set the replica bind method to sasl gssapi - properties = {RA_NAME: r'meTo_$host:$port', + properties = {RA_NAME: 'meTo_' + supplier2.host + ':' + str(supplier2.port), RA_METHOD: 'SASL/GSSAPI', RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]} - supplier1.agreement.delete(suffix=SUFFIX, consumer_host=supplier2.host, consumer_port=supplier2.port) m1_m2_agmt = supplier1.agreement.create(suffix=SUFFIX, host=supplier2.host, port=supplier2.port, properties=properties) if not m1_m2_agmt: log.fatal("Fail to create a supplier -> supplier replica agreement") @@ -147,10 +129,9 @@ def test_gssapi_repl(topology_m2): # Creating agreement from supplier 2 to supplier 1 # Set the replica bind method to sasl gssapi - properties = {RA_NAME: r'meTo_$host:$port', + properties = {RA_NAME: 'meTo_' + supplier1.host + ':' + str(supplier1.port), RA_METHOD: 'SASL/GSSAPI', RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]} - supplier2.agreement.delete(suffix=SUFFIX, consumer_host=supplier1.host, consumer_port=supplier1.port) m2_m1_agmt = supplier2.agreement.create(suffix=SUFFIX, host=supplier1.host, port=supplier1.port, properties=properties) if not m2_m1_agmt: log.fatal("Fail to create a supplier -> supplier replica agreement") @@ -169,9 +150,15 @@ def test_gssapi_repl(topology_m2): # Check replication is working... if supplier1.testReplication(DEFAULT_SUFFIX, supplier2): - log.info('Replication is working.') + log.info('Replication is working: supplier1 -> supplier2') else: - log.fatal('Replication is not working.') + log.fatal('Replication is not working: supplier1 -> supplier2') + assert False + + if supplier2.testReplication(DEFAULT_SUFFIX, supplier1): + log.info('Replication is working: supplier2 -> supplier1') + else: + log.fatal('Replication is not working: supplier2 -> supplier1') assert False # Add a user to supplier 1 diff --git a/src/lib389/lib389/topologies.py b/src/lib389/lib389/topologies.py index 33341f669..84e620cb3 100644 --- a/src/lib389/lib389/topologies.py +++ b/src/lib389/lib389/topologies.py @@ -499,6 +499,63 @@ def topology_m2(request): topology.logcap = LogCapture() return topology +@pytest.fixture(scope="module") +def topology_m2_gssapi(request): + """Create Replication Deployment with two suppliers with GSSAPI enabled. + + Similar to topology_st_gssapi but for two suppliers. Configures Kerberos + realm, principals and keytabs for ldap/ldapkdc1. and ldap/ldapkdc2., + SASL mappings, and disables SSL port on both instances so GSSAPI can be used. + """ + hostname = socket.gethostname().split('.', 1) + assert len(hostname) == 2 + domain = hostname[1] + REALM = domain.upper() + host_supplier_1 = 'ldapkdc1.' + domain + host_supplier_2 = 'ldapkdc2.' + domain + + topology = create_topology({ReplicaRole.SUPPLIER: 2}, request=request, + cleanup_cb=lambda x: krb.destroy_realm()) + + supplier1 = topology.ms["supplier1"] + supplier2 = topology.ms["supplier2"] + supplier1.host = host_supplier_1 + supplier2.host = host_supplier_2 + + krb = MitKrb5(realm=REALM, debug=DEBUGGING) + if krb.check_realm(): + krb.destroy_realm() + krb.create_realm() + + krb.create_principal(principal=f'ldap/{host_supplier_1}') + krb.create_principal(principal=f'ldap/{host_supplier_2}') + krb.create_keytab(principal=f'ldap/{host_supplier_1}', keytab='/etc/krb5.keytab') + krb.create_keytab(principal=f'ldap/{host_supplier_2}', keytab='/etc/krb5.keytab') + + os.chown('/etc/krb5.keytab', supplier1.get_user_uid(), supplier1.get_group_gid()) + + for inst, host in [(supplier1, host_supplier_1), (supplier2, host_supplier_2)]: + saslmappings = SaslMappings(inst) + for m in saslmappings.list(): + m.delete() + saslmappings.create(properties={ + 'cn': 'suffix map', + 'nsSaslMapRegexString': '\\(.*\\)', + 'nsSaslMapBaseDNTemplate': inst.creation_suffix, + 'nsSaslMapFilterTemplate': '(uid=\\1)' + }) + inst.realm = krb + inst.config.set('nsslapd-localhost', host) + inst.sslport = None + + supplier1.restart() + supplier2.restart() + supplier1.clearTmpDir(__file__) + supplier2.clearTmpDir(__file__) + + topology.logcap = LogCapture() + return topology + @pytest.fixture(scope="module") def topology_m3(request): -- 2.52.0