-
Notifications
You must be signed in to change notification settings - Fork 3.8k
CASSANDRA-20128: Support audit logging for JMX operations #3728
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
8b8d068 to
f8ac711
Compare
JeetKunDoug
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some questions/suggestions, but glad to see the audit logging applied to JMX as well. Thanks!
f8ac711 to
86f6015
Compare
| { | ||
| private static String user(Subject subject) | ||
| { | ||
| return String.format("%s", subject == null ? null : subject.getPrincipals().stream().map(Objects::toString).collect(Collectors.joining(", "))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand this is not a hot path, but following the decision on this thread, we may want to stay away from streams in non test code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch - especially given that I wrote myself on that thread "+1 to forbidding Stream usage entirely".
| if (args[0] == null) | ||
| throw new IllegalArgumentException("Null MBeanServer"); | ||
| // Corresponds to MBeanServer.invoke | ||
| if (methodName.equals("invoke") && args.length == 4) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: "invoke".equals(methodName) for consistency with others?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the only real change here is that we wrap the existing code in a try/catch block so we can propagate the success/failure event . The code is exactly the same as before, so I think it's fine to keep. The only actual behavior change is that we are doing
AccessControlContext acc = AccessController.getContext();
Subject subject = Subject.getSubject(acc);
before we do the first two checks. I think that's fine
frankgh
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall, I have some general comments. Let me know what you think
| import java.lang.reflect.Method; | ||
| import javax.security.auth.Subject; | ||
|
|
||
| public interface JmxInvocationListener |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add javadocs on public interfaces and methods ?
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable | ||
| { | ||
| // See AuthorizationProxy.invoke | ||
| if (("setMBeanServer").equals(method.getName())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: remove redundant unnecessary parentheses
| if (("setMBeanServer").equals(method.getName())) | |
| if ("setMBeanServer".equals(method.getName())) |
| default void onInvocation(Subject subject, Method method, Object[] args) | ||
| {} | ||
|
|
||
| default void onFailure(Subject subject, Method method, Object[] args, String reason) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we pass the cause here instead of the string reason? The idea is so we can add the cause to the log entry during failure , i.e. log(entry, cause);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you want failures to be presented in AuditLogEntry? Full stack trace, or just a string reason (or something in the middle, like class: message)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll use the existing org.apache.cassandra.audit.AuditLogManager#log(org.apache.cassandra.audit.AuditLogEntry, java.lang.Exception) entrypoint
| final InvocationHandler handler = new JmxHandler(); | ||
| final Class<?>[] interfaces = { MBeanServerForwarder.class }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid using final for variables
| final InvocationHandler handler = new JmxHandler(); | |
| final Class<?>[] interfaces = { MBeanServerForwarder.class }; | |
| InvocationHandler handler = new JmxHandler(); | |
| Class<?>[] interfaces = { MBeanServerForwarder.class }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can do, this was copied from JMXServerUtils
| if (args[0] == null) | ||
| throw new IllegalArgumentException("Null MBeanServer"); | ||
| // Corresponds to MBeanServer.invoke | ||
| if (methodName.equals("invoke") && args.length == 4) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the only real change here is that we wrap the existing code in a try/catch block so we can propagate the success/failure event . The code is exactly the same as before, so I think it's fine to keep. The only actual behavior change is that we are doing
AccessControlContext acc = AccessController.getContext();
Subject subject = Subject.getSubject(acc);
before we do the first two checks. I think that's fine
| public void testJMXAuditLogs() throws Throwable | ||
| { | ||
| // Need to use distinct ports, otherwise would get RMI registry object ID collision, even with server shutdown between | ||
| testJMXAuditLogs(false, getAutomaticallyAllocatedPort(InetAddress.getLoopbackAddress())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was going to suggest using SocketUtils.findAvailablePort but it looks like it always requires a fall back port number. Maybe we can change the signature of that method to take an Integer object instead of the primitive, and reuse the same code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the benefit of findAvailablePort? Looks like getAutomaticallyAllocatedPort has more similar usage within test code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the benefit is that the code already exists under the test code, and that we avoid duplication. However since the existing method doesn't fit exactly the requirement, I think it's fine to keep the new method.
| LOGIN_SUCCESS(AuditLogEntryCategory.AUTH), | ||
| LIST_SUPERUSERS(AuditLogEntryCategory.DCL); | ||
| LIST_SUPERUSERS(AuditLogEntryCategory.DCL), | ||
| JMX(AuditLogEntryCategory.JMX); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking we should split this into JMX_SUCCESS and JMX_ERROR. I can see use cases where you might only be interested in audit logs for failed JMX operations. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather start with a basic on / off flag for now, and we can make this more refined once it gets used. There's room for more specific inclusion / exclusion criteria, like excluding "boilerplate" GetClassLoader calls made by the JMX client, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's reasonable. We can enhance it later as needed.
frankgh
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
bbotella
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1! (nb)
|
Closed via c853eff |
https://issues.apache.org/jira/browse/CASSANDRA-20128