Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions artemis-console/src/main/resources/META-INF/jolokia/detectors
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 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.

!org.jolokia.server.detector.misc.ActiveMQDetector
!org.jolokia.server.detector.jee.GeronimoDetector
!org.jolokia.server.detector.jee.JBossDetector
!org.jolokia.server.detector.jee.TomcatDetector
!org.jolokia.server.detector.jee.JettyDetector
!org.jolokia.server.detector.jee.GlassfishDetector
!org.jolokia.server.detector.jee.WeblogicDetector
!org.jolokia.server.detector.jee.WebsphereDetector
!org.jolokia.server.detector.misc.LightstreamerDetector
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# 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.

# Log4J 2 configuration

# Monitor config file every X seconds for updates
monitorInterval = 5

rootLogger.level = INFO
rootLogger.appenderRef.console.ref = console
rootLogger.appenderRef.log_file.ref = log_file

logger.activemq.name=org.apache.activemq
logger.activemq.level=INFO

logger.artemis_server.name=org.apache.activemq.artemis.core.server
logger.artemis_server.level=INFO

logger.artemis_journal.name=org.apache.activemq.artemis.journal
logger.artemis_journal.level=INFO

logger.artemis_utils.name=org.apache.activemq.artemis.utils
logger.artemis_utils.level=INFO

# CriticalAnalyzer: If you have issues with the CriticalAnalyzer, setting this to TRACE would give
# you extra troubleshooting info, but do not use TRACE regularly as it would incur extra CPU usage.
logger.critical_analyzer.name=org.apache.activemq.artemis.utils.critical
logger.critical_analyzer.level=INFO

# Audit loggers: to enable change levels from OFF to INFO
logger.audit_base.name = org.apache.activemq.audit.base
logger.audit_base.level = INFO
logger.audit_base.appenderRef.audit_log_file.ref = audit_log_file
logger.audit_base.additivity = false

logger.audit_resource.name = org.apache.activemq.audit.resource
logger.audit_resource.level = INFO
logger.audit_resource.appenderRef.audit_log_file.ref = audit_log_file
logger.audit_resource.additivity = false

logger.audit_message.name = org.apache.activemq.audit.message
logger.audit_message.level = INFO
logger.audit_message.appenderRef.audit_log_file.ref = audit_log_file
logger.audit_message.additivity = false

# Jetty logger levels
logger.jetty.name=org.eclipse.jetty
logger.jetty.level=INFO

# web console authenticator too verbose for impatient client
logger.authentication_filter.name=io.hawt.web.auth.AuthenticationFilter
logger.authentication_filter.level=ERROR

# Quorum related logger levels
logger.curator.name=org.apache.curator
logger.curator.level=WARN
logger.zookeeper.name=org.apache.zookeeper
logger.zookeeper.level=ERROR


# Console appender
appender.console.type=Console
appender.console.name=console
appender.console.layout.type=PatternLayout
appender.console.layout.pattern=%d %-5level [%logger] %msg%n

# Log file appender
appender.log_file.type = RollingFile
appender.log_file.name = log_file
appender.log_file.fileName = ${sys:artemis.instance}/log/artemis.log
appender.log_file.filePattern = ${sys:artemis.instance}/log/artemis.log.%d{yyyy-MM-dd}
appender.log_file.layout.type = PatternLayout
appender.log_file.layout.pattern = %d %-5level [%logger] %msg%n
appender.log_file.policies.type = Policies
appender.log_file.policies.cron.type = CronTriggeringPolicy
appender.log_file.policies.cron.schedule = 0 0 0 * * ?
appender.log_file.policies.cron.evaluateOnStartup = true

# Audit log file appender
appender.audit_log_file.type = RollingFile
appender.audit_log_file.name = audit_log_file
appender.audit_log_file.fileName = ${sys:artemis.instance}/log/audit.log
appender.audit_log_file.filePattern = ${sys:artemis.instance}/log/audit.log.%d{yyyy-MM-dd}
appender.audit_log_file.layout.type = PatternLayout
appender.audit_log_file.layout.pattern = %d [AUDIT](%t) %msg%n
appender.audit_log_file.policies.type = Policies
appender.audit_log_file.policies.cron.type = CronTriggeringPolicy
appender.audit_log_file.policies.cron.schedule = 0 0 0 * * ?
appender.audit_log_file.policies.cron.evaluateOnStartup = true
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package org.apache.activemq.artemis.tests.smoke.jmxrbac;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

import javax.management.MBeanServerConnection;
Expand All @@ -26,18 +28,35 @@
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.stream.Stream;

