Audit hooks on JSON/RPC and SSH commands using Gerrit plugins.
New @Audit annotation to enable invocation of AuditService injected on GerritGlobalModule. Annotation applies to JSON/RPC interfaces whilst on the SSH side audit is "hooked" directly into SshLog class. Enables the integration of Audit plugins through the implementation of audit-listeners. Dynamic loading and unloading of audit plugins is supported through the @Extension/@Listener association and automatically loaded and unloaded using Plugin module self-registration. In order to implement a new AuditListener implementation you only to: 1) Define an implementation of AuditListener and annotate as: @Listener public class MyAuditTrail extends AuditListener 2) Define a Plugin Module to bind your Audit implementation in the configure() method as: DynamicSet.bind(binder(), AuditListener.class) .to(MyAuditTrail.class); Change-Id: Iaa26c4687a4ef4cbe27fe8396a5e0b8f6627536f Signed-off-by: Luca Milanesio <luca.milanesio@gmail.com>
This commit is contained in:
committed by
Edwin Kempin
parent
d5e87c3aad
commit
27ba2ac5e6
@@ -228,7 +228,7 @@ class CommandFactoryProvider implements Provider<CommandFactory> {
|
||||
}
|
||||
|
||||
/** Split a command line into a string array. */
|
||||
static String[] split(String commandLine) {
|
||||
static public String[] split(String commandLine) {
|
||||
final List<String> list = new ArrayList<String>();
|
||||
boolean inquote = false;
|
||||
boolean inDblQuote = false;
|
||||
|
||||
@@ -154,4 +154,8 @@ final class DispatchCommand extends BaseCommand {
|
||||
usage.append("\n");
|
||||
return usage.toString();
|
||||
}
|
||||
|
||||
public String getCommandName() {
|
||||
return commandName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package com.google.gerrit.sshd;
|
||||
|
||||
import com.google.gerrit.extensions.events.LifecycleListener;
|
||||
import com.google.gerrit.audit.AuditEvent;
|
||||
import com.google.gerrit.audit.AuditService;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.PeerDaemonUser;
|
||||
@@ -40,6 +42,7 @@ import org.eclipse.jgit.util.QuotedString;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
@@ -58,12 +61,14 @@ class SshLog implements LifecycleListener {
|
||||
private final Provider<SshSession> session;
|
||||
private final Provider<Context> context;
|
||||
private final AsyncAppender async;
|
||||
private final AuditService auditService;
|
||||
|
||||
@Inject
|
||||
SshLog(final Provider<SshSession> session, final Provider<Context> context,
|
||||
final SitePaths site, @GerritServerConfig Config config) {
|
||||
final SitePaths site, @GerritServerConfig Config config, AuditService auditService) {
|
||||
this.session = session;
|
||||
this.context = context;
|
||||
this.auditService = auditService;
|
||||
|
||||
final DailyRollingFileAppender dst = new DailyRollingFileAppender();
|
||||
dst.setName(LOG_NAME);
|
||||
@@ -96,6 +101,7 @@ class SshLog implements LifecycleListener {
|
||||
|
||||
void onLogin() {
|
||||
async.append(log("LOGIN FROM " + session.get().getRemoteAddressAsString()));
|
||||
audit("0", "LOGIN", new String[] {});
|
||||
}
|
||||
|
||||
void onAuthFail(final SshSession sd) {
|
||||
@@ -121,6 +127,7 @@ class SshLog implements LifecycleListener {
|
||||
}
|
||||
|
||||
async.append(event);
|
||||
audit("FAIL", "AUTH", new String[] {sd.getRemoteAddressAsString()});
|
||||
}
|
||||
|
||||
void onExecute(int exitValue) {
|
||||
@@ -158,10 +165,18 @@ class SshLog implements LifecycleListener {
|
||||
event.setProperty(P_STATUS, status);
|
||||
|
||||
async.append(event);
|
||||
audit(status, getCommand(commandLine), CommandFactoryProvider.split(commandLine));
|
||||
}
|
||||
|
||||
private String getCommand(String commandLine) {
|
||||
commandLine = commandLine.trim();
|
||||
int spacePos = commandLine.indexOf(' ');
|
||||
return (spacePos > 0 ? commandLine.substring(0, spacePos):commandLine);
|
||||
}
|
||||
|
||||
void onLogout() {
|
||||
async.append(log("LOGOUT"));
|
||||
audit("0", "LOGOUT", new String[] {});
|
||||
}
|
||||
|
||||
private LoggingEvent log(final String msg) {
|
||||
@@ -192,7 +207,6 @@ class SshLog implements LifecycleListener {
|
||||
|
||||
} else if (user instanceof PeerDaemonUser) {
|
||||
userName = PeerDaemonUser.USER_NAME;
|
||||
|
||||
}
|
||||
|
||||
event.setProperty(P_USER_NAME, userName);
|
||||
@@ -400,4 +414,44 @@ class SshLog implements LifecycleListener {
|
||||
public void setLogger(Logger logger) {
|
||||
}
|
||||
}
|
||||
|
||||
void audit(Object result, String commandName, String[] args) {
|
||||
final Context ctx = context.get();
|
||||
final String sid = extractSessionId(ctx);
|
||||
final long created = extractCreated(ctx);
|
||||
final String what = extractWhat(commandName, args);
|
||||
auditService.dispatch(new AuditEvent(sid, extractCurrentUser(ctx), "ssh:"
|
||||
+ what, created, Arrays.asList(args), result));
|
||||
}
|
||||
|
||||
private String extractWhat(String commandName, String[] args) {
|
||||
String result = commandName;
|
||||
if ("gerrit".equals(commandName)) {
|
||||
if (args.length > 1)
|
||||
result = "gerrit"+"."+args[1];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private long extractCreated(final Context ctx) {
|
||||
return (ctx != null) ? ctx.created : System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private CurrentUser extractCurrentUser(final Context ctx) {
|
||||
if (ctx != null) {
|
||||
SshSession session = ctx.getSession();
|
||||
return (session == null) ? null : session.getCurrentUser();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String extractSessionId(final Context ctx) {
|
||||
if (ctx != null) {
|
||||
SshSession session = ctx.getSession();
|
||||
return (session == null) ? null : IdGenerator.format(session.getSessionId());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user