Repository: karaf Updated Branches: refs/heads/master d7a2185a7 -> e908c4a9b
[KARAF-4487] Add support for GSSAPI authentication. Project: http://git-wip-us.apache.org/repos/asf/karaf/repo Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/43d3aa6d Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/43d3aa6d Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/43d3aa6d Branch: refs/heads/master Commit: 43d3aa6deb7bbe400622f6b5da082af098f90225 Parents: d7a2185 Author: [email protected] <[email protected]> Authored: Wed Aug 24 12:35:10 2016 +0200 Committer: Jean-Baptiste Onofré <[email protected]> Committed: Wed Dec 7 18:43:39 2016 +0100 ---------------------------------------------------------------------- .../modules/ldap/GSSAPILdapLoginModule.java | 151 +++++++ .../modules/ldap/GSSAPILdapLoginModuleTest.java | 431 +++++++++++++++++++ .../jaas/modules/ldap/gssapi.ldap.properties | 37 ++ .../karaf/jaas/modules/ldap/gssapi.login.config | 26 ++ .../developer-guide/security-framework.adoc | 75 ++++ .../src/main/asciidoc/user-guide/security.adoc | 2 + 6 files changed, 722 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/karaf/blob/43d3aa6d/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModule.java ---------------------------------------------------------------------- diff --git a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModule.java b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModule.java new file mode 100644 index 0000000..ba6ed5b --- /dev/null +++ b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModule.java @@ -0,0 +1,151 @@ +/* + * Licensed 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. + * under the License. + */ +package org.apache.karaf.jaas.modules.ldap; + +import org.apache.karaf.jaas.boot.principal.RolePrincipal; +import org.apache.karaf.jaas.boot.principal.UserPrincipal; +import org.apache.karaf.jaas.modules.AbstractKarafLoginModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +import java.io.IOException; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +/** + * Specific LDAPLoginModule to be used with GSSAPI. Uses the specified realm as login context. + */ +public class GSSAPILdapLoginModule extends AbstractKarafLoginModule { + + private static Logger logger = LoggerFactory.getLogger(LDAPLoginModule.class); + + public static final String REALM_PROPERTY = "gssapiRealm"; + + private LoginContext context; + + @Override + public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { + super.initialize(subject, callbackHandler, options); + } + + @Override + public boolean login() throws LoginException { + if (!options.containsKey(REALM_PROPERTY)) { + logger.warn(REALM_PROPERTY + " is not set"); + throw new LoginException("cannot authenticate through the delegating realm"); + } + + context = new LoginContext((String) options.get(REALM_PROPERTY), this.subject, this.callbackHandler); + context.login(); + + try { + return Subject.doAs(context.getSubject(), (PrivilegedExceptionAction<Boolean>) () -> doLogin()); + } catch (PrivilegedActionException pExcp) { + logger.error("error with delegated authentication", pExcp); + throw new LoginException(pExcp.getMessage()); + } + } + + protected boolean doLogin() throws LoginException { + + //force GSSAPI for login + Map<String, Object> opts = new HashMap<>(this.options); + opts.put(LDAPOptions.AUTHENTICATION, "GSSAPI"); + + ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + try { + LDAPOptions lOptions = new LDAPOptions(opts); + + NameCallback[] callbacks = new NameCallback[1]; + callbacks[0] = new NameCallback("Username: "); + + try { + callbackHandler.handle(callbacks); + } catch (IOException ioException) { + logger.error("error with callback handler", ioException); + throw new LoginException(ioException.getMessage()); + } catch (UnsupportedCallbackException unsupportedCallbackException) { + logger.error("error with callback handler", unsupportedCallbackException); + throw new LoginException(unsupportedCallbackException.getMessage() + " not available to obtain information from user."); + } + + user = callbacks[0].getName(); + + principals = new HashSet<>(); + + String[] userDnAndNamespace; + try (LDAPCache cache = LDAPCache.getCache(lOptions)) { + + try { + logger.debug("Get the user DN."); + userDnAndNamespace = cache.getUserDnAndNamespace(user); + + } catch (Exception e) { + logger.warn("Can't connect to the LDAP server: {}", e.getMessage(), e); + throw new LoginException("Can't connect to the LDAP server: " + e.getMessage()); + } + + if (userDnAndNamespace == null) { + return false; + } + + principals.add(new UserPrincipal(user)); + + try { + String[] roles = cache.getUserRoles(user, userDnAndNamespace[0], userDnAndNamespace[1]); + for (String role : roles) { + principals.add(new RolePrincipal(role)); + } + } catch (Exception e) { + throw new LoginException("Can't get user " + user + " roles: " + e.getMessage()); + } + + return true; + } + } finally { + ManagedSSLSocketFactory.setSocketFactory(null); + Thread.currentThread().setContextClassLoader(tccl); + } + } + + @Override + public boolean abort() throws LoginException { + return true; + } + + @Override + public boolean commit() throws LoginException { + boolean ret = super.commit(); + principals.addAll(subject.getPrincipals(KerberosPrincipal.class)); + return ret; + } + + @Override + public boolean logout() throws LoginException { + subject.getPrincipals().removeAll(principals); + principals.clear(); + return true; + } +} http://git-wip-us.apache.org/repos/asf/karaf/blob/43d3aa6d/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModuleTest.java ---------------------------------------------------------------------- diff --git a/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModuleTest.java b/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModuleTest.java new file mode 100644 index 0000000..9e34749 --- /dev/null +++ b/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/GSSAPILdapLoginModuleTest.java @@ -0,0 +1,431 @@ +/* + * Licensed 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. + * under the License. + */ +package org.apache.karaf.jaas.modules.ldap; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.SystemUtils; +import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms; +import org.apache.directory.api.ldap.model.entry.DefaultEntry; +import org.apache.directory.api.ldap.model.entry.Entry; +import org.apache.directory.api.ldap.model.exception.LdapException; +import org.apache.directory.api.util.Strings; +import org.apache.directory.server.annotations.CreateKdcServer; +import org.apache.directory.server.annotations.CreateLdapServer; +import org.apache.directory.server.annotations.CreateTransport; +import org.apache.directory.server.annotations.SaslMechanism; +import org.apache.directory.server.core.annotations.ApplyLdifs; +import org.apache.directory.server.core.annotations.ContextEntry; +import org.apache.directory.server.core.annotations.CreateDS; +import org.apache.directory.server.core.annotations.CreateIndex; +import org.apache.directory.server.core.annotations.CreatePartition; +import org.apache.directory.server.core.integ.FrameworkRunner; +import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor; +import org.apache.directory.server.kerberos.kdc.AbstractKerberosITest; +import org.apache.directory.server.kerberos.kdc.KerberosTestUtils; +import org.apache.directory.server.ldap.handlers.sasl.cramMD5.CramMd5MechanismHandler; +import org.apache.directory.server.ldap.handlers.sasl.digestMD5.DigestMd5MechanismHandler; +import org.apache.directory.server.ldap.handlers.sasl.gssapi.GssapiMechanismHandler; +import org.apache.directory.server.ldap.handlers.sasl.ntlm.NtlmMechanismHandler; +import org.apache.directory.server.ldap.handlers.sasl.plain.PlainMechanismHandler; +import org.apache.directory.server.protocol.shared.transport.TcpTransport; +import org.apache.directory.server.protocol.shared.transport.Transport; +import org.apache.directory.shared.kerberos.codec.types.EncryptionType; +import org.apache.directory.shared.kerberos.crypto.checksum.ChecksumType; +import org.apache.felix.utils.properties.Properties; +import org.apache.karaf.jaas.boot.principal.RolePrincipal; +import org.apache.karaf.jaas.boot.principal.UserPrincipal; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.kerberos.KerberosTicket; +import javax.security.auth.login.LoginException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.Principal; +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RunWith(FrameworkRunner.class) +@CreateDS(name = "GSSAPILdapLoginModuleTest-class", + partitions = + { + @CreatePartition( + name = "example", + suffix = "dc=example,dc=com", + contextEntry = @ContextEntry( + entryLdif = + "dn: dc=example,dc=com\n" + + "dc: example\n" + + "objectClass: top\n" + + "objectClass: domain\n\n"), + indexes = + { + @CreateIndex(attribute = "objectClass"), + @CreateIndex(attribute = "dc"), + @CreateIndex(attribute = "ou") + }) + }, + additionalInterceptors = + { + KeyDerivationInterceptor.class + }) +@CreateLdapServer( + transports = + { + @CreateTransport(protocol = "LDAP") + }, + saslHost = "localhost", + saslPrincipal = "ldap/[email protected]", + saslMechanisms = + { + @SaslMechanism(name = SupportedSaslMechanisms.PLAIN, implClass = PlainMechanismHandler.class), + @SaslMechanism(name = SupportedSaslMechanisms.CRAM_MD5, implClass = CramMd5MechanismHandler.class), + @SaslMechanism(name = SupportedSaslMechanisms.DIGEST_MD5, implClass = DigestMd5MechanismHandler.class), + @SaslMechanism(name = SupportedSaslMechanisms.GSSAPI, implClass = GssapiMechanismHandler.class), + @SaslMechanism(name = SupportedSaslMechanisms.NTLM, implClass = NtlmMechanismHandler.class), + @SaslMechanism(name = SupportedSaslMechanisms.GSS_SPNEGO, implClass = NtlmMechanismHandler.class) + }) +@CreateKdcServer( + transports = + { + @CreateTransport(protocol = "UDP", port = 6088), + @CreateTransport(protocol = "TCP", port = 6088) + }) +@ApplyLdifs({ + "dn: ou=users,dc=example,dc=com", + "objectClass: top", + "objectClass: organizationalUnit", + "ou: users", + + "dn: ou=groups,dc=example,dc=com", + "objectClass: top", + "objectClass: organizationalUnit", + "ou: groups", + + "dn: cn=admin,ou=groups,dc=example,dc=com", + "objectClass: top", + "objectClass: groupOfNames", + "cn: admin", + "member: uid=hnelson,ou=users,dc=example,dc=com" +}) +public class GSSAPILdapLoginModuleTest extends AbstractKerberosITest { + + private static boolean loginConfigUpdated; + + @Before + public void setUp() throws Exception { + super.setUp(); + + // Set up a partition for EXAMPLE.COM and add user and service principals to test authentication with. + KerberosTestUtils.fixServicePrincipalName( + "ldap/" + KerberosTestUtils.getHostName() + "@EXAMPLE.COM", null, getLdapServer()); + setupEnv(TcpTransport.class, + EncryptionType.AES128_CTS_HMAC_SHA1_96, ChecksumType.HMAC_SHA1_96_AES128); + + kdcServer.getConfig().setPaEncTimestampRequired(false); + + String basedir = System.getProperty("basedir"); + if (basedir == null) { + basedir = new File(".").getCanonicalPath(); + } + File config = new File(basedir + "/target/test-classes/org/apache/karaf/jaas/modules/ldap/gssapi.login.config"); + + System.setProperty("java.security.auth.login.config", config.toString()); + + updatePort(); + } + + public void updatePort() throws Exception { + if (!loginConfigUpdated) { + String basedir = System.getProperty("basedir"); + if (basedir == null) { + basedir = new File(".").getCanonicalPath(); + } + + // Read in ldap.properties and substitute in the correct port + File f = new File(basedir + "/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties"); + + FileInputStream inputStream = new FileInputStream(f); + String content = IOUtils.toString(inputStream, "UTF-8"); + inputStream.close(); + content = content.replaceAll("portno", "" + super.getLdapServer().getPort()); + content = content.replaceAll("address", KerberosTestUtils.getHostName()); + + File f2 = new File(basedir + "/target/test-classes/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties"); + FileOutputStream outputStream = new FileOutputStream(f2); + IOUtils.write(content, outputStream, "UTF-8"); + outputStream.close(); + loginConfigUpdated = true; + } + + } + + @After + public void tearDown() throws Exception { + LDAPCache.clear(); + super.tearDown(); + } + + @Test + public void testSuccess() throws Exception { + + Properties options = ldapLoginModuleOptions(); + GSSAPILdapLoginModule module = new GSSAPILdapLoginModule(); + + CallbackHandler cb = new CallbackHandler() { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback cb : callbacks) { + if (cb instanceof NameCallback) { + ((NameCallback) cb).setName("hnelson"); + } else if (cb instanceof PasswordCallback) { + ((PasswordCallback) cb).setPassword("secret".toCharArray()); + } + } + } + }; + Subject subject = new Subject(); + module.initialize(subject, cb, null, options); + + assertEquals("Precondition", 0, subject.getPrincipals().size()); + assertTrue(module.login()); + assertTrue(module.commit()); + + assertEquals(3, subject.getPrincipals().size()); + + boolean foundKrb5User = false; + boolean foundUser = false; + boolean foundRole = false; + boolean foundTicket = false; + + for (Principal pr : subject.getPrincipals()) { + if (pr instanceof KerberosPrincipal) { + assertEquals("[email protected]", pr.getName()); + foundKrb5User = true; + } else if (pr instanceof UserPrincipal) { + assertEquals("hnelson", pr.getName()); + foundUser = true; + } else if (pr instanceof RolePrincipal) { + assertEquals("admin", pr.getName()); + foundRole = true; + } + } + for (Object crd : subject.getPrivateCredentials()) { + if (crd instanceof KerberosTicket) { + assertEquals("[email protected]", ((KerberosTicket) crd).getClient().getName()); + assertEquals("krbtgt/[email protected]", ((KerberosTicket) crd).getServer().getName()); + foundTicket = true; + break; + } + } + + assertTrue("Principals should contains kerberos user", foundKrb5User); + assertTrue("Principals should contains ldap user", foundUser); + assertTrue("Principals should contains ldap role", foundRole); + assertTrue("PricatePrincipals should contains kerberos ticket", foundTicket); + + assertTrue(module.logout()); + assertEquals("Principals should be gone as the user has logged out", 0, subject.getPrincipals().size()); + } + + @Test(expected = LoginException.class) + public void testUsernameFailure() throws Exception { + + Properties options = ldapLoginModuleOptions(); + GSSAPILdapLoginModule module = new GSSAPILdapLoginModule(); + + CallbackHandler cb = new CallbackHandler() { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback cb : callbacks) { + if (cb instanceof NameCallback) { + ((NameCallback) cb).setName("hnelson0"); + } else if (cb instanceof PasswordCallback) { + ((PasswordCallback) cb).setPassword("secret".toCharArray()); + } + } + } + }; + Subject subject = new Subject(); + module.initialize(subject, cb, null, options); + + assertEquals("Precondition", 0, subject.getPrincipals().size()); + assertTrue(module.login()); // should throw LoginException + } + + @Test(expected = LoginException.class) + public void testPasswordFailure() throws Exception { + + Properties options = ldapLoginModuleOptions(); + GSSAPILdapLoginModule module = new GSSAPILdapLoginModule(); + + CallbackHandler cb = new CallbackHandler() { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback cb : callbacks) { + if (cb instanceof NameCallback) { + ((NameCallback) cb).setName("hnelson"); + } else if (cb instanceof PasswordCallback) { + ((PasswordCallback) cb).setPassword("secret0".toCharArray()); + } + } + } + }; + Subject subject = new Subject(); + module.initialize(subject, cb, null, options); + + assertEquals("Precondition", 0, subject.getPrincipals().size()); + assertTrue(module.login()); + } + + @Test(expected = LoginException.class) + public void testUserNotFound() throws Exception { + + Properties options = ldapLoginModuleOptions(); + GSSAPILdapLoginModule module = new GSSAPILdapLoginModule(); + + CallbackHandler cb = new CallbackHandler() { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback cb : callbacks) { + if (cb instanceof NameCallback) { + ((NameCallback) cb).setName("test"); + } else if (cb instanceof PasswordCallback) { + ((PasswordCallback) cb).setPassword("test".toCharArray()); + } + } + } + }; + Subject subject = new Subject(); + module.initialize(subject, cb, null, options); + + assertEquals("Precondition", 0, subject.getPrincipals().size()); + assertFalse(module.login()); + } + + @Test(expected = LoginException.class) + public void testNoRealm() throws Exception { + + Properties options = ldapLoginModuleOptions(); + options.remove(GSSAPILdapLoginModule.REALM_PROPERTY); + GSSAPILdapLoginModule module = new GSSAPILdapLoginModule(); + + CallbackHandler cb = new CallbackHandler() { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback cb : callbacks) { + if (cb instanceof NameCallback) { + ((NameCallback) cb).setName("hnelson0"); + } else if (cb instanceof PasswordCallback) { + ((PasswordCallback) cb).setPassword("secret".toCharArray()); + } + } + } + }; + Subject subject = new Subject(); + module.initialize(subject, cb, null, options); + + assertEquals("Precondition", 0, subject.getPrincipals().size()); + assertTrue(module.login()); // should throw LoginException + } + + protected void setupEnv(Class<? extends Transport> transport, EncryptionType encryptionType, + ChecksumType checksumType) + throws Exception { + // create krb5.conf with proper encryption type + String krb5confPath = createKrb5Conf(checksumType, encryptionType, transport == TcpTransport.class); + System.setProperty("java.security.krb5.conf", krb5confPath); + + // change encryption type in KDC + kdcServer.getConfig().setEncryptionTypes(Collections.singleton(encryptionType)); + + // create principals + createPrincipal("uid=" + USER_UID, "Last", "admin", + USER_UID, USER_PASSWORD, USER_UID + "@" + REALM); + + createPrincipal("uid=krbtgt", "KDC Service", "KDC Service", + "krbtgt", "secret", "krbtgt/" + REALM + "@" + REALM); + + String servicePrincipal = LDAP_SERVICE_NAME + "/" + HOSTNAME + "@" + REALM; + createPrincipal("uid=ldap", "Service", "LDAP Service", + "ldap", "randall", servicePrincipal); + } + + private String createKrb5Conf(ChecksumType checksumType, EncryptionType encryptionType, boolean isTcp) throws IOException { + File file = folder.newFile("krb5.conf"); + + String data = ""; + + data += "[libdefaults]" + SystemUtils.LINE_SEPARATOR; + data += "default_realm = " + REALM + SystemUtils.LINE_SEPARATOR; + data += "default_tkt_enctypes = " + encryptionType.getName() + SystemUtils.LINE_SEPARATOR; + data += "default_tgs_enctypes = " + encryptionType.getName() + SystemUtils.LINE_SEPARATOR; + data += "permitted_enctypes = " + encryptionType.getName() + SystemUtils.LINE_SEPARATOR; + // data += "default_checksum = " + checksumType.getName() + SystemUtils.LINE_SEPARATOR; + // data += "ap_req_checksum_type = " + checksumType.getName() + SystemUtils.LINE_SEPARATOR; + data += "default-checksum_type = " + checksumType.getName() + SystemUtils.LINE_SEPARATOR; + + if (isTcp) { + data += "udp_preference_limit = 1" + SystemUtils.LINE_SEPARATOR; + } + + + data += "[realms]" + SystemUtils.LINE_SEPARATOR; + data += REALM + " = {" + SystemUtils.LINE_SEPARATOR; + data += "kdc = " + HOSTNAME + ":" + kdcServer.getTransports()[0].getPort() + SystemUtils.LINE_SEPARATOR; + data += "}" + SystemUtils.LINE_SEPARATOR; + + data += "[domain_realm]" + SystemUtils.LINE_SEPARATOR; + data += "." + Strings.lowerCaseAscii(REALM) + " = " + REALM + SystemUtils.LINE_SEPARATOR; + data += Strings.lowerCaseAscii(REALM) + " = " + REALM + SystemUtils.LINE_SEPARATOR; + + FileUtils.writeStringToFile(file, data); + + return file.getAbsolutePath(); + } + + private void createPrincipal(String rdn, String sn, String cn, + String uid, String userPassword, String principalName) throws LdapException { + Entry entry = new DefaultEntry(); + entry.setDn(rdn + "," + USERS_DN); + entry.add("objectClass", "top", "person", "inetOrgPerson", "krb5principal", "krb5kdcentry"); + entry.add("cn", cn); + entry.add("sn", sn); + entry.add("uid", uid); + entry.add("userPassword", userPassword); + entry.add("krb5PrincipalName", principalName); + entry.add("krb5KeyVersionNumber", "0"); + conn.add(entry); + } + + protected Properties ldapLoginModuleOptions() throws IOException { + String basedir = System.getProperty("basedir"); + if (basedir == null) { + basedir = new File(".").getCanonicalPath(); + } + File file = new File(basedir + "/target/test-classes/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties"); + return new Properties(file); + } +} http://git-wip-us.apache.org/repos/asf/karaf/blob/43d3aa6d/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties ---------------------------------------------------------------------- diff --git a/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties new file mode 100644 index 0000000..8186a73 --- /dev/null +++ b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.ldap.properties @@ -0,0 +1,37 @@ +################################################################################ +# +# 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. +# +################################################################################ + +debug=true +connection.url=ldap://address:portno +connection.username=hnelson +connection.password=secret +connection.protocol= +authentication=GSSAPI +gssapiRealm=krb5TestRealm + +user.base.dn=ou=users,dc=example,dc=com +user.filter=(uid=%u) +user.search.subtree=true + +role.base.dn=ou=groups,dc=example,dc=com +role.name.attribute=cn +role.filter=(member=%fqdn) +role.search.subtree=true + +initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory http://git-wip-us.apache.org/repos/asf/karaf/blob/43d3aa6d/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.login.config ---------------------------------------------------------------------- diff --git a/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.login.config b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.login.config new file mode 100644 index 0000000..84635f9 --- /dev/null +++ b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/gssapi.login.config @@ -0,0 +1,26 @@ +// ################################################################################ +// # +// # 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. +// # +// ################################################################################ +krb5TestRealm { + org.apache.karaf.jaas.modules.krb5.Krb5LoginModule REQUIRED + refreshKrb5Config = true + password-stacking = storePass + doNotPrompt = false + useTicketCache = true + debug=true; +}; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/karaf/blob/43d3aa6d/manual/src/main/asciidoc/developer-guide/security-framework.adoc ---------------------------------------------------------------------- diff --git a/manual/src/main/asciidoc/developer-guide/security-framework.adoc b/manual/src/main/asciidoc/developer-guide/security-framework.adoc index b70bcc9..962bd75 100644 --- a/manual/src/main/asciidoc/developer-guide/security-framework.adoc +++ b/manual/src/main/asciidoc/developer-guide/security-framework.adoc @@ -516,6 +516,81 @@ performed directly on the LDAP backend. The Kerberos login module uses the Oracle JVM Krb5 internal login module. +Here is a simple configuration : +---- +<jaas:config name="krb5" rank="1"> + <jaas:module className="org.apache.karaf.jaas.modules.krb5.Krb5LoginModule"> + refreshKrb5Config = true + password-stacking = storePass + doNotPrompt = false + useTicketCache = true + </jaas:module> +</jaas:config> +---- + +You must specify a krb5 configuration file through the "java.security.krb5.conf" system property. +Here is a simple example of a krb5 configuration file : +---- +[libdefaults] + default_realm = EXAMPLE.COM + dns_lookup_realm = false + dns_lookup_kdc = false + ticket_lifetime = 24h + renew_lifetime = 365d + forwardable = true + +[realms] + + EXAMPLE.COM = { + kdc = kdc.example.com + admin_server = kdc.example.com + default_domain = example.com + } + +[domain_realm] + .example.com = EXAMPLE.COM + example.com = EXAMPLE.COM +---- + +===== GSSAPILdapLoginModule + +|=== +|LoginModule |BackendEngineFactory + +|org.apache.karaf.jaas.modules.ldap.GSSAPILdapLoginModule +| +|=== + +The GSSAPI module uses the GSSAPI mechanism to handle authentication to a LDAP server. +Typical use is using this and a Kerberos Login Module to connect to an ActiveDirectory Server, or any other LDAP server that needs a Kerberos tickets for authentication. + +Here is a simple configuration, that use as Kerberos login module as authentication backend : +---- +<jaas:config name="ldap" rank="1"> + <jaas:module className="org.apache.karaf.jaas.modules.ldap.GSSAPILdapLoginModule"flags="required"> + gssapiRealm=krb5 + initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory + connection.url=ldap://activedirectory_host:389 + user.base.dn=ou=Users,ou=there,DC=local + user.filter=(sAMAccountName=%u) + user.search.subtree=true + role.base.dn=ou=Groups,ou=there,DC=local + role.name.attribute=cn + role.filter=(member=%fqdn) + role.search.subtree=true + </jaas:module> +</jaas:config> +<jaas:config name="krb5" rank="1"> + <jaas:module className="org.apache.karaf.jaas.modules.krb5.Krb5LoginModule"> + refreshKrb5Config = true + password-stacking = storePass + doNotPrompt = false + useTicketCache = true + </jaas:module> +</jaas:config> +---- +Note the 'gssapiRealm' property of the LDAP login module that match the name of the Kerberos Configuration. + ===== SyncopeLoginModule |=== http://git-wip-us.apache.org/repos/asf/karaf/blob/43d3aa6d/manual/src/main/asciidoc/user-guide/security.adoc ---------------------------------------------------------------------- diff --git a/manual/src/main/asciidoc/user-guide/security.adoc b/manual/src/main/asciidoc/user-guide/security.adoc index afeba11..4d6e293 100644 --- a/manual/src/main/asciidoc/user-guide/security.adoc +++ b/manual/src/main/asciidoc/user-guide/security.adoc @@ -59,6 +59,8 @@ Apache Karaf provides additional login modules (see the developer guide for deta * LDAPLoginModule uses a LDAP server as backend * SyncopeLoginModule uses Apache Syncope as backend * OsgiConfigLoginModule uses a configuration as backend +* Krb5LoginModule uses a Kerberos Server as backend +* GSSAPILdapLoginModule uses a LDAP server as backend but delegate LDAP server authentication to an other backend (typically Krb5LoginModule) You can manage an existing realm, login module, or create your own realm using the `jaas:realm-manage` command.
