From 8a04ee07de8a7c863daf37a07a1176131151caa0 Mon Sep 17 00:00:00 2001 From: Andy Taylor Date: Mon, 13 Jan 2020 11:45:45 +0000 Subject: [PATCH] ARTEMIS-2648 - audit logging improvements https://issues.apache.org/jira/browse/ARTEMIS-2648 --- .../cli/commands/etc/logging.properties | 6 +- .../activemq/artemis/logs/AuditLogger.java | 357 ++++++++++++++++-- .../protocol/AbstractRemotingConnection.java | 11 + .../spi/core/protocol/RemotingConnection.java | 7 + .../protocol/core/impl/ChannelImplTest.java | 10 + .../amqp/broker/AMQPConnectionCallback.java | 2 +- .../amqp/proton/AMQPConnectionContext.java | 5 + .../amqp/proton/handler/EventHandler.java | 3 + .../amqp/proton/handler/ProtonHandler.java | 6 + .../artemis/protocol/amqp/sasl/PlainSASL.java | 7 +- .../broker/AMQPConnectionCallbackTest.java | 3 +- .../core/protocol/mqtt/MQTTConnection.java | 11 + .../protocol/mqtt/MQTTProtocolHandler.java | 5 + .../protocol/openwire/OpenWireConnection.java | 7 +- .../core/protocol/stomp/StompConnection.java | 11 + .../protocol/stomp/StompProtocolManager.java | 4 + .../impl/ActiveMQServerControlImpl.java | 57 ++- .../impl/ManagementRemotingConnection.java | 10 + .../management/impl/QueueControlImpl.java | 69 +++- .../core/ServerSessionPacketHandler.java | 5 + .../core/impl/ActiveMQPacketHandler.java | 6 + .../core/server/impl/ServerConsumerImpl.java | 4 + .../core/server/impl/ServerSessionImpl.java | 2 +- .../management/ArtemisMBeanServerBuilder.java | 12 + .../management/ArtemisMBeanServerGuard.java | 5 + .../server/management/JaasAuthenticator.java | 11 +- .../security/ActiveMQJAASSecurityManager.java | 16 +- .../core/security/jaas/AuditLoginModule.java | 38 ++ .../security/jaas/CertificateLoginModule.java | 4 +- .../jaas/ExternalCertificateLoginModule.java | 4 +- .../core/security/jaas/GuestLoginModule.java | 5 +- .../core/security/jaas/InVMLoginModule.java | 4 +- .../core/security/jaas/Krb5LoginModule.java | 8 +- .../core/security/jaas/LDAPLoginModule.java | 4 +- .../security/jaas/PropertiesLoginModule.java | 4 +- .../component/AuthenticationFilter.java | 59 +++ .../artemis/component/JolokiaFilter.java | 69 ++++ .../artemis/component/WebServerComponent.java | 7 + docs/user-manual/en/logging.md | 47 ++- .../activemq/server0/logging.properties | 81 ++++ tests/smoke-tests/pom.xml | 42 +++ .../servers/audit-logging/broker.xml | 190 ++++++++++ .../servers/audit-logging/logging.properties} | 14 +- .../servers/audit-logging/login.config | 29 ++ .../servers/audit-logging/management.xml | 49 +++ .../servers/audit-logging2/broker.xml | 190 ++++++++++ .../audit-logging2/logging.properties} | 14 +- .../servers/audit-logging2/login.config | 29 ++ .../servers/audit-logging2/management.xml | 49 +++ .../logging/AuditLoggerResourceTest.java | 166 ++++++++ .../tests/smoke/logging}/AuditLoggerTest.java | 179 ++++----- 51 files changed, 1747 insertions(+), 190 deletions(-) create mode 100644 artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/AuditLoginModule.java create mode 100644 artemis-web/src/main/java/org/apache/activemq/artemis/component/AuthenticationFilter.java create mode 100644 artemis-web/src/main/java/org/apache/activemq/artemis/component/JolokiaFilter.java create mode 100644 examples/features/standard/security-ldap/src/main/resources/activemq/server0/logging.properties create mode 100644 tests/smoke-tests/src/main/resources/servers/audit-logging/broker.xml rename tests/{integration-tests/src/test/resources/audit.logging.properties => smoke-tests/src/main/resources/servers/audit-logging/logging.properties} (86%) create mode 100644 tests/smoke-tests/src/main/resources/servers/audit-logging/login.config create mode 100644 tests/smoke-tests/src/main/resources/servers/audit-logging/management.xml create mode 100644 tests/smoke-tests/src/main/resources/servers/audit-logging2/broker.xml rename tests/{integration-tests/src/test/resources/audit.logging.hot.properties => smoke-tests/src/main/resources/servers/audit-logging2/logging.properties} (87%) create mode 100644 tests/smoke-tests/src/main/resources/servers/audit-logging2/login.config create mode 100644 tests/smoke-tests/src/main/resources/servers/audit-logging2/management.xml create mode 100644 tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/logging/AuditLoggerResourceTest.java rename tests/{integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management => smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/logging}/AuditLoggerTest.java (52%) diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/logging.properties b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/logging.properties index 230bdf94d6..1bfc4dc99f 100644 --- a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/logging.properties +++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/logging.properties @@ -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 diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/logs/AuditLogger.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/logs/AuditLogger.java index 773c631a9a..74cafb9277 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/logs/AuditLogger.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/logs/AuditLogger.java @@ -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 remoteUrl = new ThreadLocal<>(); + + ThreadLocal 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 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); } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/protocol/AbstractRemotingConnection.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/protocol/AbstractRemotingConnection.java index 5cbcbbfdae..f69d1d2a82 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/protocol/AbstractRemotingConnection.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/protocol/AbstractRemotingConnection.java @@ -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; diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/protocol/RemotingConnection.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/protocol/RemotingConnection.java index 41cd0507a7..89452f64b8 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/protocol/RemotingConnection.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/protocol/RemotingConnection.java @@ -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 diff --git a/artemis-core-client/src/test/java/org/apache/activemq/artemis/core/protocol/core/impl/ChannelImplTest.java b/artemis-core-client/src/test/java/org/apache/activemq/artemis/core/protocol/core/impl/ChannelImplTest.java index 7d3fb23939..4863028293 100644 --- a/artemis-core-client/src/test/java/org/apache/activemq/artemis/core/protocol/core/impl/ChannelImplTest.java +++ b/artemis-core-client/src/test/java/org/apache/activemq/artemis/core/protocol/core/impl/ChannelImplTest.java @@ -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; diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPConnectionCallback.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPConnectionCallback.java index e99a53a503..6cf4c8a6d5 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPConnectionCallback.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPConnectionCallback.java @@ -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: diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/AMQPConnectionContext.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/AMQPConnectionContext.java index 63e18a7529..505e229913 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/AMQPConnectionContext.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/AMQPConnectionContext.java @@ -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(); diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/EventHandler.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/EventHandler.java index 34fba0c7ba..717c6cd138 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/EventHandler.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/EventHandler.java @@ -91,4 +91,7 @@ public interface EventHandler { return true; } + default String getRemoteAddress() { + return ""; + } } diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/ProtonHandler.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/ProtonHandler.java index b874f4eade..b693b91c01 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/ProtonHandler.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/proton/handler/ProtonHandler.java @@ -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()) { diff --git a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/PlainSASL.java b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/PlainSASL.java index cdc49ff4ea..e018cf1062 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/PlainSASL.java +++ b/artemis-protocols/artemis-amqp-protocol/src/main/java/org/apache/activemq/artemis/protocol/amqp/sasl/PlainSASL.java @@ -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; diff --git a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPConnectionCallbackTest.java b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPConnectionCallbackTest.java index e8800362a2..2d03dd5d75 100644 --- a/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPConnectionCallbackTest.java +++ b/artemis-protocols/artemis-amqp-protocol/src/test/java/org/apache/activemq/artemis/protocol/amqp/broker/AMQPConnectionCallbackTest.java @@ -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)); diff --git a/artemis-protocols/artemis-mqtt-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/mqtt/MQTTConnection.java b/artemis-protocols/artemis-mqtt-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/mqtt/MQTTConnection.java index 6b35aed623..9f7d6f4951 100644 --- a/artemis-protocols/artemis-mqtt-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/mqtt/MQTTConnection.java +++ b/artemis-protocols/artemis-mqtt-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/mqtt/MQTTConnection.java @@ -52,6 +52,7 @@ public class MQTTConnection implements RemotingConnection { private final List failureListeners = new CopyOnWriteArrayList<>(); private final List 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; diff --git a/artemis-protocols/artemis-mqtt-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/mqtt/MQTTProtocolHandler.java b/artemis-protocols/artemis-mqtt-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/mqtt/MQTTProtocolHandler.java index 8b0aae8827..7266cd5575 100644 --- a/artemis-protocols/artemis-mqtt-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/mqtt/MQTTProtocolHandler.java +++ b/artemis-protocols/artemis-mqtt-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/mqtt/MQTTProtocolHandler.java @@ -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) { diff --git a/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java b/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java index c8b3f883c7..2fa60de087 100644 --- a/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java +++ b/artemis-protocols/artemis-openwire-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/openwire/OpenWireConnection.java @@ -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(); @@ -1709,7 +1714,7 @@ public class OpenWireConnection extends AbstractRemotingConnection implements Se } - private void recoverOperationContext() { + private void recoverOperationContext() { server.getStorageManager().setContext(this.operationContext); } diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java index c050104674..56ab51a945 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompConnection.java @@ -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; diff --git a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java index a1d560815a..7c7caed6a7 100644 --- a/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java +++ b/artemis-protocols/artemis-stomp-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/stomp/StompProtocolManager.java @@ -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 plugin.beforeDeliver(this, reference)); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java index b53dc8261e..e5b0950ffa 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java @@ -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); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerBuilder.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerBuilder.java index e4e743bced..58fc5cf31d 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerBuilder.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerBuilder.java @@ -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"); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerGuard.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerGuard.java index 115b31aaf1..7752691a9c 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerGuard.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerGuard.java @@ -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"); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/JaasAuthenticator.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/JaasAuthenticator.java index 739392ae73..81e61060bf 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/JaasAuthenticator.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/JaasAuthenticator.java @@ -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 { + + Subject subject = new Subject(); try { - Subject subject = new Subject(); 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); } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java index a26119b5c4..1c22412ad0 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java @@ -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); } - lc.login(); + 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) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/AuditLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/AuditLoginModule.java new file mode 100644 index 0000000000..515d283b16 --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/AuditLoginModule.java @@ -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); + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/CertificateLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/CertificateLoginModule.java index dcbd964aa3..7c1808b765 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/CertificateLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/CertificateLoginModule.java @@ -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) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/ExternalCertificateLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/ExternalCertificateLoginModule.java index 81d34c97b0..76c6d8918e 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/ExternalCertificateLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/ExternalCertificateLoginModule.java @@ -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; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/GuestLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/GuestLoginModule.java index 5907b27675..b430fc3a64 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/GuestLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/GuestLoginModule.java @@ -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"); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/InVMLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/InVMLoginModule.java index f1347fb7b7..35ca7b9881 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/InVMLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/InVMLoginModule.java @@ -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"); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModule.java index 07cb4c0bde..1047e0827f 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/Krb5LoginModule.java @@ -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 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; } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java index ce7f125861..8def7665e3 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java @@ -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; } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java index a516a9bdbf..e021b5bc35 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java @@ -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) { diff --git a/artemis-web/src/main/java/org/apache/activemq/artemis/component/AuthenticationFilter.java b/artemis-web/src/main/java/org/apache/activemq/artemis/component/AuthenticationFilter.java new file mode 100644 index 0000000000..86df4992cd --- /dev/null +++ b/artemis-web/src/main/java/org/apache/activemq/artemis/component/AuthenticationFilter.java @@ -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() { + } +} diff --git a/artemis-web/src/main/java/org/apache/activemq/artemis/component/JolokiaFilter.java b/artemis-web/src/main/java/org/apache/activemq/artemis/component/JolokiaFilter.java new file mode 100644 index 0000000000..462709ab0c --- /dev/null +++ b/artemis-web/src/main/java/org/apache/activemq/artemis/component/JolokiaFilter.java @@ -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() { + } +} diff --git a/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java b/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java index 9f74e0d666..3d52393538 100644 --- a/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java +++ b/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java @@ -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); diff --git a/docs/user-manual/en/logging.md b/docs/user-manual/en/logging.md index 3ad23e3938..4aaa26f30e 100644 --- a/docs/user-manual/en/logging.md +++ b/docs/user-manual/en/logging.md @@ -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 diff --git a/examples/features/standard/security-ldap/src/main/resources/activemq/server0/logging.properties b/examples/features/standard/security-ldap/src/main/resources/activemq/server0/logging.properties new file mode 100644 index 0000000000..cae2127456 --- /dev/null +++ b/examples/features/standard/security-ldap/src/main/resources/activemq/server0/logging.properties @@ -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 \ No newline at end of file diff --git a/tests/smoke-tests/pom.xml b/tests/smoke-tests/pom.xml index 8f805074fd..f267872498 100644 --- a/tests/smoke-tests/pom.xml +++ b/tests/smoke-tests/pom.xml @@ -267,6 +267,48 @@ + + test-compile + create-createAuditLogging + + create + + + ${basedir}/target/classes/servers/audit-logging + false + true + admin + admin + ${basedir}/target/audit-logging + + + --java-options + -Djava.rmi.server.hostname=localhost + + + + + + test-compile + create-createAuditLogging2 + + create + + + ${basedir}/target/classes/servers/audit-logging2 + false + true + admin + admin + ${basedir}/target/audit-logging2 + + + --java-options + -Djava.rmi.server.hostname=localhost + + + + test-compile create-jmx-failback diff --git a/tests/smoke-tests/src/main/resources/servers/audit-logging/broker.xml b/tests/smoke-tests/src/main/resources/servers/audit-logging/broker.xml new file mode 100644 index 0000000000..73224c67bc --- /dev/null +++ b/tests/smoke-tests/src/main/resources/servers/audit-logging/broker.xml @@ -0,0 +1,190 @@ + + + + + + + + 0.0.0.0 + + true + + + NIO + + ./data/paging + + ./data/bindings + + ./data/journal + + ./data/large-messages + + true + + 2 + + -1 + + 1000 + + + + + + + + + + + + + + + + + + + + + + 5000 + + + 90 + + + + + + + + + + + + tcp://localhost:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;useKQueue;amqpCredits=1000;amqpLowCredits=300 + + + tcp://localhost:5672?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=AMQP;useEpoll=true;useKQueue=true;amqpCredits=1000;amqpLowCredits=300 + + + tcp://localhost:61613?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=STOMP;useEpoll=true;useKQueue=true + + + tcp://localhost:5445?protocols=HORNETQ,STOMP;useEpoll=true;useKQueue=true + + + tcp://localhost:1883?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=MQTT;useEpoll=true;useKQueue=true + + + + + + + + + + + + + + + + + + + + + + + + DLQ + ExpiryQueue + 0 + + -1 + 10 + PAGE + true + true + true + true + + + + DLQ + ExpiryQueue + 0 + + -1 + 10 + PAGE + true + true + true + true + + + + +
+ + + +
+
+ + + +
+
+ + + +
+ +
+ +
+
diff --git a/tests/integration-tests/src/test/resources/audit.logging.properties b/tests/smoke-tests/src/main/resources/servers/audit-logging/logging.properties similarity index 86% rename from tests/integration-tests/src/test/resources/audit.logging.properties rename to tests/smoke-tests/src/main/resources/servers/audit-logging/logging.properties index 72e4d4cf30..3063e38fd5 100644 --- a/tests/integration-tests/src/test/resources/audit.logging.properties +++ b/tests/smoke-tests/src/main/resources/servers/audit-logging/logging.properties @@ -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 diff --git a/tests/smoke-tests/src/main/resources/servers/audit-logging/login.config b/tests/smoke-tests/src/main/resources/servers/audit-logging/login.config new file mode 100644 index 0000000000..f415f79637 --- /dev/null +++ b/tests/smoke-tests/src/main/resources/servers/audit-logging/login.config @@ -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"; +}; \ No newline at end of file diff --git a/tests/smoke-tests/src/main/resources/servers/audit-logging/management.xml b/tests/smoke-tests/src/main/resources/servers/audit-logging/management.xml new file mode 100644 index 0000000000..e49dbb40ec --- /dev/null +++ b/tests/smoke-tests/src/main/resources/servers/audit-logging/management.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/smoke-tests/src/main/resources/servers/audit-logging2/broker.xml b/tests/smoke-tests/src/main/resources/servers/audit-logging2/broker.xml new file mode 100644 index 0000000000..4d49b1176e --- /dev/null +++ b/tests/smoke-tests/src/main/resources/servers/audit-logging2/broker.xml @@ -0,0 +1,190 @@ + + + + + + + + 0.0.0.0 + + true + + + NIO + + ./data/paging + + ./data/bindings + + ./data/journal + + ./data/large-messages + + true + + 2 + + -1 + + 1000 + + + + + + + + + + + + + + + + + + + + + + 5000 + + + 90 + + + + + + + + + + + + tcp://localhost:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;useKQueue;amqpCredits=1000;amqpLowCredits=300 + + + tcp://localhost:5672?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=AMQP;useEpoll=true;useKQueue=true;amqpCredits=1000;amqpLowCredits=300 + + + tcp://localhost:61613?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=STOMP;useEpoll=true;useKQueue=true + + + tcp://localhost:5445?protocols=HORNETQ,STOMP;useEpoll=true;useKQueue=true + + + tcp://localhost:1883?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=MQTT;useEpoll=true;useKQueue=true + + + + + + + + + + + + + + + + + + + + + + + + DLQ + ExpiryQueue + 0 + + -1 + 10 + PAGE + true + true + true + true + + + + DLQ + ExpiryQueue + 0 + + -1 + 10 + PAGE + true + true + true + true + + + + +
+ + + +
+
+ + + +
+
+ + + +
+ +
+ +
+
diff --git a/tests/integration-tests/src/test/resources/audit.logging.hot.properties b/tests/smoke-tests/src/main/resources/servers/audit-logging2/logging.properties similarity index 87% rename from tests/integration-tests/src/test/resources/audit.logging.hot.properties rename to tests/smoke-tests/src/main/resources/servers/audit-logging2/logging.properties index 3fea0b8225..1f375f9b8c 100644 --- a/tests/integration-tests/src/test/resources/audit.logging.hot.properties +++ b/tests/smoke-tests/src/main/resources/servers/audit-logging2/logging.properties @@ -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 diff --git a/tests/smoke-tests/src/main/resources/servers/audit-logging2/login.config b/tests/smoke-tests/src/main/resources/servers/audit-logging2/login.config new file mode 100644 index 0000000000..f415f79637 --- /dev/null +++ b/tests/smoke-tests/src/main/resources/servers/audit-logging2/login.config @@ -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"; +}; \ No newline at end of file diff --git a/tests/smoke-tests/src/main/resources/servers/audit-logging2/management.xml b/tests/smoke-tests/src/main/resources/servers/audit-logging2/management.xml new file mode 100644 index 0000000000..e49dbb40ec --- /dev/null +++ b/tests/smoke-tests/src/main/resources/servers/audit-logging2/management.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/logging/AuditLoggerResourceTest.java b/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/logging/AuditLoggerResourceTest.java new file mode 100644 index 0000000000..76eb635448 --- /dev/null +++ b/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/logging/AuditLoggerResourceTest.java @@ -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 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); + } + } + } +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/AuditLoggerTest.java b/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/logging/AuditLoggerTest.java similarity index 52% rename from tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/AuditLoggerTest.java rename to tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/logging/AuditLoggerTest.java index aa0f65a807..34029599e2 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/AuditLoggerTest.java +++ b/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/logging/AuditLoggerTest.java @@ -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 = 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 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(); - try { - logManager.readConfiguration(inputStream); - inputStream.close(); - } catch (IOException e) { - System.out.println("error loading logging conifg"); - e.printStackTrace(); - } + 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 { + 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 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); } } }