You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by se...@apache.org on 2021/06/14 18:35:05 UTC

[directory-studio] branch kerby-gssapi-tests created (now c35509a)

This is an automated email from the ASF dual-hosted git repository.

seelmann pushed a change to branch kerby-gssapi-tests
in repository https://gitbox.apache.org/repos/asf/directory-studio.git.


      at c35509a  Use Kerby for GSSAPI tests and enable SASL in 389ds

This branch includes the following new commits:

     new c35509a  Use Kerby for GSSAPI tests and enable SASL in 389ds

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[directory-studio] 01/01: Use Kerby for GSSAPI tests and enable SASL in 389ds

Posted by se...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

seelmann pushed a commit to branch kerby-gssapi-tests
in repository https://gitbox.apache.org/repos/asf/directory-studio.git

commit c35509ad346f1e04247d929268a18c4c544ae575
Author: Stefan Seelmann <ma...@stefan-seelmann.de>
AuthorDate: Mon Jun 14 20:34:35 2021 +0200

    Use Kerby for GSSAPI tests and enable SASL in 389ds
---
 Jenkinsfile                                        |  35 +++----
 .../ui/widgets/AuthenticationParameterPage.java    |   2 +-
 .../core/DirectoryApiConnectionWrapperTest.java    | 103 +++++++++++++++++-
 .../integration/junit5/Fedora389dsLdapServer.java  |   5 +-
 .../test/integration/junit5/OpenLdapServer.java    |   2 +-
 .../test/integration/junit5/Fedora389ds.ldif       |  43 ++++++++
 .../integration/ui/NewConnectionWizardTest.java    |  89 +++++++++++-----
 tools/testlab/README.md                            |  77 ++++++++++++++
 tools/testlab/kerby-data/backend/json-backend.json | 116 +++++++++++++++++++++
 tools/testlab/kerby-data/conf/adminClient.conf     |  23 ++++
 tools/testlab/kerby-data/conf/adminServer.conf     |  24 +++++
 tools/testlab/kerby-data/conf/backend.conf         |  20 ++++
 tools/testlab/kerby-data/conf/kdc.conf             |  23 ++++
 tools/testlab/kerby-data/conf/krb5.conf            |  29 ++++++
 tools/testlab/kerby-data/keytabs/admin.keytab      | Bin 0 -> 148 bytes
 tools/testlab/krb5.conf                            |  29 ++++++
 tools/testlab/ldap.keytab                          | Bin 0 -> 328 bytes
 17 files changed, 562 insertions(+), 58 deletions(-)

