ARTEMIS-2648 - audit logging improvements

https://issues.apache.org/jira/browse/ARTEMIS-2648
This commit is contained in:
Andy Taylor 2020-01-13 11:45:45 +00:00
parent 00985b609f
commit 8a04ee07de
51 changed files with 1747 additions and 190 deletions

View File

@ -17,7 +17,7 @@
# Additional logger names to configure (root logger is always configured)
# Root logger option
loggers=org.eclipse.jetty,org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.journal,org.apache.activemq.artemis.jms.server,org.apache.activemq.artemis.integration.bootstrap,org.apache.activemq.audit.base,org.apache.activemq.audit.message
loggers=org.eclipse.jetty,org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.journal,org.apache.activemq.artemis.jms.server,org.apache.activemq.artemis.integration.bootstrap,org.apache.activemq.audit.base,org.apache.activemq.audit.message,org.apache.activemq.audit.resource
# Root logger level
logger.level=INFO
@ -36,6 +36,10 @@ logger.org.apache.activemq.audit.base.level=ERROR
logger.org.apache.activemq.audit.base.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.base.useParentHandlers=false
logger.org.apache.activemq.audit.resource.level=ERROR
logger.org.apache.activemq.audit.resource.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.resource.useParentHandlers=false
logger.org.apache.activemq.audit.message.level=ERROR
logger.org.apache.activemq.audit.message.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.message.useParentHandlers=false

View File

