From 4181cdbb96141bb6f1d18cc06a7e1ef025c69f37 Mon Sep 17 00:00:00 2001 From: Domenico Francesco Bruscino Date: Fri, 5 Dec 2025 08:39:53 +0100 Subject: [PATCH] ARTEMIS-5795 Set security config using system properties --- .../artemis/cli/factory/BrokerFactory.java | 50 ++- .../cli/factory/BrokerFactoryTest.java | 421 ++++++++++++++++++ .../cli/factory/TestBrokerFactoryHandler.java | 87 ++++ .../org/apache/activemq/artemis/broker/test | 17 + .../config/ActiveMQDefaultConfiguration.java | 18 + .../activemq/artemis/dto/PropertyDTO.java | 8 + docs/user-manual/security.adoc | 54 ++- 7 files changed, 653 insertions(+), 2 deletions(-) create mode 100644 artemis-cli/src/test/java/org/apache/activemq/artemis/cli/factory/BrokerFactoryTest.java create mode 100644 artemis-cli/src/test/java/org/apache/activemq/artemis/cli/factory/TestBrokerFactoryHandler.java create mode 100644 artemis-cli/src/test/resources/META-INF/services/org/apache/activemq/artemis/broker/test diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/factory/BrokerFactory.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/factory/BrokerFactory.java index 5a1a28c48c9..181207239e8 100644 --- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/factory/BrokerFactory.java +++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/factory/BrokerFactory.java @@ -18,10 +18,18 @@ import java.io.IOException; import java.net.URI; +import java.util.ArrayList; +import java.util.Objects; +import java.util.Optional; +import java.util.Properties; +import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; import org.apache.activemq.artemis.cli.ConfigurationException; import org.apache.activemq.artemis.core.server.ActivateCallback; import org.apache.activemq.artemis.dto.BrokerDTO; +import org.apache.activemq.artemis.dto.JaasSecurityDTO; +import org.apache.activemq.artemis.dto.PropertyDTO; +import org.apache.activemq.artemis.dto.SecurityManagerDTO; import org.apache.activemq.artemis.dto.ServerDTO; import org.apache.activemq.artemis.integration.Broker; import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager; @@ -29,6 +37,11 @@ public class BrokerFactory { + private static final String SECURITY_JAAS_DOMAIN_PROP_NAME = "domain"; + private static final String SECURITY_JAAS_CERTIFICATE_DOMAIN_PROP_NAME = "certificateDomain"; + private static final String SECURITY_MANAGER_CLASS_NAME_PROP_NAME = "className"; + private static final String SECURITY_MANAGER_PROPERTIES_PREFIX = "properties."; + private static BrokerDTO createBrokerConfiguration(URI configURI, String artemisHome, String artemisInstance, @@ -44,7 +57,42 @@ private static BrokerDTO createBrokerConfiguration(URI configURI, } catch (IOException ioe) { throw new ConfigurationException("Invalid configuration URI, can't find configuration scheme: " + configURI.getScheme()); } - return factory.createBroker(configURI, artemisHome, artemisInstance, artemisURIInstance); + BrokerDTO broker = factory.createBroker(configURI, artemisHome, artemisInstance, artemisURIInstance); + + populateSecurityWithSystemProperties(broker); + + return broker; + } + + private static void populateSecurityWithSystemProperties(BrokerDTO broker) { + Properties systemProperties = System.getProperties(); + String systemSecurityJaasPropertyPrefix = ActiveMQDefaultConfiguration.getDefaultSystemSecurityJaasPropertyPrefix(); + String systemSecurityManagerPropertyPrefix = ActiveMQDefaultConfiguration.getDefaultSystemSecurityManagerPropertyPrefix(); + + if (systemProperties.containsKey(systemSecurityJaasPropertyPrefix + SECURITY_JAAS_DOMAIN_PROP_NAME)) { + broker.security = broker.security instanceof JaasSecurityDTO ? + (JaasSecurityDTO) broker.security : new JaasSecurityDTO(); + } else if (systemProperties.containsKey(systemSecurityManagerPropertyPrefix + SECURITY_MANAGER_CLASS_NAME_PROP_NAME)) { + broker.security = broker.security instanceof SecurityManagerDTO ? + (SecurityManagerDTO) broker.security : new SecurityManagerDTO(); + } + + if (broker.security instanceof JaasSecurityDTO security) { + security.domain = Optional.ofNullable((String)systemProperties.get( + systemSecurityJaasPropertyPrefix + SECURITY_JAAS_DOMAIN_PROP_NAME)).orElse(security.domain); + security.certificateDomain = Optional.ofNullable((String)systemProperties.get( + systemSecurityJaasPropertyPrefix + SECURITY_JAAS_CERTIFICATE_DOMAIN_PROP_NAME)).orElse(security.certificateDomain); + } else if (broker.security instanceof SecurityManagerDTO security) { + security.className = Optional.ofNullable((String)systemProperties.get( + systemSecurityManagerPropertyPrefix + SECURITY_MANAGER_CLASS_NAME_PROP_NAME)).orElse(security.className); + security.properties = Objects.requireNonNullElse(security.properties, new ArrayList<>()); + systemProperties.forEach((key, value) -> { + if (((String)key).startsWith(systemSecurityManagerPropertyPrefix + SECURITY_MANAGER_PROPERTIES_PREFIX)) { + security.properties.add(new PropertyDTO(((String)key).substring( + systemSecurityManagerPropertyPrefix.length() + SECURITY_MANAGER_PROPERTIES_PREFIX.length()), (String)value)); + } + }); + } } public static BrokerDTO createBrokerConfiguration(String configuration, diff --git a/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/factory/BrokerFactoryTest.java b/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/factory/BrokerFactoryTest.java new file mode 100644 index 00000000000..70de3643a1f --- /dev/null +++ b/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/factory/BrokerFactoryTest.java @@ -0,0 +1,421 @@ +/* + * 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.activemq.artemis.cli.factory; + +import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; +import org.apache.activemq.artemis.dto.BrokerDTO; +import org.apache.activemq.artemis.dto.JaasSecurityDTO; +import org.apache.activemq.artemis.dto.PropertyDTO; +import org.apache.activemq.artemis.dto.SecurityDTO; +import org.apache.activemq.artemis.dto.SecurityManagerDTO; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BrokerFactoryTest { + + private static final String testBrokerConfiguration = "test://config"; + + private final String securityJaasPropertyPrefix = ActiveMQDefaultConfiguration.getDefaultSystemSecurityJaasPropertyPrefix(); + private final String securityManagerPropertyPrefix = ActiveMQDefaultConfiguration.getDefaultSystemSecurityManagerPropertyPrefix(); + private final List systemPropertiesToClear = new ArrayList<>(); + + @BeforeEach + public void setUp() { + TestBrokerFactoryHandler.clear(); + } + + @AfterEach + public void tearDown() { + TestBrokerFactoryHandler.clear(); + for (String property : systemPropertiesToClear) { + System.clearProperty(property); + } + systemPropertiesToClear.clear(); + } + + private void setSystemProperty(String key, String value) { + System.setProperty(key, value); + systemPropertiesToClear.add(key); + } + + @Test + public void testCreateBrokerConfiguration() throws Exception { + final String testArtemisHome = "test-home"; + final String testArtemisInstance = "test-instance"; + final URI testArtemisURIInstance = URI.create(testArtemisInstance); + + TestBrokerFactoryHandler.setBroker(new BrokerDTO()); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration( + testBrokerConfiguration, testArtemisHome, testArtemisInstance, testArtemisURIInstance); + + assertNotNull(createdBroker); + assertEquals(testBrokerConfiguration, TestBrokerFactoryHandler.getBrokerURI().toString()); + assertEquals(testArtemisHome, TestBrokerFactoryHandler.getArtemisHome()); + assertEquals(testArtemisInstance, TestBrokerFactoryHandler.getArtemisInstance()); + assertEquals(testArtemisURIInstance, TestBrokerFactoryHandler.getArtemisURIInstance()); + } + + @Test + public void testCreateBrokerConfigurationWithJaasDomainFromSystemProperties() throws Exception { + setSystemProperty(securityJaasPropertyPrefix + "domain", "testDomain"); + + TestBrokerFactoryHandler.setBroker(new BrokerDTO()); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testJaasSecurity(createdBroker.security, "testDomain", null); + } + + @Test + public void testCreateBrokerConfigurationWithJaasCertificateDomainFromSystemProperties() throws Exception { + setSystemProperty(securityJaasPropertyPrefix + "certificateDomain", "testCertificateDomain"); + + TestBrokerFactoryHandler.setBroker(new BrokerDTO()); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + assertNull(createdBroker.security); + } + + @Test + public void testCreateBrokerConfigurationWithJaasDomainAndCertificateDomainFromSystemProperties() throws Exception { + setSystemProperty(securityJaasPropertyPrefix + "domain", "testDomain"); + setSystemProperty(securityJaasPropertyPrefix + "certificateDomain", "testCertificateDomain"); + + TestBrokerFactoryHandler.setBroker(new BrokerDTO()); + + BrokerDTO broker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(broker); + testJaasSecurity(broker.security, "testDomain", "testCertificateDomain"); + } + + @Test + public void testCreateBrokerConfigurationWithNewJaasDomainFromExistingJaasSecurityAndSystemProperties() throws Exception { + setSystemProperty(securityJaasPropertyPrefix + "domain", "newTestDomain"); + + JaasSecurityDTO security = new JaasSecurityDTO(); + security.domain = "testDomain"; + security.certificateDomain = "testCertificateDomain"; + BrokerDTO broker = new BrokerDTO(); + broker.security = security; + TestBrokerFactoryHandler.setBroker(broker); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testJaasSecurity(createdBroker.security, "newTestDomain", "testCertificateDomain"); + } + + @Test + public void testCreateBrokerConfigurationWithNewJaasCertificateDomainFromExistingJaasSecurityAndSystemProperties() throws Exception { + setSystemProperty(securityJaasPropertyPrefix + "certificateDomain", "newTestCertificateDomain"); + + JaasSecurityDTO security = new JaasSecurityDTO(); + security.domain = "testDomain"; + security.certificateDomain = "testCertificateDomain"; + BrokerDTO broker = new BrokerDTO(); + broker.security = security; + TestBrokerFactoryHandler.setBroker(broker); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testJaasSecurity(createdBroker.security, "testDomain", "newTestCertificateDomain"); + } + + @Test + public void testCreateBrokerConfigurationWithNewJaasDomainFromExistingSecurityManagerAndSystemProperties() throws Exception { + setSystemProperty(securityJaasPropertyPrefix + "domain", "newTestDomain"); + + SecurityManagerDTO security = new SecurityManagerDTO(); + security.className = "testClass"; + BrokerDTO broker = new BrokerDTO(); + broker.security = security; + TestBrokerFactoryHandler.setBroker(broker); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testJaasSecurity(createdBroker.security, "newTestDomain", null); + } + + @Test + public void testCreateBrokerConfigurationWithNewJaasCertificateDomainFromExistingSecurityManagerAndSystemProperties() throws Exception { + setSystemProperty(securityJaasPropertyPrefix + "certificateDomain", "newTestCertificateDomain"); + + SecurityManagerDTO security = new SecurityManagerDTO(); + security.className = "testClass"; + BrokerDTO broker = new BrokerDTO(); + broker.security = security; + TestBrokerFactoryHandler.setBroker(broker); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testSecurityManager(createdBroker.security, "testClass", Collections.emptyList()); + } + + @Test + public void testCreateBrokerConfigurationWithNewJaasDomainAndCertificateDomainFromExistingJaasSecurityAndSystemProperties() throws Exception { + setSystemProperty(securityJaasPropertyPrefix + "domain", "newTestDomain"); + setSystemProperty(securityJaasPropertyPrefix + "certificateDomain", "newTestCertificateDomain"); + + JaasSecurityDTO security = new JaasSecurityDTO(); + security.domain = "testDomain"; + security.certificateDomain = "testCertificateDomain"; + BrokerDTO broker = new BrokerDTO(); + broker.security = security; + TestBrokerFactoryHandler.setBroker(broker); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testJaasSecurity(createdBroker.security, "newTestDomain", "newTestCertificateDomain"); + } + + @Test + public void testCreateBrokerConfigurationWithNewJaasDomainAndCertificateDomainFromExistingSecurityManagerAndSystemProperties() throws Exception { + setSystemProperty(securityJaasPropertyPrefix + "domain", "newTestDomain"); + setSystemProperty(securityJaasPropertyPrefix + "certificateDomain", "newTestCertificateDomain"); + + SecurityManagerDTO security = new SecurityManagerDTO(); + security.className = "testClassName"; + BrokerDTO broker = new BrokerDTO(); + broker.security = security; + TestBrokerFactoryHandler.setBroker(broker); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testJaasSecurity(createdBroker.security, "newTestDomain", "newTestCertificateDomain"); + } + + private void testJaasSecurity(SecurityDTO security, String expectedDomain, String expectedCertificateDomain) throws Exception { + assertNotNull(security); + assertInstanceOf(JaasSecurityDTO.class, security); + JaasSecurityDTO jaasSecurity = (JaasSecurityDTO) security; + assertEquals(expectedDomain, jaasSecurity.domain); + assertEquals(expectedCertificateDomain, jaasSecurity.certificateDomain); + } + + @Test + public void testCreateBrokerConfigurationWithSecurityManagerClassNameFromSystemProperties() throws Exception { + setSystemProperty(securityManagerPropertyPrefix + "className", "testClassName"); + + TestBrokerFactoryHandler.setBroker(new BrokerDTO()); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testSecurityManager(createdBroker.security, "testClassName", Collections.emptyList()); + } + + @Test + public void testCreateBrokerConfigurationWithSecurityManagerPropertiesFromSystemProperties() throws Exception { + setSystemProperty(securityManagerPropertyPrefix + "properties.testKey1", "testValue1"); + setSystemProperty(securityManagerPropertyPrefix + "properties.testKey2", "testValue2"); + + TestBrokerFactoryHandler.setBroker(new BrokerDTO()); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + assertNull(createdBroker.security); + } + + @Test + public void testCreateBrokerConfigurationWithSecurityManagerClassNameAndPropertiesFromSystemProperties() throws Exception { + setSystemProperty(securityManagerPropertyPrefix + "className", "testClassName"); + setSystemProperty(securityManagerPropertyPrefix + "properties.testKey1", "testValue1"); + setSystemProperty(securityManagerPropertyPrefix + "properties.testKey2", "testValue2"); + + TestBrokerFactoryHandler.setBroker(new BrokerDTO()); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testSecurityManager(createdBroker.security, "testClassName", List.of( + new PropertyDTO("testKey1", "testValue1"), new PropertyDTO("testKey2", "testValue2"))); + } + + @Test + public void testCreateBrokerConfigurationWithNewSecurityManagerClassNameFromExistingSecurityManagerAndSystemProperties() throws Exception { + setSystemProperty(securityManagerPropertyPrefix + "className", "newTestClassName"); + + SecurityManagerDTO security = new SecurityManagerDTO(); + security.className = "testClassName"; + security.properties = new ArrayList<>(List.of( + new PropertyDTO("testKey1", "testValue1"), + new PropertyDTO("testKey2", "testValue2"))); + BrokerDTO broker = new BrokerDTO(); + broker.security = security; + TestBrokerFactoryHandler.setBroker(broker); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testSecurityManager(createdBroker.security, "newTestClassName", List.of( + new PropertyDTO("testKey1", "testValue1"), new PropertyDTO("testKey2", "testValue2"))); + } + + @Test + public void testCreateBrokerConfigurationWithNewSecurityManagerPropertiesFromExistingSecurityManagerAndSystemProperties() throws Exception { + setSystemProperty(securityManagerPropertyPrefix + "properties.testKey1", "newTestValue1"); + setSystemProperty(securityManagerPropertyPrefix + "properties.newTestKey2", "newTestValue2"); + + SecurityManagerDTO security = new SecurityManagerDTO(); + security.className = "testClassName"; + security.properties = new ArrayList<>(List.of( + new PropertyDTO("testKey1", "testValue1"), + new PropertyDTO("testKey2", "testValue2"))); + BrokerDTO broker = new BrokerDTO(); + broker.security = security; + TestBrokerFactoryHandler.setBroker(broker); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testSecurityManager(createdBroker.security, "testClassName", List.of( + new PropertyDTO("testKey1", "testValue1"), new PropertyDTO("testKey2", "testValue2"), + new PropertyDTO("testKey1", "newTestValue1"), new PropertyDTO("newTestKey2", "newTestValue2"))); + } + + @Test + public void testCreateBrokerConfigurationWithNewSecurityManagerClassNameAndPropertiesFromExistingSecurityManagerAndSystemProperties() throws Exception { + setSystemProperty(securityManagerPropertyPrefix + "className", "newTestClassName"); + setSystemProperty(securityManagerPropertyPrefix + "properties.testKey1", "newTestValue1"); + setSystemProperty(securityManagerPropertyPrefix + "properties.newTestKey2", "newTestValue2"); + + SecurityManagerDTO security = new SecurityManagerDTO(); + security.className = "testClassName"; + security.properties = new ArrayList<>(List.of( + new PropertyDTO("testKey1", "testValue1"), + new PropertyDTO("testKey2", "testValue2"))); + BrokerDTO broker = new BrokerDTO(); + broker.security = security; + TestBrokerFactoryHandler.setBroker(broker); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testSecurityManager(createdBroker.security, "newTestClassName", List.of( + new PropertyDTO("testKey1", "testValue1"), new PropertyDTO("testKey2", "testValue2"), + new PropertyDTO("testKey1", "newTestValue1"), new PropertyDTO("newTestKey2", "newTestValue2"))); + } + + @Test + public void testCreateBrokerConfigurationWithNewSecurityManagerClassNameFromExistingJaasSecurityAndSystemProperties() throws Exception { + setSystemProperty(securityManagerPropertyPrefix + "className", "newTestClassName"); + + JaasSecurityDTO security = new JaasSecurityDTO(); + security.domain = "testDomain"; + security.certificateDomain = "testCertificateDomain"; + BrokerDTO broker = new BrokerDTO(); + broker.security = security; + TestBrokerFactoryHandler.setBroker(broker); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testSecurityManager(createdBroker.security, "newTestClassName", Collections.emptyList()); + } + + @Test + public void testCreateBrokerConfigurationWithNewSecurityManagerPropertiesFromExistingJaasSecurityAndSystemProperties() throws Exception { + setSystemProperty(securityManagerPropertyPrefix + "properties.testKey1", "newTestValue1"); + setSystemProperty(securityManagerPropertyPrefix + "properties.newTestKey2", "newTestValue2"); + + JaasSecurityDTO security = new JaasSecurityDTO(); + security.domain = "testDomain"; + security.certificateDomain = "testCertificateDomain"; + BrokerDTO broker = new BrokerDTO(); + broker.security = security; + TestBrokerFactoryHandler.setBroker(broker); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testJaasSecurity(createdBroker.security, "testDomain", "testCertificateDomain"); + } + + @Test + public void testCreateBrokerConfigurationWithNewSecurityManagerClassNameAndPropertiesFromExistingJaasSecurityAndSystemProperties() throws Exception { + setSystemProperty(securityManagerPropertyPrefix + "className", "newTestClassName"); + setSystemProperty(securityManagerPropertyPrefix + "properties.testKey1", "newTestValue1"); + setSystemProperty(securityManagerPropertyPrefix + "properties.newTestKey2", "newTestValue2"); + + JaasSecurityDTO security = new JaasSecurityDTO(); + security.domain = "testDomain"; + security.certificateDomain = "testCertificateDomain"; + BrokerDTO broker = new BrokerDTO(); + broker.security = security; + TestBrokerFactoryHandler.setBroker(broker); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testSecurityManager(createdBroker.security, "newTestClassName", List.of( + new PropertyDTO("testKey1", "newTestValue1"), new PropertyDTO("newTestKey2", "newTestValue2"))); + } + + private void testSecurityManager(SecurityDTO security, String expectedClassName, List expectedProperties) throws Exception { + assertNotNull(security); + assertInstanceOf(SecurityManagerDTO.class, security); + SecurityManagerDTO securityManager = (SecurityManagerDTO)security; + assertEquals(expectedClassName, securityManager.className); + + if (expectedProperties != null) { + assertEquals(expectedProperties.size(), securityManager.properties.size()); + assertTrue(expectedProperties.stream().allMatch(expectedProperty -> + securityManager.properties.stream().anyMatch(property -> + Objects.equals(expectedProperty.key, property.key) && + Objects.equals(expectedProperty.value, property.value)))); + } else { + assertNull(securityManager.properties); + } + } + + @Test + public void testJaasSecurityTakesPrecedenceOverSecurityManager() throws Exception { + setSystemProperty(securityJaasPropertyPrefix + "domain", "testDomain"); + setSystemProperty(securityManagerPropertyPrefix + "className", "testClassName"); + + TestBrokerFactoryHandler.setBroker(new BrokerDTO()); + + BrokerDTO createdBroker = BrokerFactory.createBrokerConfiguration("test://config", null, null, null); + + assertNotNull(createdBroker); + testJaasSecurity(createdBroker.security, "testDomain", null); + } +} diff --git a/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/factory/TestBrokerFactoryHandler.java b/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/factory/TestBrokerFactoryHandler.java new file mode 100644 index 00000000000..7bae00bc59c --- /dev/null +++ b/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/factory/TestBrokerFactoryHandler.java @@ -0,0 +1,87 @@ +/* + * 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.activemq.artemis.cli.factory; + +import org.apache.activemq.artemis.dto.BrokerDTO; + +import java.net.URI; + +public class TestBrokerFactoryHandler implements BrokerFactoryHandler { + + private static URI brokerURI; + private static String artemisHome; + private static String artemisInstance; + private static URI artemisURIInstance; + private static BrokerDTO broker; + + public static URI getBrokerURI() { + return brokerURI; + } + + public static void setBrokerURI(URI brokerURI) { + TestBrokerFactoryHandler.brokerURI = brokerURI; + } + + public static String getArtemisHome() { + return artemisHome; + } + + public static void setArtemisHome(String artemisHome) { + TestBrokerFactoryHandler.artemisHome = artemisHome; + } + + public static String getArtemisInstance() { + return artemisInstance; + } + + public static void setArtemisInstance(String artemisInstance) { + TestBrokerFactoryHandler.artemisInstance = artemisInstance; + } + + public static URI getArtemisURIInstance() { + return artemisURIInstance; + } + + public static void setArtemisURIInstance(URI artemisURIInstance) { + TestBrokerFactoryHandler.artemisURIInstance = artemisURIInstance; + } + + public static BrokerDTO getBroker() { + return broker; + } + + public static void setBroker(BrokerDTO broker) { + TestBrokerFactoryHandler.broker = broker; + } + + public static void clear() { + brokerURI = null; + artemisHome = null; + artemisInstance = null; + artemisURIInstance = null; + broker = null; + } + + @Override + public BrokerDTO createBroker(URI brokerURI, String artemisHome, String artemisInstance, URI artemisURIInstance) throws Exception { + TestBrokerFactoryHandler.brokerURI = brokerURI; + TestBrokerFactoryHandler.artemisHome = artemisHome; + TestBrokerFactoryHandler.artemisInstance = artemisInstance; + TestBrokerFactoryHandler.artemisURIInstance = artemisURIInstance; + return broker; + } +} diff --git a/artemis-cli/src/test/resources/META-INF/services/org/apache/activemq/artemis/broker/test b/artemis-cli/src/test/resources/META-INF/services/org/apache/activemq/artemis/broker/test new file mode 100644 index 00000000000..4259e4bb648 --- /dev/null +++ b/artemis-cli/src/test/resources/META-INF/services/org/apache/activemq/artemis/broker/test @@ -0,0 +1,17 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- +class=org.apache.activemq.artemis.cli.factory.TestBrokerFactoryHandler diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java index c7f7af4ec5b..3d31bb5cb90 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java @@ -571,6 +571,12 @@ public static String getDefaultHapolicyBackupStrategy() { public static final String DEFAULT_SYSTEM_WEB_PROPERTY_PREFIX = "webconfig."; + public static final String DEFAULT_SYSTEM_SECURITY_PROPERTY_PREFIX = "securityconfig."; + + public static final String DEFAULT_SYSTEM_SECURITY_JAAS_PROPERTY_PREFIX = "jaas."; + + public static final String DEFAULT_SYSTEM_SECURITY_MANAGER_PROPERTY_PREFIX = "manager."; + public static final String BROKER_PROPERTIES_SYSTEM_PROPERTY_NAME = "broker.properties"; public static final String BROKER_PROPERTIES_KEY_SURROUND = "\""; @@ -1726,6 +1732,18 @@ public static String getDefaultSystemWebPropertyPrefix() { return DEFAULT_SYSTEM_WEB_PROPERTY_PREFIX; } + public static String getDefaultSystemSecurityPropertyPrefix() { + return DEFAULT_SYSTEM_SECURITY_PROPERTY_PREFIX; + } + + public static String getDefaultSystemSecurityJaasPropertyPrefix() { + return getDefaultSystemSecurityPropertyPrefix() + DEFAULT_SYSTEM_SECURITY_JAAS_PROPERTY_PREFIX; + } + + public static String getDefaultSystemSecurityManagerPropertyPrefix() { + return getDefaultSystemSecurityPropertyPrefix() + DEFAULT_SYSTEM_SECURITY_MANAGER_PROPERTY_PREFIX; + } + public static String getDefaultBrokerPropertiesKeySurround() { return BROKER_PROPERTIES_KEY_SURROUND; } diff --git a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/PropertyDTO.java b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/PropertyDTO.java index c1015a830d9..9fe37f35a5f 100644 --- a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/PropertyDTO.java +++ b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/PropertyDTO.java @@ -30,4 +30,12 @@ public class PropertyDTO { @XmlAttribute public String value; + + public PropertyDTO() { + } + + public PropertyDTO(String key, String value) { + this.key = key; + this.value = value; + } } diff --git a/docs/user-manual/security.adoc b/docs/user-manual/security.adoc index 4b345aa3402..136cecc7d74 100644 --- a/docs/user-manual/security.adoc +++ b/docs/user-manual/security.adoc @@ -20,6 +20,55 @@ Security is enabled by default. To disable security completely set the `security ---- +== Boot-time Security Configuration via System Properties + +Security configuration can be provided at boot time via system properties, which will override any security settings defined in `bootstrap.xml`. +This is useful for containerized deployments or scenarios where configuration needs to be injected without modifying configuration files. + +=== JAAS Security via System Properties + +To configure JAAS security at boot time, use the following system properties: + +securityconfig.jaas.domain:: +The name of the JAAS login configuration entry to use for authentication. +This is equivalent to the `domain` attribute of the `` element in `bootstrap.xml`. + +securityconfig.jaas.certificateDomain:: +The name of the JAAS login configuration entry to use for certificate-based authentication. +This is equivalent to the `certificate-domain` attribute of the `` element in `bootstrap.xml`. + +For example, to start the broker with JAAS security configured via system properties: + +[,sh] +---- +./artemis run -Dsecurityconfig.jaas.domain=MyDomain -Dsecurityconfig.jaas.certificateDomain=MyCertDomain +---- + +=== Custom Security Manager via System Properties + +To configure a custom security manager at boot time, use the following system properties: + +securityconfig.manager.className:: +The fully qualified class name of the security manager implementation. +This is equivalent to the `class-name` attribute of the `` element in `bootstrap.xml`. + +securityconfig.manager.properties.:: +Properties to pass to the security manager. +Each property key after the `properties.` prefix will be passed as a configuration property to the security manager. + +For example, to start the broker with the basic security manager configured via system properties: + +[,sh] +---- +./artemis run \ + -Dsecurityconfig.manager.className=org.apache.activemq.artemis.spi.core.security.ActiveMQBasicSecurityManager \ + -Dsecurityconfig.manager.properties.bootstrapUser=admin \ + -Dsecurityconfig.manager.properties.bootstrapPassword=admin \ + -Dsecurityconfig.manager.properties.bootstrapRole=amq +---- + +NOTE: When security configuration is provided via system properties, it takes precedence over any security configuration in `bootstrap.xml`. + == Caching Security Operations For performance reasons both *authentication and authorization is cached* independently. @@ -404,6 +453,7 @@ All user & role data is stored in the broker's bindings journal which means any When using the Java Authentication and Authorization Service (JAAS) much of the configuration depends on which login module is used. However, there are a few commonalities for every case. The first place to look is in `bootstrap.xml`. +Alternatively, JAAS security can be configured at boot time via system properties as described in <>. Here is an example using the `PropertiesLogin` JAAS login module which reads user, password, and role information from properties files: [,xml] @@ -1571,7 +1621,9 @@ For details about masking passwords in broker.xml please see the xref:masking-pa The underpinnings of the broker's security implementation can be changed if so desired. The broker uses a component called a "security manager" to implement the actual authentication and authorization checks. -By default, the broker uses `org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager` to provide JAAS integration, but users can provide their own implementation of `org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager5` and configure it in `bootstrap.xml` using the `security-manager` element, e.g.: +By default, the broker uses `org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager` to provide JAAS integration, but users can provide their own implementation of `org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager5` and configure it in `bootstrap.xml` using the `security-manager` element. +Alternatively, a custom security manager can be configured at boot time via system properties as described in <>. +Here is an example of configuring a custom security manager in `bootstrap.xml`: [,xml] ----