You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by cl...@apache.org on 2022/12/15 16:51:47 UTC

[activemq-artemis] 01/02: ARTEMIS-3866 Authorize management message sending using context subject

This is an automated email from the ASF dual-hosted git repository.

clebertsuconic pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git

commit fa8d487ff2cef2c5fc0e5541a4b554ae8c54cf0d
Author: Domenico Francesco Bruscino <br...@apache.org>
AuthorDate: Mon Jun 20 18:51:55 2022 +0200

    ARTEMIS-3866 Authorize management message sending using context subject
---
 .../core/security/impl/SecurityStoreImpl.java      | 21 ++++++
 .../artemis/tests/smoke/jmxrbac/JmxRBACTest.java   | 82 ++++++++++++++++++++--
 2 files changed, 99 insertions(+), 4 deletions(-)

diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java
index a834779c1f..e364723121 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java
@@ -17,6 +17,8 @@
 package org.apache.activemq.artemis.core.security.impl;
 
 import javax.security.auth.Subject;
+import java.security.AccessControlContext;
+import java.security.AccessController;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
@@ -26,6 +28,7 @@ import org.apache.activemq.artemis.api.core.Pair;
 import org.apache.activemq.artemis.api.core.SimpleString;
 import org.apache.activemq.artemis.api.core.management.CoreNotificationType;
 import org.apache.activemq.artemis.api.core.management.ManagementHelper;
+import org.apache.activemq.artemis.core.management.impl.ManagementRemotingConnection;
 import org.apache.activemq.artemis.core.remoting.CertificateUtil;
 import org.apache.activemq.artemis.core.security.CheckType;
 import org.apache.activemq.artemis.core.security.Role;
@@ -168,6 +171,16 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
                subject = cacheEntry.getB();
                validatedUser = getUserFromSubject(subject);
             }