@ -23,10 +23,12 @@ import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageLogger;
import javax.management.ObjectName;
import javax.security.auth.Subject;
import java.security.AccessController;
import java.security.Principal;
import java.util.Arrays;
import java.util.Set;
/**
* Logger Code 60
@ -46,26 +48,81 @@ import java.util.Arrays;
public interface AuditLogger extends BasicLogger {
AuditLogger LOGGER = Logger.getMessageLogger(AuditLogger.class, "org.apache.activemq.audit.base");
AuditLogger RESOURCE_LOGGER = Logger.getMessageLogger(AuditLogger.class, "org.apache.activemq.audit.resource");
AuditLogger MESSAGE_LOGGER = Logger.getMessageLogger(AuditLogger.class, "org.apache.activemq.audit.message");
ThreadLocal<String> remoteUrl = new ThreadLocal<>();
ThreadLocal<Subject> currentCaller = new ThreadLocal<>();
static boolean isAnyLoggingEnabled() {
return isEnabled() || isMessageEnabled() || isResourceLoggingEnabled();
}
static boolean isEnabled() {
return LOGGER.isEnabled(Logger.Level.INFO);
}
static boolean isResourceLoggingEnabled() {
return RESOURCE_LOGGER.isEnabled(Logger.Level.INFO);
}
static boolean isMessageEnabled() {
return MESSAGE_LOGGER.isEnabled(Logger.Level.INFO);
}
static String getCaller() {
Subject subject = Subject.getSubject(AccessController.getContext());
String caller = "anonymous";
if (subject == null) {
subject = currentCaller.get();
}
return getCaller(subject);
}
static String getCaller(String user) {
Subject subject = Subject.getSubject(AccessController.getContext());
if (subject == null) {
subject = currentCaller.get();
}
if (subject == null) {
return user + (remoteUrl.get() == null ? "@unknown" : remoteUrl.get());
}
return getCaller(subject);
}
static String getCaller(Subject subject) {
String user = "anonymous";
String roles = "";
String url = remoteUrl.get() == null ? "@unknown" : remoteUrl.get();
if (subject != null) {
caller = "";
for (Principal principal : subject.getPrincipals()) {
caller += principal.getName() + "|";
Set<Principal> principals = subject.getPrincipals();
for (Principal principal : principals) {
if (principal.getClass().getName().endsWith("UserPrincipal")) {
user = principal.getName();
} else if (principal.getClass().getName().endsWith("RolePrincipal")) {
roles = "(" + principal.getName() + ")";
}
}
return caller;
}
return user + roles + url;
}
static void setCurrentCaller(Subject caller) {
currentCaller.set(caller);
}
static void setRemoteAddress(String remoteAddress) {
String actualAddress;
if (remoteAddress.startsWith("/")) {
actualAddress = "@" + remoteAddress.substring(1);
} else {
actualAddress = "@" + remoteAddress;
}
remoteUrl.set(actualAddress);
}
static String getRemoteAddress() {
return remoteUrl.get();
}
static String arrayToString(Object value) {
@ -201,7 +258,7 @@ public interface AuditLogger extends BasicLogger {
void getUnRoutedMessageCount(String user, Object source, Object... args);
static void sendMessage(Object source, String user, Object... args) {
LOGGER.sendMessage(user == null ? getCaller() : user, source, arrayToString(args));
LOGGER.sendMessage(getCaller(user), source, arrayToString(args));
}
@LogMessage(level = Logger.Level.INFO)
@ -617,7 +674,7 @@ public interface AuditLogger extends BasicLogger {
void deployQueue(String user, Object source, Object... args);
static void createQueue(Object source, String user, Object... args) {
LOGGER.createQueue(user == null ? getCaller() : user, source, arrayToString(args));
RESOURCE_LOGGER.createQueue(getCaller(user), source, arrayToString(args));
}
@LogMessage(level = Logger.Level.INFO)
@ -673,7 +730,7 @@ public interface AuditLogger extends BasicLogger {
void getAddressNames(String user, Object source, Object... args);
static void destroyQueue(Object source, String user, Object... args) {
LOGGER.destroyQueue(user == null ? getCaller() : user, source, arrayToString(args));
LOGGER.destroyQueue(getCaller(user), source, arrayToString(args));
}
@LogMessage(level = Logger.Level.INFO)
@ -2193,7 +2250,7 @@ public interface AuditLogger extends BasicLogger {
void getUniqueName(String user, Object source, Object... args);
static void serverSessionCreateAddress(Object source, String user, Object... args) {
LOGGER.serverSessionCreateAddress2(user == null ? getCaller() : user, source, arrayToString(args));
LOGGER.serverSessionCreateAddress2(getCaller(user), source, arrayToString(args));
}
@LogMessage(level = Logger.Level.INFO)
@ -2201,7 +2258,7 @@ public interface AuditLogger extends BasicLogger {
void serverSessionCreateAddress2(String user, Object source, Object... args);
static void handleManagementMessage(Object source, String user, Object... args) {
LOGGER.handleManagementMessage2(user == null ? getCaller() : user, source, arrayToString(args));
LOGGER.handleManagementMessage2(getCaller(user), source, arrayToString(args));
}
@LogMessage(level = Logger.Level.INFO)
@ -2219,7 +2276,7 @@ public interface AuditLogger extends BasicLogger {
static void createCoreConsumer(Object source, String user, Object... args) {
LOGGER.createCoreConsumer(user == null ? getCaller() : user, source, arrayToString(args));
LOGGER.createCoreConsumer(getCaller(user), source, arrayToString(args));
}
@LogMessage(level = Logger.Level.INFO)
@ -2227,7 +2284,7 @@ public interface AuditLogger extends BasicLogger {
void createCoreConsumer(String user, Object source, Object... args);
static void createSharedQueue(Object source, String user, Object... args) {
LOGGER.createSharedQueue(user == null ? getCaller() : user, source, arrayToString(args));
LOGGER.createSharedQueue(getCaller(user), source, arrayToString(args));
}
@LogMessage(level = Logger.Level.INFO)
@ -2250,22 +2307,12 @@ public interface AuditLogger extends BasicLogger {
@Message(id = 601268, value = "User {0} is getting produced message rate on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
void getProducedRate(String user, Object source, Object... args);
//hot path log using a different logger
static void coreSendMessage(Object source, String user, Object... args) {
MESSAGE_LOGGER.coreSendMessage(user == null ? getCaller() : user, source, arrayToString(args));
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601500, value = "User {0} is sending a core message on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
void coreSendMessage(String user, Object source, Object... args);
static void getAcknowledgeAttempts(Object source) {
LOGGER.getMessagesAcknowledged(getCaller(), source);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601501, value = "User {0} is getting messages acknowledged attempts on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
@Message(id = 601269, value = "User {0} is getting messages acknowledged attempts on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
void getAcknowledgeAttempts(String user, Object source, Object... args);
static void getRingSize(Object source, Object... args) {
@ -2273,7 +2320,7 @@ public interface AuditLogger extends BasicLogger {
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601502, value = "User {0} is getting ring size on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
@Message(id = 601270, value = "User {0} is getting ring size on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
void getRingSize(String user, Object source, Object... args);
@ -2282,7 +2329,7 @@ public interface AuditLogger extends BasicLogger {
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601503, value = "User {0} is getting retroactiveResource property on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
@Message(id = 601271, value = "User {0} is getting retroactiveResource property on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
void isRetroactiveResource(String user, Object source, Object... args);
static void getDiskStoreUsage(Object source) {
@ -2290,7 +2337,7 @@ public interface AuditLogger extends BasicLogger {
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601504, value = "User {0} is getting disk store usage on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
@Message(id = 601272, value = "User {0} is getting disk store usage on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
void getDiskStoreUsage(String user, Object source, Object... args);
static void getDiskStoreUsagePercentage(Object source) {
@ -2298,7 +2345,7 @@ public interface AuditLogger extends BasicLogger {
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601505, value = "User {0} is getting disk store usage percentage on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
@Message(id = 601273, value = "User {0} is getting disk store usage percentage on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
void getDiskStoreUsagePercentage(String user, Object source, Object... args);
static void isGroupRebalance(Object source) {
@ -2306,7 +2353,7 @@ public interface AuditLogger extends BasicLogger {
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601506, value = "User {0} is getting group rebalance property on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
@Message(id = 601274, value = "User {0} is getting group rebalance property on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
void isGroupRebalance(String user, Object source, Object... args);
static void getGroupBuckets(Object source) {
@ -2314,7 +2361,7 @@ public interface AuditLogger extends BasicLogger {
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601507, value = "User {0} is getting group buckets on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
@Message(id = 601275, value = "User {0} is getting group buckets on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
void getGroupBuckets(String user, Object source, Object... args);
static void getGroupFirstKey(Object source) {
@ -2322,7 +2369,7 @@ public interface AuditLogger extends BasicLogger {
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601508, value = "User {0} is getting group first key on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
@Message(id = 601276, value = "User {0} is getting group first key on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
void getGroupFirstKey(String user, Object source, Object... args);
static void getCurrentDuplicateIdCacheSize(Object source) {
@ -2341,4 +2388,254 @@ public interface AuditLogger extends BasicLogger {
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601510, value = "User {0} is clearing duplicate ID cache on target resource: {1} {2}", format = Message.Format.MESSAGE_FORMAT)
void clearDuplicateIdCache(String user, Object source, Object... args);
/*
* This logger is for message production and consumption and is on the hot path so enabled independently
*
* */
//hot path log using a different logger
static void coreSendMessage(String user, Object context) {
MESSAGE_LOGGER.sendMessage(getCaller(user), context);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601500, value = "User {0} is sending a core message with Context: {1}", format = Message.Format.MESSAGE_FORMAT)
void sendMessage(String user, Object context);
//hot path log using a different logger
static void coreConsumeMessage(String queue) {
MESSAGE_LOGGER.consumeMessage(getCaller(), queue);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601501, value = "User {0} is consuming a message from {1}", format = Message.Format.MESSAGE_FORMAT)
void consumeMessage(String user, String address);
/*
* This logger is focused on user interaction from the console or thru resource specific functions in the management layer/JMX
* */
static void createAddressSuccess(String name, String routingTypes) {
RESOURCE_LOGGER.createAddressSuccess(getCaller(), name, routingTypes);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601701, value = "User {0} successfully created Address: {1} with routing types {2}", format = Message.Format.MESSAGE_FORMAT)
void createAddressSuccess(String user, String name, String routingTypes);
static void createAddressFailure(String name, String routingTypes) {
RESOURCE_LOGGER.createAddressFailure(getCaller(), name, routingTypes);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601702, value = "User {0} failed to created Address: {1} with routing types {2}", format = Message.Format.MESSAGE_FORMAT)
void createAddressFailure(String user, String name, String routingTypes);
static void updateAddressSuccess(String name, String routingTypes) {
RESOURCE_LOGGER.updateAddressSuccess(getCaller(), name, routingTypes);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601703, value = "User {0} successfully updated Address: {1} with routing types {2}", format = Message.Format.MESSAGE_FORMAT)
void updateAddressSuccess(String user, String name, String routingTypes);
static void updateAddressFailure(String name, String routingTypes) {
RESOURCE_LOGGER.updateAddressFailure(getCaller(), name, routingTypes);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601704, value = "User {0} successfully updated Address: {1} with routing types {2}", format = Message.Format.MESSAGE_FORMAT)
void updateAddressFailure(String user, String name, String routingTypes);
static void deleteAddressSuccess(String name) {
RESOURCE_LOGGER.deleteAddressSuccess(getCaller(), name);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601705, value = "User {0} successfully deleted Address: {1}", format = Message.Format.MESSAGE_FORMAT)
void deleteAddressSuccess(String user, String name);
static void deleteAddressFailure(String name) {
RESOURCE_LOGGER.deleteAddressFailure(getCaller(), name);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601706, value = "User {0} failed to deleted Address: {1}", format = Message.Format.MESSAGE_FORMAT)
void deleteAddressFailure(String user, String name);
static void createQueueSuccess(String name, String address, String routingType) {
RESOURCE_LOGGER.createQueueSuccess(getCaller(), name, address, routingType);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601707, value = "User {0} successfully created Queue: {1} on Address: {2} with routing type {3}", format = Message.Format.MESSAGE_FORMAT)
void createQueueSuccess(String user, String name, String address, String routingType);
static void createQueueFailure(String name, String address, String routingType) {
RESOURCE_LOGGER.createQueueFailure(getCaller(), name, address, routingType);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601708, value = "User {0} failed to create Queue: {1} on Address: {2} with routing type {3}", format = Message.Format.MESSAGE_FORMAT)
void createQueueFailure(String user, String name, String address, String routingType);
static void updateQueueSuccess(String name, String routingType) {
RESOURCE_LOGGER.updateQueueSuccess(getCaller(), name, routingType);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601709, value = "User {0} successfully updated Queue: {1} with routing type {2}", format = Message.Format.MESSAGE_FORMAT)
void updateQueueSuccess(String user, String name, String routingType);
static void updateQueueFailure(String name, String routingType) {
RESOURCE_LOGGER.updateQueueFailure(getCaller(), name, routingType);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601710, value = "User {0} failed to update Queue: {1} with routing type {2}", format = Message.Format.MESSAGE_FORMAT)
void updateQueueFailure(String user, String name, String routingType);
static void destroyQueueSuccess(String name) {
RESOURCE_LOGGER.destroyQueueSuccess(getCaller(), name);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601711, value = "User {0} successfully deleted Queue: {1}", format = Message.Format.MESSAGE_FORMAT)
void destroyQueueSuccess(String user, String name);
static void destroyQueueFailure(String name) {
RESOURCE_LOGGER.destroyQueueFailure(getCaller(), name);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601712, value = "User {0} failed to delete Queue: {1}", format = Message.Format.MESSAGE_FORMAT)
void destroyQueueFailure(String user, String name);
static void removeMessagesSuccess(int removed, String queue) {
RESOURCE_LOGGER.removeMessagesSuccess(getCaller(), removed, queue);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601713, value = "User {0} has removed {1} messages from Queue: {2}", format = Message.Format.MESSAGE_FORMAT)
void removeMessagesSuccess(String user, int removed, String queue);
static void removeMessagesFailure(String queue) {
RESOURCE_LOGGER.removeMessagesFailure(getCaller(), queue);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601714, value = "User {0} failed to remove messages from Queue: {1}", format = Message.Format.MESSAGE_FORMAT)
void removeMessagesFailure(String user, String queue);
static void userSuccesfullyLoggedInAudit(Subject subject) {
RESOURCE_LOGGER.userSuccesfullyLoggedIn(getCaller(subject));
}
static void userSuccesfullyLoggedInAudit() {
RESOURCE_LOGGER.userSuccesfullyLoggedIn(getCaller());
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601715, value = "User {0} successfully authorized", format = Message.Format.MESSAGE_FORMAT)
void userSuccesfullyLoggedIn(String caller);
static void userFailedLoggedInAudit(String reason) {
RESOURCE_LOGGER.userFailedLoggedIn(getCaller(), reason);
}
static void userFailedLoggedInAudit(Subject subject, String reason) {
RESOURCE_LOGGER.userFailedLoggedIn(getCaller(subject), reason);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601716, value = "User {0} failed authorization, reason: {1}", format = Message.Format.MESSAGE_FORMAT)
void userFailedLoggedIn(String user, String reason);
static void objectInvokedSuccessfully(ObjectName objectName, String operationName) {
RESOURCE_LOGGER.objectInvokedSuccessfully(getCaller(), objectName, operationName);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601717, value = "User {0} accessed {2} on management object {1}", format = Message.Format.MESSAGE_FORMAT)
void objectInvokedSuccessfully(String caller, ObjectName objectName, String operationName);
static void objectInvokedFailure(ObjectName objectName, String operationName) {
RESOURCE_LOGGER.objectInvokedFailure(getCaller(), objectName, operationName);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601718, value = "User {0} does not have correct role to access {2} on management object {1}", format = Message.Format.MESSAGE_FORMAT)
void objectInvokedFailure(String caller, ObjectName objectName, String operationName);
static void pauseQueueSuccess(String queueName) {
RESOURCE_LOGGER.pauseQueueSuccess(getCaller(), queueName);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601719, value = "User {0} has paused queue {1}", format = Message.Format.MESSAGE_FORMAT)
void pauseQueueSuccess(String user, String queueName);
static void pauseQueueFailure(String queueName) {
RESOURCE_LOGGER.pauseQueueFailure(getCaller(), queueName);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601720, value = "User {0} failed to pause queue {1}", format = Message.Format.MESSAGE_FORMAT)
void pauseQueueFailure(String user, String queueName);
static void resumeQueueSuccess(String queueName) {
RESOURCE_LOGGER.resumeQueueSuccess(getCaller(), queueName);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601721, value = "User {0} has paused queue {1}", format = Message.Format.MESSAGE_FORMAT)
void resumeQueueSuccess(String user, String queueName);
static void resumeQueueFailure(String queueName) {
RESOURCE_LOGGER.pauseQueueFailure(getCaller(), queueName);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601722, value = "User {0} failed to resume queue {1}", format = Message.Format.MESSAGE_FORMAT)
void resumeQueueFailure(String user, String queueName);
static void sendMessageSuccess(String queueName, String user) {
RESOURCE_LOGGER.sendMessageSuccess(getCaller(), queueName, user);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601723, value = "User {0} sent message to {1} as user {2}", format = Message.Format.MESSAGE_FORMAT)
void sendMessageSuccess(String user, String queueName, String sendUser);
static void sendMessageFailure(String queueName, String user) {
RESOURCE_LOGGER.sendMessageFailure(getCaller(), queueName, user);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601724, value = "User {0} failed to send message to {1} as user {2}", format = Message.Format.MESSAGE_FORMAT)
void sendMessageFailure(String user, String queueName, String sendUser);
static void browseMessagesSuccess(String queueName, int numMessages) {
RESOURCE_LOGGER.browseMessagesSuccess(getCaller(), queueName, numMessages);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601725, value = "User {0} browsed {2} messages from queue {1}", format = Message.Format.MESSAGE_FORMAT)
void browseMessagesSuccess(String user, String queueName, int numMessages);
static void browseMessagesFailure(String queueName) {
RESOURCE_LOGGER.browseMessagesFailure(getCaller(), queueName);
}
@LogMessage(level = Logger.Level.INFO)
@Message(id = 601726, value = "User {0} failed to browse messages from queue {1}", format = Message.Format.MESSAGE_FORMAT)
void browseMessagesFailure(String user, String queueName);
}

View File

