This is an automated email from the ASF dual-hosted git repository. robbie pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git
commit 9513405020b413cd182d8b819572bb050223984a Author: Timothy Bish <tabish...@gmail.com> AuthorDate: Thu May 13 14:33:35 2021 -0400 ARTEMIS-2813 Update MiniKDC and fix Kerberos tests on JDK 11+ Updates the MiniKDC dependency and adds another now needed dep in order to get the Kerberos tests working on JDK 11+ builds. --- tests/integration-tests/pom.xml | 11 ++- .../tests/integration/amqp/JMSSaslGssapiTest.java | 110 ++++++++++++++++----- .../src/test/resources/minikdc-krb5-template.conf | 30 ++++++ 3 files changed, 121 insertions(+), 30 deletions(-) diff --git a/tests/integration-tests/pom.xml b/tests/integration-tests/pom.xml index 65140fa..66eaaa2 100644 --- a/tests/integration-tests/pom.xml +++ b/tests/integration-tests/pom.xml @@ -390,7 +390,13 @@ <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-minikdc</artifactId> - <version>2.8.1</version> + <version>3.3.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.directory.server</groupId> + <artifactId>apacheds-interceptor-kerberos</artifactId> + <version>${directory-version}</version> <scope>test</scope> </dependency> <dependency> @@ -554,8 +560,7 @@ <artifactId>maven-surefire-plugin</artifactId> <configuration> <excludes combine.children="append"> - <!-- These fail, likely due to due to isues with the old Kerberos test harness bits --> - <exclude>**/JMSSaslGssapiTest.java</exclude> + <!-- This fails, likely due to due to issues of compatibility with newer KDC updates --> <exclude>**/SaslKrb5LDAPSecurityTest.java</exclude> <!-- This is no longer possible on JDK11 because the old KRB5 cipher suites it requires were diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSSaslGssapiTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSSaslGssapiTest.java index 7b6e220..99c4ae8 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSSaslGssapiTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSSaslGssapiTest.java @@ -16,24 +16,37 @@ */ package org.apache.activemq.artemis.tests.integration.amqp; -import javax.jms.Connection; -import javax.jms.JMSSecurityException; -import javax.jms.MessageConsumer; -import javax.jms.MessageProducer; -import javax.jms.Session; -import javax.jms.TextMessage; +import java.io.BufferedReader; import java.io.File; +import java.io.FileReader; +import java.io.OutputStream; import java.net.URI; import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.WritableByteChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; +import java.util.Properties; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import javax.jms.Connection; +import javax.jms.JMSSecurityException; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; + import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnector; import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; import org.apache.activemq.artemis.core.security.Role; @@ -47,21 +60,25 @@ import org.apache.activemq.artemis.protocol.amqp.proton.handler.ProtonHandler; import org.apache.activemq.artemis.protocol.amqp.sasl.ClientSASL; import org.apache.activemq.artemis.protocol.amqp.sasl.ClientSASLFactory; import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; -import org.apache.activemq.artemis.tests.util.JavaVersionUtil; import org.apache.activemq.artemis.tests.util.Wait; import org.apache.activemq.artemis.utils.RandomUtil; import org.apache.hadoop.minikdc.MiniKdc; +import org.apache.kerby.kerberos.kerb.keytab.Keytab; +import org.apache.kerby.kerberos.kerb.keytab.KeytabEntry; +import org.apache.kerby.kerberos.kerb.type.base.PrincipalName; import org.apache.qpid.jms.JmsConnectionFactory; import org.apache.qpid.jms.sasl.GssapiMechanism; import org.apache.qpid.proton.amqp.Symbol; -import org.junit.After; -import org.junit.Assume; -import org.junit.Before; +import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class JMSSaslGssapiTest extends JMSClientTestSupport { + private static final Logger LOG = LoggerFactory.getLogger(JMSSaslGssapiTest.class); + static { String path = System.getProperty("java.security.auth.login.config"); if (path == null) { @@ -72,25 +89,48 @@ public class JMSSaslGssapiTest extends JMSClientTestSupport { } } } - MiniKdc kdc = null; - private final boolean debug = false; + + private static final String KRB5_TCP_PORT_TEMPLATE = "MINI_KDC_PORT"; + private static final String KRB5_CONFIG_TEMPLATE = "minikdc-krb5-template.conf"; + private static final String KRB5_DEFAULT_KEYTAB = "target/test.krb5.keytab"; + + private static MiniKdc kdc; + private static final boolean debug = false; @BeforeClass - public static void checkAssumptions() throws Exception { - Assume.assumeTrue("Test only runs on JDK 8", JavaVersionUtil.isJava8()); - } + public static void setUpKerberos() throws Exception { + Properties kdcConf = MiniKdc.createConf(); + kdcConf.setProperty("debug", Boolean.toString(debug)); - @Before - public void setUpKerberos() throws Exception { - kdc = new MiniKdc(MiniKdc.createConf(), temporaryFolder.newFolder("kdc")); + // Creates a single server for the tests as there were failures when spawning and killing one per unit test. + kdc = new MiniKdc(kdcConf, new File("target/")); kdc.start(); // hard coded match, default_keytab_name in minikdc-krb5.conf template - File userKeyTab = new File("target/test.krb5.keytab"); + File userKeyTab = new File(KRB5_DEFAULT_KEYTAB); kdc.createPrincipal(userKeyTab, "client", "amqp/localhost"); + // We need to hard code the default keyTab into the Krb5 configuration file which is not possible + // with this version of MiniKDC so we use a template file and replace the port with the value from + // the MiniKDC instance we just started. + rewriteKrbConfFile(kdc); + if (debug) { - for (java.util.logging.Logger logger : new java.util.logging.Logger[] {java.util.logging.Logger.getLogger("javax.security.sasl"), java.util.logging.Logger.getLogger("org.apache.qpid.proton")}) { + LOG.debug("java.security.krb5.conf='{}'", System.getProperty("java.security.krb5.conf")); + try (BufferedReader br = new BufferedReader(new FileReader(System.getProperty("java.security.krb5.conf")))) { + br.lines().forEach(line -> LOG.debug(line)); + } + + Keytab kt = Keytab.loadKeytab(userKeyTab); + for (PrincipalName name : kt.getPrincipals()) { + for (KeytabEntry entry : kt.getKeytabEntries(name)) { + LOG.info("KeyTab Entry: PrincipalName:" + entry.getPrincipal() + " ; KeyInfo:" + entry.getKey().getKeyType()); + } + } + + for (java.util.logging.Logger logger : new java.util.logging.Logger[] {java.util.logging.Logger.getLogger("logincontext"), + java.util.logging.Logger.getLogger("javax.security.sasl"), + java.util.logging.Logger.getLogger("org.apache.qpid.proton")}) { logger.setLevel(java.util.logging.Level.FINEST); logger.addHandler(new java.util.logging.ConsoleHandler()); for (java.util.logging.Handler handler : logger.getHandlers()) { @@ -100,8 +140,8 @@ public class JMSSaslGssapiTest extends JMSClientTestSupport { } } - @After - public void stopKerberos() throws Exception { + @AfterClass + public static void stopKerberos() throws Exception { if (kdc != null) { kdc.stop(); } @@ -125,6 +165,10 @@ public class JMSSaslGssapiTest extends JMSClientTestSupport { roles.add(role); server.getSecurityRepository().addMatch(getQueueName().toString(), roles); + if (debug) { + // The default produces so much log spam that debugging the exchanges becomes impossible. + server.getConfiguration().setMessageExpiryScanPeriod(30_000); + } } @Override @@ -134,7 +178,6 @@ public class JMSSaslGssapiTest extends JMSClientTestSupport { @Override protected URI getBrokerQpidJMSConnectionURI() { - try { int port = AMQP_PORT; @@ -164,7 +207,7 @@ public class JMSSaslGssapiTest extends JMSClientTestSupport { try { Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - javax.jms.Queue queue = session.createQueue(getQueueName()); + Queue queue = session.createQueue(getQueueName()); MessageConsumer consumer = session.createConsumer(queue); MessageProducer producer = session.createProducer(queue); @@ -174,7 +217,6 @@ public class JMSSaslGssapiTest extends JMSClientTestSupport { TextMessage m = (TextMessage) consumer.receive(1000); assertNotNull(m); assertEquals(text, m.getText()); - } finally { connection.close(); } @@ -182,7 +224,6 @@ public class JMSSaslGssapiTest extends JMSClientTestSupport { @Test(timeout = 600000) public void testSaslPlainConnectionDenied() throws Exception { - JmsConnectionFactory factory = new JmsConnectionFactory(new URI("amqp://localhost:" + AMQP_PORT + "?amqp.saslMechanisms=PLAIN")); try { factory.createConnection("plain", "secret"); @@ -192,7 +233,7 @@ public class JMSSaslGssapiTest extends JMSClientTestSupport { } } - @Test + @Test(timeout = 900000) public void testOutboundWithSlowMech() throws Exception { final Map<String, Object> config = new LinkedHashMap<>(); config.put(TransportConstants.HOST_PROP_NAME, "localhost"); config.put(TransportConstants.PORT_PROP_NAME, String.valueOf(AMQP_PORT)); @@ -270,4 +311,19 @@ public class JMSSaslGssapiTest extends JMSClientTestSupport { lifeCycleListener.stop(); } } + + private static void rewriteKrbConfFile(MiniKdc server) throws Exception { + final Path template = Paths.get(JMSSaslGssapiTest.class.getClassLoader().getResource(KRB5_CONFIG_TEMPLATE).toURI()); + final String krb5confTemplate = new String(Files.readAllBytes(template), StandardCharsets.UTF_8); + final String replacementPort = Integer.toString(server.getPort()); + + // Replace the port template with the current actual port of the MiniKDC Server instance. + final String krb5confUpdated = krb5confTemplate.replaceAll(KRB5_TCP_PORT_TEMPLATE, replacementPort); + + try (OutputStream outputStream = Files.newOutputStream(server.getKrb5conf().toPath()); + WritableByteChannel channel = Channels.newChannel(outputStream)) { + + channel.write(ByteBuffer.wrap(krb5confUpdated.getBytes(StandardCharsets.UTF_8))); + } + } } diff --git a/tests/integration-tests/src/test/resources/minikdc-krb5-template.conf b/tests/integration-tests/src/test/resources/minikdc-krb5-template.conf new file mode 100644 index 0000000..1d08811 --- /dev/null +++ b/tests/integration-tests/src/test/resources/minikdc-krb5-template.conf @@ -0,0 +1,30 @@ +# +# 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 = 1 + kdc_tcp_port = MINI_KDC_PORT + default_keytab_name = FILE:target/test.krb5.keytab + +[realms] + EXAMPLE.COM = { + kdc = localhost:MINI_KDC_PORT + } +