+         } else {
+            if (user == null && password == null && connection instanceof ManagementRemotingConnection) {
+               AccessControlContext accessControlContext = AccessController.getContext();
+               if (accessControlContext != null) {
+                  check = false;
+                  userIsValid = true;
+                  subject = Subject.getSubject(accessControlContext);
+                  validatedUser = getUserFromSubject(subject);
+               }
+            }
          }
          if (check) {
             if (securityManager instanceof ActiveMQSecurityManager5) {
@@ -382,6 +395,14 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
     */
    private Subject getSubjectForAuthorization(SecurityAuth auth, ActiveMQSecurityManager5 securityManager) {
       Pair<Boolean, Subject> cached = authenticationCache.getIfPresent(createAuthenticationCacheKey(auth.getUsername(), auth.getPassword(), auth.getRemotingConnection()));
+
+      if (cached == null && auth.getUsername() == null && auth.getPassword() == null && auth.getRemotingConnection() instanceof ManagementRemotingConnection) {
+         AccessControlContext accessControlContext = AccessController.getContext();
+         if (accessControlContext != null) {
+            cached = new Pair<>(true, Subject.getSubject(accessControlContext));
+         }
+      }
+
       /*
        * We don't need to worry about the cached boolean being false as users always have to
        * successfully authenticate before requesting authorization for anything.
diff --git a/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/jmxrbac/JmxRBACTest.java b/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/jmxrbac/JmxRBACTest.java
index bc0e1517a4..edd31ed7ba 100644
--- a/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/jmxrbac/JmxRBACTest.java
+++ b/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/jmxrbac/JmxRBACTest.java
@@ -26,7 +26,11 @@ import javax.management.remote.JMXServiceURL;
 import java.util.Collections;
 
 import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
+import org.apache.activemq.artemis.api.core.Message;
+import org.apache.activemq.artemis.api.core.RoutingType;
+import org.apache.activemq.artemis.api.core.SimpleString;
 import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl;
+import org.apache.activemq.artemis.api.core.management.AddressControl;
 import org.apache.activemq.artemis.api.core.management.ObjectNameBuilder;
 import org.apache.activemq.artemis.tests.smoke.common.SmokeTestBase;
 import org.apache.activemq.artemis.util.ServerUtil;
@@ -40,11 +44,14 @@ public class JmxRBACTest extends SmokeTestBase {
    private static final String JMX_SERVER_HOSTNAME = "localhost";
    private static final int JMX_SERVER_PORT = 10099;
 
+   public static final String BROKER_NAME = "0.0.0.0";
+
    public static final String SERVER_NAME_0 = "jmx-rbac";
 
    public static final String SERVER_ADMIN = "admin";
    public static final String SERVER_USER = "user";
 
+   public static final String ADDRESS_TEST = "TEST";
 
    @Before
    public void before() throws Exception {
@@ -79,8 +86,7 @@ public class JmxRBACTest extends SmokeTestBase {
       try {
          //Create an user.
          MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
-         String brokerName = "0.0.0.0";  // configured e.g. in broker.xml <broker-name> element
-         ObjectNameBuilder objectNameBuilder = ObjectNameBuilder.create(ActiveMQDefaultConfiguration.getDefaultJmxDomain(), brokerName, true);
+         ObjectNameBuilder objectNameBuilder = ObjectNameBuilder.create(ActiveMQDefaultConfiguration.getDefaultJmxDomain(), BROKER_NAME, true);
          ActiveMQServerControl activeMQServerControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getActiveMQServerObjectName(), ActiveMQServerControl.class, false);
          ObjectName memoryObjectName = new ObjectName("java.lang:type=Memory");
 
@@ -116,8 +122,7 @@ public class JmxRBACTest extends SmokeTestBase {
 
       try {
          MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
-         String brokerName = "0.0.0.0";  // configured e.g. in broker.xml <broker-name> element
-         ObjectNameBuilder objectNameBuilder = ObjectNameBuilder.create(ActiveMQDefaultConfiguration.getDefaultJmxDomain(), brokerName, true);
+         ObjectNameBuilder objectNameBuilder = ObjectNameBuilder.create(ActiveMQDefaultConfiguration.getDefaultJmxDomain(), BROKER_NAME, true);
          ActiveMQServerControl activeMQServerControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getActiveMQServerObjectName(), ActiveMQServerControl.class, false);
          ObjectName memoryObjectName = new ObjectName("java.lang:type=Memory");
 
@@ -133,4 +138,73 @@ public class JmxRBACTest extends SmokeTestBase {
          jmxConnector.close();
       }
    }
+
+   @Test
+   public void testSendMessageWithoutUserAndPassword() throws Exception {
+      // Without this, the RMI server would bind to the default interface IP (the user's local IP mostly)
+      System.setProperty("java.rmi.server.hostname", JMX_SERVER_HOSTNAME);
+
+      // I don't specify both ports here manually on purpose. See actual RMI registry connection port extraction below.
+      String urlString = "service:jmx:rmi:///jndi/rmi://" + JMX_SERVER_HOSTNAME + ":" + JMX_SERVER_PORT + "/jmxrmi";
+
+      JMXServiceURL url = new JMXServiceURL(urlString);
+      JMXConnector jmxConnector;
+
+      try {
+         //Connect using the admin.
+         jmxConnector = JMXConnectorFactory.connect(url, Collections.singletonMap(
+            "jmx.remote.credentials", new String[] {SERVER_ADMIN, SERVER_ADMIN}));
+         System.out.println("Successfully connected to: " + urlString);
+      } catch (Exception e) {
+         jmxConnector = null;
+         e.printStackTrace();
+         Assert.fail(e.getMessage());
+      }
+
+      try {
+         MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
+         ObjectNameBuilder objectNameBuilder = ObjectNameBuilder.create(ActiveMQDefaultConfiguration.getDefaultJmxDomain(), BROKER_NAME, true);
+         ActiveMQServerControl activeMQServerControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getActiveMQServerObjectName(), ActiveMQServerControl.class, false);
+
+         activeMQServerControl.createAddress(ADDRESS_TEST, RoutingType.MULTICAST.name());
+         AddressControl testAddressControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getAddressObjectName(SimpleString.toSimpleString(ADDRESS_TEST)), AddressControl.class, false);
+
+         testAddressControl.sendMessage(null, Message.TEXT_TYPE, ADDRESS_TEST, true, null, null);
+
+
+         try {
+            activeMQServerControl.removeUser(SERVER_USER);
+         } catch (Exception ignore) {
+         }
+         activeMQServerControl.addUser(SERVER_USER, SERVER_USER, "amq-user", true);
+      } finally {
+         jmxConnector.close();
+      }
+
+      try {
+         //Connect using an user.
+         jmxConnector = JMXConnectorFactory.connect(url, Collections.singletonMap(
+            "jmx.remote.credentials", new String[] {SERVER_USER, SERVER_USER}));
+         System.out.println("Successfully connected to: " + urlString);
+      } catch (Exception e) {
+         jmxConnector = null;
+         e.printStackTrace();
+         Assert.fail(e.getMessage());
+      }
+
+      try {
+         MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
+         ObjectNameBuilder objectNameBuilder = ObjectNameBuilder.create(ActiveMQDefaultConfiguration.getDefaultJmxDomain(), BROKER_NAME, true);
+         AddressControl testAddressControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getAddressObjectName(SimpleString.toSimpleString("TEST")), AddressControl.class, false);
+
+         try {
+            testAddressControl.sendMessage(null, Message.TEXT_TYPE, ADDRESS_TEST, true, null, null);
+            Assert.fail(SERVER_USER + " should not have permissions to send a message to the address " + ADDRESS_TEST);
+         } catch (Exception e) {
+            Assert.assertEquals(SecurityException.class, e.getClass());
+         }
+      } finally {
+         jmxConnector.close();
+      }
+   }
 }