【问题标题】:ActiveMQ Artemis unable to establish connection to Spring Boot ClientActiveMQ Artemis 无法建立与 Spring Boot Client 的连接
【发布时间】:2022-02-04 13:21:32
【问题描述】:

我正在运行 ActiveMQ Artemis 2.8.1,当我尝试使用 Spring Boot 应用程序通过 SSL 连接到它时出现以下错误。

ERROR [org.apache.activemq.artemis.core.server] AMQ224018: Failed to create session: java.lang.SecurityException: java.io.IOException: Configuration Error:
Line 11: expected [{], found [sufficient]

此外,我在以前从未见过的日志中发现了这一行。

INFO  [org.apache.activemq.artemis.core.server] AMQ221053: Disallowing use of vulnerable protocol 'SSLv2Hello' on acceptor 'netty-ssl-acceptor'. See http://www.oracle.com/technetwork/topics/security/poodlecve-2014-3566-2339408.html for more details.

我最初运行 ActiveMQ Artemis 2.18.0,然后出于兼容性原因将 artemis.profile 中的 ARTEMIS_HOME 更改为指向包含 2.8.1 的目录。我想知道这种变化是否与此有关。 我该如何解决这个问题?

broker.xml:

<?xml version='1.0'?>
    <!--
    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.
    -->

    <configuration xmlns="urn:activemq"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xi="http://www.w3.org/2001/XInclude"
               xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">

    <core xmlns="urn:activemq:core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="urn:activemq:core ">

      <name>0.0.0.0</name>


      <persistence-enabled>true</persistence-enabled>

      <!-- this could be ASYNCIO, MAPPED, NIO
           ASYNCIO: Linux Libaio
           MAPPED: mmap files
           NIO: Plain Java Files
       -->
      <journal-type>ASYNCIO</journal-type>

      <paging-directory>data/paging</paging-directory>

      <bindings-directory>data/bindings</bindings-directory>

      <journal-directory>data/journal</journal-directory>

      <large-messages-directory>data/large-messages</large-messages-directory>

      <journal-datasync>true</journal-datasync>

      <journal-min-files>2</journal-min-files>

      <journal-pool-files>10</journal-pool-files>

      <journal-file-size>10M</journal-file-size>
      
      <!--
       This value was determined through a calculation.
       Your system could perform 125 writes per millisecond
       on the current journal configuration.
       That translates as a sync write every 8000 nanoseconds.

       Note: If you specify 0 the system will perform writes directly to the disk.
             We recommend this to be 0 if you are using journalType=MAPPED and journal-datasync=false.
      -->
      <journal-buffer-timeout>8000</journal-buffer-timeout>


      <!--
        When using ASYNCIO, this will determine the writing queue depth for libaio.
       -->
      <journal-max-io>4096</journal-max-io>
      <!--
        You can verify the network health of a particular NIC by specifying the <network-check-NIC> element.
         <network-check-NIC>theNicName</network-check-NIC>
        -->

      <!--
        Use this to use an HTTP server to validate the network
         <network-check-URL-list>http://www.apache.org</network-check-URL-list> -->

      <!-- <network-check-period>10000</network-check-period> -->
      <!-- <network-check-timeout>1000</network-check-timeout> -->

      <!-- this is a comma separated list, no spaces, just DNS or IPs
           it should accept IPV6

           Warning: Make sure you understand your network topology as this is meant to validate if your network is valid.
                    Using IPs that could eventually disappear or be partially visible may defeat the purpose.
                    You can use a list of multiple IPs, and if any successful ping will make the server OK to continue running -->
      <!-- <network-check-list>10.0.0.1</network-check-list> -->

      <!-- use this to customize the ping used for ipv4 addresses -->
      <!-- <network-check-ping-command>ping -c 1 -t %d %s</network-check-ping-command> -->

      <!-- use this to customize the ping used for ipv6 addresses -->
      <!-- <network-check-ping6-command>ping6 -c 1 %2$s</network-check-ping6-command> -->




      <!-- how often we are looking for how many bytes are being used on the disk in ms -->
      <disk-scan-period>5000</disk-scan-period>

      <!-- once the disk hits this limit the system will block, or close the connection in certain protocols
           that won't support flow control. -->
      <max-disk-usage>90</max-disk-usage>

      <!-- should the broker detect dead locks and other issues -->
      <critical-analyzer>true</critical-analyzer>

      <critical-analyzer-timeout>120000</critical-analyzer-timeout>

      <critical-analyzer-check-period>60000</critical-analyzer-check-period>

      <critical-analyzer-policy>HALT</critical-analyzer-policy>

      <!-- the system will enter into page mode once you hit this limit.
           This is an estimate in bytes of how much the messages are using in memory

            The system will use half of the available memory (-Xmx) by default for the global-max-size.
            You may specify a different value here if you need to customize it to your needs.

            <global-max-size>100Mb</global-max-size>

      -->

      <acceptors>

         <!-- useEpoll means: it will use Netty epoll if you are on a system (Linux) that supports it -->
         <!-- amqpCredits: The number of credits sent to AMQP producers -->
         <!-- amqpLowCredits: The server will send the # credits specified at amqpCredits at this low mark -->

         <!-- Note: If an acceptor needs to be compatible with HornetQ and/or Artemis 1.x clients add
                    "anycastPrefix=jms.queue.;multicastPrefix=jms.topic." to the acceptor url.
                    See https://issues.apache.org/jira/browse/ARTEMIS-1644 for more information. -->

         <!-- Acceptor for every supported protocol -->
         <acceptor name="artemis">tcp://0.0.0.0:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;amqpCredits=1000;amqpLowCredits=300</acceptor>

         <!-- AMQP Acceptor.  Listens on default AMQP port for AMQP traffic.-->
         <acceptor name="amqp">tcp://0.0.0.0:5672?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=AMQP;useEpoll=true;amqpCredits=1000;amqpLowCredits=300</acceptor>

         <!-- STOMP Acceptor. -->
         <acceptor name="stomp">tcp://0.0.0.0:61613?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=STOMP;useEpoll=true</acceptor>

         <!-- HornetQ Compatibility Acceptor.  Enables HornetQ Core and STOMP for legacy HornetQ clients. -->
         <acceptor name="hornetq">tcp://0.0.0.0:5445?anycastPrefix=jms.queue.;multicastPrefix=jms.topic.;protocols=HORNETQ,STOMP;useEpoll=true</acceptor>

         <!-- MQTT Acceptor -->
         <acceptor name="mqtt">tcp://0.0.0.0:1883?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=MQTT;useEpoll=true</acceptor>

     <!-- SSL Acceptor -->
     <acceptor name="netty-ssl-acceptor">tcp://0.0.0.0:61617?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;verifyHost=false;anycastPrefix=jms.queue;multicastPrefix=jms.topic.;sslEnabled=true;keyStorePath=/opt/sprink-broker/etc/oceanBroker.jks;keyStorePassword=changeit;trustStorePath=/opt/sprink-broker/etc/oceanBroker.ts;trustStorePassword=changeit;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE</acceptor>

     <acceptor name="mqtt-ssl-acceptor">tcp://0.0.0.0:8883?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;anycastPrefix=jms.queue;multicastPrefix=jms.topic.;sslEnabled=true;keyStorePath=/opt/sprink-broker/etc/oceanBroker.jks;keyStorePassword=changeit;trustStorePath=/opt/sprink-broker/etc/oceanBroker.ts;trustStorePassword=changeit;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE</acceptor>

      </acceptors>


      <security-settings>
         <security-setting match="#">
            <permission type="createNonDurableQueue" roles="amq, users"/>
            <permission type="deleteNonDurableQueue" roles="amq, users"/>
            <permission type="createDurableQueue" roles="amq, users"/>
            <permission type="deleteDurableQueue" roles="amq, users"/>
            <permission type="createAddress" roles="amq, users"/>
            <permission type="deleteAddress" roles="amq, users"/>
            <permission type="consume" roles="amq, users"/>
            <permission type="browse" roles="amq, users"/>
            <permission type="send" roles="amq, users"/>
            <!-- we need this otherwise ./artemis data imp wouldn't work -->
            <permission type="manage" roles="amq"/>
         </security-setting>
      </security-settings>

      <address-settings>
         <!-- if you define auto-create on certain queues, management has to be auto-create -->
         <address-setting match="activemq.management#">
            <dead-letter-address>DLQ</dead-letter-address>
            <expiry-address>ExpiryQueue</expiry-address>
            <redelivery-delay>0</redelivery-delay>
            <!-- with -1 only the global-max-size is in use for limiting -->
            <max-size-bytes>-1</max-size-bytes>
            <message-counter-history-day-limit>10</message-counter-history-day-limit>
            <address-full-policy>PAGE</address-full-policy>
            <auto-create-queues>true</auto-create-queues>
            <auto-create-addresses>true</auto-create-addresses>
            <auto-create-jms-queues>true</auto-create-jms-queues>
            <auto-create-jms-topics>true</auto-create-jms-topics>
         </address-setting>
         <!--default for catch all-->
         <address-setting match="#">
            <dead-letter-address>DLQ</dead-letter-address>
            <expiry-address>ExpiryQueue</expiry-address>
            <redelivery-delay>0</redelivery-delay>
            <!-- with -1 only the global-max-size is in use for limiting -->
            <max-size-bytes>-1</max-size-bytes>
            <message-counter-history-day-limit>10</message-counter-history-day-limit>
            <address-full-policy>PAGE</address-full-policy>
            <auto-create-queues>true</auto-create-queues>
            <auto-create-addresses>true</auto-create-addresses>
            <auto-create-jms-queues>true</auto-create-jms-queues>
            <auto-create-jms-topics>true</auto-create-jms-topics>
         </address-setting>
      </address-settings>

      <addresses>
         <address name="DLQ">
            <anycast>
               <queue name="DLQ" />
            </anycast>
         </address>
         <address name="ExpiryQueue">
            <anycast>
               <queue name="ExpiryQueue" />
            </anycast>
         </address>

      </addresses>


      <!-- Uncomment the following if you want to use the Standard LoggingActiveMQServerPlugin pluging to log in events
      <broker-plugins>
         <broker-plugin class-name="org.apache.activemq.artemis.core.server.plugin.impl.LoggingActiveMQServerPlugin">
            <property key="LOG_ALL_EVENTS" value="true"/>
            <property key="LOG_CONNECTION_EVENTS" value="true"/>
            <property key="LOG_SESSION_EVENTS" value="true"/>
            <property key="LOG_CONSUMER_EVENTS" value="true"/>
            <property key="LOG_DELIVERING_EVENTS" value="true"/>
            <property key="LOG_SENDING_EVENTS" value="true"/>
            <property key="LOG_INTERNAL_EVENTS" value="true"/>
         </broker-plugin>
      </broker-plugins>
      -->

   </core>
</configuration>

用于连接 ActiveMQ Artemis 的 Spring Boot 配置文件:

import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;

import javax.jms.DeliveryMode;
import javax.jms.JMSException;


@Configuration
@EnableJms
public class MQTTConfig {

    @Value("${activemq.broker-url}")
    private String brokerUrl;

    @Value("${activemq.ssl-url}")
    private String sslUrl;

    @Value("${JMS_BROKER_TRUSTSTORE}")
    private String pathToTrustStore;

    @Value("${JMS_BROKER_KEYSTORE}")
    private String pathToKeystore;

    @Value("${JMS_BROKER_TRUSTSTORE_PASSWORD}")
    private String truststorePassword;

    @Value("${JMS_BROKER_KEYSTORE_PASSWORD}")
    private String keystorePassword;

    @Bean
    public ActiveMQConnectionFactory artemisSSLConnectionFactory() {
        ActiveMQConnectionFactory artemisConnectionFactory = new ActiveMQConnectionFactory("tcp://ip:port?&" + "sslEnabled=true&" +
                "trustStorePath=" + pathToTrustStore + "&trustStorePassword=xxxxx");
        artemisConnectionFactory.setUser("user");
        artemisConnectionFactory.setPassword("xxxxx");
        return artemisConnectionFactory;
    }

    /**
     * Initialise {@link JmsTemplate} as required
     */
    @Bean
    public JmsTemplate jmsTemplate() throws JMSException {
        JmsTemplate jmsTemplate = new JmsTemplate();
        jmsTemplate.setConnectionFactory(artemisSSLConnectionFactory());

        //setting PuSubDomain to true configures JmsTemplate to work with topics instead of queues
        jmsTemplate.setPubSubDomain(true);
        return jmsTemplate;
    }

    /**
     * Initialise {@link DefaultJmsListenerContainerFactory} as required
     */
    @Bean
    public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() throws JMSException {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(artemisSSLConnectionFactory());
        //setting PuSubDomain to true configures the DefaultJmsListenerContainerFactory to work with topics instead of queues
        factory.setPubSubDomain(true);
        return factory;
    }

}

artemis.roles-properties:

amq = admin
users=user
guests=guest

artemis-users.properties:

admin = xxxxx
system=xxxxx
user=xxxxx
guest=xxxxx

login.config:

activemq {
    org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule required
       debug=false
       reload=true
       org.apache.activemq.jaas.properties.user="artemis-users.properties"
       org.apache.activemq.jaas.properties.role="artemis-roles.properties";

    org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule sufficient
       debug=false
       org.apache.activemq.jaas.guest.user="guest"
       org.apache.activemq.jaas.guest.role="guests";
};

【问题讨论】:

    标签: spring-boot ssl activemq-artemis


    【解决方案1】:

    您的配置肯定有问题。错误消息甚至是这样说的:

    ERROR [org.apache.activemq.artemis.core.server] AMQ224018: Failed to create session: java.lang.SecurityException: java.io.IOException: Configuration Error:
    Line 11: expected [{], found [sufficient]
    

    鉴于它在查找 { 时发现了 sufficient 这个词,我得出结论认为问题出在您的 etc/login.config 上,因为几乎可以肯定这是出现 sufficient 这个词的唯一地方,而且它与安全性有关.

    您粘贴的 login.config 在我看来很好,所以我猜您粘贴的 login.config 的内容实际上并未被代理使用。

    此外,AMQ221053 消息自 1.0 版本以来一直在代码库中。见ARTEMIS-36

    最后,您在客户端和代理上都指定密钥库和信任库而不在代理上指定needClientAuth=truewantClientAuth=true 有点奇怪。如果您想进行双向 SSL,您只需在客户端上指定密钥库并在代理上指定信任库,但如果没有 needClientAuth=truewantClientAuth=true,这实际上不会发生。

    【讨论】:

    • 你是对的。在文件的最底部,在login.config 文件中的}; 之后有一些额外的文本。这可能是我从另一个login.config 文件复制时出现的。删除该文本纠正了问题,我现在可以连接到代理。另外,我不打算做相互 SSL;我在 Spring Boot 客户端上只有信任库,没有密钥库。此设置为我提供了我需要的单向身份验证,是否应该在那里进行任何更改?
    • 如果您进行双向 SSL,则代理不需要信任库(通过 @987654340 中的 acceptor 上的 trustStorePathtrustStorePassword 配置@) 并且客户端不需要密钥库(例如,MQTTConfig 中的 pathToKeystore)。它将简化您的配置/代码以删除那些不必要的元素。
    • 知道了,pathToKeystore 值只是从我的application.properties 文件中获取的,我没有在连接工厂设置中使用它。我将从代理中删除它和 trustStore 相关元素。
    猜你喜欢
    • 2018-01-31
    • 2020-02-24
    • 2020-12-29
    • 2021-01-23
    • 1970-01-01
    • 2020-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多