From ee25bc0971631336d95be56feddc5913871ed26a Mon Sep 17 00:00:00 2001 From: Mohammad Arshad Date: Thu, 13 Sep 2018 01:20:27 +0530 Subject: [PATCH 1/2] ZOOKEEPER-1260:Audit logging in ZooKeeper Servers. --- conf/log4j.properties | 17 + src/java/main/org/apache/zookeeper/Login.java | 7 + .../main/org/apache/zookeeper/ZKUtil.java | 38 ++ .../zookeeper/audit/AuditConstants.java | 37 ++ .../zookeeper/audit/ZKAuditLogFormatter.java | 56 +++ .../apache/zookeeper/audit/ZKAuditLogger.java | 133 ++++++ .../apache/zookeeper/cli/GetAclCommand.java | 24 +- .../org/apache/zookeeper/server/DataTree.java | 18 + .../server/FinalRequestProcessor.java | 158 ++++++- .../org/apache/zookeeper/server/Request.java | 28 ++ .../apache/zookeeper/server/ServerCnxn.java | 23 + .../zookeeper/server/ServerCnxnFactory.java | 10 + .../zookeeper/server/ZooKeeperServerMain.java | 12 + .../server/quorum/QuorumPeerMain.java | 14 + .../zookeeper/server/util/AuthUtil.java | 42 ++ .../zookeeper/audit/AuditLogPerfReading.java | 74 +++ .../audit/ZKAuditLogFormatterTest.java | 45 ++ .../audit/ZKAuditLoggerPerformance.java | 151 +++++++ .../zookeeper/audit/ZKAuditLoggerTest.java | 423 ++++++++++++++++++ .../zookeeper/server/util/AuthUtilTest.java | 57 +++ .../src/documentation/content/xdocs/index.xml | 1 + .../src/documentation/content/xdocs/site.xml | 1 + .../content/xdocs/zookeeperAdmin.xml | 13 + .../content/xdocs/zookeeperAuditLogs.xml | 205 +++++++++ .../resources/images/zkAuditLogs.jpg | Bin 0 -> 28450 bytes 25 files changed, 1563 insertions(+), 24 deletions(-) create mode 100644 src/java/main/org/apache/zookeeper/audit/AuditConstants.java create mode 100644 src/java/main/org/apache/zookeeper/audit/ZKAuditLogFormatter.java create mode 100644 src/java/main/org/apache/zookeeper/audit/ZKAuditLogger.java create mode 100644 src/java/main/org/apache/zookeeper/server/util/AuthUtil.java create mode 100644 src/java/test/org/apache/zookeeper/audit/AuditLogPerfReading.java create mode 100644 src/java/test/org/apache/zookeeper/audit/ZKAuditLogFormatterTest.java create mode 100644 src/java/test/org/apache/zookeeper/audit/ZKAuditLoggerPerformance.java create mode 100644 src/java/test/org/apache/zookeeper/audit/ZKAuditLoggerTest.java create mode 100644 src/java/test/org/apache/zookeeper/server/util/AuthUtilTest.java create mode 100644 zookeeper-docs/src/documentation/content/xdocs/zookeeperAuditLogs.xml create mode 100644 zookeeper-docs/src/documentation/resources/images/zkAuditLogs.jpg diff --git a/conf/log4j.properties b/conf/log4j.properties index 4a2ede95503..c938a2b7e28 100644 --- a/conf/log4j.properties +++ b/conf/log4j.properties @@ -63,3 +63,20 @@ log4j.appender.TRACEFILE.File=${zookeeper.tracelog.dir}/${zookeeper.tracelog.fil log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout ### Notice we are including log4j's NDC here (%x) log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L][%x] - %m%n +# +# zk audit logging +# +zookeeper.auditlog.file=zookeeper_audit.log +zookeeper.auditlog.threshold=INFO +audit.logger=INFO, RFAAUDIT +log4j.logger.org.apache.zookeeper.audit.ZKAuditLogger=${audit.logger} +log4j.additivity.org.apache.zookeeper.audit.ZKAuditLogger=false +log4j.appender.RFAAUDIT=org.apache.log4j.RollingFileAppender +log4j.appender.RFAAUDIT.File=${zookeeper.log.dir}/${zookeeper.auditlog.file} +log4j.appender.RFAAUDIT.layout=org.apache.log4j.PatternLayout +log4j.appender.RFAAUDIT.layout.ConversionPattern=%d{ISO8601} %p %c{2}: %m%n +log4j.appender.RFAAUDIT.Threshold=${zookeeper.auditlog.threshold} + +# Max log file size of 10MB +log4j.appender.RFAAUDIT.MaxFileSize=10MB +log4j.appender.RFAAUDIT.MaxBackupIndex=10 diff --git a/src/java/main/org/apache/zookeeper/Login.java b/src/java/main/org/apache/zookeeper/Login.java index d97d6c1fde4..4b81e2c0327 100644 --- a/src/java/main/org/apache/zookeeper/Login.java +++ b/src/java/main/org/apache/zookeeper/Login.java @@ -286,6 +286,13 @@ public Subject getSubject() { return subject; } + public String getUserName() { + if (principal == null || principal.isEmpty()) { + return System.getProperty("user.name", ""); + } + return principal; + } + public String getLoginContextName() { return loginContextName; } diff --git a/src/java/main/org/apache/zookeeper/ZKUtil.java b/src/java/main/org/apache/zookeeper/ZKUtil.java index a6abf2f42e1..73fdf18780a 100644 --- a/src/java/main/org/apache/zookeeper/ZKUtil.java +++ b/src/java/main/org/apache/zookeeper/ZKUtil.java @@ -27,6 +27,7 @@ import org.apache.zookeeper.AsyncCallback.VoidCallback; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.common.PathUtils; +import org.apache.zookeeper.data.ACL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -168,4 +169,41 @@ private static void visitSubTreeDFSHelper(ZooKeeper zk, final String path, return; // ignore } } + + /** + * @param perms + * ACL permissions + * @return string representation of permissions + */ + public static String getPermString(int perms) { + StringBuilder p = new StringBuilder(); + if ((perms & ZooDefs.Perms.CREATE) != 0) { + p.append('c'); + } + if ((perms & ZooDefs.Perms.DELETE) != 0) { + p.append('d'); + } + if ((perms & ZooDefs.Perms.READ) != 0) { + p.append('r'); + } + if ((perms & ZooDefs.Perms.WRITE) != 0) { + p.append('w'); + } + if ((perms & ZooDefs.Perms.ADMIN) != 0) { + p.append('a'); + } + return p.toString(); + } + + public static String aclToString(List acls) { + StringBuilder sb = new StringBuilder(); + for (ACL acl : acls) { + sb.append(acl.getId().getScheme()); + sb.append(":"); + sb.append(acl.getId().getId()); + sb.append(":"); + sb.append(getPermString(acl.getPerms())); + } + return sb.toString(); + } } \ No newline at end of file diff --git a/src/java/main/org/apache/zookeeper/audit/AuditConstants.java b/src/java/main/org/apache/zookeeper/audit/AuditConstants.java new file mode 100644 index 00000000000..63c15d511cd --- /dev/null +++ b/src/java/main/org/apache/zookeeper/audit/AuditConstants.java @@ -0,0 +1,37 @@ +/** + * 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.zookeeper.audit; + +public class AuditConstants { + public static final String SUCCESS = "success"; + public static final String FAILURE = "failure"; + // operation is performed, result is not known yet + public static final String INVOKED = "invoked"; + public static final String KEY_VAL_SEPARATOR = "="; + public static final char PAIR_SEPARATOR = '\t'; + + public static final String OP_START = "serverStart"; + public static final String OP_STOP = "serverStop"; + public static final String OP_CREATE = "create"; + public static final String OP_DELETE = "delete"; + public static final String OP_SETDATA = "setData"; + public static final String OP_SETACL = "setAcl"; + public static final String OP_MULTI_OP = "multiOperation"; + public static final String OP_RECONFIG = "reconfig"; + public static final String OP_DEL_EZNODE_EXP = "ephemeralZNodeDeletionOnSessionCloseOrExpire"; +} diff --git a/src/java/main/org/apache/zookeeper/audit/ZKAuditLogFormatter.java b/src/java/main/org/apache/zookeeper/audit/ZKAuditLogFormatter.java new file mode 100644 index 00000000000..c81cb4be5b2 --- /dev/null +++ b/src/java/main/org/apache/zookeeper/audit/ZKAuditLogFormatter.java @@ -0,0 +1,56 @@ +/** + * 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.zookeeper.audit; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +public class ZKAuditLogFormatter { + private Map fieldVsValue = new LinkedHashMap(); + + /** + * Add fields to be logged + */ + public void addField(String key, String value) { + fieldVsValue.put(key, value); + } + + /** + * Gives the string to be logged, ignores fields with null values + */ + public String format() { + StringBuilder buffer = new StringBuilder(); + boolean first = true; + for (Entry entry : fieldVsValue.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (null != value) { + // if first field then no need to add the tabs + if (first) { + first = false; + } else { + buffer.append(AuditConstants.PAIR_SEPARATOR); + } + buffer.append(key.toLowerCase()).append(AuditConstants.KEY_VAL_SEPARATOR) + .append(value); + } + } + return buffer.toString(); + } +} diff --git a/src/java/main/org/apache/zookeeper/audit/ZKAuditLogger.java b/src/java/main/org/apache/zookeeper/audit/ZKAuditLogger.java new file mode 100644 index 00000000000..959015e2a2a --- /dev/null +++ b/src/java/main/org/apache/zookeeper/audit/ZKAuditLogger.java @@ -0,0 +1,133 @@ +/** + * 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.zookeeper.audit; + +import org.apache.zookeeper.server.ServerCnxnFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ZKAuditLogger { + public static final String SYSPROP_AUDIT_ENABLE = "zookeeper.audit.enable"; + private static final Logger LOG = LoggerFactory.getLogger(ZKAuditLogger.class); + // By default audit logging is disabled + private static boolean auditEnabled = Boolean.getBoolean(SYSPROP_AUDIT_ENABLE); + + /** + * @return true if audit log is enabled + */ + public static boolean isAuditEnabled() { + return auditEnabled; + } + + // @VisisbleForTesting + public static void setAuditEnabled(boolean auditEnabled) { + ZKAuditLogger.auditEnabled = auditEnabled; + } + + /** + * + * Prints audit log based on log level specified + * + */ + public static enum LogLevel { + ERROR { + @Override + public void printLog(String logMsg) { + LOG.error(logMsg); + } + }, + INFO { + @Override + public void printLog(String logMsg) { + LOG.info(logMsg); + } + }; + public abstract void printLog(String logMsg); + } + + public static enum Keys { + USER, OPERATION, RESULT, IP, ACL, ZNODE, SESSION; + } + + public static void logInvoked(String user, String operation) { + log(LogLevel.INFO, user, operation, AuditConstants.INVOKED); + } + + public static void logSuccess(String user, String operation) { + log(LogLevel.INFO, user, operation, AuditConstants.SUCCESS); + } + + public static void logFailure(String user, String operation) { + log(LogLevel.ERROR, user, operation, AuditConstants.FAILURE); + } + + private static void log(LogLevel level, String user, String operation, String logType) { + level.printLog(createLog(user, operation, null, null, null, null, logType)); + } + + public static void logSuccess(String user, String operation, String znode, String acl, + String session, String ip) { + LogLevel.INFO.printLog( + createLog(user, operation, znode, acl, session, ip, AuditConstants.SUCCESS)); + } + + public static void logFailure(String user, String operation, String znode, String acl, + String session, String ip) { + LogLevel.ERROR.printLog( + createLog(user, operation, znode, acl, session, ip, AuditConstants.FAILURE)); + } + + /** + * A helper api for creating an audit log string. + */ + public static String createLog(String user, String operation, String znode, String acl, + String session, String ip, String status) { + ZKAuditLogFormatter fmt = new ZKAuditLogFormatter(); + fmt.addField(Keys.SESSION.name(), session); + fmt.addField(Keys.USER.name(), user); + fmt.addField(Keys.IP.name(), ip); + fmt.addField(Keys.OPERATION.name(), operation); + fmt.addField(Keys.ZNODE.name(), znode); + fmt.addField(Keys.ACL.name(), acl); + fmt.addField(Keys.RESULT.name(), status); + return fmt.format(); + } + + /** + * add audit log for server start and register server stop log + */ + public static void addZKStartStopAuditLog() { + if (isAuditEnabled()) { + ZKAuditLogger.logSuccess(getZKUser(), AuditConstants.OP_START); + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + ZKAuditLogger.logInvoked(getZKUser(), AuditConstants.OP_STOP); + } + }); + } + } + + /** + * User who has started the ZooKeeper server user, it will be the logged-in + * user. If no user logged-in then system user + */ + public static String getZKUser() { + return ServerCnxnFactory.getUserName(); + } +} diff --git a/src/java/main/org/apache/zookeeper/cli/GetAclCommand.java b/src/java/main/org/apache/zookeeper/cli/GetAclCommand.java index b5feb60794d..1289e549990 100644 --- a/src/java/main/org/apache/zookeeper/cli/GetAclCommand.java +++ b/src/java/main/org/apache/zookeeper/cli/GetAclCommand.java @@ -23,7 +23,7 @@ import org.apache.commons.cli.Parser; import org.apache.commons.cli.PosixParser; import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZKUtil; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; @@ -75,7 +75,7 @@ public boolean exec() throws CliException { for (ACL a : acl) { out.println(a.getId() + ": " - + getPermString(a.getPerms())); + + ZKUtil.getPermString(a.getPerms())); } if (cl.hasOption("s")) { @@ -83,24 +83,4 @@ public boolean exec() throws CliException { } return false; } - - private static String getPermString(int perms) { - StringBuilder p = new StringBuilder(); - if ((perms & ZooDefs.Perms.CREATE) != 0) { - p.append('c'); - } - if ((perms & ZooDefs.Perms.DELETE) != 0) { - p.append('d'); - } - if ((perms & ZooDefs.Perms.READ) != 0) { - p.append('r'); - } - if ((perms & ZooDefs.Perms.WRITE) != 0) { - p.append('w'); - } - if ((perms & ZooDefs.Perms.ADMIN) != 0) { - p.append('a'); - } - return p.toString(); - } } diff --git a/src/java/main/org/apache/zookeeper/server/DataTree.java b/src/java/main/org/apache/zookeeper/server/DataTree.java index f17be307d30..3e829d79423 100644 --- a/src/java/main/org/apache/zookeeper/server/DataTree.java +++ b/src/java/main/org/apache/zookeeper/server/DataTree.java @@ -35,6 +35,8 @@ import org.apache.zookeeper.Watcher.WatcherType; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.OpCode; +import org.apache.zookeeper.audit.AuditConstants; +import org.apache.zookeeper.audit.ZKAuditLogger; import org.apache.zookeeper.common.PathTrie; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; @@ -1005,8 +1007,10 @@ void killSession(long session, long zxid) { HashSet list = ephemerals.remove(session); if (list != null) { for (String path : list) { + boolean deleted = false; try { deleteNode(path, zxid); + deleted = true; if (LOG.isDebugEnabled()) { LOG .debug("Deleting ephemeral node " + path @@ -1018,6 +1022,20 @@ void killSession(long session, long zxid) { + " while removing ephemeral for dead session 0x" + Long.toHexString(session)); } + String sessionHex = "0x" + Long.toHexString(session); + if (deleted) { + if (ZKAuditLogger.isAuditEnabled()) { + ZKAuditLogger.logSuccess(ZKAuditLogger.getZKUser(), + AuditConstants.OP_DEL_EZNODE_EXP, path, null, + sessionHex, null); + } + } else { + if (ZKAuditLogger.isAuditEnabled()) { + ZKAuditLogger.logFailure(ZKAuditLogger.getZKUser(), + AuditConstants.OP_DEL_EZNODE_EXP, path, null, + sessionHex, null); + } + } } } } diff --git a/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java index 345184c6236..2acbb672524 100644 --- a/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java @@ -30,14 +30,19 @@ import org.apache.zookeeper.OpResult.ErrorResult; import org.apache.zookeeper.OpResult.SetDataResult; import org.apache.zookeeper.Watcher.WatcherType; +import org.apache.zookeeper.ZKUtil; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.OpCode; +import org.apache.zookeeper.audit.AuditConstants; +import org.apache.zookeeper.audit.ZKAuditLogger; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.CheckWatchesRequest; import org.apache.zookeeper.proto.Create2Response; +import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.CreateResponse; +import org.apache.zookeeper.proto.DeleteRequest; import org.apache.zookeeper.proto.ExistsRequest; import org.apache.zookeeper.proto.ExistsResponse; import org.apache.zookeeper.proto.GetACLRequest; @@ -50,7 +55,9 @@ import org.apache.zookeeper.proto.GetDataResponse; import org.apache.zookeeper.proto.RemoveWatchesRequest; import org.apache.zookeeper.proto.ReplyHeader; +import org.apache.zookeeper.proto.SetACLRequest; import org.apache.zookeeper.proto.SetACLResponse; +import org.apache.zookeeper.proto.SetDataRequest; import org.apache.zookeeper.proto.SetDataResponse; import org.apache.zookeeper.proto.SetWatches; import org.apache.zookeeper.proto.SyncRequest; @@ -154,6 +161,7 @@ public void processRequest(Request request) { Record rsp = null; try { if (request.getHdr() != null && request.getHdr().getType() == OpCode.error) { + addFailedTxnAduitLog(request); /* * When local session upgrading is disabled, leader will * reject the ephemeral node creation due to session expire. @@ -202,7 +210,7 @@ public void processRequest(Request request) { case OpCode.multi: { lastOp = "MULT"; rsp = new MultiResponse() ; - + boolean multiFailed = false; for (ProcessTxnResult subTxnResult : rc.multiResult) { OpResult subResult ; @@ -213,35 +221,44 @@ public void processRequest(Request request) { break; case OpCode.create: subResult = new CreateResult(subTxnResult.path); + addSuccessAudit(request, cnxn, AuditConstants.OP_CREATE, subTxnResult.path); break; case OpCode.create2: case OpCode.createTTL: case OpCode.createContainer: subResult = new CreateResult(subTxnResult.path, subTxnResult.stat); + addSuccessAudit(request, cnxn, AuditConstants.OP_CREATE, subTxnResult.path); break; case OpCode.delete: case OpCode.deleteContainer: subResult = new DeleteResult(); + addSuccessAudit(request, cnxn, AuditConstants.OP_DELETE, subTxnResult.path); break; case OpCode.setData: subResult = new SetDataResult(subTxnResult.stat); + addSuccessAudit(request, cnxn, AuditConstants.OP_SETDATA, subTxnResult.path); break; case OpCode.error: subResult = new ErrorResult(subTxnResult.err) ; + multiFailed = true; break; default: + multiFailed = true; throw new IOException("Invalid type of op"); } ((MultiResponse)rsp).add(subResult); } - + if (multiFailed) { + addFailureAudit(request, cnxn, AuditConstants.OP_MULTI_OP, null); + } break; } case OpCode.create: { lastOp = "CREA"; rsp = new CreateResponse(rc.path); err = Code.get(rc.err); + addAuditLog(request, cnxn, AuditConstants.OP_CREATE, rc.path, null, err); break; } case OpCode.create2: @@ -250,30 +267,36 @@ public void processRequest(Request request) { lastOp = "CREA"; rsp = new Create2Response(rc.path, rc.stat); err = Code.get(rc.err); + addAuditLog(request, cnxn, AuditConstants.OP_CREATE, rc.path, null, err); break; } case OpCode.delete: case OpCode.deleteContainer: { lastOp = "DELE"; err = Code.get(rc.err); + addAuditLog(request, cnxn, AuditConstants.OP_DELETE, rc.path, null, err); break; } case OpCode.setData: { lastOp = "SETD"; rsp = new SetDataResponse(rc.stat); err = Code.get(rc.err); + addAuditLog(request, cnxn, AuditConstants.OP_SETDATA, rc.path, null, err); break; } case OpCode.reconfig: { lastOp = "RECO"; rsp = new GetDataResponse(((QuorumZooKeeperServer)zks).self.getQuorumVerifier().toString().getBytes(), rc.stat); err = Code.get(rc.err); + addAuditLog(request, cnxn, AuditConstants.OP_RECONFIG, rc.path, null, err); break; } case OpCode.setACL: { lastOp = "SETA"; rsp = new SetACLResponse(rc.stat); err = Code.get(rc.err); + addAuditLog(request, cnxn, AuditConstants.OP_SETACL, rc.path, getACLs(request), + err); break; } case OpCode.closeSession: { @@ -481,4 +504,135 @@ public void shutdown() { LOG.info("shutdown of request processor complete"); } + private void addSuccessAudit(Request request, ServerCnxn cnxn, String op, + String path) { + addSuccessAudit(request, cnxn, op, path, null); + } + + private void addSuccessAudit(Request request, ServerCnxn cnxn, String op, + String path, + String acl) { + if (!ZKAuditLogger.isAuditEnabled()) { + return; + } + ZKAuditLogger.logSuccess(request.getUsers(), op, path, acl, + cnxn.getSessionIdHex(), + cnxn.getHostAddress()); + } + + private void addFailureAudit(Request request, ServerCnxn cnxn, String op, + String path) { + addFailureAudit(request, cnxn, op, path, null); + } + + private void addFailureAudit(Request request, ServerCnxn cnxn, String op, + String path, + String acl) { + if (!ZKAuditLogger.isAuditEnabled()) { + return; + } + ZKAuditLogger.logFailure(request.getUsers(), op, path, acl, + cnxn.getSessionIdHex(), + cnxn.getHostAddress()); + } + + private void addAuditLog(Request request, ServerCnxn cnxn, String op, + String path, String acl, + Code err) { + if (!ZKAuditLogger.isAuditEnabled()) { + return; + } + if (err == Code.OK) { + ZKAuditLogger.logSuccess(request.getUsers(), op, path, acl, + cnxn.getSessionIdHex(), + cnxn.getHostAddress()); + } else { + ZKAuditLogger.logFailure(request.getUsers(), op, path, acl, + cnxn.getSessionIdHex(), + cnxn.getHostAddress()); + } + } + + private String getACLs(Request request) { + ByteBuffer reqData = request.request.duplicate(); + reqData.rewind(); + SetACLRequest setACLRequest = new SetACLRequest(); + try { + ByteBufferInputStream.byteBuffer2Record(reqData, setACLRequest); + } catch (IOException e) { + e.printStackTrace(); + } + return ZKUtil.aclToString(setACLRequest.getAcl()); + } + + private void addFailedTxnAduitLog(Request request) { + if (!ZKAuditLogger.isAuditEnabled()) { + return; + } + String op = AuditConstants.OP_CREATE; + String path = null; + long sessionId = -1; + String address = null; + String acls = null; + ByteBuffer reqData = request.request.duplicate(); + reqData.rewind(); + try { + sessionId = request.cnxn.getSessionId(); + switch (request.type) { + case OpCode.create: + case OpCode.create2: + case OpCode.createContainer: + op = AuditConstants.OP_CREATE; + CreateRequest createRequest = new CreateRequest(); + ByteBufferInputStream.byteBuffer2Record(reqData, createRequest); + path = createRequest.getPath(); + break; + case OpCode.delete: + case OpCode.deleteContainer: + op = AuditConstants.OP_DELETE; + // path = new String(request.request.array()); + DeleteRequest deleteRequest = new DeleteRequest(); + ByteBufferInputStream.byteBuffer2Record(reqData, deleteRequest); + path = deleteRequest.getPath(); + break; + case OpCode.setData: + op = AuditConstants.OP_SETDATA; + SetDataRequest setDataRequest = new SetDataRequest(); + ByteBufferInputStream.byteBuffer2Record(reqData, + setDataRequest); + path = setDataRequest.getPath(); + break; + case OpCode.setACL: + op = AuditConstants.OP_SETACL; + SetACLRequest setACLRequest = new SetACLRequest(); + ByteBufferInputStream.byteBuffer2Record(reqData, setACLRequest); + path = setACLRequest.getPath(); + acls = ZKUtil.aclToString(setACLRequest.getAcl()); + break; + case OpCode.multi: + op = AuditConstants.OP_MULTI_OP; + break; + case OpCode.reconfig: + op = AuditConstants.OP_RECONFIG; + break; + default: + // This should not happen as audit is logged only from known + // operations. This is added to fix find bug issue. + op = "Unknown"; + break; + } + + if (request.cnxn.getRemoteSocketAddress() != null + && request.cnxn.getRemoteSocketAddress() + .getAddress() != null) { + address = request.cnxn.getRemoteSocketAddress().getAddress() + .getHostAddress(); + } + ZKAuditLogger.logFailure(request.getUsers(), op, path, acls, + "0x" + Long.toHexString(sessionId), address); + } catch (Throwable e) { + LOG.error("Failed to audit log request {} failure", request.type, + e); + } + } } diff --git a/src/java/main/org/apache/zookeeper/server/Request.java b/src/java/main/org/apache/zookeeper/server/Request.java index ede9280441a..fea8e4bc639 100644 --- a/src/java/main/org/apache/zookeeper/server/Request.java +++ b/src/java/main/org/apache/zookeeper/server/Request.java @@ -27,6 +27,7 @@ import org.apache.zookeeper.common.Time; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; +import org.apache.zookeeper.server.util.AuthUtil; import org.apache.zookeeper.txn.TxnHeader; /** @@ -297,4 +298,31 @@ public void setException(KeeperException e) { public KeeperException getException() { return e; } + + /** + * Returns comma separated list of users authenticated in the current + * session + */ + public String getUsers() { + if (authInfo == null) { + return (String) null; + } + if (authInfo.size() == 1) { + return AuthUtil.getUser(authInfo.get(0)); + } + StringBuilder users = new StringBuilder(); + boolean first = true; + for (Id id : authInfo) { + String user = AuthUtil.getUser(id); + if (user != null) { + if (first) { + first = false; + } else { + users.append(","); + } + users.append(user); + } + } + return users.toString(); + } } diff --git a/src/java/main/org/apache/zookeeper/server/ServerCnxn.java b/src/java/main/org/apache/zookeeper/server/ServerCnxn.java index c60503fe7fc..4ad21f64f2c 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/ServerCnxn.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.security.cert.Certificate; @@ -391,4 +392,26 @@ public void cleanupWriterSocket(PrintWriter pwriter) { } } } + + /** + * Returns the IP address or empty string. + */ + public String getHostAddress() { + InetSocketAddress remoteSocketAddress = getRemoteSocketAddress(); + if (remoteSocketAddress == null) { + return ""; + } + InetAddress address = remoteSocketAddress.getAddress(); + if (address == null) { + return ""; + } + return address.getHostAddress(); + } + + /** + * Get session id in hexadecimal notation. + */ + public String getSessionIdHex() { + return "0x" + Long.toHexString(getSessionId()); + } } diff --git a/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java index dbe47a261b7..1f3d179b99b 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java @@ -54,6 +54,7 @@ public abstract class ServerCnxnFactory { */ static final ByteBuffer closeConn = ByteBuffer.allocate(0); + private static String loginUser = System.getProperty("user.name", ""); public abstract int getLocalPort(); public abstract Iterable getConnections(); @@ -238,10 +239,19 @@ protected void configureSaslLogin() throws IOException { try { saslServerCallbackHandler = new SaslServerCallbackHandler(Configuration.getConfiguration()); login = new Login(serverSection, saslServerCallbackHandler, new ZKConfig() ); + loginUser = login.getUserName(); login.startThreadIfNeeded(); } catch (LoginException e) { throw new IOException("Could not configure server because SASL configuration did not allow the " + " ZooKeeper server to authenticate itself properly: " + e); } } + + /** + * User who has started the ZooKeeper server user, it will be the logged-in + * user. If no user logged-in then system user + */ + public static String getUserName() { + return loginUser; + } } diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java index 12b8dcd9204..cb9a1cc4ff4 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java @@ -25,6 +25,8 @@ import javax.management.JMException; import org.apache.yetus.audience.InterfaceAudience; +import org.apache.zookeeper.audit.AuditConstants; +import org.apache.zookeeper.audit.ZKAuditLogger; import org.apache.zookeeper.jmx.ManagedUtil; import org.apache.zookeeper.server.admin.AdminServer; import org.apache.zookeeper.server.admin.AdminServer.AdminServerException; @@ -66,21 +68,26 @@ public static void main(String[] args) { LOG.error("Invalid arguments, exiting abnormally", e); LOG.info(USAGE); System.err.println(USAGE); + addServerStartFailureAuditLog(); System.exit(2); } catch (ConfigException e) { LOG.error("Invalid config, exiting abnormally", e); System.err.println("Invalid config, exiting abnormally"); + addServerStartFailureAuditLog(); System.exit(2); } catch (DatadirException e) { LOG.error("Unable to access datadir, exiting abnormally", e); System.err.println("Unable to access datadir, exiting abnormally"); + addServerStartFailureAuditLog(); System.exit(3); } catch (AdminServerException e) { LOG.error("Unable to start AdminServer, exiting abnormally", e); System.err.println("Unable to start AdminServer, exiting abnormally"); + addServerStartFailureAuditLog(); System.exit(4); } catch (Exception e) { LOG.error("Unexpected exception, exiting abnormally", e); + addServerStartFailureAuditLog(); System.exit(1); } LOG.info("Exiting normally"); @@ -156,6 +163,7 @@ public void runFromConfig(ServerConfig config) Integer.getInteger("znode.container.maxPerMinute", 10000) ); containerManager.start(); + ZKAuditLogger.addZKStartStopAuditLog(); // Watch status of ZooKeeper server. It will do a graceful shutdown // if the server is not running or hits an internal error. @@ -182,6 +190,10 @@ public void runFromConfig(ServerConfig config) } } + private static void addServerStartFailureAuditLog() { + ZKAuditLogger.logFailure(ZKAuditLogger.getZKUser(), AuditConstants.OP_START); + } + /** * Shutdown the serving instance */ diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java index 3da6e20edd4..b37a1f0b6ef 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java @@ -25,6 +25,8 @@ import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.zookeeper.audit.AuditConstants; +import org.apache.zookeeper.audit.ZKAuditLogger; import org.apache.zookeeper.jmx.ManagedUtil; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZKDatabase; @@ -84,27 +86,38 @@ public static void main(String[] args) { LOG.error("Invalid arguments, exiting abnormally", e); LOG.info(USAGE); System.err.println(USAGE); + addServerStartFailureAuditLog(); System.exit(2); } catch (ConfigException e) { LOG.error("Invalid config, exiting abnormally", e); System.err.println("Invalid config, exiting abnormally"); + addServerStartFailureAuditLog(); System.exit(2); } catch (DatadirException e) { LOG.error("Unable to access datadir, exiting abnormally", e); System.err.println("Unable to access datadir, exiting abnormally"); + addServerStartFailureAuditLog(); System.exit(3); } catch (AdminServerException e) { LOG.error("Unable to start AdminServer, exiting abnormally", e); System.err.println("Unable to start AdminServer, exiting abnormally"); + addServerStartFailureAuditLog(); System.exit(4); } catch (Exception e) { LOG.error("Unexpected exception, exiting abnormally", e); + addServerStartFailureAuditLog(); System.exit(1); } LOG.info("Exiting normally"); System.exit(0); } + private static void addServerStartFailureAuditLog() { + if (ZKAuditLogger.isAuditEnabled()) { + ZKAuditLogger.logFailure(ZKAuditLogger.getZKUser(), AuditConstants.OP_START); + } + } + protected void initializeAndRun(String[] args) throws ConfigException, IOException, AdminServerException { @@ -198,6 +211,7 @@ public void runFromConfig(QuorumPeerConfig config) quorumPeer.initialize(); quorumPeer.start(); + ZKAuditLogger.addZKStartStopAuditLog(); quorumPeer.join(); } catch (InterruptedException e) { // warn, but generally this is ok diff --git a/src/java/main/org/apache/zookeeper/server/util/AuthUtil.java b/src/java/main/org/apache/zookeeper/server/util/AuthUtil.java new file mode 100644 index 00000000000..abde0361c45 --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/util/AuthUtil.java @@ -0,0 +1,42 @@ +/** + * 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.zookeeper.server.util; + +import org.apache.zookeeper.data.Id; + +public class AuthUtil { + /** + * Gives user name + * + * @param id + * contains scheme and authentication info + * @return returns null if authentication scheme does not exist or + * authentication provider returns null as user + */ + public static String getUser(Id id) { + if ("digest".equals(id.getScheme())) { + /** + * format is already enforced in server code. so no need to check it + * again, just assume it is in correct format + */ + return id.getId().split(":")[0]; + } else { + return id.getId(); + } + } +} diff --git a/src/java/test/org/apache/zookeeper/audit/AuditLogPerfReading.java b/src/java/test/org/apache/zookeeper/audit/AuditLogPerfReading.java new file mode 100644 index 00000000000..46d4e6534eb --- /dev/null +++ b/src/java/test/org/apache/zookeeper/audit/AuditLogPerfReading.java @@ -0,0 +1,74 @@ +/** + * 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.zookeeper.audit; + +/** + * Audit log performance reading + */ +public class AuditLogPerfReading { + // time taken by create operations + private long create; + // time taken by setData operations + private long setData; + // time taken by delete operations + private long delete; + + public long getCreate() { + return create; + } + + public void setCreate(long create) { + this.create = create; + } + + public long getSetData() { + return setData; + } + + public void setSetData(long setData) { + this.setData = setData; + } + + public long getDelete() { + return delete; + } + + public void setDelete(long delete) { + this.delete = delete; + } + + public String report() { + StringBuilder builder = new StringBuilder(); + builder.append("create="); + builder.append(create); + builder.append(" ms\n"); + builder.append("setData="); + builder.append(setData); + builder.append(" ms\n"); + builder.append("delete="); + builder.append(delete); + builder.append(" ms\n"); + return builder.toString(); + } + + @Override + public String toString() { + return "create=" + create + ", setData=" + setData + ", delete=" + + delete; + } +} \ No newline at end of file diff --git a/src/java/test/org/apache/zookeeper/audit/ZKAuditLogFormatterTest.java b/src/java/test/org/apache/zookeeper/audit/ZKAuditLogFormatterTest.java new file mode 100644 index 00000000000..4d30b1e19e1 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/audit/ZKAuditLogFormatterTest.java @@ -0,0 +1,45 @@ +/** + * 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.zookeeper.audit; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class ZKAuditLogFormatterTest { + + @Test + public void testFormat() { + ZKAuditLogFormatter formatter = new ZKAuditLogFormatter(); + formatter.addField("k1", "Value1"); + formatter.addField("k2", "Value2"); + String actual = formatter.format(); + String expected = "k1=Value1\tk2=Value2"; + assertEquals(expected, actual); + } + + @Test + public void testFormatShouldIgnoreKeyIfValueIsNull() { + ZKAuditLogFormatter formatter = new ZKAuditLogFormatter(); + formatter.addField("k1", null); + formatter.addField("k2", "Value2"); + String actual = formatter.format(); + String expected = "k2=Value2"; + assertEquals(expected, actual); + } +} diff --git a/src/java/test/org/apache/zookeeper/audit/ZKAuditLoggerPerformance.java b/src/java/test/org/apache/zookeeper/audit/ZKAuditLoggerPerformance.java new file mode 100644 index 00000000000..20ebf81e4b7 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/audit/ZKAuditLoggerPerformance.java @@ -0,0 +1,151 @@ +/** + * 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.zookeeper.audit; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.common.Time; +import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.test.ClientBase.CountdownWatcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ZKAuditLoggerPerformance { + private static final Logger LOG = LoggerFactory + .getLogger(ZKAuditLoggerPerformance.class); + private ZooKeeper zkClient; + private String parentPath; + private int numberOfRecords; + + public ZKAuditLoggerPerformance(ZooKeeper zkClient, String parentPath, + int numberOfRecords) { + this.zkClient = zkClient; + this.parentPath = parentPath; + this.numberOfRecords = numberOfRecords; + } + + public void create() throws Exception { + for (int i = 0; i < numberOfRecords; i++) { + zkClient.create(getPath(i), "0123456789".getBytes(), + Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + + } + } + + public void setData() throws Exception { + for (int i = 0; i < numberOfRecords; i++) { + zkClient.setData(getPath(i), "9876543210".getBytes(), -1); + } + } + + public void delete() throws Exception { + for (int i = 0; i < numberOfRecords; i++) { + zkClient.delete(getPath(i), -1); + } + } + + public AuditLogPerfReading doOperations() throws Exception { + AuditLogPerfReading perfReading = new AuditLogPerfReading(); + // create + long startTime = Time.currentElapsedTime(); + create(); + perfReading.setCreate(Time.currentElapsedTime() - startTime); + + // setData + startTime = Time.currentElapsedTime(); + setData(); + perfReading.setSetData(Time.currentElapsedTime() - startTime); + + // delete + startTime = Time.currentElapsedTime(); + delete(); + perfReading.setDelete(Time.currentElapsedTime() - startTime); + return perfReading; + } + + private String getPath(int i) { + return parentPath + "zNode" + i; + } + + public static void main(String[] args) { + if (args.length != 3) { + System.err.println( + "USAGE: ZKAuditLoggerPerformance connectionString parentPath numberOfRecords"); + System.exit(1); + } + String cxnString = args[0]; + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zkClient = null; + try { + zkClient = new ZooKeeper(cxnString, 60000, watcher); + watcher.waitForConnected(30000); + } catch (InterruptedException | TimeoutException | IOException e) { + String msg = "ZooKeeper client can not connect to " + cxnString; + logErrorAndExit(e, msg); + } + String parentPath = args[1]; + try { + Stat exists = zkClient.exists(parentPath, false); + if (exists == null) { + System.err.println( + "Parent path '" + parentPath + "' must exist."); + System.exit(1); + } + } catch (KeeperException | InterruptedException e1) { + String msg = "Error while checking the existence of parent path"; + logErrorAndExit(e1, msg); + } + int recordCount = 0; + try { + recordCount = Integer.parseInt(args[2]); + } catch (NumberFormatException e) { + String msg = "Failed to parse '" + args[2] + "' to integer"; + LOG.error(msg, e); + System.err.println(msg); + System.exit(1); + } + ZKAuditLoggerPerformance auditLoggingPerf = new ZKAuditLoggerPerformance( + zkClient, + parentPath, recordCount); + AuditLogPerfReading doOperations = null; + try { + doOperations = auditLoggingPerf.doOperations(); + } catch (Exception e) { + String msg = "Error while doing operations."; + LOG.error(msg, e); + System.err.println(msg); + System.exit(1); + } + System.out + .println("Time taken for " + recordCount + " operations are:"); + System.out.println(doOperations.report()); + System.exit(0); + } + + private static void logErrorAndExit(Exception e, String msg) { + LOG.error(msg, e); + System.err.println(msg + ", error=" + e.getMessage()); + System.exit(1); + } +} diff --git a/src/java/test/org/apache/zookeeper/audit/ZKAuditLoggerTest.java b/src/java/test/org/apache/zookeeper/audit/ZKAuditLoggerTest.java new file mode 100644 index 00000000000..4210240e818 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/audit/ZKAuditLoggerTest.java @@ -0,0 +1,423 @@ +/** + * 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.zookeeper.audit; + +import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.StringReader; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Layout; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.log4j.SimpleLayout; +import org.apache.log4j.WriterAppender; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.KeeperException.Code; +import org.apache.zookeeper.Op; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.ZKUtil; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.server.Request; +import org.apache.zookeeper.server.ServerCnxn; +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; +import org.apache.zookeeper.test.ClientBase; +import org.apache.zookeeper.test.ClientBase.CountdownWatcher; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +public class ZKAuditLoggerTest extends QuorumPeerTestBase { + private static final Logger LOG = Logger.getLogger(ZKAuditLoggerTest.class); + private static int SERVER_COUNT = 3; + private MainThread[] mt; + private ZooKeeper zk; + + @Test + public void testAuditLogs() throws Exception { + System.setProperty(ZKAuditLogger.SYSPROP_AUDIT_ENABLE, "true"); + // setup the logger to capture all logs + Layout layout = new SimpleLayout(); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + WriterAppender appender = new WriterAppender(layout, os); + appender.setImmediateFlush(true); + appender.setThreshold(Level.INFO); + Logger zlogger = Logger.getLogger(ZKAuditLogger.class); + zlogger.addAppender(appender); + try { + mt = startQuorum(); + CountdownWatcher watcher = new CountdownWatcher(); + zk = new ZooKeeper( + "127.0.0.1:" + mt[0].getQuorumPeer().getClientPort(), + ClientBase.CONNECTION_TIMEOUT, watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + String expectedAuditLog = getStartLog(); + List logs = readAuditLog(os, SERVER_COUNT); + verifyLogs(expectedAuditLog, logs); + + String path = "/a"; + verifyCreateAuditLogs(os, path); + verifySetDataAuditLogs(os, path); + verifySetACLAuditLogs(os, path); + verifyMultiOperationAuditLogs(os); + verifyDeleteAuditLogs(os, path); + verifyEphemralZNodeAuditLogs(os); + + } finally { + zlogger.removeAppender(appender); + os.close(); + } + } + + private void verifyEphemralZNodeAuditLogs(ByteArrayOutputStream os) + throws Exception { + String ephemralPath = "/ephemral"; + CountdownWatcher watcher2 = new CountdownWatcher(); + ZooKeeper zk2 = new ZooKeeper( + "127.0.0.1:" + mt[0].getQuorumPeer().getClientPort(), + ClientBase.CONNECTION_TIMEOUT, watcher2); + watcher2.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + zk2.create(ephemralPath, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.EPHEMERAL); + String session2 = "0x" + Long.toHexString(zk2.getSessionId()); + verifyLog(getAuditLog(AuditConstants.OP_CREATE, ephemralPath, + AuditConstants.SUCCESS, null, + session2), readAuditLog(os)); + zk2.close(); + waitForDeletion(zk, ephemralPath, 100); + // verify that ephemeral node deletion on session close are captured + // in audit log + // Because these operations are done by ZooKeeper server itself, + // there are no IP user is zkServer user, not any client user + verifyLogs( + getAuditLog(AuditConstants.OP_DEL_EZNODE_EXP, ephemralPath, + AuditConstants.SUCCESS, + null, session2, ZKAuditLogger.getZKUser(), null), + readAuditLog(os, SERVER_COUNT)); + } + + private void verifyMultiOperationAuditLogs(ByteArrayOutputStream os) + throws InterruptedException, KeeperException, IOException { + List ops = new ArrayList(); + + String multiop = "/b"; + Op create = Op.create(multiop, "".getBytes(), + ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + Op setData = Op.setData(multiop, "newData".getBytes(), -1); + // check does nothing so it is audit logged + Op check = Op.check(multiop, -1); + Op delete = Op.delete(multiop, -1); + + ops.add(create); + ops.add(setData); + ops.add(check); + ops.add(delete); + + zk.multi(ops); + List multiOpLogs = readAuditLog(os, 3); + // verify that each multi operation success is logged + verifyLog(getAuditLog(AuditConstants.OP_CREATE, multiop), + multiOpLogs.get(0)); + verifyLog(getAuditLog(AuditConstants.OP_SETDATA, multiop), + multiOpLogs.get(1)); + verifyLog(getAuditLog(AuditConstants.OP_DELETE, multiop), + multiOpLogs.get(2)); + + ops = new ArrayList(); + ops.add(create); + ops.add(create); + try { + zk.multi(ops); + } catch (KeeperException exception) { + Code code = exception.code(); + assertEquals(Code.NODEEXISTS, code); + } + + // Verify that multi operation failure is logged, and there is no path + // mentioned in the audit log + verifyLog(getAuditLog(AuditConstants.OP_MULTI_OP, null, + AuditConstants.FAILURE), + readAuditLog(os)); + } + + private void verifyDeleteAuditLogs(ByteArrayOutputStream os, String path) + throws InterruptedException, IOException, KeeperException { + try { + zk.delete(path, -100); + } catch (KeeperException exception) { + Code code = exception.code(); + assertEquals(Code.BADVERSION, code); + } + verifyLog(getAuditLog(AuditConstants.OP_DELETE, path, + AuditConstants.FAILURE), + readAuditLog(os)); + zk.delete(path, -1); + verifyLog(getAuditLog(AuditConstants.OP_DELETE, path), + readAuditLog(os)); + } + + private void verifySetACLAuditLogs(ByteArrayOutputStream os, String path) + throws InterruptedException, IOException, KeeperException { + ArrayList openAclUnsafe = ZooDefs.Ids.OPEN_ACL_UNSAFE; + try { + zk.setACL(path, openAclUnsafe, -100); + } catch (KeeperException exception) { + Code code = exception.code(); + assertEquals(Code.BADVERSION, code); + } + verifyLog(getAuditLog(AuditConstants.OP_SETACL, path, + AuditConstants.FAILURE, + ZKUtil.aclToString(openAclUnsafe)), readAuditLog(os)); + zk.setACL(path, openAclUnsafe, -1); + verifyLog(getAuditLog(AuditConstants.OP_SETACL, path, + AuditConstants.SUCCESS, + ZKUtil.aclToString(openAclUnsafe)), readAuditLog(os)); + } + + private void verifySetDataAuditLogs(ByteArrayOutputStream os, String path) + throws InterruptedException, IOException, KeeperException { + try { + zk.setData(path, "newData".getBytes(), -100); + } catch (KeeperException exception) { + Code code = exception.code(); + assertEquals(Code.BADVERSION, code); + } + verifyLog(getAuditLog(AuditConstants.OP_SETDATA, path, + AuditConstants.FAILURE), + readAuditLog(os)); + zk.setData(path, "newdata".getBytes(), -1); + verifyLog(getAuditLog(AuditConstants.OP_SETDATA, path), + readAuditLog(os)); + } + + private void verifyCreateAuditLogs(ByteArrayOutputStream os, String path) + throws KeeperException, InterruptedException, IOException { + zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + // success log + verifyLog(getAuditLog(AuditConstants.OP_CREATE, path), + readAuditLog(os)); + try { + zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } catch (KeeperException exception) { + Code code = exception.code(); + assertEquals(Code.NODEEXISTS, code); + } + // Verify create operation log + verifyLog(getAuditLog(AuditConstants.OP_CREATE, path, + AuditConstants.FAILURE), + readAuditLog(os)); + } + + private String getStartLog() { + /** + *
+         * user=userName operation=ZooKeeperServer start  result=success
+         * 
+ */ + return ZKAuditLogger.createLog(ZKAuditLogger.getZKUser(), + AuditConstants.OP_START, null, + null, null, null, AuditConstants.SUCCESS); + } + + private String getAuditLog(String operation, String znode) { + return getAuditLog(operation, znode, AuditConstants.SUCCESS); + } + + private String getAuditLog(String operation, String znode, String status) { + return getAuditLog(operation, znode, status, null); + } + + private String getAuditLog(String operation, String znode, String status, + String acl) { + String session = getSession(); + return getAuditLog(operation, znode, status, acl, session); + } + + private String getAuditLog(String operation, String znode, String status, + String acl, + String session) { + String user = getUser(); + String ip = getIp(); + return getAuditLog(operation, znode, status, acl, session, user, ip); + } + + private String getAuditLog(String operation, String znode, String status, + String acl, + String session, String user, String ip) { + + String auditLog = ZKAuditLogger.createLog(user, operation, znode, acl, + session, ip, status); + LOG.info("expected audit log for operation '" + operation + "' is '" + + auditLog + "'"); + return auditLog; + } + + private String getSession() { + return "0x" + Long.toHexString(zk.getSessionId()); + } + + private String getUser() { + ServerCnxn next = getServerCnxn(); + Request request = new Request(next, -1, -1, -1, null, + next.getAuthInfo()); + return request.getUsers(); + } + + private String getIp() { + ServerCnxn next = getServerCnxn(); + InetSocketAddress remoteSocketAddress = next.getRemoteSocketAddress(); + InetAddress address = remoteSocketAddress.getAddress(); + return address.getHostAddress(); + } + + private ServerCnxn getServerCnxn() { + Iterable connections = mt[0].getQuorumPeer() + .getActiveServer() + .getServerCnxnFactory().getConnections(); + ServerCnxn next = connections.iterator().next(); + return next; + } + + private void verifyLog(String expectedLog, String log) { + String searchString = " - "; + int logStartIndex = log.indexOf(searchString); + String auditLog = log.substring(logStartIndex + searchString.length()); + assertEquals(expectedLog, auditLog); + + } + + private void verifyLogs(String expectedLog, List logs) { + for (String log : logs) { + verifyLog(expectedLog, log); + } + } + + private String readAuditLog(ByteArrayOutputStream os) throws IOException { + return readAuditLog(os, 1).get(0); + } + + private List readAuditLog(ByteArrayOutputStream os, + int numberOfLogEntry) + throws IOException { + return readAuditLog(os, numberOfLogEntry, false); + } + + private List readAuditLog(ByteArrayOutputStream os, + int numberOfLogEntry, + boolean skipEphemralDeletion) throws IOException { + List logs = new ArrayList(); + LineNumberReader r = new LineNumberReader( + new StringReader(os.toString())); + String line = ""; + while ((line = r.readLine()) != null) { + if (skipEphemralDeletion + && line.contains(AuditConstants.OP_DEL_EZNODE_EXP)) { + continue; + } + logs.add(line); + } + os.reset(); + assertEquals( + "Expected number of log entries are not generated. Logs are " + + logs, + numberOfLogEntry, logs.size()); + return logs; + + } + + private MainThread[] startQuorum() throws IOException { + final int clientPorts[] = new int[SERVER_COUNT]; + StringBuilder sb = new StringBuilder(); + String server; + + for (int i = 0; i < SERVER_COUNT; i++) { + clientPorts[i] = PortAssignment.unique(); + server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + + ":" + + PortAssignment.unique() + ":participant;127.0.0.1:" + + clientPorts[i]; + sb.append(server + "\n"); + + } + String currentQuorumCfgSection = sb.toString(); + MainThread mt[] = new MainThread[SERVER_COUNT]; + + // start all the servers + for (int i = 0; i < SERVER_COUNT; i++) { + mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, + false); + mt[i].start(); + } + + // ensure all servers started + for (int i = 0; i < SERVER_COUNT; i++) { + Assert.assertTrue("waiting for server " + i + " being up", + ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], + CONNECTION_TIMEOUT)); + } + return mt; + } + + private void waitForDeletion(ZooKeeper zooKeeper, String path, long timeout) + throws Exception { + long elapsedTime = 0; + long waitInterval = 10; + Stat exists = zooKeeper.exists(path, false); + while (exists != null && elapsedTime < timeout) { + try { + Thread.sleep(waitInterval); + } catch (InterruptedException e) { + Assert.fail("CurrentEpoch update failed"); + } + elapsedTime = elapsedTime + waitInterval; + exists = zooKeeper.exists(path, false); + } + Assert.assertNull("Node " + path + " not deleted in " + timeout + " ms", + exists); + } + + @After + public void tearDown() { + System.clearProperty(ZKAuditLogger.SYSPROP_AUDIT_ENABLE); + System.clearProperty("zookeeper.admin.enableServer"); + for (int i = 0; i < SERVER_COUNT; i++) { + try { + if (mt[i] != null) { + mt[i].shutdown(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + +} diff --git a/src/java/test/org/apache/zookeeper/server/util/AuthUtilTest.java b/src/java/test/org/apache/zookeeper/server/util/AuthUtilTest.java new file mode 100644 index 00000000000..5688923bc7f --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/util/AuthUtilTest.java @@ -0,0 +1,57 @@ +/** + * 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.zookeeper.server.util; + +import static org.junit.Assert.*; + +import org.apache.zookeeper.data.Id; +import org.junit.Test; + +public class AuthUtilTest { + + @Test + public void testGetUserFromAllAuthenticationScheme() { + System.setProperty("zookeeper.authProvider.sasl", + "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); + System.setProperty("zookeeper.authProvider.x509", + "org.apache.zookeeper.server.auth.X509AuthenticationProvider"); + + String user = "zkUser"; + Id id = new Id("digest", user + ":password"); + String result = AuthUtil.getUser(id); + assertEquals(user, result); + + String principal = "zkCli/hadoop.hadoop.com"; + id = new Id("sasl", principal); + assertEquals(principal, AuthUtil.getUser(id)); + + String ip = "192.168.1.2"; + id = new Id("ip", ip); + assertEquals(ip, AuthUtil.getUser(id)); + + String certificate = "CN=host-192.168.1.2,OU=OrganizationUnit,O=Organization,L=Location,ST=State,C=IN"; + id = new Id("x509", certificate); + assertEquals(certificate, AuthUtil.getUser(id)); + } + + public void testGetUserShouldReturnNullIfAuthenticationNotConfigured() { + Id id = new Id("invalid Authentication Scheme", "user"); + String result = AuthUtil.getUser(id); + assertNull(result); + } +} diff --git a/zookeeper-docs/src/documentation/content/xdocs/index.xml b/zookeeper-docs/src/documentation/content/xdocs/index.xml index 969e482cabe..e067e39d237 100644 --- a/zookeeper-docs/src/documentation/content/xdocs/index.xml +++ b/zookeeper-docs/src/documentation/content/xdocs/index.xml @@ -66,6 +66,7 @@
  • Hierarchical quorums
  • Observers - non-voting ensemble members that easily improve ZooKeeper's scalability
  • Dynamic Reconfiguration - a guide on how to use dynamic reconfiguration in ZooKeeper
  • +
  • Audit Logging - a guide on how to configure audit logs in ZooKeeper Server and what contents are logged.
  • diff --git a/zookeeper-docs/src/documentation/content/xdocs/site.xml b/zookeeper-docs/src/documentation/content/xdocs/site.xml index 614fa6cc67b..1f36f7590d5 100644 --- a/zookeeper-docs/src/documentation/content/xdocs/site.xml +++ b/zookeeper-docs/src/documentation/content/xdocs/site.xml @@ -52,6 +52,7 @@ See http://forrest.apache.org/docs/linking.html for more info. + diff --git a/zookeeper-docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/zookeeper-docs/src/documentation/content/xdocs/zookeeperAdmin.xml index d82e2347407..131229349ef 100644 --- a/zookeeper-docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/zookeeper-docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -974,6 +974,19 @@ server.3=zoo3:2888:3888 than what was allowed in 3.5.3 (1152921504606846975) + + audit.enable + + (Java system property: + zookeeper.audit.enable) + + + New in 3.5.5: + By default audit logs are disabled. Set to "true" to enable it. Default value is "false". See + ZooKeeper audit logs for more information. + + + diff --git a/zookeeper-docs/src/documentation/content/xdocs/zookeeperAuditLogs.xml b/zookeeper-docs/src/documentation/content/xdocs/zookeeperAuditLogs.xml new file mode 100644 index 00000000000..4771b63533b --- /dev/null +++ b/zookeeper-docs/src/documentation/content/xdocs/zookeeperAuditLogs.xml @@ -0,0 +1,205 @@ + + + +
    + ZooKeeper Audit Logging + + + Licensed 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. + + + + This document contains information about Audit Logs in ZooKeeper. + + +
    + ZooKeeper Audit Logs + Apache ZooKeeper supports audit logs form version 3.5.5. By default audit logs are disabled. To enable audit + logs configure audit.enable=true in conf/zoo.cfg. Audit logs are not logged on all the ZooKeeper servers, but logged + only on the servers where client is connected as depicted in below figure. + + + + + + The audit log captures detailed information for the operations that are selected to be audited. The audit + information is written as a set of key=value pairs for the following keys + + Audit Log Content + + + + Key + Value + + + + + session + client session id + + + user + + comma separated list of users who are associate with a client session. To know who is taken as user in audit logs + refer section + + + + + ip + client IP address + + + operation + any one of the selected operations for audit. Possible values are + (serverStart| serverStop| create| delete| setData| setAcl| multiOperation| reconfig| ephemeralZNodeDeleteOnSessionClose) + + + + znode + path of the znode + + + acl + String representation of znode ACL like cdrwa(create, delete,read, write, admin). This is logged + only for setAcl operation + + + result + result of the operation. Possible values are (success|failure|invoked). Result "invoked" is used + for serverStop operation because stop is logged before ensuring that server actually stopped. + + + + +
    + Below are sample audit logs for all operations, where client is connected from 192.168.1.2, client principal is + zkcli@HADOOP.COM, server principal is zookeeper/192.168.1.3@HADOOP.COM + + user=zookeeper/192.168.1.3 operation=serverStart result=success + session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=create znode=/a result=success + session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=create znode=/a result=failure + session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=setData znode=/a result=failure + session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=setData znode=/a result=success + session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=setAcl znode=/a acl=world:anyone:cdrwa result=failure + session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=setAcl znode=/a acl=world:anyone:cdrwa result=success + session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=create znode=/b result=success + session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=setData znode=/b result=success + session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=delete znode=/b result=success + session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=multiOperation result=failure + session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=delete znode=/a result=failure + session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=delete znode=/a result=success + session=0x19344730001 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=create znode=/ephemral result=success + session=0x19344730001 user=zookeeper/192.168.1.3 operation=ephemeralZNodeDeletionOnSessionCloseOrExpire znode=/ephemral result=success + session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=reconfig znode=/zookeeper/config result=success + user=zookeeper/192.168.1.3 operation=serverStop result=invoked + +
    +
    + ZooKeeper Audit Log Configuration + By default audit logs are disabled. To enable audit logs configure audit.enable=true in conf/zoo.cfg. Audit + logging is done using log4j. Following is the default log4j configuration for audit logs in conf/log4j.properties + + + # + # zk audit logging + # + zookeeper.auditlog.file=zookeeper_audit.log + zookeeper.auditlog.threshold=INFO + audit.logger=INFO, RFAAUDIT + log4j.logger.org.apache.zookeeper.audit.ZKAuditLogger=${audit.logger} + log4j.additivity.org.apache.zookeeper.audit.ZKAuditLogger=false + log4j.appender.RFAAUDIT=org.apache.log4j.RollingFileAppender + log4j.appender.RFAAUDIT.File=${zookeeper.log.dir}/${zookeeper.auditlog.file} + log4j.appender.RFAAUDIT.layout=org.apache.log4j.PatternLayout + log4j.appender.RFAAUDIT.layout.ConversionPattern=%d{ISO8601} %p %c{2}: %m%n + log4j.appender.RFAAUDIT.Threshold=${zookeeper.auditlog.threshold} + + # Max log file size of 10MB + log4j.appender.RFAAUDIT.MaxFileSize=10MB + log4j.appender.RFAAUDIT.MaxBackupIndex=10 + + Change above configuration to customize the auditlog file, number of backups, max file size etc. +
    +
    + Who is taken as user in audit logs? + By default there are only four authentication provider + + + IPAuthenticationProvider + + + SASLAuthenticationProvider + + + X509AuthenticationProvider + + + DigestAuthenticationProvider + + + User is decided based on the configured authentication provider. + + + When IPAuthenticationProvider is configured then authenticated IP is taken as user + + + When SASLAuthenticationProvider is configured then client principal is taken as user + + + + When X509AuthenticationProvider is configured then client certificate is taken as user + + + + When DigestAuthenticationProvider is configured then authenticated user is user + + + + For custom authentication provider whatever is stored in org.apache.zookeeper.data.Id.id is taken as user. + Generally only user name is stored in this field but it is up to the custom authentication provider what they store + in it. For audit logging value of org.apache.zookeeper.data.Id.id would be taken as user. + + In ZooKeeper Server not all the operations are done by clients but some operations are done by the server + itself. For example when client closes the session, ephemeral znodes are delete by the Server. These deletion are + not done by clients directly but it is done the server itself these are called system operations. For these system + operations the user associated with the ZooKeeper server are taken as user while audit logging these operations. For + example if in ZooKeeper server principal is zookeeper/hadoop.hadoop.com@HADOOP.COM then this becomes the system user + and all the system operations will be logged with this user name. + + user=zookeeper/hadoop.hadoop.com@HADOOP.COM operation=serverStart result=success + If there is no user associate with ZooKeeper server then the user who started the ZooKeeper server is taken as + the user. For example if server started by root then root is taken as the system user + + user=root operation=serverStart result=success + Single client can attach multiple authentication schemes to a session, in this case all authenticated schemes + will taken taken as user and will be presented as comma separated list. For example if a client is authenticate with + principal zkcli@HADOOP.COM and ip 127.0.0.1 then create znode audit log will be as: + session=0x10c0bcb0000 user=zkcli@HADOOP.COM,127.0.0.1 ip=127.0.0.1 operation=create znode=/a result=success +
    +
    diff --git a/zookeeper-docs/src/documentation/resources/images/zkAuditLogs.jpg b/zookeeper-docs/src/documentation/resources/images/zkAuditLogs.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fd6c376561017daf00514803e6dfb82fdf315553 GIT binary patch literal 28450 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 r!1_v#Gx8jN* Date: Thu, 13 Sep 2018 02:09:18 +0530 Subject: [PATCH 2/2] ZOOKEEPER-1260: Review comment fix --- src/java/main/org/apache/zookeeper/ZKUtil.java | 15 +++++++++++++++ .../zookeeper/server/FinalRequestProcessor.java | 11 ++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/ZKUtil.java b/src/java/main/org/apache/zookeeper/ZKUtil.java index 73fdf18780a..dfbfddb1b32 100644 --- a/src/java/main/org/apache/zookeeper/ZKUtil.java +++ b/src/java/main/org/apache/zookeeper/ZKUtil.java @@ -20,8 +20,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Deque; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.apache.zookeeper.AsyncCallback.StringCallback; import org.apache.zookeeper.AsyncCallback.VoidCallback; @@ -33,6 +35,7 @@ public class ZKUtil { private static final Logger LOG = LoggerFactory.getLogger(ZKUtil.class); + private static final Map permCache = new HashMap(); /** * Recursively delete the node with the given path. *

    @@ -176,6 +179,18 @@ private static void visitSubTreeDFSHelper(ZooKeeper zk, final String path, * @return string representation of permissions */ public static String getPermString(int perms) { + String permString = permCache.get(perms); + if (permString != null) { + return permString; + } + permString = constructPermString(perms); + if (!permString.isEmpty()) { + permCache.put(perms, permString); + } + return permString; + } + + private static String constructPermString(int perms) { StringBuilder p = new StringBuilder(); if ((perms & ZooDefs.Perms.CREATE) != 0) { p.append('c'); diff --git a/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java index 2acbb672524..3580939f414 100644 --- a/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java @@ -161,7 +161,7 @@ public void processRequest(Request request) { Record rsp = null; try { if (request.getHdr() != null && request.getHdr().getType() == OpCode.error) { - addFailedTxnAduitLog(request); + addFailedTxnAuditLog(request); /* * When local session upgrading is disabled, leader will * reject the ephemeral node creation due to session expire. @@ -295,8 +295,10 @@ public void processRequest(Request request) { lastOp = "SETA"; rsp = new SetACLResponse(rc.stat); err = Code.get(rc.err); - addAuditLog(request, cnxn, AuditConstants.OP_SETACL, rc.path, getACLs(request), - err); + /** Here audit enable check is done to avoid getACLs() call in case audit is disabled. */ + if (ZKAuditLogger.isAuditEnabled()) { + addAuditLog(request, cnxn, AuditConstants.OP_SETACL, rc.path, getACLs(request), err); + } break; } case OpCode.closeSession: { @@ -565,7 +567,7 @@ private String getACLs(Request request) { return ZKUtil.aclToString(setACLRequest.getAcl()); } - private void addFailedTxnAduitLog(Request request) { + private void addFailedTxnAuditLog(Request request) { if (!ZKAuditLogger.isAuditEnabled()) { return; } @@ -590,7 +592,6 @@ private void addFailedTxnAduitLog(Request request) { case OpCode.delete: case OpCode.deleteContainer: op = AuditConstants.OP_DELETE; - // path = new String(request.request.array()); DeleteRequest deleteRequest = new DeleteRequest(); ByteBufferInputStream.byteBuffer2Record(reqData, deleteRequest); path = deleteRequest.getPath();