From 2ec296e659309d924c11d8aeb0e4e188fbea1fa2 Mon Sep 17 00:00:00 2001 From: Xiaoyu Yao Date: Fri, 25 Jan 2019 10:50:23 -0800 Subject: [PATCH] HDDS-991. Fix failures in TestSecureOzoneCluster. Contributed by Ajay Kumar. --- .../ozone/om/exceptions/OMException.java | 7 +- .../ozone/om/exceptions/package-info.java | 0 .../OzoneManagerSecurityProtocol.java | 14 +- ...ManagerProtocolClientSideTranslatorPB.java | 165 ++++++++++++------ .../OzoneDelegationTokenSecretManager.java | 10 +- .../src/main/proto/OzoneManagerProtocol.proto | 6 + .../hadoop/ozone/TestSecureOzoneCluster.java | 83 ++++++--- .../apache/hadoop/ozone/om/OzoneManager.java | 86 +++++---- ...ManagerProtocolServerSideTranslatorPB.java | 2 +- .../OzoneManagerRequestHandler.java | 11 +- 10 files changed, 255 insertions(+), 129 deletions(-) rename hadoop-ozone/{ozone-manager => common}/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java (97%) rename hadoop-ozone/{ozone-manager => common}/src/main/java/org/apache/hadoop/ozone/om/exceptions/package-info.java (100%) diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java similarity index 97% rename from hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java rename to hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java index 80463e3a7a..4af7d568df 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java @@ -125,6 +125,11 @@ public enum ResultCodes { COMPLETE_MULTIPART_UPLOAD_FAILED, ENTITY_TOO_SMALL, ABORT_MULTIPART_UPLOAD_FAILED, - INVALID_REQUEST; + INVALID_REQUEST, + INVALID_AUTH_METHOD, + INVALID_TOKEN, + TOKEN_EXPIRED, + TOKEN_ERROR_OTHER, + UNKNOWN } } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/exceptions/package-info.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/package-info.java similarity index 100% rename from hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/exceptions/package-info.java rename to hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/package-info.java diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerSecurityProtocol.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerSecurityProtocol.java index 15873d0b97..3e90899bd9 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerSecurityProtocol.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerSecurityProtocol.java @@ -17,13 +17,13 @@ */ package org.apache.hadoop.ozone.om.protocol; -import java.io.IOException; import org.apache.hadoop.io.Text; import org.apache.hadoop.io.retry.Idempotent; import org.apache.hadoop.ozone.om.OMConfigKeys; import org.apache.hadoop.ozone.security.OzoneTokenIdentifier; import org.apache.hadoop.security.KerberosInfo; import org.apache.hadoop.security.token.Token; +import org.apache.hadoop.ozone.om.exceptions.OMException; /** * Security protocol for a secure OzoneManager. @@ -37,31 +37,31 @@ public interface OzoneManagerSecurityProtocol { * * @param renewer the designated renewer for the token * @return Token - * @throws IOException + * @throws OMException */ @Idempotent Token getDelegationToken(Text renewer) - throws IOException; + throws OMException; /** * Renew an existing delegation token. * * @param token delegation token obtained earlier * @return the new expiration time - * @throws IOException + * @throws OMException */ @Idempotent long renewDelegationToken(Token token) - throws IOException; + throws OMException; /** * Cancel an existing delegation token. * * @param token delegation token - * @throws IOException + * @throws OMException */ @Idempotent void cancelDelegationToken(Token token) - throws IOException; + throws OMException; } diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java index e833f5e428..158395360e 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java @@ -41,7 +41,7 @@ import org.apache.hadoop.ozone.om.helpers.S3SecretValue; import org.apache.hadoop.ozone.om.helpers.ServiceInfo; import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; +import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.protocol.proto .OzoneManagerProtocolProtos.AllocateBlockRequest; import org.apache.hadoop.ozone.protocol.proto @@ -189,6 +189,12 @@ .OzoneManagerProtocolProtos.CancelDelegationTokenResponseProto; import org.apache.hadoop.security.token.Token; +import static org.apache.hadoop.ozone.om.exceptions.OMException.*; +import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_ERROR_OTHER; +import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.UNKNOWN; +import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status.OK; +import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status.ACCESS_DENIED; + /** * The client side implementation of OzoneManagerProtocol. */ @@ -288,7 +294,7 @@ public void createVolume(OmVolumeArgs args) throws IOException { CreateVolumeResponse resp = submitRequest(omRequest) .getCreateVolumeResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Volume creation failed, error:" + resp.getStatus()); } @@ -314,7 +320,7 @@ public void setOwner(String volume, String owner) throws IOException { SetVolumePropertyResponse resp = submitRequest(omRequest) .getSetVolumePropertyResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Volume owner change failed, error:" + resp.getStatus()); } @@ -340,7 +346,7 @@ public void setQuota(String volume, long quota) throws IOException { SetVolumePropertyResponse resp = submitRequest(omRequest) .getSetVolumePropertyResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Volume quota change failed, error:" + resp.getStatus()); } @@ -369,9 +375,9 @@ public boolean checkVolumeAccess(String volume, OzoneAclInfo userAcl) throws CheckVolumeAccessResponse resp = submitRequest(omRequest) .getCheckVolumeAccessResponse(); - if (resp.getStatus() == Status.ACCESS_DENIED) { + if (resp.getStatus() == ACCESS_DENIED) { return false; - } else if (resp.getStatus() == Status.OK) { + } else if (resp.getStatus() == OK) { return true; } else { throw new @@ -397,7 +403,7 @@ public OmVolumeArgs getVolumeInfo(String volume) throws IOException { InfoVolumeResponse resp = submitRequest(omRequest).getInfoVolumeResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Info Volume failed, error:" + resp.getStatus()); } @@ -422,7 +428,7 @@ public void deleteVolume(String volume) throws IOException { DeleteVolumeResponse resp = submitRequest(omRequest) .getDeleteVolumeResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Delete Volume failed, error:" + resp.getStatus()); } @@ -490,7 +496,7 @@ private List listVolume(ListVolumeRequest request) ListVolumeResponse resp = submitRequest(omRequest).getListVolumeResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("List volume failed, error: " + resp.getStatus()); } @@ -520,7 +526,7 @@ public void createBucket(OmBucketInfo bucketInfo) throws IOException { CreateBucketResponse resp = submitRequest(omRequest) .getCreateBucketResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Bucket creation failed, error: " + resp.getStatus()); } @@ -548,7 +554,7 @@ public OmBucketInfo getBucketInfo(String volume, String bucket) InfoBucketResponse resp = submitRequest(omRequest).getInfoBucketResponse(); - if (resp.getStatus() == Status.OK) { + if (resp.getStatus() == OK) { return OmBucketInfo.getFromProtobuf(resp.getBucketInfo()); } else { throw new IOException("Info Bucket failed, error: " @@ -576,7 +582,7 @@ public void setBucketProperty(OmBucketArgs args) SetBucketPropertyResponse resp = submitRequest(omRequest) .getSetBucketPropertyResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Setting bucket property failed, error: " + resp.getStatus()); } @@ -614,7 +620,7 @@ public List listBuckets(String volumeName, ListBucketsResponse resp = submitRequest(omRequest) .getListBucketsResponse(); - if (resp.getStatus() == Status.OK) { + if (resp.getStatus() == OK) { buckets.addAll( resp.getBucketInfoList().stream() .map(OmBucketInfo::getFromProtobuf) @@ -677,7 +683,7 @@ public OpenKeySession openKey(OmKeyArgs args) throws IOException { CreateKeyResponse resp = submitRequest(omRequest).getCreateKeyResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Create key failed, error:" + resp.getStatus()); } return new OpenKeySession(resp.getID(), @@ -703,7 +709,7 @@ public OmKeyLocationInfo allocateBlock(OmKeyArgs args, long clientId) AllocateBlockResponse resp = submitRequest(omRequest) .getAllocateBlockResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Allocate block failed, error:" + resp.getStatus()); } @@ -733,7 +739,7 @@ public void commitKey(OmKeyArgs args, long clientId) CommitKeyResponse resp = submitRequest(omRequest).getCommitKeyResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Commit key failed, error:" + resp.getStatus()); } @@ -756,7 +762,7 @@ public OmKeyInfo lookupKey(OmKeyArgs args) throws IOException { LookupKeyResponse resp = submitRequest(omRequest).getLookupKeyResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Lookup key failed, error:" + resp.getStatus()); } @@ -780,7 +786,7 @@ public void renameKey(OmKeyArgs args, String toKeyName) throws IOException { RenameKeyResponse resp = submitRequest(omRequest).getRenameKeyResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Rename key failed, error:" + resp.getStatus()); } @@ -807,7 +813,7 @@ public void deleteKey(OmKeyArgs args) throws IOException { DeleteKeyResponse resp = submitRequest(omRequest).getDeleteKeyResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Delete key failed, error:" + resp.getStatus()); } @@ -831,7 +837,7 @@ public void deleteBucket(String volume, String bucket) throws IOException { DeleteBucketResponse resp = submitRequest(omRequest) .getDeleteBucketResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Delete Bucket failed, error:" + resp.getStatus()); } @@ -865,7 +871,7 @@ public List listKeys(String volumeName, String bucketName, ListKeysResponse resp = submitRequest(omRequest).getListKeysResponse(); - if (resp.getStatus() == Status.OK) { + if (resp.getStatus() == OK) { keys.addAll( resp.getKeyInfoList().stream() .map(OmKeyInfo::getFromProtobuf) @@ -892,7 +898,7 @@ public void createS3Bucket(String userName, String s3BucketName) S3CreateBucketResponse resp = submitRequest(omRequest) .getCreateS3BucketResponse(); - if(resp.getStatus() != Status.OK) { + if(resp.getStatus() != OK) { throw new IOException("Creating S3 bucket failed, error: " + resp.getStatus()); } @@ -912,7 +918,7 @@ public void deleteS3Bucket(String s3BucketName) throws IOException { S3DeleteBucketResponse resp = submitRequest(omRequest) .getDeleteS3BucketResponse(); - if(resp.getStatus() != Status.OK) { + if(resp.getStatus() != OK) { throw new IOException("Creating S3 bucket failed, error: " + resp.getStatus()); } @@ -933,7 +939,7 @@ public String getOzoneBucketMapping(String s3BucketName) S3BucketInfoResponse resp = submitRequest(omRequest) .getInfoS3BucketResponse(); - if(resp.getStatus() != Status.OK) { + if(resp.getStatus() != OK) { throw new IOException("GetOzoneBucketMapping failed, error:" + resp .getStatus()); } @@ -963,7 +969,7 @@ public List listS3Buckets(String userName, String startKey, S3ListBucketsResponse resp = submitRequest(omRequest) .getListS3BucketsResponse(); - if (resp.getStatus() == Status.OK) { + if (resp.getStatus() == OK) { buckets.addAll( resp.getBucketInfoList().stream() .map(OmBucketInfo::getFromProtobuf) @@ -986,7 +992,7 @@ public S3SecretValue getS3Secret(String kerberosID) throws IOException { final GetS3SecretResponse resp = submitRequest(omRequest) .getGetS3SecretResponse(); - if(resp.getStatus() != Status.OK) { + if(resp.getStatus() != OK) { throw new IOException("Fetch S3 Secret failed, error: " + resp.getStatus()); } else { @@ -1022,7 +1028,7 @@ public OmMultipartInfo initiateMultipartUpload(OmKeyArgs omKeyArgs) throws MultipartInfoInitiateResponse resp = submitRequest(omRequest) .getInitiateMultiPartUploadResponse(); - if (resp.getStatus() != Status.OK) { + if (resp.getStatus() != OK) { throw new IOException("Initiate Multipart upload failed, error:" + resp .getStatus()); } @@ -1064,7 +1070,7 @@ public OmMultipartCommitUploadPartInfo commitMultipartUploadPart( MultipartCommitUploadPartResponse response = submitRequest(omRequest) .getCommitMultiPartUploadResponse(); - if (response.getStatus() != Status.OK) { + if (response.getStatus() != OK) { throw new IOException("Commit multipart upload part key failed, error:" + response.getStatus()); } @@ -1099,7 +1105,7 @@ public OmMultipartUploadCompleteInfo completeMultipartUpload( MultipartUploadCompleteResponse response = submitRequest(omRequest) .getCompleteMultiPartUploadResponse(); - if (response.getStatus() != Status.OK) { + if (response.getStatus() != OK) { throw new IOException("Complete multipart upload failed, error:" + response.getStatus()); } @@ -1130,7 +1136,7 @@ public void abortMultipartUpload(OmKeyArgs omKeyArgs) throws IOException { MultipartUploadAbortResponse response = submitRequest(omRequest).getAbortMultiPartUploadResponse(); - if (response.getStatus() != Status.OK) { + if (response.getStatus() != OK) { throw new IOException("Abort multipart upload failed, error:" + response.getStatus()); } @@ -1147,7 +1153,7 @@ public List getServiceList() throws IOException { final ServiceListResponse resp = submitRequest(omRequest) .getServiceListResponse(); - if (resp.getStatus() == Status.OK) { + if (resp.getStatus() == OK) { return resp.getServiceInfoList().stream() .map(ServiceInfo::getFromProtobuf) .collect(Collectors.toList()); @@ -1162,11 +1168,11 @@ public List getServiceList() throws IOException { * * @param renewer the designated renewer for the token * @return Token - * @throws IOException + * @throws OMException */ @Override public Token getDelegationToken(Text renewer) - throws IOException { + throws OMException { GetDelegationTokenRequestProto req = GetDelegationTokenRequestProto .newBuilder() .setRenewer(renewer == null ? "" : renewer.toString()) @@ -1176,15 +1182,23 @@ public Token getDelegationToken(Text renewer) .setGetDelegationTokenRequest(req) .build(); - final GetDelegationTokenResponseProto resp = submitRequest(omRequest) - .getGetDelegationTokenResponse(); - if (resp.getStatus() == Status.OK) { - return resp.getResponse().hasToken() ? - OMPBHelper.convertToDelegationToken(resp.getResponse().getToken()) - : null; - } else { - throw new IOException("Get Delegation Token failed, error : " + resp - .getStatus()); + final GetDelegationTokenResponseProto resp; + try { + resp = submitRequest(omRequest).getGetDelegationTokenResponse(); + + if (resp.getStatus() == OK) { + return resp.getResponse().hasToken() ? + OMPBHelper.convertToDelegationToken(resp.getResponse().getToken()) + : null; + } + throw new OMException("Get delegation token failed with response:" + + resp.getStatus(), toResultStatus(resp.getStatus())); + } catch (IOException e) { + if(e instanceof OMException) { + throw (OMException)e; + } + throw new OMException("Get delegation token failed.", e, + TOKEN_ERROR_OTHER); } } @@ -1193,11 +1207,10 @@ public Token getDelegationToken(Text renewer) * * @param token delegation token obtained earlier * @return the new expiration time - * @throws IOException */ @Override public long renewDelegationToken(Token token) - throws IOException { + throws OMException { RenewDelegationTokenRequestProto req = RenewDelegationTokenRequestProto.newBuilder(). setToken(OMPBHelper.convertToTokenProto(token)). @@ -1207,13 +1220,21 @@ public long renewDelegationToken(Token token) .setRenewDelegationTokenRequest(req) .build(); - final RenewDelegationTokenResponseProto resp = submitRequest(omRequest) - .getRenewDelegationTokenResponse(); - if (resp.getStatus() == Status.OK) { - return resp.getResponse().getNewExpiryTime(); - } else { - throw new IOException("Renew Delegation Token failed, error : " + resp - .getStatus()); + final RenewDelegationTokenResponseProto resp; + try { + resp = submitRequest(omRequest) + .getRenewDelegationTokenResponse(); + if (resp.getStatus() == OK) { + return resp.getResponse().getNewExpiryTime(); + } + throw new OMException("Renew delegation token failed with response:" + + resp.getStatus(), toResultStatus(resp.getStatus())); + } catch (IOException e) { + if(e instanceof OMException) { + throw (OMException)e; + } + throw new OMException("Renew delegation token failed.", e, + TOKEN_ERROR_OTHER); } } @@ -1221,11 +1242,10 @@ public long renewDelegationToken(Token token) * Cancel an existing delegation token. * * @param token delegation token - * @throws IOException */ @Override public void cancelDelegationToken(Token token) - throws IOException { + throws OMException { CancelDelegationTokenRequestProto req = CancelDelegationTokenRequestProto .newBuilder() .setToken(OMPBHelper.convertToTokenProto(token)) @@ -1235,11 +1255,40 @@ public void cancelDelegationToken(Token token) .setCancelDelegationTokenRequest(req) .build(); - final CancelDelegationTokenResponseProto resp = submitRequest(omRequest) - .getCancelDelegationTokenResponse(); - if (resp.getStatus() != Status.OK) { - throw new IOException("Cancel Delegation Token failed, error : " + resp - .getStatus()); + final CancelDelegationTokenResponseProto resp; + try { + resp = submitRequest(omRequest).getCancelDelegationTokenResponse(); + if (resp.getStatus() == OK) { + return; + } + throw new OMException("Cancel delegation token failed with response:" + + resp.getStatus(), toResultStatus(resp.getStatus())); + } catch (IOException e) { + if(e instanceof OMException) { + throw (OMException)e; + } + throw new OMException("Cancel delegation token failed.", e, + TOKEN_ERROR_OTHER); + } + } + + /** + * Converts proto status to OMException result code. + * + * @param status Proto status received from rpc call. + */ + public ResultCodes toResultStatus(Status status) { + switch (status) { + case INVALID_AUTH_METHOD: + return ResultCodes.INVALID_AUTH_METHOD; + case INVALID_TOKEN: + return ResultCodes.INVALID_TOKEN; + case TOKEN_EXPIRED: + return ResultCodes.TOKEN_EXPIRED; + case TOKEN_ERROR_OTHER: + return TOKEN_ERROR_OTHER; + default: + return UNKNOWN; } } } diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSecretManager.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSecretManager.java index 0b40f96de3..8c4b90be3d 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSecretManager.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSecretManager.java @@ -22,6 +22,7 @@ import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.security.x509.SecurityConfig; import org.apache.hadoop.io.Text; +import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.security.OzoneSecretStore.OzoneManagerSecretState; import org.apache.hadoop.ozone.security.OzoneTokenIdentifier.TokenInfo; import org.apache.hadoop.security.AccessControlException; @@ -41,6 +42,8 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_EXPIRED; + /** * SecretManager for Ozone Master. Responsible for signing identifiers with * private key, @@ -172,8 +175,7 @@ private void updateIdentifierDetails(OzoneTokenIdentifier identifier) { */ @Override public synchronized long renewToken(Token token, - String renewer) - throws IOException { + String renewer) throws IOException { ByteArrayInputStream buf = new ByteArrayInputStream(token.getIdentifier()); DataInputStream in = new DataInputStream(buf); OzoneTokenIdentifier id = OzoneTokenIdentifier.readProtoBuf(in); @@ -184,10 +186,10 @@ public synchronized long renewToken(Token token, long now = Time.monotonicNow(); if (id.getMaxDate() < now) { - throw new InvalidToken(renewer + " tried to renew an expired token " + throw new OMException(renewer + " tried to renew an expired token " + formatTokenId(id) + " max expiration date: " + Time.formatTime(id.getMaxDate()) - + " currentTime: " + Time.formatTime(now)); + + " currentTime: " + Time.formatTime(now), TOKEN_EXPIRED); } validateToken(id); if ((id.getRenewer() == null) || (id.getRenewer().toString().isEmpty())) { diff --git a/hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto b/hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto index 98958e6cc7..1889a28945 100644 --- a/hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto +++ b/hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto @@ -208,6 +208,12 @@ enum Status { ABORT_MULTIPART_UPLOAD_FAILED = 31; S3_SECRET_NOT_FOUND = 32; + + INVALID_AUTH_METHOD = 33; + INVALID_TOKEN = 34; + TOKEN_EXPIRED = 35; + TOKEN_ERROR_OTHER = 36; + } diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java index 165b249cb2..5e3e2f6334 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java @@ -21,6 +21,9 @@ import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ENABLED; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SECURITY_ENABLED_KEY; +import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_AUTH_METHOD; +import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_ERROR_OTHER; +import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_EXPIRED; import static org.slf4j.event.Level.INFO; import java.io.File; @@ -50,7 +53,6 @@ import org.apache.hadoop.io.Text; import org.apache.hadoop.ipc.Client; import org.apache.hadoop.ipc.RPC; -import org.apache.hadoop.ipc.RemoteException; import org.apache.hadoop.ipc.Server; import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem; import org.apache.hadoop.minikdc.MiniKdc; @@ -58,13 +60,13 @@ import org.apache.hadoop.ozone.om.OMConfigKeys; import org.apache.hadoop.ozone.om.OMStorage; import org.apache.hadoop.ozone.om.OzoneManager; +import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.protocolPB.OzoneManagerProtocolClientSideTranslatorPB; import org.apache.hadoop.ozone.om.protocolPB.OzoneManagerProtocolPB; import org.apache.hadoop.ozone.security.OzoneTokenIdentifier; import org.apache.hadoop.security.KerberosAuthException; import org.apache.hadoop.security.SaslRpcServer.AuthMethod; import org.apache.hadoop.security.UserGroupInformation; -import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod; import org.apache.hadoop.security.authentication.client.AuthenticationException; import org.apache.hadoop.security.token.Token; import org.apache.hadoop.test.GenericTestUtils; @@ -80,7 +82,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.ws.rs.HEAD; /** * Test class to for security enabled Ozone cluster. @@ -345,6 +346,7 @@ public void testDelegationToken() throws Exception { // Capture logs for assertions LogCapturer logs = LogCapturer.captureLogs(Server.AUDITLOG); + LogCapturer omLogs = LogCapturer.captureLogs(OzoneManager.getLogger()); GenericTestUtils .setLogLevel(LoggerFactory.getLogger(Server.class.getName()), INFO); @@ -407,18 +409,28 @@ public Void run() throws Exception { Assert.assertFalse(logs.getOutput().contains( "Auth successful for " + username + " (auth:TOKEN)")); LambdaTestUtils.intercept(IOException.class, "Delete Volume failed," - + " error:VOLUME_NOT_FOUND", () -> omClient.deleteVolume("vol1")); + + " error:VOLUME_NOT_FOUND", () -> omClient.deleteVolume("vol1")); Assert.assertTrue(logs.getOutput().contains("Auth successful for " + username + " (auth:TOKEN)")); // Case 4: Test failure of token renewal. // Call to renewDelegationToken will fail but it will confirm that // initial connection via DT succeeded - LambdaTestUtils.intercept(RemoteException.class, "Delegation " - + "Token can be renewed only with kerberos or web authentication", - () -> omClient.renewDelegationToken(token)); + omLogs.clearOutput(); + + LambdaTestUtils.intercept(OMException.class, "Renew delegation token " + + "failed", + () -> { + try { + omClient.renewDelegationToken(token); + } catch (OMException ex) { + Assert.assertTrue(ex.getResult().equals(INVALID_AUTH_METHOD)); + throw ex; + } + }); Assert.assertTrue(logs.getOutput().contains( "Auth successful for " + username + " (auth:TOKEN)")); + omLogs.clearOutput(); //testUser.setAuthenticationMethod(AuthMethod.KERBEROS); UserGroupInformation.setLoginUser(ugi); omClient = new OzoneManagerProtocolClientSideTranslatorPB( @@ -438,16 +450,25 @@ public Void run() throws Exception { // Case 6: Test failure of token cancellation. // Get Om client, this time authentication using Token will fail as - // token is expired + // token is not in cache anymore. omClient = new OzoneManagerProtocolClientSideTranslatorPB( RPC.getProxy(OzoneManagerProtocolPB.class, omVersion, OmUtils.getOmAddress(conf), testUser, conf, NetUtils.getDefaultSocketFactory(conf), Client.getRpcTimeout(conf)), RandomStringUtils.randomAscii(5)); - LambdaTestUtils.intercept(RemoteException.class, "can't be found in cache", - () -> omClient.cancelDelegationToken(token)); + LambdaTestUtils.intercept(OMException.class, "Cancel delegation " + + "token failed", + () -> { + try { + omClient.cancelDelegationToken(token); + } catch (OMException ex) { + Assert.assertTrue(ex.getResult().equals(TOKEN_ERROR_OTHER)); + throw ex; + } + }); + Assert.assertTrue(logs.getOutput().contains("Auth failed for")); - } finally { + } finally { om.stop(); om.join(); } @@ -469,6 +490,7 @@ private void generateKeyPair(OzoneConfiguration config) throws Exception { public void testDelegationTokenRenewal() throws Exception { GenericTestUtils .setLogLevel(LoggerFactory.getLogger(Server.class.getName()), INFO); + LogCapturer omLogs = LogCapturer.captureLogs(OzoneManager.getLogger()); // Setup secure OM for start. OzoneConfiguration newConf = new OzoneConfiguration(conf); @@ -502,16 +524,35 @@ public void testDelegationTokenRenewal() throws Exception { // Renew delegation token long expiryTime = omClient.renewDelegationToken(token); Assert.assertTrue(expiryTime > 0); + omLogs.clearOutput(); // Test failure of delegation renewal - // 1. When renewer doesn't match (implicitly covers when renewer is + // 1. When token maxExpiryTime exceeds + Thread.sleep(500); + LambdaTestUtils.intercept(OMException.class, + "Renew delegation token failed", + () -> { + try { + omClient.renewDelegationToken(token); + } catch (OMException ex) { + Assert.assertTrue(ex.getResult().equals(TOKEN_EXPIRED)); + throw ex; + } + }); + + omLogs.clearOutput(); + + // 2. When renewer doesn't match (implicitly covers when renewer is // null or empty ) Token token2 = omClient.getDelegationToken(new Text("randomService")); - LambdaTestUtils.intercept(RemoteException.class, - " with non-matching renewer randomService", + LambdaTestUtils.intercept(OMException.class, + "Renew delegation token failed", () -> omClient.renewDelegationToken(token2)); + Assert.assertTrue(omLogs.getOutput().contains(" with non-matching " + + "renewer randomService")); + omLogs.clearOutput(); - // 2. Test tampered token + // 3. Test tampered token OzoneTokenIdentifier tokenId = OzoneTokenIdentifier.readProtoBuf( token.getIdentifier()); tokenId.setRenewer(new Text("om")); @@ -519,15 +560,13 @@ public void testDelegationTokenRenewal() throws Exception { Token tamperedToken = new Token<>( tokenId.getBytes(), token2.getPassword(), token2.getKind(), token2.getService()); - LambdaTestUtils.intercept(RemoteException.class, - "can't be found in cache", + LambdaTestUtils.intercept(OMException.class, + "Renew delegation token failed", () -> omClient.renewDelegationToken(tamperedToken)); + Assert.assertTrue(omLogs.getOutput().contains("can't be found in " + + "cache")); + omLogs.clearOutput(); - // 3. When token maxExpiryTime exceeds - Thread.sleep(500); - LambdaTestUtils.intercept(RemoteException.class, - "om tried to renew an expired" + " token", - () -> omClient.renewDelegationToken(token)); } finally { om.stop(); om.join(); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java index 7abc20897e..5b2120cd22 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java @@ -100,7 +100,6 @@ import org.apache.hadoop.ozone.security.OzoneBlockTokenSecretManager; import org.apache.hadoop.ozone.security.OzoneDelegationTokenSecretManager; import org.apache.hadoop.ozone.util.OzoneVersionInfo; -import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.SecurityUtil; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod; @@ -157,6 +156,8 @@ .OZONE_OM_METRICS_SAVE_INTERVAL; import static org.apache.hadoop.ozone.om.OMConfigKeys .OZONE_OM_METRICS_SAVE_INTERVAL_DEFAULT; +import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_AUTH_METHOD; +import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_ERROR_OTHER; import static org.apache.hadoop.ozone.protocol.proto .OzoneManagerProtocolProtos.OzoneManagerService .newReflectiveBlockingService; @@ -1031,30 +1032,37 @@ private static UserGroupInformation getRemoteUser() throws IOException { */ @Override public Token getDelegationToken(Text renewer) - throws IOException { + throws OMException { final boolean success; final String tokenId; Token token; + try { + if (!isAllowedDelegationTokenOp()) { + throw new OMException("Delegation Token can be issued only with " + + "kerberos or web authentication", + INVALID_AUTH_METHOD); + } + if (delegationTokenMgr == null || !delegationTokenMgr.isRunning()) { + LOG.warn("trying to get DT with no secret manager running in OM."); + return null; + } - if (!isAllowedDelegationTokenOp()) { - throw new IOException("Delegation Token can be issued only with " - + "kerberos or web authentication"); - } - if (delegationTokenMgr == null || !delegationTokenMgr.isRunning()) { - LOG.warn("trying to get DT with no secret manager running in OM."); - return null; - } + UserGroupInformation ugi = getRemoteUser(); + String user = ugi.getUserName(); + Text owner = new Text(user); + Text realUser = null; + if (ugi.getRealUser() != null) { + realUser = new Text(ugi.getRealUser().getUserName()); + } - UserGroupInformation ugi = getRemoteUser(); - String user = ugi.getUserName(); - Text owner = new Text(user); - Text realUser = null; - if (ugi.getRealUser() != null) { - realUser = new Text(ugi.getRealUser().getUserName()); + return delegationTokenMgr.createToken(owner, renewer, realUser); + } catch (OMException oex) { + throw oex; + } catch (IOException ex) { + LOG.error("Get Delegation token failed, cause: {}", ex.getMessage()); + throw new OMException("Get Delegation token failed.", ex, + TOKEN_ERROR_OTHER); } - - token = delegationTokenMgr.createToken(owner, renewer, realUser); - return token; } /** @@ -1066,24 +1074,31 @@ public Token getDelegationToken(Text renewer) */ @Override public long renewDelegationToken(Token token) - throws InvalidToken, IOException { + throws OMException { long expiryTime; try { if (!isAllowedDelegationTokenOp()) { - throw new IOException("Delegation Token can be renewed only with " - + "kerberos or web authentication"); + throw new OMException("Delegation Token can be renewed only with " + + "kerberos or web authentication", + INVALID_AUTH_METHOD); } String renewer = getRemoteUser().getShortUserName(); expiryTime = delegationTokenMgr.renewToken(token, renewer); - } catch (AccessControlException ace) { - final OzoneTokenIdentifier id = OzoneTokenIdentifier.readProtoBuf( - token.getIdentifier()); - LOG.error("Delegation token renewal failed for dt: {}, cause: {}", - id.toString(), ace.getMessage()); - throw ace; + } catch (OMException oex) { + throw oex; + } catch (IOException ex) { + OzoneTokenIdentifier id = null; + try { + id = OzoneTokenIdentifier.readProtoBuf(token.getIdentifier()); + } catch (IOException exe) { + } + LOG.error("Delegation token renewal failed for dt id: {}, cause: {}", + id, ex.getMessage()); + throw new OMException("Delegation token renewal failed for dt: " + token, + ex, TOKEN_ERROR_OTHER); } return expiryTime; } @@ -1095,16 +1110,19 @@ public long renewDelegationToken(Token token) */ @Override public void cancelDelegationToken(Token token) - throws IOException { + throws OMException { OzoneTokenIdentifier id = null; try { String canceller = getRemoteUser().getUserName(); id = delegationTokenMgr.cancelToken(token, canceller); - LOG.trace("Delegation token renewed for dt: {}", id); - } catch (AccessControlException ace) { - LOG.error("Delegation token renewal failed for dt: {}, cause: {}", id, - ace.getMessage()); - throw ace; + LOG.trace("Delegation token cancelled for dt: {}", id); + } catch (OMException oex) { + throw oex; + } catch (IOException ex) { + LOG.error("Delegation token cancellation failed for dt id: {}, cause: {}", + id, ex.getMessage()); + throw new OMException("Delegation token renewal failed for dt: " + token, + ex, TOKEN_ERROR_OTHER); } } /** diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerProtocolServerSideTranslatorPB.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerProtocolServerSideTranslatorPB.java index aff879591a..9522d76f90 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerProtocolServerSideTranslatorPB.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerProtocolServerSideTranslatorPB.java @@ -57,7 +57,7 @@ public OzoneManagerProtocolServerSideTranslatorPB( } /** - * Submit requests to Ratis server for OM HA implmentation. + * Submit requests to Ratis server for OM HA implementation. * TODO: Once HA is implemented fully, we should have only one server side * translator for OM protocol. */ diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java index f7a03d9c6e..53dba69bcd 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java @@ -163,7 +163,6 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetDelegationTokenResponseProto; import org.apache.hadoop.security.proto.SecurityProtos.RenewDelegationTokenRequestProto; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.RenewDelegationTokenResponseProto; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretResponse; import org.apache.hadoop.security.token.Token; @@ -418,6 +417,14 @@ private Status exceptionToResponseStatus(IOException ex) { return Status.ENTITY_TOO_SMALL; case ABORT_MULTIPART_UPLOAD_FAILED: return Status.ABORT_MULTIPART_UPLOAD_FAILED; + case INVALID_AUTH_METHOD: + return Status.INVALID_AUTH_METHOD; + case INVALID_TOKEN: + return Status.INVALID_TOKEN; + case TOKEN_EXPIRED: + return Status.TOKEN_EXPIRED; + case TOKEN_ERROR_OTHER: + return Status.TOKEN_ERROR_OTHER; default: return Status.INTERNAL_ERROR; } @@ -963,7 +970,7 @@ private GetDelegationTokenResponseProto getDelegationToken( .convertToTokenProto(token)).build()); } rb.setStatus(Status.OK); - } catch (IOException ex) { + } catch (IOException ex) { rb.setStatus(exceptionToResponseStatus(ex)); } return rb.build();