HDDS-1605. Implement AuditLogging for OM HA Bucket write requests. (#867)

This commit is contained in:
Bharat Viswanadham 2019-06-06 10:13:36 -07:00 committed by Hanisha Koneru
parent 1f244b4227
commit 0b8a2608e0
10 changed files with 218 additions and 31 deletions

View File

@ -73,4 +73,14 @@ public class AuditLogger {
msg.getThrowable());
}
public void logWrite(AuditMessage auditMessage) {
if (auditMessage.getThrowable() == null) {
this.logger.logIfEnabled(FQCN, Level.INFO, WRITE_MARKER, auditMessage,
auditMessage.getThrowable());
} else {
this.logger.logIfEnabled(FQCN, Level.ERROR, WRITE_MARKER, auditMessage,
auditMessage.getThrowable());
}
}
}

View File

@ -2458,6 +2458,10 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
return auditMap;
}
public AuditLogger getAuditLogger() {
return AUDIT;
}
@Override
public AuditMessage buildAuditMessageForSuccess(AuditAction op,
Map<String, String> auditMap) {

View File

@ -20,11 +20,18 @@ package org.apache.hadoop.ozone.om.request;
import java.io.IOException;
import java.net.InetAddress;
import java.util.LinkedHashMap;
import java.util.Map;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.apache.hadoop.ipc.ProtobufRpcEngine;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.audit.AuditAction;
import org.apache.hadoop.ozone.audit.AuditEventStatus;
import org.apache.hadoop.ozone.audit.AuditLogger;
import org.apache.hadoop.ozone.audit.AuditMessage;
import org.apache.hadoop.ozone.om.OzoneManager;
import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerRatisUtils;
import org.apache.hadoop.ozone.om.response.OMClientResponse;
@ -41,7 +48,7 @@ import org.apache.hadoop.security.UserGroupInformation;
* OMClientRequest provides methods which every write OM request should
* implement.
*/
public abstract class OMClientRequest {
public abstract class OMClientRequest implements RequestAuditor {
private final OMRequest omRequest;
@ -173,4 +180,34 @@ public abstract class OMClientRequest {
return omResponse.build();
}
/**
* Log the auditMessage.
* @param auditLogger
* @param auditMessage
*/
protected void auditLog(AuditLogger auditLogger, AuditMessage auditMessage) {
auditLogger.logWrite(auditMessage);
}
@Override
public AuditMessage buildAuditMessage(AuditAction op,
Map< String, String > auditMap, Throwable throwable,
OzoneManagerProtocolProtos.UserInfo userInfo) {
return new AuditMessage.Builder()
.setUser(userInfo != null ? userInfo.getUserName() : null)
.atIp(userInfo != null ? userInfo.getRemoteAddress() : null)
.forOperation(op.getAction())
.withParams(auditMap)
.withResult(throwable != null ? AuditEventStatus.FAILURE.toString() :
AuditEventStatus.SUCCESS.toString())
.withException(throwable)
.build();
}
@Override
public Map<String, String> buildVolumeAuditMap(String volume) {
Map<String, String> auditMap = new LinkedHashMap<>();
auditMap.put(OzoneConsts.VOLUME, volume);
return auditMap;
}
}

View File

@ -0,0 +1,50 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.hadoop.ozone.om.request;
import java.util.Map;
import org.apache.hadoop.ozone.audit.AuditAction;
import org.apache.hadoop.ozone.audit.AuditMessage;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
/**
* Interface for OM Requests to convert to audit objects.
*/
public interface RequestAuditor {
/**
* Build AuditMessage.
* @param op
* @param auditMap
* @param throwable
* @param userInfo
* @return
*/
AuditMessage buildAuditMessage(AuditAction op,
Map<String, String> auditMap, Throwable throwable,
OzoneManagerProtocolProtos.UserInfo userInfo);
/**
* Build auditMap with specified volume.
* @param volume
* @return auditMap.
*/
Map<String, String> buildVolumeAuditMap(String volume);
}

View File

@ -28,6 +28,8 @@ import org.apache.hadoop.crypto.CipherSuite;
import org.apache.hadoop.crypto.key.KeyProvider;
import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension;
import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.ozone.audit.AuditLogger;
import org.apache.hadoop.ozone.audit.OMAction;
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.ozone.om.OMMetrics;
import org.apache.hadoop.ozone.om.OzoneManager;
@ -117,6 +119,9 @@ public class OMBucketCreateRequest extends OMClientRequest {
OzoneManagerProtocolProtos.Status.OK);
OmBucketInfo omBucketInfo = OmBucketInfo.getFromProtobuf(bucketInfo);
AuditLogger auditLogger = ozoneManager.getAuditLogger();
OzoneManagerProtocolProtos.UserInfo userInfo = getOmRequest().getUserInfo();
try {
// check Acl
if (ozoneManager.getAclsEnabled()) {
@ -128,6 +133,8 @@ public class OMBucketCreateRequest extends OMClientRequest {
LOG.error("Bucket creation failed for bucket:{} in volume:{}",
bucketName, volumeName, ex);
omMetrics.incNumBucketCreateFails();
auditLog(auditLogger, buildAuditMessage(OMAction.CREATE_BUCKET,
omBucketInfo.toAuditMap(), ex, userInfo));
return new OMBucketCreateResponse(omBucketInfo,
createErrorOMResponse(omResponse, ex));
}
@ -135,6 +142,7 @@ public class OMBucketCreateRequest extends OMClientRequest {
String volumeKey = metadataManager.getVolumeKey(volumeName);
String bucketKey = metadataManager.getBucketKey(volumeName, bucketName);
IOException exception = null;
metadataManager.getLock().acquireVolumeLock(volumeName);
metadataManager.getLock().acquireBucketLock(volumeName, bucketName);
try {
@ -152,28 +160,36 @@ public class OMBucketCreateRequest extends OMClientRequest {
OMException.ResultCodes.BUCKET_ALREADY_EXISTS);
}
LOG.debug("created bucket: {} in volume: {}", bucketName, volumeName);
omMetrics.incNumBuckets();
// Update table cache.
metadataManager.getBucketTable().addCacheEntry(new CacheKey<>(bucketKey),
new CacheValue<>(Optional.of(omBucketInfo), transactionLogIndex));
// return response.
omResponse.setCreateBucketResponse(
CreateBucketResponse.newBuilder().build());
return new OMBucketCreateResponse(omBucketInfo, omResponse.build());
} catch (IOException ex) {
omMetrics.incNumBucketCreateFails();
LOG.error("Bucket creation failed for bucket:{} in volume:{}",
bucketName, volumeName, ex);
return new OMBucketCreateResponse(omBucketInfo,
createErrorOMResponse(omResponse, ex));
exception = ex;
} finally {
metadataManager.getLock().releaseBucketLock(volumeName, bucketName);
metadataManager.getLock().releaseVolumeLock(volumeName);
}
// Performing audit logging outside of the lock.
auditLog(auditLogger, buildAuditMessage(OMAction.CREATE_BUCKET,
omBucketInfo.toAuditMap(), exception, userInfo));
// return response.
if (exception == null) {
LOG.debug("created bucket: {} in volume: {}", bucketName, volumeName);
omMetrics.incNumBuckets();
omResponse.setCreateBucketResponse(
CreateBucketResponse.newBuilder().build());
return new OMBucketCreateResponse(omBucketInfo, omResponse.build());
} else {
omMetrics.incNumBucketCreateFails();
LOG.error("Bucket creation failed for bucket:{} in volume:{}",
bucketName, volumeName, exception);
return new OMBucketCreateResponse(omBucketInfo,
createErrorOMResponse(omResponse, exception));
}
}

View File

@ -19,14 +19,16 @@
package org.apache.hadoop.ozone.om.request.bucket;
import java.io.IOException;
import java.util.Map;
import com.google.common.base.Optional;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
import org.apache.hadoop.ozone.security.acl.OzoneObj;
import org.apache.hadoop.ozone.audit.AuditLogger;
import org.apache.hadoop.ozone.audit.OMAction;
import org.apache.hadoop.ozone.om.request.OMClientRequest;
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.ozone.om.OMMetrics;
@ -35,6 +37,8 @@ import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
import org.apache.hadoop.ozone.om.response.bucket.OMBucketDeleteResponse;
import org.apache.hadoop.ozone.om.response.OMClientResponse;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
.DeleteBucketResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
@ -71,6 +75,14 @@ public class OMBucketDeleteRequest extends OMClientRequest {
.setStatus(OzoneManagerProtocolProtos.Status.OK)
.setCmdType(omRequest.getCmdType());
AuditLogger auditLogger = ozoneManager.getAuditLogger();
Map<String, String> auditMap = buildVolumeAuditMap(volumeName);
auditMap.put(OzoneConsts.BUCKET, bucketName);
OzoneManagerProtocolProtos.UserInfo userInfo = getOmRequest().getUserInfo();
try {
// check Acl
if (ozoneManager.getAclsEnabled()) {
@ -82,10 +94,13 @@ public class OMBucketDeleteRequest extends OMClientRequest {
omMetrics.incNumBucketDeleteFails();
LOG.error("Delete bucket failed for bucket:{} in volume:{}", bucketName,
volumeName, ex);
auditLog(auditLogger, buildAuditMessage(OMAction.DELETE_BUCKET,
auditMap, ex, userInfo));
return new OMBucketDeleteResponse(volumeName, bucketName,
createErrorOMResponse(omResponse, ex));
}
IOException exception = null;
// acquire lock
omMetadataManager.getLock().acquireBucketLock(volumeName, bucketName);
try {
@ -113,20 +128,29 @@ public class OMBucketDeleteRequest extends OMClientRequest {
new CacheKey<>(bucketKey),
new CacheValue<>(Optional.absent(), transactionLogIndex));
// return response.
} catch (IOException ex) {
exception = ex;
} finally {
omMetadataManager.getLock().releaseBucketLock(volumeName, bucketName);
}
// Performing audit logging outside of the lock.
auditLog(auditLogger, buildAuditMessage(OMAction.DELETE_BUCKET,
auditMap, exception, userInfo));
// return response.
if (exception == null) {
LOG.debug("Deleted bucket:{} in volume:{}", bucketName, volumeName);
omResponse.setDeleteBucketResponse(
DeleteBucketResponse.newBuilder().build());
return new OMBucketDeleteResponse(volumeName, bucketName,
omResponse.build());
} catch (IOException ex) {
} else {
omMetrics.incNumBucketDeleteFails();
LOG.error("Delete bucket failed for bucket:{} in volume:{}", bucketName,
volumeName, ex);
volumeName, exception);
return new OMBucketDeleteResponse(volumeName, bucketName,
createErrorOMResponse(omResponse, ex));
} finally {
omMetadataManager.getLock().releaseBucketLock(volumeName, bucketName);
createErrorOMResponse(omResponse, exception));
}
}
}

View File

@ -22,6 +22,9 @@ import java.io.IOException;
import java.util.List;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import org.apache.hadoop.ozone.audit.AuditLogger;
import org.apache.hadoop.ozone.audit.OMAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -48,6 +51,8 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
.OMRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
.OMResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
.SetBucketPropertyRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
.SetBucketPropertyResponse;
import org.apache.hadoop.utils.db.cache.CacheKey;
@ -68,6 +73,11 @@ public class OMBucketSetPropertyRequest extends OMClientRequest {
public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager,
long transactionLogIndex) {
SetBucketPropertyRequest setBucketPropertyRequest =
getOmRequest().getSetBucketPropertyRequest();
Preconditions.checkNotNull(setBucketPropertyRequest);
OMMetrics omMetrics = ozoneManager.getOmMetrics();
// This will never be null, on a real Ozone cluster. For tests this might
@ -80,8 +90,7 @@ public class OMBucketSetPropertyRequest extends OMClientRequest {
OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
BucketArgs bucketArgs =
getOmRequest().getSetBucketPropertyRequest().getBucketArgs();
BucketArgs bucketArgs = setBucketPropertyRequest.getBucketArgs();
OmBucketArgs omBucketArgs = OmBucketArgs.getFromProtobuf(bucketArgs);
String volumeName = bucketArgs.getVolumeName();
@ -93,6 +102,9 @@ public class OMBucketSetPropertyRequest extends OMClientRequest {
OzoneManagerProtocolProtos.Status.OK);
OmBucketInfo omBucketInfo = null;
AuditLogger auditLogger = ozoneManager.getAuditLogger();
OzoneManagerProtocolProtos.UserInfo userInfo = getOmRequest().getUserInfo();
try {
// check Acl
if (ozoneManager.getAclsEnabled()) {
@ -104,12 +116,16 @@ public class OMBucketSetPropertyRequest extends OMClientRequest {
if (omMetrics != null) {
omMetrics.incNumBucketUpdateFails();
}
auditLog(auditLogger, buildAuditMessage(OMAction.UPDATE_BUCKET,
omBucketArgs.toAuditMap(), ex, userInfo));
LOG.error("Setting bucket property failed for bucket:{} in volume:{}",
bucketName, volumeName, ex);
return new OMBucketSetPropertyResponse(omBucketInfo,
createErrorOMResponse(omResponse, ex));
}
IOException exception = null;
// acquire lock
omMetadataManager.getLock().acquireBucketLock(volumeName, bucketName);
@ -170,21 +186,31 @@ public class OMBucketSetPropertyRequest extends OMClientRequest {
new CacheKey<>(bucketKey),
new CacheValue<>(Optional.of(omBucketInfo), transactionLogIndex));
// return response.
} catch (IOException ex) {
exception = ex;
} finally {
omMetadataManager.getLock().releaseBucketLock(volumeName, bucketName);
}
// Performing audit logging outside of the lock.
auditLog(auditLogger, buildAuditMessage(OMAction.UPDATE_BUCKET,
omBucketArgs.toAuditMap(), exception, userInfo));
// return response.
if (exception == null) {
LOG.debug("Setting bucket property for bucket:{} in volume:{}",
bucketName, volumeName);
omResponse.setSetBucketPropertyResponse(
SetBucketPropertyResponse.newBuilder().build());
return new OMBucketSetPropertyResponse(omBucketInfo, omResponse.build());
} catch (IOException ex) {
} else {
if (omMetrics != null) {
omMetrics.incNumBucketUpdateFails();
}
LOG.error("Setting bucket property failed for bucket:{} in volume:{}",
bucketName, volumeName, ex);
bucketName, volumeName, exception);
return new OMBucketSetPropertyResponse(omBucketInfo,
createErrorOMResponse(omResponse, ex));
} finally {
omMetadataManager.getLock().releaseBucketLock(volumeName, bucketName);
createErrorOMResponse(omResponse, exception));
}
}

View File

@ -30,6 +30,8 @@ import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.audit.AuditLogger;
import org.apache.hadoop.ozone.audit.AuditMessage;
import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.ozone.om.OMMetrics;
@ -48,6 +50,7 @@ import org.apache.hadoop.ozone.om.response.OMClientResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
import org.apache.hadoop.util.Time;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
/**
@ -61,6 +64,7 @@ public class TestOMBucketCreateRequest {
private OzoneManager ozoneManager;
private OMMetrics omMetrics;
private OMMetadataManager omMetadataManager;
private AuditLogger auditLogger;
@Before
@ -73,6 +77,9 @@ public class TestOMBucketCreateRequest {
omMetadataManager = new OmMetadataManagerImpl(ozoneConfiguration);
when(ozoneManager.getMetrics()).thenReturn(omMetrics);
when(ozoneManager.getMetadataManager()).thenReturn(omMetadataManager);
auditLogger = Mockito.mock(AuditLogger.class);
when(ozoneManager.getAuditLogger()).thenReturn(auditLogger);
Mockito.doNothing().when(auditLogger).logWrite(any(AuditMessage.class));
}
@After

View File

@ -30,6 +30,8 @@ import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.audit.AuditLogger;
import org.apache.hadoop.ozone.audit.AuditMessage;
import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.ozone.om.OMMetrics;
@ -43,6 +45,7 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
.OMRequest;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
/**
@ -56,6 +59,7 @@ public class TestOMBucketDeleteRequest {
private OzoneManager ozoneManager;
private OMMetrics omMetrics;
private OMMetadataManager omMetadataManager;
private AuditLogger auditLogger;
@Before
@ -68,6 +72,9 @@ public class TestOMBucketDeleteRequest {
omMetadataManager = new OmMetadataManagerImpl(ozoneConfiguration);
when(ozoneManager.getMetrics()).thenReturn(omMetrics);
when(ozoneManager.getMetadataManager()).thenReturn(omMetadataManager);
auditLogger = Mockito.mock(AuditLogger.class);
when(ozoneManager.getAuditLogger()).thenReturn(auditLogger);
Mockito.doNothing().when(auditLogger).logWrite(any(AuditMessage.class));
}
@After

View File

@ -30,6 +30,8 @@ import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.audit.AuditLogger;
import org.apache.hadoop.ozone.audit.AuditMessage;
import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.ozone.om.OMMetrics;
@ -45,6 +47,7 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
.SetBucketPropertyRequest;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
/**
@ -59,7 +62,7 @@ public class TestOMBucketSetPropertyRequest {
private OzoneManager ozoneManager;
private OMMetrics omMetrics;
private OMMetadataManager omMetadataManager;
private AuditLogger auditLogger;
@Before
public void setup() throws Exception {
@ -71,6 +74,9 @@ public class TestOMBucketSetPropertyRequest {
omMetadataManager = new OmMetadataManagerImpl(ozoneConfiguration);
when(ozoneManager.getMetrics()).thenReturn(omMetrics);
when(ozoneManager.getMetadataManager()).thenReturn(omMetadataManager);
auditLogger = Mockito.mock(AuditLogger.class);
when(ozoneManager.getAuditLogger()).thenReturn(auditLogger);
Mockito.doNothing().when(auditLogger).logWrite(any(AuditMessage.class));
}
@After