diff --git a/Jenkinsfile b/Jenkinsfile
index a70db52..dffdaa0 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -37,13 +37,7 @@ pipeline {
       }
       steps {
         script {
-          docker.image('osixia/openldap:1.5.0').withRun('-e LDAP_TLS_VERIFY_CLIENT=never') { openldap ->
-            docker.image('389ds/dirsrv').withRun('-e DS_DM_PASSWORD=admin', 'bash -c "set -m; /usr/lib/dirsrv/dscontainer -r & while ! /usr/lib/dirsrv/dscontainer -H; do sleep 5; done; sleep 5; /usr/sbin/dsconf localhost backend create --suffix dc=example,dc=org --be-name example; fg"') { fedora389ds ->
-              docker.image('apachedirectory/maven-build:jdk-8').inside("--link=${openldap.id}:openldap -e OPENLDAP_HOST=openldap -e OPENLDAP_PORT=389 -e OPENLDAP_PORT_SSL=636 --link=${fedora389ds.id}:fedora389ds -e FEDORA_389DS_HOST=fedora389ds -e FEDORA_389DS_PORT=3389 -e FEDORA_389DS_PORT_SSL=3636") {
-                sh 'export DISPLAY=:99; env; ps aux'
-              }
-            }
-          }
+          inTestLab({ sh 'export DISPLAY=:99; env; ps aux' })
         }
       }
       post {
@@ -64,13 +58,7 @@ pipeline {
           }
           steps {
             script {
-              docker.image('osixia/openldap:1.5.0').withRun('-e LDAP_TLS_VERIFY_CLIENT=never') { openldap ->
-                docker.image('389ds/dirsrv').withRun('-e DS_DM_PASSWORD=admin', 'bash -c "set -m; /usr/lib/dirsrv/dscontainer -r & while ! /usr/lib/dirsrv/dscontainer -H; do sleep 5; done; sleep 5; /usr/sbin/dsconf localhost backend create --suffix dc=example,dc=org --be-name example; fg"') { fedora389ds ->
-                  docker.image('apachedirectory/maven-build:jdk-11').inside("--link=${openldap.id}:openldap -e OPENLDAP_HOST=openldap -e OPENLDAP_PORT=389 -e OPENLDAP_PORT_SSL=636 --link=${fedora389ds.id}:fedora389ds -e FEDORA_389DS_HOST=fedora389ds -e FEDORA_389DS_PORT=3389 -e FEDORA_389DS_PORT_SSL=3636") {
-                    sh 'export DISPLAY=:99; mvn -V -U -f pom-first.xml clean install && mvn -V clean install -Dorg.eclipse.swtbot.search.timeout=20000 -Denable-ui-tests'
-                  }
-                }
-              }
+              inTestLab({ sh 'export DISPLAY=:99; mvn -V -U -f pom-first.xml clean install && mvn -V clean install -Dorg.eclipse.swtbot.search.timeout=20000 -Denable-ui-tests' })
             }
           }
           post {
@@ -91,13 +79,7 @@ pipeline {
           }
           steps {
             script {
-              docker.image('osixia/openldap:1.5.0').withRun('-e LDAP_TLS_VERIFY_CLIENT=never') { openldap ->
-                docker.image('389ds/dirsrv').withRun('-e DS_DM_PASSWORD=admin', 'bash -c "set -m; /usr/lib/dirsrv/dscontainer -r & while ! /usr/lib/dirsrv/dscontainer -H; do sleep 5; done; sleep 5; /usr/sbin/dsconf localhost backend create --suffix dc=example,dc=org --be-name example; fg"') { fedora389ds ->
-                  docker.image('apachedirectory/maven-build:jdk-17').inside("--link=${openldap.id}:openldap -e OPENLDAP_HOST=openldap -e OPENLDAP_PORT=389 -e OPENLDAP_PORT_SSL=636 --link=${fedora389ds.id}:fedora389ds -e FEDORA_389DS_HOST=fedora389ds -e FEDORA_389DS_PORT=3389 -e FEDORA_389DS_PORT_SSL=3636") {
-                    sh 'export DISPLAY=:99; mvn -V -U -f pom-first.xml clean install && mvn -V clean install -Dorg.eclipse.swtbot.search.timeout=20000 -Denable-ui-tests'
-                  }
-                }
-              }
+              inTestLab({ sh 'export DISPLAY=:99; mvn -V -U -f pom-first.xml clean install && mvn -V clean install -Dorg.eclipse.swtbot.search.timeout=20000 -Denable-ui-tests' })
             }
           }
           post {
@@ -150,3 +132,14 @@ pipeline {
   }
 }
 
+def inTestLab(Closure action){
+  docker.image('coheigea/kerby').withRun('-h kerby.example.com -v $(pwd)/tools/testlab/kerby-data:/kerby-data') { kerby ->
+    docker.image('osixia/openldap:1.5.0').withRun('-h openldap.example.com -v $(pwd)/tools/testlab/ldap.keytab:/etc/krb5.keytab -v $(pwd)/tools/testlab/krb5.conf:/etc/krb5.conf -e LDAP_TLS_VERIFY_CLIENT=never') { openldap ->
+      docker.image('389ds/dirsrv').withRun('-h fedora389ds.example.com -v $(pwd)/tools/testlab/ldap.keytab:/etc/krb5.keytab -v $(pwd)/tools/testlab/krb5.conf:/etc/krb5.conf -e DS_DM_PASSWORD=admin', 'bash -c "zypper install -y cyrus-sasl-crammd5 cyrus-sasl-digestmd5 cyrus-sasl-gssapi; set -m; /usr/lib/dirsrv/dscontainer -r & while ! /usr/lib/dirsrv/dscontainer -H; do sleep 5; done; sleep 5; /usr/sbin/dsconf localhost backend create --suffix dc=example,dc=org --be-name example; fg"') { fe [...]
+        docker.image('apachedirectory/maven-build:jdk-8').inside("--link=${kerby.id}:kerby.example.com --link=${openldap.id}:openldap.example.com -v \$(pwd)/tools/testlab/krb5.conf:/etc/krb5.conf -e OPENLDAP_HOST=openldap -e OPENLDAP_PORT=389 -e OPENLDAP_PORT_SSL=636 --link=${fedora389ds.id}:fedora389ds.example.com -e FEDORA_389DS_HOST=fedora389ds -e FEDORA_389DS_PORT=3389 -e FEDORA_389DS_PORT_SSL=3636") {
+          action()
+        }
+      }
+    }
+  }
+}
diff --git a/plugins/connection.ui/src/main/java/org/apache/directory/studio/connection/ui/widgets/AuthenticationParameterPage.java b/plugins/connection.ui/src/main/java/org/apache/directory/studio/connection/ui/widgets/AuthenticationParameterPage.java
index a82cb5d..f59b136 100644
--- a/plugins/connection.ui/src/main/java/org/apache/directory/studio/connection/ui/widgets/AuthenticationParameterPage.java
+++ b/plugins/connection.ui/src/main/java/org/apache/directory/studio/connection/ui/widgets/AuthenticationParameterPage.java
@@ -218,7 +218,7 @@ public class AuthenticationParameterPage extends AbstractConnectionParameterPage
 
     private String getSaslRealm()
     {
-        return saslRealmText.getText();
+        return Strings.isEmpty( saslRealmText.getText() ) ? null : saslRealmText.getText();
     }
 
 
diff --git a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/DirectoryApiConnectionWrapperTest.java b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/DirectoryApiConnectionWrapperTest.java
index 49000f2..982c4a6 100644
--- a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/DirectoryApiConnectionWrapperTest.java
+++ b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/DirectoryApiConnectionWrapperTest.java
@@ -92,6 +92,7 @@ import org.apache.directory.studio.connection.core.ConnectionCorePlugin;
 import org.apache.directory.studio.connection.core.ConnectionParameter;
 import org.apache.directory.studio.connection.core.ConnectionParameter.AuthenticationMethod;
 import org.apache.directory.studio.connection.core.ConnectionParameter.EncryptionMethod;
+import org.apache.directory.studio.connection.core.ConnectionParameter.Krb5CredentialConfiguration;
 import org.apache.directory.studio.connection.core.ICertificateHandler.TrustLevel;
 import org.apache.directory.studio.connection.core.IReferralHandler;
 import org.apache.directory.studio.connection.core.event.ConnectionEventRegistry;
@@ -378,7 +379,7 @@ public class DirectoryApiConnectionWrapperTest
      * Test binding to the server using SASL and no encryption.
      */
     @ParameterizedTest
-    @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds)
+    @LdapServersSource(mode = Mode.All)
     public void testSaslBindPlain( TestLdapServer ldapServer )
     {
         ldapServer.setConfidentialityRequired( false );
@@ -432,7 +433,7 @@ public class DirectoryApiConnectionWrapperTest
      * Test binding to the server using SASL and ldaps:// encryption.
      */
     @ParameterizedTest
-    @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds)
+    @LdapServersSource(mode = Mode.All)
     public void testSaslBindLdaps( TestLdapServer ldapServer )
     {
         ldapServer.setConfidentialityRequired( true );
@@ -462,7 +463,7 @@ public class DirectoryApiConnectionWrapperTest
      * Test binding to the server using SASL and StartTLS encryption.
      */
     @ParameterizedTest
-    @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds)
+    @LdapServersSource(mode = Mode.All)
     public void testSaslBindStartTls( TestLdapServer ldapServer )
     {
         ldapServer.setConfidentialityRequired( true );
@@ -488,6 +489,102 @@ public class DirectoryApiConnectionWrapperTest
 
 
     /**
+     * Test binding to the server using GSSAPI and no encryption.
+     */
+    @ParameterizedTest
+    @LdapServersSource(mode = Mode.All, except = LdapServerType.ApacheDS, reason = "Missing OSGi import: org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntryModifier cannot be found by org.apache.directory.server.protocol.shared_2.0.0.AM26")
+    public void testSaslGssapiBindPlain( TestLdapServer ldapServer )
+    {
+        ldapServer.setConfidentialityRequired( false );
+        StudioProgressMonitor monitor = getProgressMonitor();
+        Connection connection = getConnection( monitor, ldapServer, "hnelson", "secret" );
+        connection.setAuthMethod( AuthenticationMethod.SASL_GSSAPI );
+        connection.getConnectionParameter().setKrb5CredentialConfiguration( Krb5CredentialConfiguration.OBTAIN_TGT );
+
+        assertFalse( connectionWrapper.isConnected() );
+
+        connectionWrapper.connect( monitor );
+        connectionWrapper.bind( monitor );
+
+        assertTrue( connectionWrapper.isConnected() );
+        assertFalse( connectionWrapper.isSecured() );
+        assertNull( monitor.getException() );
+
+        connectionWrapper.unbind();
+        connectionWrapper.disconnect();
+        assertFalse( connectionWrapper.isConnected() );
+    }
+
+
+    /**
+     * Test binding to the server using GSSAPI and ldaps:// encryption.
+     */
+    @ParameterizedTest
+    @LdapServersSource(mode = Mode.All, except = LdapServerType.ApacheDS, reason = "Missing OSGi import: org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntryModifier cannot be found by org.apache.directory.server.protocol.shared_2.0.0.AM26")
+    public void testSaslGssapiBindLdaps( TestLdapServer ldapServer ) throws Exception
+    {
+        // obtain native TGT
+        String[] cmd =
+            { "/bin/sh", "-c", "echo secret | /usr/bin/kinit hnelson" };
+        Process process = Runtime.getRuntime().exec( cmd );
+        int exitCode = process.waitFor();
+        assertEquals( 0, exitCode );
+
+        ldapServer.setConfidentialityRequired( true );
+        StudioProgressMonitor monitor = getProgressMonitor();
+        Connection connection = getConnection( monitor, ldapServer, "hnelson", "secret" );
+        connection.setPort( ldapServer.getPortSSL() );
+        connection.setEncryptionMethod( EncryptionMethod.LDAPS );
+        connection.setAuthMethod( AuthenticationMethod.SASL_GSSAPI );
+        connection.getConnectionParameter().setKrb5CredentialConfiguration( Krb5CredentialConfiguration.USE_NATIVE );
+        acceptAllCertificates();
+
+        assertFalse( connectionWrapper.isConnected() );
+
+        connectionWrapper.connect( monitor );
+        connectionWrapper.bind( monitor );
+
+        assertTrue( connectionWrapper.isConnected() );
+        assertTrue( connectionWrapper.isSecured() );
+        assertNull( monitor.getException() );
+
+        connectionWrapper.unbind();
+        connectionWrapper.disconnect();
+        assertFalse( connectionWrapper.isConnected() );
+    }
+
+
+    /**
+     * Test binding to the server using GSSAPI and StartTLS encryption.
+     */
+    @ParameterizedTest
+    @LdapServersSource(mode = Mode.All, except = LdapServerType.ApacheDS, reason = "Missing OSGi import: org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntryModifier cannot be found by org.apache.directory.server.protocol.shared_2.0.0.AM26")
+    public void testSaslGssapiBindStartTls( TestLdapServer ldapServer )
+    {
+        ldapServer.setConfidentialityRequired( true );
+        StudioProgressMonitor monitor = getProgressMonitor();
+        Connection connection = getConnection( monitor, ldapServer, "hnelson", "secret" );
+        connection.setEncryptionMethod( EncryptionMethod.START_TLS );
+        connection.setAuthMethod( AuthenticationMethod.SASL_GSSAPI );
+        connection.getConnectionParameter().setKrb5CredentialConfiguration( Krb5CredentialConfiguration.OBTAIN_TGT );
+        acceptAllCertificates();
+
+        assertFalse( connectionWrapper.isConnected() );
+
+        connectionWrapper.connect( monitor );
+        connectionWrapper.bind( monitor );
+
+        assertTrue( connectionWrapper.isConnected() );
+        assertTrue( connectionWrapper.isSecured() );
+        assertNull( monitor.getException() );
+
+        connectionWrapper.unbind();
+        connectionWrapper.disconnect();
+        assertFalse( connectionWrapper.isConnected() );
+    }
+
+
+    /**
      * Test failed binds to the server.
      */
     @ParameterizedTest
diff --git a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/Fedora389dsLdapServer.java b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/Fedora389dsLdapServer.java
index 7f8a0ac..c65e4ba 100644
--- a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/Fedora389dsLdapServer.java
+++ b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/Fedora389dsLdapServer.java
@@ -21,8 +21,6 @@
 package org.apache.directory.studio.test.integration.junit5;
 
 
-import static org.apache.directory.studio.test.integration.junit5.Constants.LOCALHOST;
-
 import org.apache.directory.api.ldap.model.entry.DefaultModification;
 import org.apache.directory.api.ldap.model.entry.Modification;
 import org.apache.directory.api.ldap.model.entry.ModificationOperation;
@@ -36,7 +34,7 @@ import org.apache.directory.api.ldap.model.entry.ModificationOperation;
  */
 public class Fedora389dsLdapServer extends TestLdapServer
 {
-    private static final String FEDORA_389DS_HOST = getEnvOrDefault( "FEDORA_389DS_HOST", LOCALHOST );
+    private static final String FEDORA_389DS_HOST = getEnvOrDefault( "FEDORA_389DS_HOST", "fedora389ds.example.com" );
     private static final int FEDORA_389DS_PORT = Integer.parseInt( getEnvOrDefault( "FEDORA_389DS_PORT", "21389" ) );
     private static final int FEDORA_389DS_PORT_SSL = Integer
         .parseInt( getEnvOrDefault( "FEDORA_389DS_PORT_SSL", "21636" ) );
@@ -65,6 +63,5 @@ public class Fedora389dsLdapServer extends TestLdapServer
                 "nsslapd-require-secure-binds", confidentialityRequired ? "on" : "off" );
             connection.modify( "cn=config", modification );
         } );
