Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@
import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus;
import org.apache.ratis.proto.RaftProtos.RaftPeerRole;
import org.apache.ratis.protocol.RaftGroupId;
import org.apache.ratis.protocol.RaftPeerId;
import org.apache.ratis.server.protocol.TermIndex;
import org.apache.ratis.util.ExitUtils;
import org.apache.ratis.util.FileUtils;
Expand Down Expand Up @@ -931,6 +932,7 @@ private OzoneDelegationTokenSecretManager createDelegationTokenSecretManager(
.setTokenRenewInterval(tokenRenewInterval)
.setTokenRemoverScanInterval(tokenRemoverScanInterval)
.setService(omRpcAddressTxt)
.setOzoneManager(this)
.setS3SecretManager(s3SecretManager)
.setCertificateClient(certClient)
.setOmServiceId(omNodeDetails.getServiceId())
Expand Down Expand Up @@ -1095,7 +1097,7 @@ private RPC.Server getRpcServer(OzoneConfiguration conf) throws IOException {
OzoneManagerService.newReflectiveBlockingService(omServerProtocol);

OMInterServiceProtocolServerSideImpl omInterServerProtocol =
new OMInterServiceProtocolServerSideImpl(omRatisServer,
new OMInterServiceProtocolServerSideImpl(this, omRatisServer,
isRatisEnabled);
BlockingService omInterService =
OzoneManagerInterService.newReflectiveBlockingService(
Expand Down Expand Up @@ -3919,6 +3921,34 @@ public boolean isLeaderReady() {
omRatisServer.checkLeaderStatus() == LEADER_AND_READY : true;
}

/**
* Check the leader status.
*
* @return null leader is ready
* OMLeaderNotReadyException leader is not ready
* OMNotLeaderException not leader
*/
public void checkLeaderStatus() throws OMNotLeaderException,
OMLeaderNotReadyException {
OzoneManagerRatisServer.RaftServerStatus raftServerStatus =
omRatisServer.checkLeaderStatus();
RaftPeerId raftPeerId = omRatisServer.getRaftPeerId();

switch (raftServerStatus) {
case LEADER_AND_READY: return;
case LEADER_AND_NOT_READY:
throw new OMLeaderNotReadyException(
raftPeerId.toString() + " is Leader " +
"but not ready to process request yet.");
case NOT_LEADER:
// TODO: Set suggest leaderID. Right now, client is not using suggest
// leaderID. Need to fix this.
throw new OMNotLeaderException(raftPeerId);
default: throw new IllegalStateException(
"Unknown Ratis Server state: " + raftServerStatus);
}
}

/**
* Return if Ratis is enabled or not.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.exceptions.OMLeaderNotReadyException;
import org.apache.hadoop.ozone.om.exceptions.OMNotLeaderException;
import org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServer.RaftServerStatus;
import org.apache.hadoop.ozone.om.request.BucketLayoutAwareOMKeyRequestFactory;
import org.apache.hadoop.ozone.om.request.bucket.OMBucketCreateRequest;
import org.apache.hadoop.ozone.om.request.bucket.OMBucketDeleteRequest;
Expand Down Expand Up @@ -85,7 +84,6 @@
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneObj.ObjectType;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
import org.apache.ratis.protocol.RaftPeerId;
import org.rocksdb.RocksDBException;

import java.io.IOException;
Expand Down Expand Up @@ -431,43 +429,13 @@ public static String getOMRatisSnapshotDirectory(ConfigurationSource conf) {
return snapshotDir;
}

public static void checkLeaderStatus(RaftServerStatus raftServerStatus,
RaftPeerId raftPeerId) throws ServiceException {
switch (raftServerStatus) {
case LEADER_AND_READY: return;

case LEADER_AND_NOT_READY: throw createLeaderNotReadyException(raftPeerId);

case NOT_LEADER: throw createNotLeaderException(raftPeerId);

default: throw new IllegalStateException(
"Unknown Ratis Server state: " + raftServerStatus);
public static void checkLeaderStatus(OzoneManager ozoneManager)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can drop this method.

throws ServiceException {
try {
ozoneManager.checkLeaderStatus();
} catch (OMNotLeaderException | OMLeaderNotReadyException e) {
LOG.debug(e.getMessage());
throw new ServiceException(e);
}
}

private static ServiceException createNotLeaderException(
RaftPeerId raftPeerId) {

// TODO: Set suggest leaderID. Right now, client is not using suggest
// leaderID. Need to fix this.

OMNotLeaderException notLeaderException =
new OMNotLeaderException(raftPeerId);

LOG.debug(notLeaderException.getMessage());

return new ServiceException(notLeaderException);
}

private static ServiceException createLeaderNotReadyException(
RaftPeerId raftPeerId) {

OMLeaderNotReadyException leaderNotReadyException =
new OMLeaderNotReadyException(raftPeerId.toString() + " is Leader " +
"but not ready to process request yet.");

LOG.debug(leaderNotReadyException.getMessage());

return new ServiceException(leaderNotReadyException);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public DecommissionOMResponse decommission(RpcController controller,
}

OzoneManagerRatisServer omRatisServer = ozoneManager.getOmRatisServer();
checkLeaderStatus(omRatisServer);
OzoneManagerRatisUtils.checkLeaderStatus(ozoneManager);

OMNodeDetails decommNode = ozoneManager.getPeerNode(request.getNodeId());
if (decommNode == null) {
Expand All @@ -107,10 +107,4 @@ public DecommissionOMResponse decommission(RpcController controller,
.setSuccess(true)
.build();
}

private void checkLeaderStatus(OzoneManagerRatisServer omRatisServer)
throws ServiceException {
OzoneManagerRatisUtils.checkLeaderStatus(omRatisServer.checkLeaderStatus(),
omRatisServer.getRaftPeerId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import com.google.protobuf.RpcController;
import com.google.protobuf.ServiceException;
import java.io.IOException;

import org.apache.hadoop.ozone.om.OzoneManager;
import org.apache.hadoop.ozone.om.helpers.OMNodeDetails;
import org.apache.hadoop.ozone.om.protocolPB.OMInterServiceProtocolPB;
import org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServer;
Expand All @@ -38,9 +40,11 @@ public class OMInterServiceProtocolServerSideImpl implements

private final OzoneManagerRatisServer omRatisServer;
private final boolean isRatisEnabled;
private final OzoneManager ozoneManager;

public OMInterServiceProtocolServerSideImpl(
public OMInterServiceProtocolServerSideImpl(OzoneManager ozoneMgr,
OzoneManagerRatisServer ratisServer, boolean enableRatis) {
this.ozoneManager = ozoneMgr;
this.omRatisServer = ratisServer;
this.isRatisEnabled = enableRatis;
}
Expand All @@ -60,7 +64,7 @@ public BootstrapOMResponse bootstrap(RpcController controller,
.build();
}

checkLeaderStatus();
OzoneManagerRatisUtils.checkLeaderStatus(ozoneManager);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why go through this nesting? alternatively we could ozoneManager.checkLeaderStatus

Suggested change
OzoneManagerRatisUtils.checkLeaderStatus(ozoneManager);
ozoneManager.checkLeaderStatus();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All protocol service API need throws ServiceException, while ozoneManager.checkLeaderStatus throws OMNotLeaderException and OMLeaderNotReadyException. So we need OzoneManagerRatisUtils.checkLeaderStatus wrapper to covert exceptions.

ozoneManager.checkLeaderStatus is used both in SASL path authentication and protocol level authentication. It throws the core exception. Let the caller wrap the core exception into desired outer exceptions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@neils-dev, could you help to take another look?

Copy link
Contributor

@neils-dev neils-dev May 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ChenSammi, thanks for your updated comment on handling the leader exceptions.

All protocol service API need throws ServiceException, while ozoneManager.checkLeaderStatus throws OMNotLeaderException and OMLeaderNotReadyException. So we need OzoneManagerRatisUtils.checkLeaderStatus wrapper to covert exceptions.

Having that, the OzoneManagerProtocolServerServerSideTranslatorPB.processRequest needs to throw a ServiceException should isRatisEnabled == true and it is not a leader. There looks to be two cases where this happens in processRequest:

  1. For the case that isRatisEnabled == true and s3Auth == false, that's what happens with your leader check OzoneManagerRatisUtils.checkLeaderStatus. It is handled.
  2. For the case that isRatisEnabled == true and request.hasS3Authentication == true it needs to be handled so that the ServiceException is thrown in the event of a leadership check exception. This looks to be currently wrapping the leadership exception in an IOException (InvalidToken) and returned to the caller through the error OmResponse. It is handled by the validateS3Credential (missed it on initial read). Thanks.

The javadoc for validateS3Credential can be updated to include the ServiceException that may be thrown.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @neils-dev, sorry for the late response. Just want to confirm that the comments left to address is the following, right?

The javadoc for validateS3Credential can be updated to include the ServiceException that may be thrown.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@neils-dev , the javadoc is updated. Would you like to take another look?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ChenSammi. Looks great. I tested the changes on the secure docker development clusters as well without issues - clients handle OMException raised as expected for authentication errors (S3) and client failovers for raised ServiceException wrapped OMNotALeader exceptions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @neils-dev . Do you think the PR can be merged now?

Copy link
Contributor

@neils-dev neils-dev Jun 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (I am unable to merge.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1


OMNodeDetails newOmNode = new OMNodeDetails.Builder()
.setOMNodeId(request.getNodeId())
Expand All @@ -82,9 +86,4 @@ public BootstrapOMResponse bootstrap(RpcController controller,
.setSuccess(true)
.build();
}

private void checkLeaderStatus() throws ServiceException {
OzoneManagerRatisUtils.checkLeaderStatus(omRatisServer.checkLeaderStatus(),
omRatisServer.getRaftPeerId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ private OMResponse processRequest(OMRequest request) throws
// if current OM is leader and then proceed with processing the request.
if (request.hasS3Authentication()) {
s3Auth = true;
checkLeaderStatus();
S3SecurityUtil.validateS3Credential(request, ozoneManager);
}
Copy link
Contributor

@neils-dev neils-dev May 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With validateS3Credential() checking the leader status when validating the password (S3Token) from the OMRequest, is the OzoneManagerRatisUtils.checkLeaderStatus() in the line above it unnecessary, redundant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct me if I'm wrong. With HDDS-4440 and HDDS-5881, S3 authentication will be carried as part of the OMRequest. It doesn't leverage the underline hadoop RPC SASL authentication, and these requests will not be processed by SASL. So the leader check is still needed here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ChenSammi, thanks for the comment and for the help you've given me when I started with ozone and ozone development.

With HDDS-4440 and HDDS-5881, S3 authentication will be carried as part of the OMRequest.

Yes, authentication is done using the S3Authentication fields of the OMRequest. This processRequest() in the ProtocalServerSideTranslatorPB is part of the path for processing requests with the authentication through S3SecurityUtil.validateS3Credential.

and these requests will not be processed by SASL. So the leader check is still needed here.

Yes, the leader check is needed here, however isn't the leader check done along with the call to S3SecurityUtil.validateS3Credentials as well? So in this code path the leader is checked twice. Once just prior to the call to S3SecurityUtil.ValidateS3Credential and once inside the call (within retrievePassword). Is the call to OzoneManagerRatisUtils.checkLeaderStatus(ozoneManager) still needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Will remove it.

} catch (IOException ex) {
Expand All @@ -181,7 +180,7 @@ private OMResponse processRequest(OMRequest request) throws
// To validate credentials we have already verified leader status.
// This will skip of checking leader status again if request has S3Auth.
if (!s3Auth) {
checkLeaderStatus();
OzoneManagerRatisUtils.checkLeaderStatus(ozoneManager);
}
try {
omClientRequest =
Expand Down Expand Up @@ -267,7 +266,8 @@ private ServiceException createLeaderNotReadyException() {
/**
* Submits request directly to OM.
*/
private OMResponse submitRequestDirectlyToOM(OMRequest request) {
private OMResponse submitRequestDirectlyToOM(OMRequest request) throws
ServiceException {
OMClientResponse omClientResponse = null;
long index = 0L;
try {
Expand Down Expand Up @@ -312,11 +312,6 @@ private OMResponse submitRequestDirectlyToOM(OMRequest request) {
return omClientResponse.getOMResponse();
}

private void checkLeaderStatus() throws ServiceException {
OzoneManagerRatisUtils.checkLeaderStatus(omRatisServer.checkLeaderStatus(),
omRatisServer.getRaftPeerId());
}

/**
* Create OMResponse from the specified OMRequest and exception.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@
import org.apache.hadoop.hdds.security.x509.exceptions.CertificateException;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.OzoneManager;
import org.apache.hadoop.ozone.om.S3SecretManager;
import org.apache.hadoop.ozone.om.S3SecretManagerImpl;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.exceptions.OMLeaderNotReadyException;
import org.apache.hadoop.ozone.om.exceptions.OMNotLeaderException;
import org.apache.hadoop.ozone.security.OzoneSecretStore.OzoneManagerSecretState;
import org.apache.hadoop.ozone.security.OzoneTokenIdentifier.TokenInfo;
import org.apache.hadoop.security.AccessControlException;
Expand Down Expand Up @@ -71,6 +74,7 @@ public class OzoneDelegationTokenSecretManager
private final long tokenRemoverScanInterval;
private String omCertificateSerialId;
private String omServiceId;
private final OzoneManager ozoneManager;

/**
* If the delegation token update thread holds this lock, it will not get
Expand All @@ -94,6 +98,7 @@ public OzoneDelegationTokenSecretManager(Builder b) throws IOException {
this.s3SecretManager = (S3SecretManagerImpl) b.s3SecretManager;
this.store = new OzoneSecretStore(b.ozoneConf,
this.s3SecretManager.getOmMetadataManager());
this.ozoneManager = b.ozoneManager;
isRatisEnabled = b.ozoneConf.getBoolean(
OMConfigKeys.OZONE_OM_RATIS_ENABLE_KEY,
OMConfigKeys.OZONE_OM_RATIS_ENABLE_DEFAULT);
Expand All @@ -113,6 +118,7 @@ public static class Builder {
private S3SecretManager s3SecretManager;
private CertificateClient certClient;
private String omServiceId;
private OzoneManager ozoneManager;

public OzoneDelegationTokenSecretManager build() throws IOException {
return new OzoneDelegationTokenSecretManager(this);
Expand Down Expand Up @@ -157,6 +163,11 @@ public Builder setOmServiceId(String serviceId) {
this.omServiceId = serviceId;
return this;
}

public Builder setOzoneManager(OzoneManager ozoneMgr) {
this.ozoneManager = ozoneMgr;
return this;
}
}

@Override
Expand Down Expand Up @@ -404,6 +415,18 @@ public void removeToken(OzoneTokenIdentifier ozoneTokenIdentifier) {
@Override
public byte[] retrievePassword(OzoneTokenIdentifier identifier)
throws InvalidToken {
// Tokens are a bit different in that a follower OM may be behind and
// thus not yet know of all tokens issued by the leader OM. the
// following check does not allow ANY token auth. In optimistic, it should
// allow known tokens in.
try {
ozoneManager.checkLeaderStatus();
} catch (OMNotLeaderException | OMLeaderNotReadyException e) {
InvalidToken wrappedStandby = new InvalidToken("IOException");
wrappedStandby.initCause(e);
throw wrappedStandby;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ChenSammi. I see that retrievePassword method now not only throws an exception when the s3 user cannot be authenticated but now also when the OM is not the leader. With this new InvaidToken exception (OMNotLeader / NotReady exception), the users of the retrievePassword may not accurately process the error.
Do we need to change the S3SecurityUtil.validateS3Credential handling of an exception thrown by its call to retrievePassword?

Copy link
Contributor Author

@ChenSammi ChenSammi May 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense. Will take care of that in next patch.


if (identifier.getTokenType().equals(S3AUTHINFO)) {
return validateS3AuthInfo(identifier);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@

package org.apache.hadoop.ozone.security;

import com.google.protobuf.ServiceException;
import org.apache.hadoop.hdds.annotation.InterfaceAudience;
import org.apache.hadoop.hdds.annotation.InterfaceStability;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.ozone.om.OzoneManager;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.exceptions.OMLeaderNotReadyException;
import org.apache.hadoop.ozone.om.exceptions.OMNotLeaderException;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.S3Authentication;
import org.apache.hadoop.ozone.protocolPB.OzoneManagerProtocolServerSideTranslatorPB;
Expand All @@ -44,19 +47,26 @@ private S3SecurityUtil() {
/**
* Validate S3 Credentials which are part of {@link OMRequest}.
*
* If validation is successful returns, else throw {@link OMException}
* @throws OMException
* If validation is successful returns, else throw exception.
* @throws OMException validation failure
* ServiceException Server is not leader or not ready
*/
public static void validateS3Credential(OMRequest omRequest,
OzoneManager ozoneManager) throws OMException {
OzoneManager ozoneManager) throws ServiceException, OMException {
if (ozoneManager.isSecurityEnabled()) {
OzoneTokenIdentifier s3Token = constructS3Token(omRequest);
try {
// authenticate user with signature verification through
// delegationTokenMgr validateToken via retrievePassword
ozoneManager.getDelegationTokenMgr().retrievePassword(s3Token);
} catch (SecretManager.InvalidToken e) {
// TODO: Just check are we okay to log enitre token in failure case.
if (e.getCause() != null &&
(e.getCause().getClass() == OMNotLeaderException.class ||
e.getCause().getClass() == OMLeaderNotReadyException.class)) {
throw new ServiceException(e.getCause());
}

// TODO: Just check are we okay to log entire token in failure case.
OzoneManagerProtocolServerSideTranslatorPB.getLog().error(
"signatures do NOT match for S3 identifier:{}", s3Token, e);
throw new OMException("User " + s3Token.getAwsAccessId()
Expand Down
Loading