import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.api.core.JsonUtil;
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.json.JsonObject;
import org.apache.activemq.artemis.tests.smoke.common.SmokeTestBase;
import org.apache.activemq.artemis.util.ServerUtil;
import org.apache.activemq.artemis.cli.commands.helper.HelperCreate;
import org.apache.activemq.artemis.utils.JsonLoader;
import org.apache.activemq.artemis.utils.VersionLoader;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -47,6 +66,7 @@
public class JmxRBACBrokerSecurityTest extends SmokeTestBase {

private static final String JMX_SERVER_HOSTNAME = "localhost";
private static final String JOLOKIA_URL = "http://localhost:8161/console/jolokia";
private static final int JMX_SERVER_PORT = 10099;

public static final String BROKER_NAME = "0.0.0.0";
Expand All @@ -66,7 +86,7 @@ public static void createServers() throws Exception {

{
HelperCreate cliCreateServer = helperCreate();
cliCreateServer.setRole("amq").setUser("admin").setPassword("admin").setAllowAnonymous(false).setNoWeb(false).setArtemisInstance(server0Location).
cliCreateServer.setRole("amq").setUser(SERVER_ADMIN).setPassword(SERVER_ADMIN).setAllowAnonymous(false).setNoWeb(false).setArtemisInstance(server0Location).
setConfiguration("./src/main/resources/servers/jmx-rbac-broker-security").setArgs("--java-options", "-Djava.rmi.server.hostname=localhost -Djavax.management.builder.initial=org.apache.activemq.artemis.core.server.management.ArtemisRbacMBeanServerBuilder");
cliCreateServer.createServer();
}
Expand Down Expand Up @@ -223,4 +243,102 @@ public void testSendMessageWithoutUserAndPassword() throws Exception {
jmxConnector.close();
}
}

@Test
public void testJolokiaWithServerAdmin() throws Exception {
// Read an attribute via jolokia (view permission)
String readRequest = JsonLoader.createObjectBuilder()
.add("type", "read")
.add("mbean", "org.apache.activemq.artemis:broker=\"" + BROKER_NAME + "\"")
.add("attribute", "Version")
.build()
.toString();

makeJolokiaRequest(JOLOKIA_URL, readRequest, SERVER_ADMIN, SERVER_ADMIN, response -> {
assertNotNull(response);
assertEquals(200, response.getStatusLine().getStatusCode());

String responseBody = getResponseBody(response);
assertNotNull(responseBody);

JsonObject jsonResponse = JsonUtil.readJsonObject(responseBody);
assertTrue(jsonResponse.containsKey("status"));
assertEquals(200, jsonResponse.getInt("status"));
assertTrue(jsonResponse.containsKey("value"));
assertEquals(VersionLoader.getVersion().getFullVersion(), jsonResponse.getString("value"));
});

// Query MBeans via jolokia
String queryRequest = JsonLoader.createObjectBuilder()
.add("type", "search")
.add("mbean", "org.apache.activemq.artemis:*")
.build()
.toString();

makeJolokiaRequest(JOLOKIA_URL, queryRequest, SERVER_ADMIN, SERVER_ADMIN, response -> {
assertNotNull(response);
assertEquals(200, response.getStatusLine().getStatusCode());

String responseBody = getResponseBody(response);
assertNotNull(responseBody);
});

}

@Test
public void testJolokiaDisabledDetectors() throws Exception {
// Read an attribute via jolokia (view permission)
String readRequest = JsonLoader.createObjectBuilder()
.add("type", "read")
.add("mbean", "org.apache.activemq.artemis:broker=\"" + BROKER_NAME + "\"")
.add("attribute", "Version")
.build()
.toString();

makeJolokiaRequest(JOLOKIA_URL, readRequest, SERVER_ADMIN, SERVER_ADMIN, response -> {
assertNotNull(response);
assertEquals(200, response.getStatusLine().getStatusCode());
});

// Verify artemis log does not contain AMQ229032 errors
try (Stream<String> lines = Files.lines(Path.of("target/" + SERVER_NAME_0 + "/log/artemis.log"))) {
assertTrue(lines.noneMatch(line -> line.contains("ActiveMQDetector") || line.contains("AMQ229032")));
}

// Verify audit log does not contain AMQ229032 errors
try (Stream<String> lines = Files.lines(Path.of("target/" + SERVER_NAME_0 + "/log/audit.log"))) {
assertTrue(lines.noneMatch(line -> line.contains("ActiveMQDetector") || line.contains("AMQ229032")));
}
}

private String getResponseBody(HttpResponse response) {
String responseBody;
try {
responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
return responseBody;
}

private void makeJolokiaRequest(String url, String jsonBody, String username, String password, Consumer<HttpResponse> responseConsumer) throws IOException {
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
HttpPost httpPost = new HttpPost(url);

// Set authentication header
String auth = username + ":" + password;
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
httpPost.setHeader("Authorization", "Basic " + encodedAuth);

// Set required headers for jolokia
httpPost.setHeader("Content-Type", "application/json");
httpPost.setHeader("Origin", "http://localhost");

// Set request body
StringEntity entity = new StringEntity(jsonBody, StandardCharsets.UTF_8);
httpPost.setEntity(entity);

responseConsumer.accept(httpClient.execute(httpPost));
}
}
}