-
     }
 }
diff --git a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/OpenLdapServer.java b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/OpenLdapServer.java
index 69b80cb..5a1e971 100644
--- a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/OpenLdapServer.java
+++ b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/OpenLdapServer.java
@@ -40,7 +40,7 @@ import org.apache.directory.ldap.client.api.LdapConnection;
  */
 public class OpenLdapServer extends TestLdapServer
 {
-    private static final String OPENLDAP_HOST = getEnvOrDefault( "OPENLDAP_HOST", LOCALHOST );
+    private static final String OPENLDAP_HOST = getEnvOrDefault( "OPENLDAP_HOST", "openldap.example.com" );
     private static final int OPENLDAP_PORT = Integer.parseInt( getEnvOrDefault( "OPENLDAP_PORT", "20389" ) );
     private static final int OPENLDAP_PORT_SSL = Integer.parseInt( getEnvOrDefault( "OPENLDAP_PORT_SSL", "20636" ) );
     private static final String OPENLDAP_ADMIN_DN = getEnvOrDefault( "OPENLDAP_ADMIN_DN",
diff --git a/tests/test.integration.core/src/main/resources/org/apache/directory/studio/test/integration/junit5/Fedora389ds.ldif b/tests/test.integration.core/src/main/resources/org/apache/directory/studio/test/integration/junit5/Fedora389ds.ldif
new file mode 100644
index 0000000..3c76c87
--- /dev/null
+++ b/tests/test.integration.core/src/main/resources/org/apache/directory/studio/test/integration/junit5/Fedora389ds.ldif
@@ -0,0 +1,43 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+#
+dn: cn=config
+changetype: modify
+replace: passwordStorageScheme
+passwordStorageScheme: CLEAR
+-
+replace: nsslapd-enable-upgrade-hash
+nsslapd-enable-upgrade-hash: off
+-
+
+dn: cn=Kerberos uid mapping,cn=mapping,cn=sasl,cn=config
+changetype: modify
+replace: nsSaslMapRegexString
+nsSaslMapRegexString: \(.*\)
+-
+
+dn: cn=Kerberos uid mapping,cn=mapping,cn=sasl,cn=config
+changetype: modify
+replace: nsSaslMapBaseDNTemplate
+nsSaslMapBaseDNTemplate: dc=example,dc=org
+-
+
+dn: cn=Kerberos uid mapping,cn=mapping,cn=sasl,cn=config
+changetype: modify
+replace: nsSaslMapFilterTemplate
+nsSaslMapFilterTemplate: (uid=\1)
+-
diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/NewConnectionWizardTest.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/NewConnectionWizardTest.java
index 2fcba85..fba86f3 100644
--- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/NewConnectionWizardTest.java
+++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/NewConnectionWizardTest.java
@@ -21,6 +21,7 @@
 package org.apache.directory.studio.test.integration.ui;
 
 
+import static org.apache.directory.studio.test.integration.ui.utils.Constants.LOCALHOST;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -432,7 +433,7 @@ public class NewConnectionWizardTest extends AbstractTestBase
 
 
     @ParameterizedTest
-    @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds")
+    @LdapServersSource(mode = Mode.All)
     public void testCreateConnectionNoEncryptionSaslCramMd5OK( TestLdapServer server )
     {
         setConnectionParameters( server, EncryptionMethod.NONE );
@@ -452,7 +453,7 @@ public class NewConnectionWizardTest extends AbstractTestBase
 
 
     @ParameterizedTest
-    @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds")
+    @LdapServersSource(mode = Mode.All)
     public void testCreateConnectionNoEncryptionSaslDigestMd5OK( TestLdapServer server )
     {
         setConnectionParameters( server, EncryptionMethod.NONE );
@@ -460,10 +461,6 @@ public class NewConnectionWizardTest extends AbstractTestBase
         wizardBot.selectDigestMD5Authentication();
         wizardBot.typeUser( "user.1" );
         wizardBot.typePassword( "password" );
-        if ( server.getType() == LdapServerType.ApacheDS )
-        {
-            wizardBot.typeRealm( "EXAMPLE.ORG" );
-        }
         wizardBot.selectQualityOfProtection( SaslQoP.AUTH );
         wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH );
 
@@ -476,7 +473,7 @@ public class NewConnectionWizardTest extends AbstractTestBase
 
 
     @ParameterizedTest
-    @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds")
+    @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "Only secure binds configured for 389ds")
     public void testCreateConnectionNoEncryptionSaslDigestMd5ConfidentialityRequired( TestLdapServer server )
     {
         setConnectionParameters( server, EncryptionMethod.NONE );
@@ -494,6 +491,58 @@ public class NewConnectionWizardTest extends AbstractTestBase
 
 
     @ParameterizedTest
+    @LdapServersSource(mode = Mode.All, except = LdapServerType.ApacheDS, reason = "Missing OSGi import: org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntryModifier cannot be found by org.apache.directory.server.protocol.shared_2.0.0.AM26")
+    public void testCreateConnectionNoEncryptionSaslGssapiNativeTgtOK( TestLdapServer server ) throws Exception
+    {
+        // obtain native TGT
+        String[] cmd =
+            { "/bin/sh", "-c", "echo secret | /usr/bin/kinit hnelson" };
+        Process process = Runtime.getRuntime().exec( cmd );
+        int exitCode = process.waitFor();
+        assertEquals( 0, exitCode );
+
+        setConnectionParameters( server, EncryptionMethod.NONE );
+
+        wizardBot.selectGssApiAuthentication();
+        wizardBot.selectQualityOfProtection( SaslQoP.AUTH );
+        wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH );
+        wizardBot.selectUseNativeTgt();
+        wizardBot.selectUseNativeSystemConfiguration();
+
+        String result = wizardBot.clickCheckAuthenticationButton();
+        assertNull( result, "Expected OK" );
+
+        finishAndAssertConnection( server, EncryptionMethod.NONE, AuthenticationMethod.SASL_GSSAPI,
+            "", "" );
+    }
+
+
+    @ParameterizedTest
+    @LdapServersSource(mode = Mode.All, except = LdapServerType.ApacheDS, reason = "Missing OSGi import: org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntryModifier cannot be found by org.apache.directory.server.protocol.shared_2.0.0.AM26")
+    public void testCreateConnectionNoEncryptionSaslGssapiObtainOK( TestLdapServer server )
+    {
+        setConnectionParameters( server, EncryptionMethod.NONE );
+
+        wizardBot.selectGssApiAuthentication();
+        wizardBot.selectObtainTgtFromKdc();
+        wizardBot.typeUser( "hnelson" );
+        wizardBot.typePassword( "secret" );
+        wizardBot.selectQualityOfProtection( SaslQoP.AUTH );
+        wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH );
+        wizardBot.selectUseManualConfiguration();
+        wizardBot.typeKerberosRealm( "EXAMPLE.COM" );
+        wizardBot.typeKdcHost( LOCALHOST );
+        wizardBot.typeKdcPort( 60088 );
+
+        String result = wizardBot.clickCheckAuthenticationButton();
+        assertNull( result, "Expected OK" );
+
+        finishAndAssertConnection( server, EncryptionMethod.NONE, AuthenticationMethod.SASL_GSSAPI,
+            "hnelson", "secret" );
+    }
+
+
+    @ParameterizedTest
     @LdapServersSource(mode = Mode.All)
     public void testCreateConnectionLdapsEncryptionNoAuthOK( TestLdapServer server )
     {
@@ -537,7 +586,7 @@ public class NewConnectionWizardTest extends AbstractTestBase
 
 
     @ParameterizedTest
-    @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds")
+    @LdapServersSource(mode = Mode.All)
     public void testCreateConnectionLdapsEncryptionSaslDigestMd5Ok( TestLdapServer server )
     {
         setConnectionParameters( server, EncryptionMethod.LDAPS );
@@ -545,10 +594,6 @@ public class NewConnectionWizardTest extends AbstractTestBase
         wizardBot.selectDigestMD5Authentication();
         wizardBot.typeUser( "user.1" );
         wizardBot.typePassword( "password" );
-        if ( server.getType() == LdapServerType.ApacheDS )
-        {
-            wizardBot.typeRealm( "EXAMPLE.ORG" );
-        }
         wizardBot.selectQualityOfProtection( SaslQoP.AUTH );
         wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH );
 
@@ -558,7 +603,7 @@ public class NewConnectionWizardTest extends AbstractTestBase
 
 
     @ParameterizedTest
-    @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds")
+    @LdapServersSource(mode = Mode.All)
     public void testCreateConnectionLdapsEncryptionSaslDigestMd5InvalidCredentials( TestLdapServer server )
     {
         setConnectionParameters( server, EncryptionMethod.LDAPS );
@@ -566,10 +611,6 @@ public class NewConnectionWizardTest extends AbstractTestBase
         wizardBot.selectDigestMD5Authentication();
         wizardBot.typeUser( "user.1" );
         wizardBot.typePassword( "invalid" );
-        if ( server.getType() == LdapServerType.ApacheDS )
-        {
-            wizardBot.typeRealm( "EXAMPLE.ORG" );
-        }
         wizardBot.selectQualityOfProtection( SaslQoP.AUTH );
         wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH );
 
@@ -581,7 +622,7 @@ public class NewConnectionWizardTest extends AbstractTestBase
 
 
     @ParameterizedTest
-    @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds")
+    @LdapServersSource(mode = Mode.All)
     public void testCreateConnectionLdapsEncryptionSaslDigestMd5InvalidRealm( TestLdapServer server )
     {
         setConnectionParameters( server, EncryptionMethod.LDAPS );
@@ -644,7 +685,7 @@ public class NewConnectionWizardTest extends AbstractTestBase
 
 
     @ParameterizedTest
-    @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds")
+    @LdapServersSource(mode = Mode.All)
     public void testCreateConnectionStartTlsEncryptionSaslDigestMd5OK( TestLdapServer server )
     {
         setConnectionParameters( server, EncryptionMethod.START_TLS );
@@ -652,10 +693,6 @@ public class NewConnectionWizardTest extends AbstractTestBase
         wizardBot.selectDigestMD5Authentication();
         wizardBot.typeUser( "user.1" );
         wizardBot.typePassword( "password" );
-        if ( server.getType() == LdapServerType.ApacheDS )
-        {
-            wizardBot.typeRealm( "EXAMPLE.ORG" );
-        }
         wizardBot.selectQualityOfProtection( SaslQoP.AUTH );
         wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH );
 
@@ -665,7 +702,7 @@ public class NewConnectionWizardTest extends AbstractTestBase
 
 
     @ParameterizedTest
-    @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds")
+    @LdapServersSource(mode = Mode.All)
     public void testCreateConnectionStartTlsEncryptionSaslDigestMd5InvalidCredentials( TestLdapServer server )
     {
         setConnectionParameters( server, EncryptionMethod.START_TLS );
@@ -673,10 +710,6 @@ public class NewConnectionWizardTest extends AbstractTestBase
         wizardBot.selectDigestMD5Authentication();
         wizardBot.typeUser( "user.1" );
         wizardBot.typePassword( "invalid" );
-        if ( server.getType() == LdapServerType.ApacheDS )
-        {
-            wizardBot.typeRealm( "EXAMPLE.ORG" );
-        }
         wizardBot.selectQualityOfProtection( SaslQoP.AUTH );
         wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH );
 
diff --git a/tools/testlab/README.md b/tools/testlab/README.md
new file mode 100644
index 0000000..288766f
--- /dev/null
+++ b/tools/testlab/README.md
@@ -0,0 +1,77 @@
+# Test Lab
+
+## Host preparation
+
+To be able to access the services with their FQDN add aliases to the `/etc/hosts`. 
+Note that this is a hack rather than a proper solution.
+
+```
+/etc/hosts:
+127.0.0.100     kerby.example.com
+127.0.0.101     openldap.example.com
+127.0.0.102     fedora389ds.example.com
+```
+
+## Apache Kerby as Kerberos KDC
+
+Uses Docker image `coheigea/kerby`, source at https://github.com/coheigea/testcases/tree/master/apache/docker/kerby.
+
+Start the Kerby server
+
+```
+docker run -it --rm --name kerby -h kerby.example.com -u $(id -u):$(id -g) -p 60088:60088/tcp -p 60088:60088/udp -v $(pwd)/tools/testlab/kerby-data:/kerby-data coheigea/kerby
+```
+
+Initial creation of service accounts and keytabs:
+
+```
+docker exec -it kerby bash
+stty rows 24 columns 80
+sh bin/kadmin.sh /kerby-data/conf/ -k /kerby-data/keytabs/admin.keytab
+
+addprinc -pw secret hnelson@EXAMPLE.COM
+
+addprinc -pw randall ldap/openldap.example.com@EXAMPLE.COM
+ktadd -k /kerby-data/keytabs/ldap.keytab ldap/openldap.example.com@EXAMPLE.COM
+addprinc -pw randall ldap/fedora389ds.example.com@EXAMPLE.COM
+ktadd -k /kerby-data/keytabs/ldap.keytab ldap/fedora389ds.example.com@EXAMPLE.COM
+```
+
+## OpenLDAP
+
+```
+docker run -it --rm --name openldap -h openldap.example.com -p 20389:389 -p 20636:636 -e LDAP_TLS_VERIFY_CLIENT=never -v $(pwd)/tools/testlab/ldap.keytab:/etc/krb5.keytab -v $(pwd)/tools/testlab/krb5.conf:/etc/krb5.conf osixia/openldap:1.5.0
+```
+
+## Fedora 389ds
+
+```
+docker run -it --rm --name fedora389ds -h fedora389ds.example.com -p 21389:3389 -p 21636:3636 -e DS_DM_PASSWORD=admin -v $(pwd)/tools/testlab/ldap.keytab:/etc/krb5.keytab -v $(pwd)/tools/testlab/krb5.conf:/etc/krb5.conf 389ds/dirsrv bash -c "zypper install -y cyrus-sasl-crammd5 cyrus-sasl-digestmd5 cyrus-sasl-gssapi; set -m; /usr/lib/dirsrv/dscontainer -r & while ! /usr/lib/dirsrv/dscontainer -H; do sleep 5; done; sleep 5; /usr/sbin/dsconf localhost backend create --suffix dc=example,dc= [...]
+```
+
+## Usage
+
+### GSSAPI authentication
+
+```
+export KRB5_CONFIG=$(pwd)/tools/testlab/krb5.conf
+echo "secret" | kinit hnelson
+ldapwhoami -H ldap://openldap.example.com:20389 -Y GSSAPI -N
+ldapwhoami -H ldap://fedora389ds.example.com:21389 -Y GSSAPI -N
+```
+
+### UI integration tests
+
+```
+docker run -it --rm --cpus="1.5" \
+    -u $(id -u):$(id -g) \
+    -v ~/.m2:/home/hnelson/.m2 \
+    -v $(pwd):/home/hnelson/project \
+    -v $(pwd)/tools/testlab/krb5.conf:/etc/krb5.conf \
+    --link=kerby:kerby.example.com \
+    --link=openldap:openldap.example.com -e OPENLDAP_HOST=openldap.example.com -e OPENLDAP_PORT=389 -e OPENLDAP_PORT_SSL=636 \
+    --link=fedora389ds:fedora389ds.example.com -e FEDORA_389DS_HOST=fedora389ds.example.com -e FEDORA_389DS_PORT=3389 -e FEDORA_389DS_PORT_SSL=3636 \
+    apachedirectory/maven-build:jdk-11 bash -c "mvn -V -f pom-first.xml clean install && mvn -V clean install -Denable-ui-tests"
+
+```
+
diff --git a/tools/testlab/kerby-data/backend/json-backend.json b/tools/testlab/kerby-data/backend/json-backend.json
new file mode 100644
index 0000000..0d47acd
--- /dev/null
+++ b/tools/testlab/kerby-data/backend/json-backend.json
@@ -0,0 +1,116 @@
+{
+  "adminprotocol/localhost@EXAMPLE.COM": {
+    "principal": "adminprotocol/localhost@EXAMPLE.COM",
+    "keyVersion": 1,
+    "kdcFlags": 0,
+    "disabled": false,
+    "locked": false,
+    "expireTime": "253402300799900",
+    "createdTime": "1528713224740",
+    "keys": {
+      "AES128_CTS_HMAC_SHA1_96": {
+        "kvno": 1,
+        "key": "3019A003020111A112041091CDC8A37ADE5268236C840523F87038"
+      },
+      "DES3_CBC_SHA1": {
+        "kvno": 1,
+        "key": "3021A003020110A11A0418F7AE8A9B6D7AF2E59D49DF193476CE511FFE1091C464A823"
+      }
+    }
+  },
+  "ldap/fedora389ds.example.com@EXAMPLE.COM": {
+    "principal": "ldap/fedora389ds.example.com@EXAMPLE.COM",
+    "keyVersion": 1,
+    "kdcFlags": 0,
+    "disabled": false,
+    "locked": false,
+    "expireTime": "253402300799900",
+    "createdTime": "1623646752731",
+    "keys": {
+      "DES3_CBC_SHA1": {
+        "kvno": 1,
+        "key": "3021A003020110A11A04182A1FF2DFC47AB0B0E068EA3D3476AE19511ACDE6970D5467"
+      },
+      "AES128_CTS_HMAC_SHA1_96": {
+        "kvno": 1,
+        "key": "3019A003020111A1120410B13B78BDE0374D463D877871A3C8B88F"
+      }
+    }
+  },
+  "krbtgt/EXAMPLE.COM@EXAMPLE.COM": {
+    "principal": "krbtgt/EXAMPLE.COM@EXAMPLE.COM",
+    "keyVersion": 1,
+    "kdcFlags": 0,
+    "disabled": false,
+    "locked": false,
+    "expireTime": "253402300799900",
+    "createdTime": "1528713224693",
+    "keys": {
+      "AES128_CTS_HMAC_SHA1_96": {
+        "kvno": 1,
+        "key": "3019A003020111A1120410BF87D478614F7A6D76C0462F2AD180AE"
+      },
+      "DES3_CBC_SHA1": {
+        "kvno": 1,
+        "key": "3021A003020110A11A0418627F34BF7F261964F1E55B5849B3D583BCD0795E34FB317A"
+      }
+    }
+  },
+  "ldap/openldap.example.com@EXAMPLE.COM": {
+    "principal": "ldap/openldap.example.com@EXAMPLE.COM",
+    "keyVersion": 1,
+    "kdcFlags": 0,
+    "disabled": false,
+    "locked": false,
+    "expireTime": "253402300799900",
+    "createdTime": "1623646740945",
+    "keys": {
+      "DES3_CBC_SHA1": {
+        "kvno": 1,
+        "key": "3021A003020110A11A0418DA0458C110D9435776F4BC32BFAE1C94624A7A402979D6F8"
+      },
+      "AES128_CTS_HMAC_SHA1_96": {
+        "kvno": 1,
+        "key": "3019A003020111A11204106A8382B60767C62E8766B741F92406A4"
+      }
+    }
+  },
+  "hnelson@EXAMPLE.COM": {
+    "principal": "hnelson@EXAMPLE.COM",
+    "keyVersion": 1,
+    "kdcFlags": 0,
+    "disabled": false,
+    "locked": false,
+    "expireTime": "253402300799900",
+    "createdTime": "1623569412700",
+    "keys": {
+      "DES3_CBC_SHA1": {
+        "kvno": 1,
+        "key": "3021A003020110A11A04185707CE2952922C1C8CBF43C23D8F8C5E9E8CF75D3E4A5E25"
+      },
+      "AES128_CTS_HMAC_SHA1_96": {
+        "kvno": 1,
+        "key": "3019A003020111A1120410AD214B38B69DFCCAACF15F346D417B90"
+      }
+    }
+  },
+  "kadmin/EXAMPLE.COM@EXAMPLE.COM": {
+    "principal": "kadmin/EXAMPLE.COM@EXAMPLE.COM",
+    "keyVersion": 1,
+    "kdcFlags": 0,
+    "disabled": false,
+    "locked": false,
+    "expireTime": "253402300799900",
+    "createdTime": "1528713224726",
+    "keys": {
+      "AES128_CTS_HMAC_SHA1_96": {
+        "kvno": 1,
+        "key": "3019A003020111A112041064F2D24DE79182AD88AC9A60ED6F9983"
+      },
+      "DES3_CBC_SHA1": {
+        "kvno": 1,
+        "key": "3021A003020110A11A04189BCBA454B3FB4A540746ECC1D3A283BCFBD307A15BE5EA1C"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tools/testlab/kerby-data/conf/adminClient.conf b/tools/testlab/kerby-data/conf/adminClient.conf
new file mode 100644
index 0000000..7c6909b
--- /dev/null
+++ b/tools/testlab/kerby-data/conf/adminClient.conf
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+[libdefaults]
+default_realm = EXAMPLE.COM
+admin_port = 65417
+keytab_file = admin.keytab
+protocol = adminprotocol
+server_name = localhost
diff --git a/tools/testlab/kerby-data/conf/adminServer.conf b/tools/testlab/kerby-data/conf/adminServer.conf
new file mode 100644
index 0000000..08af51c
--- /dev/null
+++ b/tools/testlab/kerby-data/conf/adminServer.conf
@@ -0,0 +1,24 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+[libdefaults]
+default_realm = EXAMPLE.COM
+admin_realm = EXAMPLE.COM
+admin_port = 65417
+keytab_file = protocol.keytab
+protocol = adminprotocol
+server_name = localhost
diff --git a/tools/testlab/kerby-data/conf/backend.conf b/tools/testlab/kerby-data/conf/backend.conf
new file mode 100644
index 0000000..3bbe481
--- /dev/null
+++ b/tools/testlab/kerby-data/conf/backend.conf
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+kdc_identity_backend = org.apache.kerby.kerberos.kdc.identitybackend.JsonIdentityBackend
+backend.json.dir = /kerby-data/backend
diff --git a/tools/testlab/kerby-data/conf/kdc.conf b/tools/testlab/kerby-data/conf/kdc.conf
new file mode 100644
index 0000000..34f2973
--- /dev/null
+++ b/tools/testlab/kerby-data/conf/kdc.conf
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+[kdcdefaults]
+  kdc_host = kerby.example.com
+  kdc_udp_port = 60088
+  kdc_tcp_port = 60088
+  kdc_realm = EXAMPLE.COM
diff --git a/tools/testlab/kerby-data/conf/krb5.conf b/tools/testlab/kerby-data/conf/krb5.conf
new file mode 100644
index 0000000..6692859
--- /dev/null
+++ b/tools/testlab/kerby-data/conf/krb5.conf
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+[libdefaults]
+    kdc_realm = EXAMPLE.COM
+    default_realm = EXAMPLE.COM
+    udp_preference_limit = 4096
+    kdc_tcp_port = 60088
+    kdc_udp_port = 60088
+
+[realms]
+    EXAMPLE.COM = {
+        kdc = localhost:60088
+    }
\ No newline at end of file
diff --git a/tools/testlab/kerby-data/keytabs/admin.keytab b/tools/testlab/kerby-data/keytabs/admin.keytab
new file mode 100644
index 0000000..55e1add
Binary files /dev/null and b/tools/testlab/kerby-data/keytabs/admin.keytab differ
diff --git a/tools/testlab/krb5.conf b/tools/testlab/krb5.conf
new file mode 100644
index 0000000..cb007b8
--- /dev/null
+++ b/tools/testlab/krb5.conf
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+[libdefaults]
+    kdc_realm = EXAMPLE.COM
+    default_realm = EXAMPLE.COM
+    udp_preference_limit = 4096
+    kdc_tcp_port = 60088
+    kdc_udp_port = 60088
+
+[realms]
+    EXAMPLE.COM = {
+        kdc = kerby.example.com:60088
+    }
\ No newline at end of file
diff --git a/tools/testlab/ldap.keytab b/tools/testlab/ldap.keytab
new file mode 100644
index 0000000..11e2700
Binary files /dev/null and b/tools/testlab/ldap.keytab differ