@ -47,6 +47,7 @@ public abstract class AbstractRemotingConnection implements RemotingConnection {
protected final long creationTime;
protected volatile boolean dataReceived;
private String clientId;
private Subject subject;
public AbstractRemotingConnection(final Connection transportConnection, final Executor executor) {
this.transportConnection = transportConnection;
@ -247,6 +248,16 @@ public abstract class AbstractRemotingConnection implements RemotingConnection {
return true;
}
@Override
public void setAuditSubject(Subject subject) {
this.subject = subject;
}
@Override
public Subject getAuditSubject() {
return subject;
}
@Override
public Subject getSubject() {
return null;

View File

@ -219,6 +219,13 @@ public interface RemotingConnection extends BufferHandler {
*/
boolean isSupportsFlowControl();
/*
* sets the currently associated subject for this connection
* */
void setAuditSubject(Subject subject);
Subject getAuditSubject();
/**
* the possibly null identity associated with this connection
* @return

View File

@ -494,6 +494,16 @@ public class ChannelImplTest {
return false;
}
@Override
public void setAuditSubject(Subject subject) {
}
@Override
public Subject getAuditSubject() {
return null;
}
@Override
public Subject getSubject() {
return null;

View File

@ -107,7 +107,7 @@ public class AMQPConnectionCallback implements FailureListener, CloseListener {
if (isPermittedMechanism(mechanism)) {
switch (mechanism) {
case PlainSASL.NAME:
result = new PlainSASL(server.getSecurityStore(), manager.getSecurityDomain());
result = new PlainSASL(server.getSecurityStore(), manager.getSecurityDomain(), connection.getProtocolConnection());
break;
case AnonymousServerSASL.NAME:

View File

@ -392,6 +392,11 @@ public class AMQPConnectionContext extends ProtonInitializable implements EventH
return connectionCallback.isWritable(readyListener);
}
@Override
public String getRemoteAddress() {
return connectionCallback.getTransportConnection().getRemoteAddress();
}
@Override
public void onRemoteOpen(Connection connection) throws Exception {
handler.requireHandler();

View File

@ -91,4 +91,7 @@ public interface EventHandler {
return true;
}
default String getRemoteAddress() {
return "";
}
}

View File

@ -28,6 +28,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.EventLoop;
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException;
import org.apache.activemq.artemis.logs.AuditLogger;
import org.apache.activemq.artemis.protocol.amqp.proton.AMQPConnectionContext;
import org.apache.activemq.artemis.protocol.amqp.proton.ProtonInitializable;
import org.apache.activemq.artemis.protocol.amqp.sasl.ClientSASL;
@ -492,6 +493,11 @@ public class ProtonHandler extends ProtonInitializable implements SaslListener {
}
try {
inDispatch = true;
if (AuditLogger.isAnyLoggingEnabled()) {
for (EventHandler h : handlers) {
AuditLogger.setRemoteAddress(h.getRemoteAddress());
}
}
while ((ev = collector.peek()) != null) {
for (EventHandler h : handlers) {
if (log.isTraceEnabled()) {

View File

@ -17,22 +17,25 @@
package org.apache.activemq.artemis.protocol.amqp.sasl;
import org.apache.activemq.artemis.core.security.SecurityStore;
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
public class PlainSASL extends ServerSASLPlain {
private final SecurityStore securityStore;
private final String securityDomain;
private RemotingConnection remotingConnection;
public PlainSASL(SecurityStore securityStore, String securityDomain) {
public PlainSASL(SecurityStore securityStore, String securityDomain, RemotingConnection remotingConnection) {
this.securityStore = securityStore;
this.securityDomain = securityDomain;
this.remotingConnection = remotingConnection;
}
@Override
protected boolean authenticate(String user, String password) {
if (securityStore.isSecurityEnabled()) {
try {
securityStore.authenticate(user, password, null, securityDomain);
securityStore.authenticate(user, password, remotingConnection, securityDomain);
return true;
} catch (Exception e) {
return false;

View File

@ -16,6 +16,7 @@
*/
package org.apache.activemq.artemis.protocol.amqp.broker;
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnection;
import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl;
import org.apache.activemq.artemis.protocol.amqp.sasl.AnonymousServerSASL;
import org.apache.activemq.artemis.protocol.amqp.sasl.GSSAPIServerSASL;
@ -32,7 +33,7 @@ public class AMQPConnectionCallbackTest {
public void getServerSASLOnlyAllowedMechs() throws Exception {
ProtonProtocolManager protonProtocolManager = new ProtonProtocolManager(new ProtonProtocolManagerFactory(), null, null, null);
protonProtocolManager.setSaslMechanisms(new String[]{PlainSASL.NAME});
AMQPConnectionCallback connectionCallback = new AMQPConnectionCallback(protonProtocolManager, null, null, new ActiveMQServerImpl());
AMQPConnectionCallback connectionCallback = new AMQPConnectionCallback(protonProtocolManager, new InVMConnection(1, null, null, null), null, new ActiveMQServerImpl());
assertEquals(1, connectionCallback.getSaslMechanisms().length);
for (String mech: connectionCallback.getSaslMechanisms()) {
assertNotNull(connectionCallback.getServerSASL(mech));

View File

@ -52,6 +52,7 @@ public class MQTTConnection implements RemotingConnection {
private final List<FailureListener> failureListeners = new CopyOnWriteArrayList<>();
private final List<CloseListener> closeListeners = new CopyOnWriteArrayList<>();
private Subject subject;
public MQTTConnection(Connection transportConnection) throws Exception {
this.transportConnection = transportConnection;
@ -258,6 +259,16 @@ public class MQTTConnection implements RemotingConnection {
return false;
}
@Override
public void setAuditSubject(Subject subject) {
this.subject = subject;
}
@Override
public Subject getAuditSubject() {
return subject;
}
@Override
public Subject getSubject() {
return null;

View File

@ -39,6 +39,7 @@ import io.netty.handler.codec.mqtt.MqttUnsubAckMessage;
import io.netty.handler.codec.mqtt.MqttUnsubscribeMessage;
import io.netty.util.ReferenceCountUtil;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.logs.AuditLogger;
import org.apache.activemq.artemis.spi.core.protocol.ConnectionEntry;
/**
@ -98,6 +99,10 @@ public class MQTTProtocolHandler extends ChannelInboundHandlerAdapter {
connection.dataReceived();
if (AuditLogger.isAnyLoggingEnabled()) {
AuditLogger.setRemoteAddress(connection.getRemoteAddress());
}
MQTTUtil.logMessage(session.getState(), message, true);
if (this.protocolManager.invokeIncoming(message, this.connection) != null) {

View File

@ -80,6 +80,7 @@ import org.apache.activemq.artemis.core.transaction.ResourceManager;
import org.apache.activemq.artemis.core.transaction.Transaction;
import org.apache.activemq.artemis.core.transaction.TransactionOperationAbstract;
import org.apache.activemq.artemis.core.transaction.TransactionPropertyIndexes;
import org.apache.activemq.artemis.logs.AuditLogger;
import org.apache.activemq.artemis.spi.core.protocol.AbstractRemotingConnection;
import org.apache.activemq.artemis.spi.core.protocol.ConnectionEntry;
import org.apache.activemq.artemis.spi.core.remoting.Connection;
@ -305,6 +306,10 @@ public class OpenWireConnection extends AbstractRemotingConnection implements Se
try {
recoverOperationContext();
if (AuditLogger.isAnyLoggingEnabled()) {
AuditLogger.setRemoteAddress(getRemoteAddress());
}
boolean responseRequired = command.isResponseRequired();
int commandId = command.getCommandId();

View File

@ -115,6 +115,7 @@ public final class StompConnection implements RemotingConnection {
private final ScheduledExecutorService scheduledExecutorService;
private final ExecutorFactory executorFactory;
private Subject subject;
@Override
public boolean isSupportReconnect() {
@ -855,6 +856,16 @@ public final class StompConnection implements RemotingConnection {
return false;
}
@Override
public void setAuditSubject(Subject subject) {
this.subject = subject;
}
@Override
public Subject getAuditSubject() {
return subject;
}
@Override
public Subject getSubject() {
return null;

View File

@ -38,6 +38,7 @@ import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.apache.activemq.artemis.core.server.ServerSession;
import org.apache.activemq.artemis.logs.AuditLogger;
import org.apache.activemq.artemis.spi.core.protocol.AbstractProtocolManager;
import org.apache.activemq.artemis.spi.core.protocol.ConnectionEntry;
import org.apache.activemq.artemis.spi.core.protocol.ProtocolManagerFactory;
@ -154,6 +155,9 @@ public class StompProtocolManager extends AbstractProtocolManager<StompFrame, St
}
try {
if (AuditLogger.isAnyLoggingEnabled()) {
AuditLogger.setRemoteAddress(connection.getRemoteAddress());
}
conn.logFrame(request, true);
if (invokeInterceptors(this.incomingInterceptors, request, conn) != null) {
return;

View File

@ -846,9 +846,7 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
@Override
public String createAddress(String name, String routingTypes) throws Exception {
if (AuditLogger.isEnabled()) {
AuditLogger.createAddress(this.server, name, routingTypes);
}
checkStarted();
clearIO();
@ -859,8 +857,15 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
}
final AddressInfo addressInfo = new AddressInfo(new SimpleString(name), set);
if (server.addAddressInfo(addressInfo)) {
return AddressInfoTextFormatter.Long.format(addressInfo, new StringBuilder()).toString();
String result = AddressInfoTextFormatter.Long.format(addressInfo, new StringBuilder()).toString();
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.createAddressSuccess(name, routingTypes);
}
return result;
} else {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.createAddressFailure(name, routingTypes);
}
throw ActiveMQMessageBundle.BUNDLE.addressAlreadyExists(addressInfo.getName());
}
} finally {
@ -870,9 +875,7 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
@Override
public String updateAddress(String name, String routingTypes) throws Exception {
if (AuditLogger.isEnabled()) {
AuditLogger.updateAddress(this.server, name, routingTypes);
}
checkStarted();
clearIO();
@ -888,8 +891,14 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
}
}
if (!server.updateAddressInfo(SimpleString.toSimpleString(name), routingTypeSet)) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.updateAddressFailure(name, routingTypes);
}
throw ActiveMQMessageBundle.BUNDLE.addressDoesNotExist(SimpleString.toSimpleString(name));
}
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.updateAddressSuccess(name, routingTypes);
}
return AddressInfoTextFormatter.Long.format(server.getAddressInfo(SimpleString.toSimpleString(name)), new StringBuilder()).toString();
} finally {
blockOnIO();
@ -904,15 +913,18 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
@Override
public void deleteAddress(String name, boolean force) throws Exception {
if (AuditLogger.isEnabled()) {
AuditLogger.deleteAddress(this.server, name, force);
}
checkStarted();
clearIO();
try {
server.removeAddressInfo(new SimpleString(name), null, force);
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.deleteAddressSuccess(name);
}
} catch (ActiveMQException e) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.deleteAddressFailure(name);
}
throw new IllegalStateException(e.getMessage());
} finally {
blockOnIO();
@ -1203,8 +1215,14 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
.setAutoDeleteMessageCount(autoDeleteMessageCount)
.setAutoCreateAddress(autoCreateAddress)
.setRingSize(ringSize));
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.createQueueSuccess( name, address, routingType);
}
return QueueTextFormatter.Long.format(queue, new StringBuilder()).toString();
} catch (ActiveMQException e) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.createQueueFailure( name, address, routingType);
}
throw new IllegalStateException(e.getMessage());
} finally {
blockOnIO();
@ -1353,8 +1371,15 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
try {
final Queue queue = server.updateQueue(name, routingType != null ? RoutingType.valueOf(routingType) : null, filter, maxConsumers, purgeOnNoConsumers, exclusive, groupRebalance, groupBuckets, groupFirstKey, nonDestructive, consumersBeforeDispatch, delayBeforeDispatch, user);
if (queue == null) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.updateQueueFailure(name, routingType);
}
throw ActiveMQMessageBundle.BUNDLE.noSuchQueue(new SimpleString(name));
}
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.updateQueueSuccess(name, routingType);
}
return QueueTextFormatter.Long.format(queue, new StringBuilder()).toString();
} finally {
blockOnIO();
@ -1490,7 +1515,17 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
clearIO();
try {
SimpleString queueName = new SimpleString(name);
try {
server.destroyQueue(queueName, null, !removeConsumers, removeConsumers, autoDeleteAddress);
} catch (Exception e) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.destroyQueueFailure(name);
}
throw e;
}
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.destroyQueueSuccess(name);
}
} finally {
blockOnIO();
}

View File

@ -181,6 +181,16 @@ public class ManagementRemotingConnection implements RemotingConnection {
return false;
}
@Override
public void setAuditSubject(Subject subject) {
}
@Override
public Subject getAuditSubject() {
return null;
}
@Override
public Subject getSubject() {
return null;

View File

@ -1000,7 +1000,19 @@ public class QueueControlImpl extends AbstractControl implements QueueControl {
try {
Filter filter = FilterImpl.createFilter(filterStr);
return queue.deleteMatchingReferences(flushLimit, filter);
int removed = 0;
try {
removed = queue.deleteMatchingReferences(flushLimit, filter);
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.removeMessagesSuccess(removed, queue.getName().toString());
}
} catch (Exception e) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.removeMessagesFailure(queue.getName().toString());
}
throw e;
}
return removed;
} finally {
blockOnIO();
}
@ -1194,8 +1206,15 @@ public class QueueControlImpl extends AbstractControl implements QueueControl {
AuditLogger.sendMessage(queue, null, headers, type, body, durable, user, "****");
}
try {
return sendMessage(queue.getAddress(), server, headers, type, body, durable, user, password, queue.getID());
String s = sendMessage(queue.getAddress(), server, headers, type, body, durable, user, password, queue.getID());
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.sendMessageSuccess(queue.getName().toString(), user);
}
return s;
} catch (Exception e) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.sendMessageFailure(queue.getName().toString(), user);
}
throw new IllegalStateException(e.getMessage());
}
}
@ -1338,8 +1357,17 @@ public class QueueControlImpl extends AbstractControl implements QueueControl {
checkStarted();
clearIO();
try {
try {
queue.pause();
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.pauseQueueSuccess(queue.getName().toString());
}
} catch (Exception e) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.pauseQueueFailure(queue.getName().toString());
}
}
} finally {
blockOnIO();
}
@ -1354,8 +1382,17 @@ public class QueueControlImpl extends AbstractControl implements QueueControl {
checkStarted();
clearIO();
try {
try {
queue.pause(persist);
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.pauseQueueSuccess(queue.getName().toString());
}
} catch (Exception e) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.pauseQueueFailure(queue.getName().toString());
}
}
} finally {
blockOnIO();
}
@ -1368,8 +1405,18 @@ public class QueueControlImpl extends AbstractControl implements QueueControl {
checkStarted();
clearIO();
try {
try {
queue.resume();
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.resumeQueueSuccess(queue.getName().toString());
}
} catch (Exception e) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.resumeQueueFailure(queue.getName().toString());
}
e.printStackTrace();
}
} finally {
blockOnIO();
}
@ -1425,9 +1472,15 @@ public class QueueControlImpl extends AbstractControl implements QueueControl {
CompositeData[] rc = new CompositeData[c.size()];
c.toArray(rc);
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.browseMessagesSuccess(queue.getName().toString(), c.size());
}
return rc;
}
} catch (ActiveMQException e) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.browseMessagesFailure(queue.getName().toString());
}
throw new IllegalStateException(e.getMessage());
} finally {
blockOnIO();
@ -1467,9 +1520,15 @@ public class QueueControlImpl extends AbstractControl implements QueueControl {
CompositeData[] rc = new CompositeData[c.size()];
c.toArray(rc);
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.browseMessagesSuccess(queue.getName().toString(), currentPageSize);
}
return rc;
}
} catch (ActiveMQException e) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.browseMessagesFailure(queue.getName().toString());
}
throw new IllegalStateException(e.getMessage());
} finally {
blockOnIO();

View File

@ -95,6 +95,7 @@ import org.apache.activemq.artemis.core.server.BindingQueryResult;
import org.apache.activemq.artemis.core.server.LargeServerMessage;
import org.apache.activemq.artemis.core.server.QueueQueryResult;
import org.apache.activemq.artemis.core.server.ServerSession;
import org.apache.activemq.artemis.logs.AuditLogger;
import org.apache.activemq.artemis.spi.core.protocol.EmbedMessageUtil;
import org.apache.activemq.artemis.spi.core.remoting.Connection;
import org.apache.activemq.artemis.utils.SimpleFuture;
@ -264,6 +265,10 @@ public class ServerSessionPacketHandler implements ChannelHandler {
if (logger.isTraceEnabled()) {
logger.trace("ServerSessionPacketHandler::handlePacket," + packet);
}
if (AuditLogger.isAnyLoggingEnabled()) {
AuditLogger.setRemoteAddress(remotingConnection.getRemoteAddress());
AuditLogger.setCurrentCaller(remotingConnection.getAuditSubject());
}
final byte type = packet.getType();
switch (type) {
case SESS_SEND: {

View File

@ -48,6 +48,7 @@ import org.apache.activemq.artemis.core.server.ServerProducer;
import org.apache.activemq.artemis.core.server.ServerSession;
import org.apache.activemq.artemis.core.server.impl.ServerProducerImpl;
import org.apache.activemq.artemis.core.version.Version;
import org.apache.activemq.artemis.logs.AuditLogger;
import org.jboss.logging.Logger;
/**
@ -82,6 +83,11 @@ public class ActiveMQPacketHandler implements ChannelHandler {
public void handlePacket(final Packet packet) {
byte type = packet.getType();
if (AuditLogger.isAnyLoggingEnabled()) {
AuditLogger.setRemoteAddress(connection.getRemoteAddress());
AuditLogger.setCurrentCaller(connection.getAuditSubject());
}
switch (type) {
case PacketImpl.CREATESESSION: {
CreateSessionMessage request = (CreateSessionMessage) packet;

View File

@ -61,6 +61,7 @@ import org.apache.activemq.artemis.core.server.management.ManagementService;
import org.apache.activemq.artemis.core.server.management.Notification;
import org.apache.activemq.artemis.core.transaction.Transaction;
import org.apache.activemq.artemis.core.transaction.impl.TransactionImpl;
import org.apache.activemq.artemis.logs.AuditLogger;
import org.apache.activemq.artemis.spi.core.protocol.SessionCallback;
import org.apache.activemq.artemis.spi.core.remoting.ReadyListener;
import org.apache.activemq.artemis.utils.FutureLatch;
@ -489,6 +490,9 @@ public class ServerConsumerImpl implements ServerConsumer, ReadyListener {
try {
Message message = reference.getMessage();
if (AuditLogger.isMessageEnabled()) {
AuditLogger.coreConsumeMessage(getQueueName().toString());
}
if (server.hasBrokerMessagePlugins()) {
server.callBrokerMessagePlugins(plugin -> plugin.beforeDeliver(this, reference));
}

View File

@ -1749,7 +1749,7 @@ public class ServerSessionImpl implements ServerSession, FailureListener {
boolean noAutoCreateQueue,
RoutingContext routingContext) throws Exception {
if (AuditLogger.isMessageEnabled()) {
AuditLogger.coreSendMessage(this, getUsername(), tx, messageParameter, direct, noAutoCreateQueue, routingContext);
AuditLogger.coreSendMessage(getUsername(), routingContext);
}
final Message message = LargeServerMessageImpl.checkLargeMessage(messageParameter, storageManager);

View File

@ -16,6 +16,8 @@
*/
package org.apache.activemq.artemis.core.server.management;
import org.apache.activemq.artemis.logs.AuditLogger;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@ -57,6 +59,16 @@ public class ArtemisMBeanServerBuilder extends MBeanServerBuilder {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//if this is invoked via jolokia the address will be set by the filter
//if not we can deduct it from RMI or it must be internal
if (AuditLogger.isAnyLoggingEnabled() && AuditLogger.getRemoteAddress() == null) {
String name = Thread.currentThread().getName();
String url = "internal";
if (name.startsWith("RMI TCP Connection")) {
url = name.substring(name.indexOf('-') + 1);
}
AuditLogger.setRemoteAddress(url);
}
if (guarded.contains(method.getName())) {
if (ArtemisMBeanServerBuilder.guard == null) {
throw new IllegalStateException("ArtemisMBeanServerBuilder not initialized");

View File

@ -17,6 +17,8 @@
package org.apache.activemq.artemis.core.server.management;
import org.apache.activemq.artemis.logs.AuditLogger;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.JMException;
@ -121,6 +123,9 @@ public class ArtemisMBeanServerGuard implements InvocationHandler {
if (currentUserHasRole(role))
return;
}
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.objectInvokedFailure(objectName, operationName);
}
throw new SecurityException("Insufficient roles/credentials for operation");
}

View File

@ -16,6 +16,8 @@
*/
package org.apache.activemq.artemis.core.server.management;
import org.apache.activemq.artemis.logs.AuditLogger;
import javax.management.remote.JMXAuthenticator;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
@ -41,8 +43,9 @@ public class JaasAuthenticator implements JMXAuthenticator {
@Override
public Subject authenticate(final Object credentials) throws SecurityException {
try {
Subject subject = new Subject();
try {
LoginContext loginContext = new LoginContext(realm, subject, new CallbackHandler() {
@Override
@ -70,8 +73,14 @@ public class JaasAuthenticator implements JMXAuthenticator {
}
});
loginContext.login();
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.userSuccesfullyLoggedInAudit(subject);
}
return subject;
} catch (LoginException e) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.userFailedLoggedInAudit(subject, e.getMessage());
}
throw new SecurityException("Authentication failed", e);
}
}

View File

@ -30,6 +30,7 @@ import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration;
import org.apache.activemq.artemis.core.security.CheckType;
import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.apache.activemq.artemis.logs.AuditLogger;
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
import org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler;
import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
@ -193,7 +194,20 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager4 {
} else {
lc = new LoginContext(configurationName, null, new JaasCallbackHandler(user, password, remotingConnection), configuration);
}
try {
lc.login();
if (AuditLogger.isAnyLoggingEnabled() && remotingConnection != null) {
remotingConnection.setAuditSubject(lc.getSubject());
}
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.userSuccesfullyLoggedInAudit(lc.getSubject());
}
} catch (LoginException e) {
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.userFailedLoggedInAudit(lc.getSubject(), e.getMessage());
}
throw e;
}
return lc.getSubject();
} finally {
if (thisLoader != currentLoader) {

View File

@ -0,0 +1,38 @@
/*
* 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.spi.core.security.jaas;
import org.apache.activemq.artemis.logs.AuditLogger;
import javax.security.auth.Subject;
import javax.security.auth.spi.LoginModule;
/*
* This is only to support auditlogging
* */
public interface AuditLoginModule extends LoginModule {
/*
* We need this because if authentication fails at the web layer then there is no way to access the unauthenticated
* subject as it is removed and the session destroyed and never gets as far as the broker
* */
default void registerFailureForAudit(String name) {
Subject subject = new Subject();
subject.getPrincipals().add(new UserPrincipal(name));
AuditLogger.setCurrentCaller(subject);
}
}

View File

@ -22,7 +22,6 @@ import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import javax.security.cert.X509Certificate;
import java.io.IOException;
import java.security.Principal;
@ -37,7 +36,7 @@ import org.jboss.logging.Logger;
* Allows for subclasses to define methods used to verify user certificates and
* find user roles. Uses CertificateCallbacks to retrieve certificates.
*/
public abstract class CertificateLoginModule extends PropertiesLoader implements LoginModule {
public abstract class CertificateLoginModule extends PropertiesLoader implements AuditLoginModule {
private static final Logger logger = Logger.getLogger(CertificateLoginModule.class);
@ -116,6 +115,7 @@ public abstract class CertificateLoginModule extends PropertiesLoader implements
*/
@Override
public boolean abort() throws LoginException {
registerFailureForAudit(username);
clear();
if (debug) {

View File

@ -21,7 +21,6 @@ import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import javax.security.cert.X509Certificate;
import java.io.IOException;
import java.security.Principal;
@ -35,7 +34,7 @@ import org.jboss.logging.Logger;
/**
* A LoginModule that propagates TLS certificates subject DN as a UserPrincipal.
*/
public class ExternalCertificateLoginModule implements LoginModule {
public class ExternalCertificateLoginModule implements AuditLoginModule {
private static final Logger logger = Logger.getLogger(ExternalCertificateLoginModule.class);
@ -90,6 +89,7 @@ public class ExternalCertificateLoginModule implements LoginModule {
@Override
public boolean abort() throws LoginException {
registerFailureForAudit(userName);
clear();
logger.debug("abort");
return true;

View File

@ -22,7 +22,6 @@ import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import java.io.IOException;
import java.security.Principal;
import java.util.HashSet;
@ -37,7 +36,7 @@ import org.jboss.logging.Logger;
* Useful for unauthenticated communication channels being used in the
* same broker as authenticated ones.
*/
public class GuestLoginModule implements LoginModule {
public class GuestLoginModule implements AuditLoginModule {
private static final Logger logger = Logger.getLogger(GuestLoginModule.class);
@ -114,7 +113,7 @@ public class GuestLoginModule implements LoginModule {
@Override
public boolean abort() throws LoginException {
registerFailureForAudit(GUEST_USER);
if (debug) {
logger.debug("abort");
}

View File

@ -24,7 +24,6 @@ import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import java.io.IOException;
import java.security.Principal;
import java.util.HashSet;
@ -35,7 +34,7 @@ import java.util.Set;
import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration;
import org.jboss.logging.Logger;
public class InVMLoginModule implements LoginModule {
public class InVMLoginModule implements AuditLoginModule {
private static final Logger logger = Logger.getLogger(InVMLoginModule.class);
@ -126,6 +125,7 @@ public class InVMLoginModule implements LoginModule {
@Override
public boolean abort() throws LoginException {
registerFailureForAudit(user);
clear();
logger.debug("abort");

View File

@ -23,7 +23,6 @@ import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import java.io.IOException;
import java.security.Principal;
import java.util.LinkedList;
@ -33,7 +32,7 @@ import java.util.Map;
/**
* populate a subject with kerberos credential from the handler
*/
public class Krb5LoginModule implements LoginModule {
public class Krb5LoginModule implements AuditLoginModule {
private static final Logger logger = Logger.getLogger(Krb5LoginModule.class);
@ -41,6 +40,7 @@ public class Krb5LoginModule implements LoginModule {
private final List<Principal> principals = new LinkedList<>();
private CallbackHandler callbackHandler;
private boolean loginSucceeded;
private Principal principal;
@Override
public void initialize(Subject subject,
@ -58,7 +58,7 @@ public class Krb5LoginModule implements LoginModule {
callbacks[0] = new Krb5Callback();
try {
callbackHandler.handle(callbacks);
Principal principal = ((Krb5Callback)callbacks[0]).getPeerPrincipal();
principal = ((Krb5Callback)callbacks[0]).getPeerPrincipal();
if (principal != null) {
principals.add(principal);
}
@ -91,6 +91,7 @@ public class Krb5LoginModule implements LoginModule {
@Override
public boolean abort() throws LoginException {
registerFailureForAudit(principal != null ? principal.getName() : null);
clear();
logger.debug("abort");
@ -99,6 +100,7 @@ public class Krb5LoginModule implements LoginModule {
}
private void clear() {
principal = null;
loginSucceeded = false;
}

View File

@ -41,7 +41,6 @@ import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@ -62,7 +61,7 @@ import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
import org.jboss.logging.Logger;
public class LDAPLoginModule implements LoginModule {
public class LDAPLoginModule implements AuditLoginModule {
private static final Logger logger = Logger.getLogger(LDAPLoginModule.class);
@ -243,6 +242,7 @@ public class LDAPLoginModule implements LoginModule {
@Override
public boolean abort() throws LoginException {
registerFailureForAudit(username);
clear();
return true;
}

View File

@ -24,7 +24,6 @@ import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import java.io.IOException;
import java.security.Principal;
import java.util.HashSet;
@ -36,7 +35,7 @@ import org.apache.activemq.artemis.utils.HashProcessor;
import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
import org.jboss.logging.Logger;
public class PropertiesLoginModule extends PropertiesLoader implements LoginModule {
public class PropertiesLoginModule extends PropertiesLoader implements AuditLoginModule {
private static final Logger logger = Logger.getLogger(PropertiesLoginModule.class);
@ -144,6 +143,7 @@ public class PropertiesLoginModule extends PropertiesLoader implements LoginModu
@Override
public boolean abort() throws LoginException {
registerFailureForAudit(user);
clear();
if (debug) {

View File

@ -0,0 +1,59 @@
/*
* 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.component;
import org.apache.activemq.artemis.logs.AuditLogger;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import javax.security.auth.Subject;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/*
* This filter intercepts the login and audits its results
* */
public class AuthenticationFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(servletRequest, servletResponse);
if (AuditLogger.isAnyLoggingEnabled()) {
int status = ((Response) servletResponse).getStatus();
//status 200 means that the user has been authenticated, anything else must be a failure
if (status == 200) {
HttpSession session = ((Request) servletRequest).getSession();
AuditLogger.userSuccesfullyLoggedInAudit(session != null ? (Subject) session.getAttribute("subject") : null);
} else {
AuditLogger.userFailedLoggedInAudit("" + status);
}
}
}
@Override
public void destroy() {
}
}

View File

@ -0,0 +1,69 @@
/*
* 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.component;
import org.apache.activemq.artemis.logs.AuditLogger;
import org.eclipse.jetty.server.Request;
import javax.security.auth.Subject;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/*
* This intercepts all calls made via jolokia
* */
public class JolokiaFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
/*
* this is the only place we can catch the remote address of the calling console client thru Jolokia.
* We set the address on the calling thread which will end up in JMX audit logging
* */
if (AuditLogger.isAnyLoggingEnabled() && servletRequest != null) {
String remoteHost = servletRequest.getRemoteHost();
AuditLogger.setRemoteAddress(remoteHost + ":" + servletRequest.getRemotePort());
}
filterChain.doFilter(servletRequest, servletResponse);
/*
* This is the only place we can get access to the authenticated subject on invocations after the login has happened.
* we set the subject for audit logging
* */
if (AuditLogger.isAnyLoggingEnabled()) {
try {
HttpSession session = ((Request) servletRequest).getSession();
Subject subject = (Subject) session.getAttribute("subject");
AuditLogger.setCurrentCaller(subject);
} catch (Throwable e) {
//best effort
}
}
}
@Override
public void destroy() {
}
}

View File

@ -22,6 +22,7 @@ import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
@ -44,10 +45,13 @@ import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.webapp.WebAppContext;
import org.jboss.logging.Logger;
import javax.servlet.DispatcherType;
public class WebServerComponent implements ExternalComponent {
private static final Logger logger = Logger.getLogger(WebServerComponent.class);
@ -303,6 +307,9 @@ public class WebServerComponent implements ExternalComponent {
} else {
webapp.setContextPath("/" + url);
}
//add the filters needed for audit logging
webapp.addFilter(new FilterHolder(JolokiaFilter.class), "/*", EnumSet.of(DispatcherType.INCLUDE, DispatcherType.REQUEST));
webapp.addFilter(new FilterHolder(AuthenticationFilter.class), "/auth/login/*", EnumSet.of(DispatcherType.REQUEST));
webapp.setWar(warDirectory.resolve(warFile).toString());
handlers.addHandler(webapp);

View File

@ -5,7 +5,7 @@ configurable via the `logging.properties` file found in the
configuration directories. This is configured by Default to log to both
the console and to a file.
There are 8 loggers available which are as follows:
There are 9 loggers available which are as follows:
Logger | Description
---|---
@ -16,6 +16,7 @@ org.apache.activemq.artemis.journal|Logs Journal calls
org.apache.activemq.artemis.jms|Logs JMS calls
org.apache.activemq.artemis.integration.bootstrap|Logs bootstrap calls
org.apache.activemq.audit.base|audit log. Disabled by default
org.apache.activemq.audit.resource|resource audit log. Disabled by default
org.apache.activemq.audit.message|message audit log. Disabled by default
@ -88,17 +89,25 @@ formatter.PATTERN.properties=pattern
formatter.PATTERN.pattern=%d{HH:mm:ss,SSS} %-5p [%c] %s%E%n
```
## Configuring Audit Log
## Configuring Audit Logging
The 2 audit loggers can be enabled to record some important operations like
create/delete queues. By default this logger is disabled. The configuration
(logging.properties) for audit log is like this by default:
There are 3 audit loggers that can be enabled separately and audit sifferent types of events, these are:
1. Base logger: This is a highly verbose logger that will capture most events that occur on JMX beans
2. Resource logger: This logs creation, updates and deletion of resources such as Addresses and Queues and also authentication, the main purpose of this is to track console activity and access to broker.
3. Message logger: this logs message production and consumption of messages and will have a potentially negatibve affect on performance
These are disabled by default in the logging.properties configuration file:
```$xslt
logger.org.apache.activemq.audit.base.level=ERROR
logger.org.apache.activemq.audit.base.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.base.useParentHandlers=false
logger.org.apache.activemq.audit.resource.level=ERROR
logger.org.apache.activemq.audit.resource.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.resource.useParentHandlers=false
logger.org.apache.activemq.audit.message.level=ERROR
logger.org.apache.activemq.audit.message.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.message.useParentHandlers=false
@ -110,21 +119,33 @@ To enable the audit log change the above level to INFO, like this:
logger.org.apache.activemq.audit.base.level=INFO
logger.org.apache.activemq.audit.base.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.base.useParentHandlers=false
logger.org.apache.activemq.audit.message.level=INFO
logger.org.apache.activemq.audit.message.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.message.useParentHandlers=false
...
```
The 2 audit loggers can be disable/enable separately. The second logger
(org.apache.activemq.audit.message) audits messages in 'hot path'
(code path that is very sensitive to performance, e.g. sending messages).
Turn on this audit logger may affect the performance.
The 3 audit loggers can be disable/enabled separately.
Once enabled, all audit records are written into a separate log
file (by default audit.log).
### Logging the clients remote address
It is possible to configure the audit loggers to log the remote address of any calling clients either through normal
clients or through the console and JMX. This is configured by enabling a specific login module in the login config file.
```$xslt
org.apache.activemq.artemis.spi.core.security.jaas.AuditLoginModule optional
debug=false;
```
> **Note:**
>
> This needs to be the first entry in the login.config file
> **Note:**
>
> This login module does no authentication, it is used only to catch client information through which ever path a client takes
## Use Custom Handlers
To use a different handler than the built-in ones, you either pick one from

View File

@ -0,0 +1,81 @@
#
# 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.
#
# Additional logger names to configure (root logger is always configured)
# Root logger option
loggers=org.eclipse.jetty,org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.journal,org.apache.activemq.artemis.jms.server,org.apache.activemq.artemis.integration.bootstrap,org.apache.activemq.audit.base,org.apache.activemq.audit.message,org.apache.activemq.audit.resource
# Root logger level
logger.level=INFO
# ActiveMQ Artemis logger levels
logger.org.apache.activemq.artemis.core.server.level=INFO
logger.org.apache.activemq.artemis.journal.level=INFO
logger.org.apache.activemq.artemis.utils.level=INFO
logger.org.apache.activemq.artemis.jms.level=INFO
logger.org.apache.activemq.artemis.integration.bootstrap.level=INFO
logger.org.eclipse.jetty.level=WARN
# Root logger handlers
logger.handlers=FILE,CONSOLE
# to enable audit change the level to INFO
logger.org.apache.activemq.audit.base.level=INFO
logger.org.apache.activemq.audit.base.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.base.useParentHandlers=false
logger.org.apache.activemq.audit.resource.level=INFO
logger.org.apache.activemq.audit.resource.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.resource.useParentHandlers=false
logger.org.apache.activemq.audit.message.level=INFO
logger.org.apache.activemq.audit.message.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.message.useParentHandlers=false
# Console handler configuration
handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler
handler.CONSOLE.properties=autoFlush
handler.CONSOLE.level=DEBUG
handler.CONSOLE.autoFlush=true
handler.CONSOLE.formatter=PATTERN
# File handler configuration
handler.FILE=org.jboss.logmanager.handlers.PeriodicRotatingFileHandler
handler.FILE.level=DEBUG
handler.FILE.properties=suffix,append,autoFlush,fileName
handler.FILE.suffix=.yyyy-MM-dd
handler.FILE.append=true
handler.FILE.autoFlush=true
handler.FILE.fileName=${artemis.instance}/log/artemis.log
handler.FILE.formatter=PATTERN
# Formatter pattern configuration
formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter
formatter.PATTERN.properties=pattern
formatter.PATTERN.pattern=%d %-5p [%c] %s%E%n
#Audit logger
handler.AUDIT_FILE=org.jboss.logmanager.handlers.PeriodicRotatingFileHandler
handler.AUDIT_FILE.level=INFO
handler.AUDIT_FILE.properties=suffix,append,autoFlush,fileName
handler.AUDIT_FILE.suffix=.yyyy-MM-dd
handler.AUDIT_FILE.append=true
handler.AUDIT_FILE.autoFlush=true
handler.AUDIT_FILE.fileName=${artemis.instance}/log/audit.log
handler.AUDIT_FILE.formatter=AUDIT_PATTERN
formatter.AUDIT_PATTERN=org.jboss.logmanager.formatters.PatternFormatter
formatter.AUDIT_PATTERN.properties=pattern
formatter.AUDIT_PATTERN.pattern=%d [AUDIT](%t) %s%E%n

View File

@ -267,6 +267,48 @@
</args>
</configuration>
</execution>
<execution>
<phase>test-compile</phase>
<id>create-createAuditLogging</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<configuration>${basedir}/target/classes/servers/audit-logging</configuration>
<noWeb>false</noWeb>
<allowAnonymous>true</allowAnonymous>
<user>admin</user>
<password>admin</password>
<instance>${basedir}/target/audit-logging</instance>
<args>
<!-- this is needed to run the server remotely -->
<arg>--java-options</arg>
<arg>-Djava.rmi.server.hostname=localhost</arg>
<!--<arg>-Djava.rmi.server.hostname=localhost -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005</arg>-->
</args>
</configuration>
</execution>
<execution>
<phase>test-compile</phase>
<id>create-createAuditLogging2</id>
<goals>
<goal>create</goal>
</goals>
<configuration>
<configuration>${basedir}/target/classes/servers/audit-logging2</configuration>
<noWeb>false</noWeb>
<allowAnonymous>true</allowAnonymous>
<user>admin</user>
<password>admin</password>
<instance>${basedir}/target/audit-logging2</instance>
<args>
<!-- this is needed to run the server remotely -->
<arg>--java-options</arg>
<arg>-Djava.rmi.server.hostname=localhost</arg>
<!--<arg>-Djava.rmi.server.hostname=localhost -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005</arg>-->
</args>
</configuration>
</execution>
<execution>
<phase>test-compile</phase>
<id>create-jmx-failback</id>

View File

@ -0,0 +1,190 @@
<?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"
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 or NIO
-->
<journal-type>NIO</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>-1</journal-pool-files>
<message-expiry-scan-period>1000</message-expiry-scan-period>
<!--
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>
<!-- 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 -->
<!-- useKQueue means: it will use Netty kqueue if you are on a system (MacOS) 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 -->
<!-- Acceptor for every supported protocol -->
<acceptor name="artemis">tcp://localhost:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;useKQueue;amqpCredits=1000;amqpLowCredits=300</acceptor>
<!-- AMQP Acceptor. Listens on default AMQP port for AMQP traffic.-->
<acceptor name="amqp">tcp://localhost:5672?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=AMQP;useEpoll=true;useKQueue=true;amqpCredits=1000;amqpLowCredits=300</acceptor>
<!-- STOMP Acceptor. -->
<acceptor name="stomp">tcp://localhost:61613?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=STOMP;useEpoll=true;useKQueue=true</acceptor>
<!-- HornetQ Compatibility Acceptor. Enables HornetQ Core and STOMP for legacy HornetQ clients. -->
<acceptor name="hornetq">tcp://localhost:5445?protocols=HORNETQ,STOMP;useEpoll=true;useKQueue=true</acceptor>
<!-- MQTT Acceptor -->
<acceptor name="mqtt">tcp://localhost:1883?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=MQTT;useEpoll=true;useKQueue=true</acceptor>
</acceptors>
<security-settings>
<security-setting match="#">
<permission type="createNonDurableQueue" roles="guest"/>
<permission type="deleteNonDurableQueue" roles="guest"/>
<permission type="createDurableQueue" roles="guest"/>
<permission type="deleteDurableQueue" roles="guest"/>
<permission type="createAddress" roles="guest"/>
<permission type="deleteAddress" roles="guest"/>
<permission type="consume" roles="guest"/>
<permission type="browse" roles="guest"/>
<permission type="send" roles="guest"/>
<!-- we need this otherwise ./artemis data imp wouldn't work -->
<permission type="manage" roles="guest"/>
</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="myQ">
<anycast>
<queue name="myQ"/>
</anycast>
</address>
<address name="DLQ">
<anycast>
<queue name="DLQ" />
</anycast>
</address>
<address name="ExpiryQueue">
<anycast>
<queue name="ExpiryQueue" />
</anycast>
</address>
</addresses>
</core>
</configuration>

View File

@ -17,7 +17,7 @@
# Additional logger names to configure (root logger is always configured)
# Root logger option
loggers=org.eclipse.jetty,org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.journal,org.apache.activemq.artemis.jms.server,org.apache.activemq.artemis.integration.bootstrap,org.apache.activemq.audit.base,org.apache.activemq.audit.message
loggers=org.eclipse.jetty,org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.journal,org.apache.activemq.artemis.jms.server,org.apache.activemq.artemis.integration.bootstrap,org.apache.activemq.audit.base,org.apache.activemq.audit.message,org.apache.activemq.audit.resource
# Root logger level
logger.level=INFO
@ -31,13 +31,17 @@ logger.org.eclipse.jetty.level=WARN
# Root logger handlers
logger.handlers=FILE,CONSOLE
logger.org.apache.activemq.audit.base.level=INFO
logger.org.apache.activemq.audit.base.level=ERROR
logger.org.apache.activemq.audit.base.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.base.useParentHandlers=false
logger.org.apache.activemq.audit.message.level=ERROR
logger.org.apache.activemq.audit.message.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.message.useParentHandlers=false
logger.org.apache.activemq.audit.message.useParentHandlers=
logger.org.apache.activemq.audit.resource.level=INFO
logger.org.apache.activemq.audit.resource.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.resource.useParentHandlers=false
# Console handler configuration
handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler
@ -53,7 +57,7 @@ handler.FILE.properties=suffix,append,autoFlush,fileName
handler.FILE.suffix=.yyyy-MM-dd
handler.FILE.append=true
handler.FILE.autoFlush=true
handler.FILE.fileName=target/artemis.log
handler.FILE.fileName=../log/artemis.log
handler.FILE.formatter=PATTERN
# Formatter pattern configuration
@ -68,7 +72,7 @@ handler.AUDIT_FILE.properties=suffix,append,autoFlush,fileName
handler.AUDIT_FILE.suffix=.yyyy-MM-dd
handler.AUDIT_FILE.append=true
handler.AUDIT_FILE.autoFlush=true
handler.AUDIT_FILE.fileName=target/audit.log
handler.AUDIT_FILE.fileName=../log/audit.log
handler.AUDIT_FILE.formatter=AUDIT_PATTERN
formatter.AUDIT_PATTERN=org.jboss.logmanager.formatters.PatternFormatter

View File

@ -0,0 +1,29 @@
/*
* 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.
*/
activemq {
org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule sufficient
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="guest";
};

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ 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.
-->
<management-context xmlns="http://activemq.org/schema">
<connector connector-port="10099" connector-host="localhost"/>
<authorisation>
<whitelist>
<entry domain="hawtio"/>
</whitelist>
<default-access>
<access method="list*" roles="guest"/>
<access method="get*" roles="guest"/>
<access method="is*" roles="guest"/>
<access method="set*" roles="guest"/>
<access method="*" roles="guest"/>
</default-access>
<role-access>
<match domain="org.apache.activemq.artemis">
<access method="list*" roles="guest"/>
<access method="get*" roles="guest"/>
<access method="is*" roles="guest"/>
<access method="set*" roles="guest"/>
<access method="*" roles="guest"/>
</match>
<!--example of how to configure a specific object-->
<!--<match domain="org.apache.activemq.artemis" key="subcomponent=queues">
<access method="list*" roles="view,update,amq"/>
<access method="get*" roles="view,update,amq"/>
<access method="is*" roles="view,update,amq"/>
<access method="set*" roles="update,amq"/>
<access method="*" roles="amq"/>
</match>-->
</role-access>
</authorisation>
</management-context>

View File

@ -0,0 +1,190 @@
<?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"
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 or NIO
-->
<journal-type>NIO</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>-1</journal-pool-files>
<message-expiry-scan-period>1000</message-expiry-scan-period>
<!--
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>
<!-- 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 -->
<!-- useKQueue means: it will use Netty kqueue if you are on a system (MacOS) 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 -->
<!-- Acceptor for every supported protocol -->
<acceptor name="artemis">tcp://localhost:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;useKQueue;amqpCredits=1000;amqpLowCredits=300</acceptor>
<!-- AMQP Acceptor. Listens on default AMQP port for AMQP traffic.-->
<acceptor name="amqp">tcp://localhost:5672?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=AMQP;useEpoll=true;useKQueue=true;amqpCredits=1000;amqpLowCredits=300</acceptor>
<!-- STOMP Acceptor. -->
<acceptor name="stomp">tcp://localhost:61613?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=STOMP;useEpoll=true;useKQueue=true</acceptor>
<!-- HornetQ Compatibility Acceptor. Enables HornetQ Core and STOMP for legacy HornetQ clients. -->
<acceptor name="hornetq">tcp://localhost:5445?protocols=HORNETQ,STOMP;useEpoll=true;useKQueue=true</acceptor>
<!-- MQTT Acceptor -->
<acceptor name="mqtt">tcp://localhost:1883?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=MQTT;useEpoll=true;useKQueue=true</acceptor>
</acceptors>
<security-settings>
<security-setting match="#">
<permission type="createNonDurableQueue" roles="guest"/>
<permission type="deleteNonDurableQueue" roles=""/>
<permission type="createDurableQueue" roles="guest"/>
<permission type="deleteDurableQueue" roles="guest"/>
<permission type="createAddress" roles="guest"/>
<permission type="deleteAddress" roles="guest"/>
<permission type="consume" roles="guest"/>
<permission type="browse" roles="guest"/>
<permission type="send" roles="guest"/>
<!-- we need this otherwise ./artemis data imp wouldn't work -->
<permission type="manage" roles="guest"/>
</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="myQ">
<anycast>
<queue name="myQ"/>
</anycast>
</address>
<address name="DLQ">
<anycast>
<queue name="DLQ" />
</anycast>
</address>
<address name="ExpiryQueue">
<anycast>
<queue name="ExpiryQueue" />
</anycast>
</address>
</addresses>
</core>
</configuration>

View File

@ -17,7 +17,7 @@
# Additional logger names to configure (root logger is always configured)
# Root logger option
loggers=org.eclipse.jetty,org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.journal,org.apache.activemq.artemis.jms.server,org.apache.activemq.artemis.integration.bootstrap,org.apache.activemq.audit.base,org.apache.activemq.audit.message
loggers=org.eclipse.jetty,org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.journal,org.apache.activemq.artemis.jms.server,org.apache.activemq.artemis.integration.bootstrap,org.apache.activemq.audit.base,org.apache.activemq.audit.message,org.apache.activemq.audit.resource
# Root logger level
logger.level=INFO
@ -37,7 +37,11 @@ logger.org.apache.activemq.audit.base.useParentHandlers=false
logger.org.apache.activemq.audit.message.level=INFO
logger.org.apache.activemq.audit.message.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.message.useParentHandlers=false
logger.org.apache.activemq.audit.message.useParentHandlers=
logger.org.apache.activemq.audit.resource.level=ERROR
logger.org.apache.activemq.audit.resource.handlers=AUDIT_FILE
logger.org.apache.activemq.audit.resource.useParentHandlers=false
# Console handler configuration
handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler
@ -53,7 +57,7 @@ handler.FILE.properties=suffix,append,autoFlush,fileName
handler.FILE.suffix=.yyyy-MM-dd
handler.FILE.append=true
handler.FILE.autoFlush=true
handler.FILE.fileName=target/artemis.log
handler.FILE.fileName=../log/artemis.log
handler.FILE.formatter=PATTERN
# Formatter pattern configuration
@ -62,13 +66,13 @@ formatter.PATTERN.properties=pattern
formatter.PATTERN.pattern=%d %-5p [%c] %s%E%n
#Audit logger
handler.AUDIT_FILE.level=DEBUG
handler.AUDIT_FILE.level=INFO
handler.AUDIT_FILE=org.jboss.logmanager.handlers.PeriodicRotatingFileHandler
handler.AUDIT_FILE.properties=suffix,append,autoFlush,fileName
handler.AUDIT_FILE.suffix=.yyyy-MM-dd
handler.AUDIT_FILE.append=true
handler.AUDIT_FILE.autoFlush=true
handler.AUDIT_FILE.fileName=target/audit.log
handler.AUDIT_FILE.fileName=../log/audit.log
handler.AUDIT_FILE.formatter=AUDIT_PATTERN
formatter.AUDIT_PATTERN=org.jboss.logmanager.formatters.PatternFormatter

View File

@ -0,0 +1,29 @@
/*
* 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.
*/
activemq {
org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule sufficient
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="guest";
};

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ 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.
-->
<management-context xmlns="http://activemq.org/schema">
<connector connector-port="10099" connector-host="localhost"/>
<authorisation>
<whitelist>
<entry domain="hawtio"/>
</whitelist>
<default-access>
<access method="list*" roles="guest"/>
<access method="get*" roles="guest"/>
<access method="is*" roles="guest"/>
<access method="set*" roles="guest"/>
<access method="*" roles="guest"/>
</default-access>
<role-access>
<match domain="org.apache.activemq.artemis">
<access method="list*" roles="guest"/>
<access method="get*" roles="guest"/>
<access method="is*" roles="guest"/>
<access method="set*" roles="guest"/>
<access method="*" roles="guest"/>
</match>
<!--example of how to configure a specific object-->
<!--<match domain="org.apache.activemq.artemis" key="subcomponent=queues">
<access method="list*" roles="view,update,amq"/>
<access method="get*" roles="view,update,amq"/>
<access method="is*" roles="view,update,amq"/>
<access method="set*" roles="update,amq"/>
<access method="*" roles="amq"/>
</match>-->
</role-access>
</authorisation>
</management-context>

View File

@ -0,0 +1,166 @@
/*
* 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.tests.smoke.logging;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.client.ClientProducer;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl;
import org.apache.activemq.artemis.api.core.management.ObjectNameBuilder;
import org.apache.activemq.artemis.api.core.management.QueueControl;
import org.apache.activemq.artemis.tests.smoke.common.SmokeTestBase;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.openmbean.CompositeData;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.HashMap;
public class AuditLoggerResourceTest extends SmokeTestBase {
private static final File auditLog = new File("target/audit-logging/log/audit.log");
private static final String JMX_SERVER_HOSTNAME = "localhost";
private static final int JMX_SERVER_PORT = 10099;
public static final String SERVER_NAME = "audit-logging";
@Before
public void before() throws Exception {
cleanupData(SERVER_NAME);
disableCheckThread();
startServer(SERVER_NAME, 0, 30000);
emptyLogFile();
}
private void emptyLogFile() throws Exception {
if (auditLog.exists()) {
try (PrintWriter writer = new PrintWriter(new FileWriter(auditLog))) {
writer.print("");
}
}
}
@Test
public void testAuditResourceLog() throws Exception {
HashMap environment = new HashMap();
String[] credentials = new String[] {"admin", "admin"};
environment.put(JMXConnector.CREDENTIALS, credentials);
// 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 = null;
try {
jmxConnector = JMXConnectorFactory.connect(url, environment);
System.out.println("Successfully connected to: " + urlString);
} catch (Exception e) {
jmxConnector = null;
e.printStackTrace();
Assert.fail(e.getMessage());
}
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);
ActiveMQServerControl serverControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getActiveMQServerObjectName(), ActiveMQServerControl.class, false);
serverControl.createAddress("auditAddress", "ANYCAST,MULTICAST");
checkAuditLogRecord(true, "successfully created Address:");
serverControl.updateAddress("auditAddress", "ANYCAST");
checkAuditLogRecord(true, "successfully updated Address:");
serverControl.deleteAddress("auditAddress");
checkAuditLogRecord(true, "successfully deleted Address:");
serverControl.createQueue("auditAddress", "auditQueue", "ANYCAST");
checkAuditLogRecord(true, "successfully created Queue:");
serverControl.updateQueue("auditQueue", "ANYCAST", -1, false);
final QueueControl queueControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection,
objectNameBuilder.getQueueObjectName(new SimpleString( "auditAddress"), new SimpleString("auditQueue"), RoutingType.ANYCAST),
QueueControl.class,
false);
checkAuditLogRecord(true, "successfully updated Queue:");
queueControl.removeAllMessages();
checkAuditLogRecord(true, "has removed 0 messages");
queueControl.sendMessage(new HashMap<>(), 0, "foo", true, "admin", "admin");
checkAuditLogRecord(true, "sent message to");
CompositeData[] browse = queueControl.browse();
checkAuditLogRecord(true, "browsed " + browse.length + " messages");
serverControl.destroyQueue("auditQueue");
checkAuditLogRecord(true, "successfully deleted Queue:");
ServerLocator locator = createNettyNonHALocator();
ClientSessionFactory sessionFactory = locator.createSessionFactory();
ClientSession session = sessionFactory.createSession("admin", "admin", false, false, true, false, 0);
ClientProducer producer = session.createProducer("myQ");
producer.send(session.createMessage(true));
locator.close();
} finally {
jmxConnector.close();
}
}
//check the audit log has a line that contains all the values
private void checkAuditLogRecord(boolean exist, String... values) throws Exception {
Assert.assertTrue(auditLog.exists());
boolean hasRecord = false;
try (BufferedReader reader = new BufferedReader(new FileReader(auditLog))) {
String line = reader.readLine();
while (line != null) {
if (line.contains(values[0])) {
boolean hasAll = true;
for (int i = 1; i < values.length; i++) {
if (!line.contains(values[i])) {
hasAll = false;
break;
}
}
if (hasAll) {
hasRecord = true;
System.out.println("audit has it: " + line);
break;
}
}
line = reader.readLine();
}
if (exist) {
Assert.assertTrue(hasRecord);
} else {
Assert.assertFalse(hasRecord);
}
}
}
}

View File

@ -14,114 +14,69 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.integration.management;
package org.apache.activemq.artemis.tests.smoke.logging;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.api.core.QueueConfiguration;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.api.core.client.ClientConsumer;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.api.core.client.ClientProducer;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.api.core.management.AddressControl;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration;
import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.ActiveMQServers;
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
import org.apache.activemq.artemis.spi.core.security.jaas.InVMLoginModule;
import org.apache.activemq.artemis.tests.util.Wait;
import org.apache.activemq.artemis.api.core.management.ObjectNameBuilder;
import org.apache.activemq.artemis.tests.smoke.common.SmokeTestBase;
import org.apache.activemq.artemis.utils.Base64;
import org.apache.activemq.artemis.utils.RandomUtil;
import org.junit.After;
import org.apache.activemq.artemis.utils.Wait;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URI;
import java.util.HashSet;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.UUID;
import java.util.logging.LogManager;
// https://issues.apache.org/jira/browse/ARTEMIS-2730 this test needs to be moved as a SmokeTest
// as it's messing up with logging configurations
@Ignore
public class AuditLoggerTest extends ManagementTestBase {
public class AuditLoggerTest extends SmokeTestBase {
private static final File auditLog = new File("target/audit.log");
private static final File auditLog = new File("target/audit-logging2/log/audit.log");
private static final String JMX_SERVER_HOSTNAME = "localhost";
private static final int JMX_SERVER_PORT = 10099;
public static final String SERVER_NAME = "audit-logging2";
private ActiveMQServer server;
private Configuration conf;
protected ClientSession session;
private ServerLocator locator;
private ClientSessionFactory sf;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
public void before() throws Exception {
cleanupData(SERVER_NAME);
disableCheckThread();
startServer(SERVER_NAME, 0, 30000);
emptyLogFile();
TransportConfiguration connectorConfig = new TransportConfiguration(INVM_CONNECTOR_FACTORY);
conf = createDefaultNettyConfig().setJMXManagementEnabled(true).addConnectorConfiguration(connectorConfig.getName(), connectorConfig);
conf.setSecurityEnabled(true);
SecurityConfiguration securityConfiguration = new SecurityConfiguration();
securityConfiguration.addUser("guest", "guest");
securityConfiguration.addUser("myUser", "myPass");
securityConfiguration.addRole("guest", "guest");
securityConfiguration.addRole("myUser", "guest");
securityConfiguration.setDefaultUser("guest");
ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager(InVMLoginModule.class.getName(), securityConfiguration);
server = addServer(ActiveMQServers.newActiveMQServer(conf, mbeanServer, securityManager, true));
server.start();
HashSet<Role> role = new HashSet<>();
//role guest cannot delete queues
role.add(new Role("guest", true, true, true, false, true, false, true, true, true, true));
server.getSecurityRepository().addMatch("#", role);
locator = createInVMNonHALocator().setBlockOnNonDurableSend(true);
locator = createNonHALocator(true).setBlockOnNonDurableSend(true);
sf = createSessionFactory(locator);
session = sf.createSession("guest", "guest", false, true, false, false, 100);
session.start();
addClientSession(session);
}
@After
@Override
public void tearDown() throws Exception {
super.tearDown();
String originalLoggingConfig = System.getProperty("logging.configuration");
if (originalLoggingConfig != null) {
File file = new File(new URI(originalLoggingConfig));
InputStream inputStream = new FileInputStream(file);
LogManager logManager = LogManager.getLogManager();
try {
logManager.readConfiguration(inputStream);
} catch (IOException e) {
System.out.println("error loading logging conifg");
e.printStackTrace();
}
inputStream.close();
}
}
private void emptyLogFile() throws Exception {
if (auditLog.exists()) {
try (PrintWriter writer = new PrintWriter(new FileWriter(auditLog))) {
@ -132,11 +87,15 @@ public class AuditLoggerTest extends ManagementTestBase {
@Test
public void testAuditLog() throws Exception {
reloadLoggingConfig("audit.logging.properties");
JMXConnector jmxConnector = getJmxConnector();
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);
SimpleString address = RandomUtil.randomSimpleString();
session.createAddress(address, RoutingType.ANYCAST, false);
final AddressControl addressControl = ManagementControlHelper.createAddressControl(address, mbeanServer);
final AddressControl addressControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getAddressObjectName(address), AddressControl.class, false);
Assert.assertEquals(0, addressControl.getQueueNames().length);
session.createQueue(new QueueConfiguration(address).setRoutingType(RoutingType.ANYCAST));
@ -153,62 +112,90 @@ public class AuditLoggerTest extends ManagementTestBase {
address = RandomUtil.randomSimpleString();
session.createAddress(address, RoutingType.ANYCAST, false);
final AddressControl addressControl2 = ManagementControlHelper.createAddressControl(address, mbeanServer);
final AddressControl addressControl2 = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getAddressObjectName(address), AddressControl.class, false);
Assert.assertEquals(1, addressControl.getQueueNames().length);
session.createQueue(new QueueConfiguration(address).setRoutingType(RoutingType.ANYCAST).setDurable(false));
Wait.waitFor(() -> addressControl2.getQueueNames().length == 1);
ClientProducer producer = session.createProducer(address);
producer.send(session.createMessage(true));
Wait.waitFor(() -> addressControl.getMessageCount() == 1);
try {
session.deleteQueue(address);
fail("Deleting queue should get exception");
Assert.fail("Deleting queue should get exception");
} catch (Exception e) {
//ignore
}
checkAuditLogRecord(true, "gets security check failure:", "guest does not have permission='DELETE_NON_DURABLE_QUEUE'");
//hot patch not in log
checkAuditLogRecord(false, "is sending a core message");
checkAuditLogRecord(true, "is sending a core message");
}
private void reloadLoggingConfig(String logFile) {
ClassLoader cl = AuditLoggerTest.class.getClassLoader();
InputStream inputStream = cl.getResourceAsStream(logFile);
LogManager logManager = LogManager.getLogManager();
protected JMXConnector getJmxConnector() throws MalformedURLException {
HashMap environment = new HashMap();
String[] credentials = new String[] {"admin", "admin"};
environment.put(JMXConnector.CREDENTIALS, credentials);
// 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 = null;
try {
logManager.readConfiguration(inputStream);
inputStream.close();
} catch (IOException e) {
System.out.println("error loading logging conifg");
jmxConnector = JMXConnectorFactory.connect(url, environment);
System.out.println("Successfully connected to: " + urlString);
} catch (Exception e) {
jmxConnector = null;
e.printStackTrace();
Assert.fail(e.getMessage());
}
return jmxConnector;
}
@Test
public void testAuditHotLog() throws Exception {
reloadLoggingConfig("audit.logging.hot.properties");
JMXConnector jmxConnector = getJmxConnector();
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);
SimpleString address = RandomUtil.randomSimpleString();
session.createAddress(address, RoutingType.ANYCAST, false);
final AddressControl addressControl = ManagementControlHelper.createAddressControl(address, mbeanServer);
final AddressControl addressControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getAddressObjectName(address), AddressControl.class, false);
Assert.assertEquals(0, addressControl.getQueueNames().length);
session.createQueue(new QueueConfiguration(address).setRoutingType(RoutingType.ANYCAST));
Assert.assertEquals(1, addressControl.getQueueNames().length);
String uniqueStr = Base64.encodeBytes(UUID.randomUUID().toString().getBytes());
addressControl.sendMessage(null, Message.BYTES_TYPE, uniqueStr, false, null, null);
Wait.waitFor(() -> addressControl.getMessageCount() == 1);
Assert.assertEquals(1, addressControl.getMessageCount());
ClientProducer producer = session.createProducer(address);
producer.send(session.createMessage(true));
producer.send(session.createMessage(true));
// addressControl.sendMessage(null, Message.BYTES_TYPE, uniqueStr, false, null, null);
Wait.waitFor(() -> addressControl.getMessageCount() == 2);
Assert.assertEquals(2, addressControl.getMessageCount());
checkAuditLogRecord(true, "sending a core message");
ClientConsumer consumer = session.createConsumer(address);
session.start();
ClientMessage clientMessage = consumer.receiveImmediate();
Assert.assertNotNull(clientMessage);
clientMessage = consumer.receiveImmediate();
Assert.assertNotNull(clientMessage);
checkAuditLogRecord(true, "is consuming a message from");
}
//check the audit log has a line that contains all the values
private void checkAuditLogRecord(boolean exist, String... values) throws Exception {
assertTrue(auditLog.exists());
Assert.assertTrue(auditLog.exists());
boolean hasRecord = false;
try (BufferedReader reader = new BufferedReader(new FileReader(auditLog))) {
String line = reader.readLine();
@ -230,9 +217,9 @@ public class AuditLoggerTest extends ManagementTestBase {
line = reader.readLine();
}
if (exist) {
assertTrue(hasRecord);
Assert.assertTrue(hasRecord);
} else {
assertFalse(hasRecord);
Assert.assertFalse(hasRecord);
}
}
}