ARTEMIS-3474 - replace whitelist with allowlist in management.xml

https://issues.apache.org/jira/browse/ARTEMIS-3474
This commit is contained in:
Andy Taylor 2021-09-13 08:39:03 +01:00 committed by Justin Bertram
parent 8f915dd627
commit 0545664b3d
19 changed files with 131 additions and 56 deletions

View File

@ -9,7 +9,7 @@ our [Hacking Guide](./docs/hacking-guide/en/SUMMARY.md).
## Build Status ## Build Status
Build Status: [![Build Status](https://travis-ci.org/apache/activemq-artemis.svg?branch=master)](https://travis-ci.org/apache/activemq-artemis) Build Status: [![Build Status](https://travis-ci.org/apache/activemq-artemis.svg?branch=main)](https://travis-ci.org/apache/activemq-artemis)
## Building the ASYNC IO library ## Building the ASYNC IO library
@ -23,7 +23,7 @@ To build the ActiveMQ Artemis ASYNCIO native libraries, please follow the instru
Our documentation is always in sync with our releases at the [Apache ActiveMQ Artemis](https://activemq.apache.org/artemis/docs.html) website. Our documentation is always in sync with our releases at the [Apache ActiveMQ Artemis](https://activemq.apache.org/artemis/docs.html) website.
Or you can also look at the current master version on [github](https://github.com/apache/activemq-artemis/blob/master/docs/user-manual/en/SUMMARY.md). Or you can also look at the current main version on [github](https://github.com/apache/activemq-artemis/blob/main/docs/user-manual/en/SUMMARY.md).
## Examples ## Examples

View File

@ -19,14 +19,17 @@ package org.apache.activemq.artemis.cli.factory.jmx;
import org.apache.activemq.artemis.cli.ConfigurationException; import org.apache.activemq.artemis.cli.ConfigurationException;
import org.apache.activemq.artemis.core.config.JMXConnectorConfiguration; import org.apache.activemq.artemis.core.config.JMXConnectorConfiguration;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.apache.activemq.artemis.core.server.management.ManagementContext; import org.apache.activemq.artemis.core.server.management.ManagementContext;
import org.apache.activemq.artemis.dto.AccessDTO; import org.apache.activemq.artemis.dto.AccessDTO;
import org.apache.activemq.artemis.dto.AllowListDTO;
import org.apache.activemq.artemis.dto.AuthorisationDTO; import org.apache.activemq.artemis.dto.AuthorisationDTO;
import org.apache.activemq.artemis.dto.EntryDTO; import org.apache.activemq.artemis.dto.EntryDTO;
import org.apache.activemq.artemis.dto.JMXConnectorDTO; import org.apache.activemq.artemis.dto.JMXConnectorDTO;
import org.apache.activemq.artemis.dto.ManagementContextDTO; import org.apache.activemq.artemis.dto.ManagementContextDTO;
import org.apache.activemq.artemis.dto.MatchDTO; import org.apache.activemq.artemis.dto.MatchDTO;
import org.apache.activemq.artemis.core.server.management.JMXAccessControlList; import org.apache.activemq.artemis.core.server.management.JMXAccessControlList;
import org.apache.activemq.artemis.dto.WhiteListDTO;
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager; import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
import org.apache.activemq.artemis.utils.FactoryFinder; import org.apache.activemq.artemis.utils.FactoryFinder;
@ -67,9 +70,23 @@ public class ManagementFactory {
if (config.getAuthorisation() != null) { if (config.getAuthorisation() != null) {
AuthorisationDTO authorisation = config.getAuthorisation(); AuthorisationDTO authorisation = config.getAuthorisation();
JMXAccessControlList accessControlList = new JMXAccessControlList(); JMXAccessControlList accessControlList = new JMXAccessControlList();
List<EntryDTO> entries = authorisation.getWhiteList().getEntries(); //deprecated but here for backward compatibility
for (EntryDTO entry : entries) { WhiteListDTO whiteList = authorisation.getWhiteList();
accessControlList.addToWhiteList(entry.domain, entry.key); if (whiteList != null) {
ActiveMQServerLogger.LOGGER.useAllowList();
for (EntryDTO entry : whiteList.getEntries()) {
accessControlList.addToAllowList(entry.domain, entry.key);
}
}
AllowListDTO allowList = authorisation.getAllowList();
if (allowList != null) {
if (whiteList != null) {
ActiveMQServerLogger.LOGGER.useOnlyAllowList();
}
for (EntryDTO entry : allowList.getEntries()) {
accessControlList.addToAllowList(entry.domain, entry.key);
}
} }
List<AccessDTO> accessList = authorisation.getDefaultAccess().getAccess(); List<AccessDTO> accessList = authorisation.getDefaultAccess().getAccess();

View File

@ -18,9 +18,9 @@
<management-context xmlns="http://activemq.org/schema"> <management-context xmlns="http://activemq.org/schema">
<!--<connector connector-port="1099"/>--> <!--<connector connector-port="1099"/>-->
<authorisation> <authorisation>
<whitelist> <allowlist>
<entry domain="hawtio"/> <entry domain="hawtio"/>
</whitelist> </allowlist>
<default-access> <default-access>
<access method="list*" roles="${role}"/> <access method="list*" roles="${role}"/>
<access method="get*" roles="${role}"/> <access method="get*" roles="${role}"/>

View File

@ -0,0 +1,36 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.dto;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.List;
@XmlRootElement(name = "allowlist")
@XmlAccessorType(XmlAccessType.FIELD)
public class AllowListDTO {
@XmlElementRef
List<EntryDTO> entry;
public List<EntryDTO> getEntries() {
return entry;
}
}

View File

@ -26,19 +26,28 @@ import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorType(XmlAccessType.FIELD)
public class AuthorisationDTO { public class AuthorisationDTO {
@XmlElementRef @XmlElementRef( required = false)
@Deprecated
WhiteListDTO whitelist; WhiteListDTO whitelist;
@XmlElementRef( required = false )
AllowListDTO allowList;
@XmlElementRef @XmlElementRef
DefaultAccessDTO defaultAccess; DefaultAccessDTO defaultAccess;
@XmlElementRef @XmlElementRef
RoleAccessDTO roleAccess; RoleAccessDTO roleAccess;
@Deprecated
public WhiteListDTO getWhiteList() { public WhiteListDTO getWhiteList() {
return whitelist; return whitelist;
} }
public AllowListDTO getAllowList() {
return allowList;
}
public DefaultAccessDTO getDefaultAccess() { public DefaultAccessDTO getDefaultAccess() {
return defaultAccess; return defaultAccess;
} }

View File

@ -25,6 +25,7 @@ import java.util.List;
@XmlRootElement(name = "whitelist") @XmlRootElement(name = "whitelist")
@XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorType(XmlAccessType.FIELD)
@Deprecated
public class WhiteListDTO { public class WhiteListDTO {
@XmlElementRef @XmlElementRef

View File

@ -2178,4 +2178,12 @@ public interface ActiveMQServerLogger extends BasicLogger {
@LogMessage(level = Logger.Level.WARN) @LogMessage(level = Logger.Level.WARN)
@Message(id = 224109, value = "BrokerBalancer {0} not found", format = Message.Format.MESSAGE_FORMAT) @Message(id = 224109, value = "BrokerBalancer {0} not found", format = Message.Format.MESSAGE_FORMAT)
void brokerBalancerNotFound(String name); void brokerBalancerNotFound(String name);
@LogMessage(level = Logger.Level.WARN)
@Message(id = 224110, value = "Configuration 'whitelist' is deprecated, please use the 'allowlist' configuration", format = Message.Format.MESSAGE_FORMAT)
void useAllowList();
@LogMessage(level = Logger.Level.WARN)
@Message(id = 224111, value = "Both 'whitelist' and 'allowlist' detected. Configuration 'whitelist' is deprecated, please use only the 'allowlist' configuration", format = Message.Format.MESSAGE_FORMAT)
void useOnlyAllowList();
} }

View File

@ -117,7 +117,7 @@ public class ArtemisMBeanServerGuard implements InvocationHandler {
} }
private boolean canBypassRBAC(ObjectName objectName) { private boolean canBypassRBAC(ObjectName objectName) {
return jmxAccessControlList.isInWhiteList(objectName); return jmxAccessControlList.isInAllowList(objectName);
} }
public boolean canInvoke(String object, String operationName) { public boolean canInvoke(String object, String operationName) {

View File

@ -33,7 +33,7 @@ public class JMXAccessControlList {
private Access defaultAccess = new Access(WILDCARD); private Access defaultAccess = new Access(WILDCARD);
private ConcurrentHashMap<String, TreeMap<String, Access>> domainAccess = new ConcurrentHashMap<>(); private ConcurrentHashMap<String, TreeMap<String, Access>> domainAccess = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, TreeMap<String, Access>> whitelist = new ConcurrentHashMap<>(); private ConcurrentHashMap<String, TreeMap<String, Access>> allowList = new ConcurrentHashMap<>();
private Comparator<String> keyComparator = (key1, key2) -> { private Comparator<String> keyComparator = (key1, key2) -> {
boolean key1ContainsWildCard = key1.contains(WILDCARD); boolean key1ContainsWildCard = key1.contains(WILDCARD);
boolean key2ContainsWildcard = key2.contains(WILDCARD); boolean key2ContainsWildcard = key2.contains(WILDCARD);
@ -48,11 +48,11 @@ public class JMXAccessControlList {
return key2.length() - key1.length(); return key2.length() - key1.length();
}; };
public void addToWhiteList(String domain, String key) { public void addToAllowList(String domain, String key) {
TreeMap<String, Access> domainMap = new TreeMap<>(keyComparator); TreeMap<String, Access> domainMap = new TreeMap<>(keyComparator);
domainMap = whitelist.putIfAbsent(domain, domainMap); domainMap = allowList.putIfAbsent(domain, domainMap);
if (domainMap == null) { if (domainMap == null) {
domainMap = whitelist.get(domain); domainMap = allowList.get(domain);
} }
Access access = new Access(domain, normalizeKey(key)); Access access = new Access(domain, normalizeKey(key));
domainMap.putIfAbsent(access.getKey(), access); domainMap.putIfAbsent(access.getKey(), access);
@ -81,8 +81,8 @@ public class JMXAccessControlList {
return defaultAccess.getMatchingRolesForMethod(methodName); return defaultAccess.getMatchingRolesForMethod(methodName);
} }
public boolean isInWhiteList(ObjectName objectName) { public boolean isInAllowList(ObjectName objectName) {
TreeMap<String, Access> domainMap = whitelist.get(objectName.getDomain()); TreeMap<String, Access> domainMap = allowList.get(objectName.getDomain());
if (domainMap != null) { if (domainMap != null) {
if (domainMap.containsKey("")) { if (domainMap.containsKey("")) {
return true; return true;
@ -223,7 +223,7 @@ public class JMXAccessControlList {
public static JMXAccessControlList createDefaultList() { public static JMXAccessControlList createDefaultList() {
JMXAccessControlList accessControlList = new JMXAccessControlList(); JMXAccessControlList accessControlList = new JMXAccessControlList();
accessControlList.addToWhiteList("hawtio", "type=*"); accessControlList.addToAllowList("hawtio", "type=*");
accessControlList.addToRoleAccess("org.apache.activemq.artemis", null, "list*", "view", "update", "amq"); accessControlList.addToRoleAccess("org.apache.activemq.artemis", null, "list*", "view", "update", "amq");
accessControlList.addToRoleAccess("org.apache.activemq.artemis", null, "get*", "view", "update", "amq"); accessControlList.addToRoleAccess("org.apache.activemq.artemis", null, "get*", "view", "update", "amq");

View File

@ -29,35 +29,35 @@ public class JMXAccessControlListTest {
@Test @Test
public void testBasicDomain() throws MalformedObjectNameException { public void testBasicDomain() throws MalformedObjectNameException {
JMXAccessControlList controlList = new JMXAccessControlList(); JMXAccessControlList controlList = new JMXAccessControlList();
controlList.addToWhiteList("org.myDomain", null); controlList.addToAllowList("org.myDomain", null);
controlList.addToWhiteList("org.myDomain.foo", null); controlList.addToAllowList("org.myDomain.foo", null);
Assert.assertTrue(controlList.isInWhiteList(new ObjectName("org.myDomain:*"))); Assert.assertTrue(controlList.isInAllowList(new ObjectName("org.myDomain:*")));
Assert.assertTrue(controlList.isInWhiteList(new ObjectName("org.myDomain.foo:*"))); Assert.assertTrue(controlList.isInAllowList(new ObjectName("org.myDomain.foo:*")));
Assert.assertFalse(controlList.isInWhiteList(new ObjectName("org.myDomain.bar:*"))); Assert.assertFalse(controlList.isInAllowList(new ObjectName("org.myDomain.bar:*")));
} }
@Test @Test
public void testBasicDomainWithProperty() throws MalformedObjectNameException { public void testBasicDomainWithProperty() throws MalformedObjectNameException {
JMXAccessControlList controlList = new JMXAccessControlList(); JMXAccessControlList controlList = new JMXAccessControlList();
controlList.addToWhiteList("org.myDomain", "type=foo"); controlList.addToAllowList("org.myDomain", "type=foo");
controlList.addToWhiteList("org.myDomain.foo", "type=bar"); controlList.addToAllowList("org.myDomain.foo", "type=bar");
Assert.assertFalse(controlList.isInWhiteList(new ObjectName("org.myDomain:*"))); Assert.assertFalse(controlList.isInAllowList(new ObjectName("org.myDomain:*")));
Assert.assertFalse(controlList.isInWhiteList(new ObjectName("org.myDomain.foo:*"))); Assert.assertFalse(controlList.isInAllowList(new ObjectName("org.myDomain.foo:*")));
Assert.assertFalse(controlList.isInWhiteList(new ObjectName("org.myDomain.bar:*"))); Assert.assertFalse(controlList.isInAllowList(new ObjectName("org.myDomain.bar:*")));
Assert.assertFalse(controlList.isInWhiteList(new ObjectName("org.myDomain:subType=foo"))); Assert.assertFalse(controlList.isInAllowList(new ObjectName("org.myDomain:subType=foo")));
Assert.assertTrue(controlList.isInWhiteList(new ObjectName("org.myDomain:type=foo"))); Assert.assertTrue(controlList.isInAllowList(new ObjectName("org.myDomain:type=foo")));
Assert.assertTrue(controlList.isInWhiteList(new ObjectName("org.myDomain:subType=bar,type=foo"))); Assert.assertTrue(controlList.isInAllowList(new ObjectName("org.myDomain:subType=bar,type=foo")));
} }
@Test @Test
public void testBasicDomainWithWildCardProperty() throws MalformedObjectNameException { public void testBasicDomainWithWildCardProperty() throws MalformedObjectNameException {
JMXAccessControlList controlList = new JMXAccessControlList(); JMXAccessControlList controlList = new JMXAccessControlList();
controlList.addToWhiteList("org.myDomain", "type=*"); controlList.addToAllowList("org.myDomain", "type=*");
Assert.assertFalse(controlList.isInWhiteList(new ObjectName("org.myDomain:*"))); Assert.assertFalse(controlList.isInAllowList(new ObjectName("org.myDomain:*")));
Assert.assertFalse(controlList.isInWhiteList(new ObjectName("org.myDomain.foo:*"))); Assert.assertFalse(controlList.isInAllowList(new ObjectName("org.myDomain.foo:*")));
Assert.assertFalse(controlList.isInWhiteList(new ObjectName("org.myDomain.bar:*"))); Assert.assertFalse(controlList.isInAllowList(new ObjectName("org.myDomain.bar:*")));
Assert.assertTrue(controlList.isInWhiteList(new ObjectName("org.myDomain:type=foo"))); Assert.assertTrue(controlList.isInAllowList(new ObjectName("org.myDomain:type=foo")));
} }
@Test @Test

View File

@ -305,28 +305,32 @@ the broker's JAAS plugin support. This is configured via the `authorisation`
element in the `management.xml` configuration file and can be used to restrict element in the `management.xml` configuration file and can be used to restrict
access to attributes and methods on MBeans. access to attributes and methods on MBeans.
There are 3 elements within the `authorisation` element, `whitelist`, There are 3 elements within the `authorisation` element, `allowlist`,
`default-access` and `role-access`. Lets discuss each in turn. `default-access` and `role-access`. Lets discuss each in turn.
Whitelist contains a list of MBeans that will bypass the authorisation, this Allowlist contains a list of MBeans that will bypass the authorisation, this
is typically used for any MBeans that are needed by the console to run etc. The is typically used for any MBeans that are needed by the console to run etc. The
default configuration is: default configuration is:
```xml ```xml
<whitelist> <allowlist>
<entry domain="hawtio"/> <entry domain="hawtio"/>
</whitelist> </allowlist>
``` ```
This means that any MBean with the domain `hawtio` will be allowed access This means that any MBean with the domain `hawtio` will be allowed access
without authorisation. for instance `hawtio:plugin=artemis`. You can also use without authorisation. for instance `hawtio:plugin=artemis`. You can also use
wildcards for the MBean properties so the following would also match. wildcards for the MBean properties so the following would also match.
```xml ```xml
<whitelist> <allowlist>
<entry domain="hawtio" key="type=*"/> <entry domain="hawtio" key="type=*"/>
</whitelist> </allowlist>
``` ```
> **Note:**
>
> The allowlist element has replaced the whitelist element which is now deprecated
The `role-access`defines how roles are mapped to particular MBeans and its The `role-access`defines how roles are mapped to particular MBeans and its
attributes and methods, the default configuration looks like: attributes and methods, the default configuration looks like:

View File

@ -25,9 +25,9 @@
trust-store-path="${data.dir}/../etc/client-ca-truststore.jks" trust-store-path="${data.dir}/../etc/client-ca-truststore.jks"
trust-store-password="ENC(1f0e6cd7ced61232730f9e82cc91c1e1)"/> trust-store-password="ENC(1f0e6cd7ced61232730f9e82cc91c1e1)"/>
<authorisation> <authorisation>
<whitelist> <allowlist>
<entry domain="hawtio"/> <entry domain="hawtio"/>
</whitelist> </allowlist>
<default-access> <default-access>
<access method="list*" roles="view,update,amq,guest"/> <access method="list*" roles="view,update,amq,guest"/>
<access method="get*" roles="view,update,amq,guest"/> <access method="get*" roles="view,update,amq,guest"/>

View File

@ -18,9 +18,9 @@
<management-context xmlns="http://activemq.org/schema"> <management-context xmlns="http://activemq.org/schema">
<connector connector-port="1099" connector-host="localhost"/> <connector connector-port="1099" connector-host="localhost"/>
<authorisation> <authorisation>
<whitelist> <allowlist>
<entry domain="hawtio"/> <entry domain="hawtio"/>
</whitelist> </allowlist>
<default-access> <default-access>
<access method="list*" roles="view,update,amq,guest"/> <access method="list*" roles="view,update,amq,guest"/>
<access method="get*" roles="view,update,amq,guest"/> <access method="get*" roles="view,update,amq,guest"/>

View File

@ -18,9 +18,9 @@
<management-context xmlns="http://activemq.org/schema"> <management-context xmlns="http://activemq.org/schema">
<connector connector-port="1099" connector-host="127.0.0.1"/> <connector connector-port="1099" connector-host="127.0.0.1"/>
<authorisation> <authorisation>
<whitelist> <allowlist>
<entry domain="hawtio"/> <entry domain="hawtio"/>
</whitelist> </allowlist>
<default-access> <default-access>
<access method="list*" roles="view,update,amq,guest"/> <access method="list*" roles="view,update,amq,guest"/>
<access method="get*" roles="view,update,amq,guest"/> <access method="get*" roles="view,update,amq,guest"/>

View File

@ -18,9 +18,9 @@
<management-context xmlns="http://activemq.org/schema"> <management-context xmlns="http://activemq.org/schema">
<connector connector-port="10099" connector-host="localhost"/> <connector connector-port="10099" connector-host="localhost"/>
<authorisation> <authorisation>
<whitelist> <allowlist>
<entry domain="hawtio"/> <entry domain="hawtio"/>
</whitelist> </allowlist>
<default-access> <default-access>
<access method="list*" roles="guest"/> <access method="list*" roles="guest"/>
<access method="get*" roles="guest"/> <access method="get*" roles="guest"/>

View File

@ -18,9 +18,9 @@
<management-context xmlns="http://activemq.org/schema"> <management-context xmlns="http://activemq.org/schema">
<connector connector-port="10099" connector-host="localhost"/> <connector connector-port="10099" connector-host="localhost"/>
<authorisation> <authorisation>
<whitelist> <allowlist>
<entry domain="hawtio"/> <entry domain="hawtio"/>
</whitelist> </allowlist>
<default-access> <default-access>
<access method="list*" roles="guest"/> <access method="list*" roles="guest"/>
<access method="get*" roles="guest"/> <access method="get*" roles="guest"/>

View File

@ -18,9 +18,9 @@
<management-context xmlns="http://activemq.org/schema"> <management-context xmlns="http://activemq.org/schema">
<connector connector-port="10099"/> <connector connector-port="10099"/>
<authorisation> <authorisation>
<whitelist> <allowlist>
<entry domain="hawtio"/> <entry domain="hawtio"/>
</whitelist> </allowlist>
<default-access> <default-access>
<access method="list*" roles="amq"/> <access method="list*" roles="amq"/>
<access method="get*" roles="amq"/> <access method="get*" roles="amq"/>

View File

@ -18,9 +18,9 @@
<management-context xmlns="http://activemq.org/schema"> <management-context xmlns="http://activemq.org/schema">
<connector connector-port="10099" connector-host="localhost" rmi-registry-port="10098" /> <connector connector-port="10099" connector-host="localhost" rmi-registry-port="10098" />
<authorisation> <authorisation>
<whitelist> <allowlist>
<entry domain="hawtio"/> <entry domain="hawtio"/>
</whitelist> </allowlist>
<default-access> <default-access>
<access method="list*" roles="amq"/> <access method="list*" roles="amq"/>
<access method="get*" roles="amq"/> <access method="get*" roles="amq"/>

View File

@ -18,9 +18,9 @@
<management-context xmlns="http://activemq.org/schema"> <management-context xmlns="http://activemq.org/schema">
<connector connector-port="1099"/> <connector connector-port="1099"/>
<authorisation> <authorisation>
<whitelist> <allowlist>
<entry domain="hawtio"/> <entry domain="hawtio"/>
</whitelist> </allowlist>
<default-access> <default-access>
<access method="list*" roles="amq"/> <access method="list*" roles="amq"/>
<access method="get*" roles="amq"/> <access method="get*" roles="amq"/>