Skip to content

Commit

Permalink
KAFKA-3070: SASL unit tests dont work with IBM JDK
Browse files Browse the repository at this point in the history
Use IBM Kerberos module for SASL tests if running on IBM JDK

Developed with edoardocomar
Based on #738 by rajinisivaram

Author: Mickael Maison <mickael.maison@gmail.com>

Reviewers: Ismael Juma <ismael@juma.me.uk>, Rajini Sivaram <rajinisivaram@googlemail.com>, Edoardo Comar <ecomar@uk.ibm.com>

Closes #2878 from mimaison/KAFKA-3070
  • Loading branch information
mimaison authored and rajinisivaram committed May 18, 2017
1 parent c1fdf57 commit 65edd64
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.apache.kafka.common.security.authenticator.SaslClientAuthenticator;
import org.apache.kafka.common.security.authenticator.SaslServerAuthenticator;
import org.apache.kafka.common.security.ssl.SslFactory;
import org.apache.kafka.common.utils.Java;
import org.apache.kafka.common.protocol.SecurityProtocol;
import org.apache.kafka.common.KafkaException;
import org.slf4j.Logger;
Expand Down Expand Up @@ -143,7 +144,7 @@ private static String defaultKerberosRealm() throws ClassNotFoundException, NoSu
Class<?> classRef;
Method getInstanceMethod;
Method getDefaultRealmMethod;
if (System.getProperty("java.vendor").contains("IBM")) {
if (Java.isIBMJdk()) {
classRef = Class.forName("com.ibm.security.krb5.internal.Config");
} else {
classRef = Class.forName("sun.security.krb5.Config");
Expand Down
4 changes: 4 additions & 0 deletions clients/src/main/java/org/apache/kafka/common/utils/Java.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,8 @@ private Java() {
public static final boolean IS_JAVA9_COMPATIBLE = JVM_MAJOR_VERSION > 1 ||
(JVM_MAJOR_VERSION == 1 && JVM_MINOR_VERSION >= 9);

public static boolean isIBMJdk() {
return System.getProperty("java.vendor").contains("IBM");
}

}
56 changes: 56 additions & 0 deletions clients/src/test/java/org/apache/kafka/common/utils/JavaTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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.
*/
package org.apache.kafka.common.utils;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class JavaTest {

private String javaVendor;

@Before
public void before() {
javaVendor = System.getProperty("java.vendor");
}

@After
public void after() {
System.setProperty("java.vendor", javaVendor);
}

@Test
public void testIsIBMJdk() {
System.setProperty("java.vendor", "Oracle Corporation");
assertFalse(Java.isIBMJdk());
System.setProperty("java.vendor", "IBM Corporation");
assertTrue(Java.isIBMJdk());
}

@Test
public void testLoadKerberosLoginModule() throws ClassNotFoundException {
String clazz = Java.isIBMJdk()
? "com.ibm.security.auth.module.Krb5LoginModule"
: "com.sun.security.auth.module.Krb5LoginModule";
Class.forName(clazz);
}

}
4 changes: 2 additions & 2 deletions core/src/test/scala/kafka/security/minikdc/MiniKdc.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import org.apache.directory.server.kerberos.shared.keytab.{Keytab, KeytabEntry}
import org.apache.directory.server.protocol.shared.transport.{TcpTransport, UdpTransport}
import org.apache.directory.server.xdbm.Index
import org.apache.directory.shared.kerberos.KerberosTime
import org.apache.kafka.common.utils.Utils
import org.apache.kafka.common.utils.{Java, Utils}

/**
* Mini KDC based on Apache Directory Server that can be embedded in tests or used from command line as a standalone
Expand Down Expand Up @@ -256,7 +256,7 @@ class MiniKdc(config: Properties, workDir: File) extends Logging {

private def refreshJvmKerberosConfig(): Unit = {
val klass =
if (System.getProperty("java.vendor").contains("IBM"))
if (Java.isIBMJdk)
Class.forName("com.ibm.security.krb5.internal.Config")
else
Class.forName("sun.security.krb5.Config")
Expand Down
49 changes: 38 additions & 11 deletions core/src/test/scala/unit/kafka/utils/JaasTestUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
package kafka.utils

import java.io.{File, BufferedWriter, FileWriter}
import java.util.Properties
import kafka.server.KafkaConfig
import org.apache.kafka.common.utils.Java

object JaasTestUtils {

Expand All @@ -27,15 +30,25 @@ object JaasTestUtils {
debug: Boolean,
serviceName: Option[String]) extends JaasModule {

def name = "com.sun.security.auth.module.Krb5LoginModule"

def entries: Map[String, String] = Map(
"useKeyTab" -> useKeyTab.toString,
"storeKey" -> storeKey.toString,
"keyTab" -> keyTab,
"principal" -> principal
) ++ serviceName.map(s => Map("serviceName" -> s)).getOrElse(Map.empty)

def name =
if (Java.isIBMJdk)
"com.ibm.security.auth.module.Krb5LoginModule"
else
"com.sun.security.auth.module.Krb5LoginModule"

def entries: Map[String, String] =
if (Java.isIBMJdk)
Map(
"principal" -> principal,
"credsType" -> "both"
) ++ (if (useKeyTab) Map("useKeytab" -> s"file:$keyTab") else Map.empty)
else
Map(
"useKeyTab" -> useKeyTab.toString,
"storeKey" -> storeKey.toString,
"keyTab" -> keyTab,
"principal" -> principal
) ++ serviceName.map(s => Map("serviceName" -> s)).getOrElse(Map.empty)
}

case class PlainLoginModule(username: String,
Expand Down Expand Up @@ -120,6 +133,20 @@ object JaasTestUtils {
val KafkaScramAdmin = "scram-admin"
val KafkaScramAdminPassword = "scram-admin-secret"

val serviceName = "kafka"

def saslConfigs(saslProperties: Option[Properties]): Properties = {
val result = saslProperties match {
case Some(properties) => properties
case None => new Properties
}
// IBM Kerberos module doesn't support the serviceName JAAS property, hence it needs to be
// passed as a Kafka property
if (Java.isIBMJdk && !result.contains(KafkaConfig.SaslKerberosServiceNameProp))
result.put(KafkaConfig.SaslKerberosServiceNameProp, serviceName)
result
}

def writeJaasContextsToFile(jaasSections: Seq[JaasSection]): File = {
val jaasFile = TestUtils.tempFile()
writeToFile(jaasFile, jaasSections)
Expand All @@ -146,7 +173,7 @@ object JaasTestUtils {
keyTab = keytabLocation.getOrElse(throw new IllegalArgumentException("Keytab location not specified for GSSAPI")).getAbsolutePath,
principal = KafkaServerPrincipal,
debug = true,
serviceName = Some("kafka"))
serviceName = Some(serviceName))
case "PLAIN" =>
PlainLoginModule(
KafkaPlainAdmin,
Expand Down Expand Up @@ -180,7 +207,7 @@ object JaasTestUtils {
keyTab = keytabLocation.getOrElse(throw new IllegalArgumentException("Keytab location not specified for GSSAPI")).getAbsolutePath,
principal = clientPrincipal,
debug = true,
serviceName = Some("kafka")
serviceName = Some(serviceName)
)
case "PLAIN" =>
PlainLoginModule(
Expand Down
12 changes: 3 additions & 9 deletions core/src/test/scala/unit/kafka/utils/TestUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ object TestUtils extends Logging {
props.putAll(sslConfigs(Mode.SERVER, false, trustStoreFile, s"server$nodeId"))

if (protocolAndPorts.exists { case (protocol, _) => usesSaslAuthentication(protocol) })
props.putAll(saslConfigs(saslProperties))
props.putAll(JaasTestUtils.saslConfigs(saslProperties))

interBrokerSecurityProtocol.foreach { protocol =>
props.put(KafkaConfig.InterBrokerSecurityProtocolProp, protocol.name)
Expand Down Expand Up @@ -509,8 +509,9 @@ object TestUtils extends Logging {
val props = new Properties
if (usesSslTransportLayer(securityProtocol))
props.putAll(sslConfigs(mode, securityProtocol == SecurityProtocol.SSL, trustStoreFile, certAlias))

if (usesSaslAuthentication(securityProtocol))
props.putAll(saslConfigs(saslProperties))
props.putAll(JaasTestUtils.saslConfigs(saslProperties))
props.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, securityProtocol.name)
props
}
Expand Down Expand Up @@ -1184,13 +1185,6 @@ object TestUtils extends Logging {
sslProps
}

def saslConfigs(saslProperties: Option[Properties]): Properties = {
saslProperties match {
case Some(properties) => properties
case None => new Properties
}
}

// a X509TrustManager to trust self-signed certs for unit tests.
def trustAllCerts: X509TrustManager = {
val trustManager = new X509TrustManager() {
Expand Down

0 comments on commit 65edd64

Please sign in to comment.