Skip to content

Commit adc4895

Browse files
author
Fabian Morgan
committed
ensure each S3 API has an associated S3 Action
1 parent 5bbfa24 commit adc4895

16 files changed

Lines changed: 582 additions & 130 deletions

File tree

hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/S3Auth.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public class S3Auth {
2929
private String userPrincipal;
3030
// Optional STS session token when using temporary credentials
3131
private String sessionToken;
32+
// S3 action without s3: prefix (e.g. PutObject), set by S3 Gateway for use in finer-grained STS permissions.
33+
private String s3Action;
3234

3335
public S3Auth(final String stringToSign,
3436
final String signature,
@@ -67,4 +69,12 @@ public String getSessionToken() {
6769
public void setSessionToken(String sessionToken) {
6870
this.sessionToken = sessionToken;
6971
}
72+
73+
public String getS3Action() {
74+
return s3Action;
75+
}
76+
77+
public void setS3Action(String s3Action) {
78+
this.s3Action = s3Action;
79+
}
7080
}

hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,9 @@ private OMResponse submitRequest(OMRequest omRequest)
328328
if (threadLocalS3Auth.get().getSessionToken() != null) {
329329
s3AuthBuilder.setSessionToken(threadLocalS3Auth.get().getSessionToken());
330330
}
331+
if (threadLocalS3Auth.get().getS3Action() != null) {
332+
s3AuthBuilder.setS3Action(threadLocalS3Auth.get().getS3Action());
333+
}
331334

332335
builder.setS3Authentication(s3AuthBuilder.build());
333336
}
@@ -1703,10 +1706,8 @@ public OmMultipartCommitUploadPartInfo commitMultipartUploadPart(
17031706
handleError(submitRequest(omRequest))
17041707
.getCommitMultiPartUploadResponse();
17051708

1706-
OmMultipartCommitUploadPartInfo info = new
1707-
OmMultipartCommitUploadPartInfo(response.getPartName(),
1708-
response.getETag());
1709-
return info;
1709+
return new OmMultipartCommitUploadPartInfo(response.getPartName(),
1710+
response.getETag());
17101711
}
17111712

17121713
@Override

hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1572,7 +1572,6 @@ message CommitKeyRequest {
15721572
}
15731573

15741574
message CommitKeyResponse {
1575-
15761575
}
15771576

15781577
message AllocateBlockRequest {
@@ -2319,6 +2318,9 @@ message S3Authentication {
23192318
optional string resolvedStsOriginalAccessKeyId = 7;
23202319
optional string resolvedStsTempAccessKeyId = 8;
23212320
optional string resolvedStsSecretKeyId = 9;
2321+
// S3 action without the s3: prefix for this request (e.g. GetObject), set by S3 Gateway for use
2322+
// in finer-grained STS permissions.
2323+
optional string s3Action = 10;
23222324
}
23232325

23242326
message RecoverLeaseRequest {

hadoop-ozone/interface-client/src/main/resources/proto.lock

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7683,6 +7683,12 @@
76837683
"name": "accessId",
76847684
"type": "string",
76857685
"optional": true
7686+
},
7687+
{
7688+
"id": 10,
7689+
"name": "s3Action",
7690+
"type": "string",
7691+
"optional": true
76867692
}
76877693
]
76887694
},

hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataReader.java

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.apache.hadoop.ozone.om.helpers.OzoneFileStatusLight;
5656
import org.apache.hadoop.ozone.om.helpers.S3VolumeContext;
5757
import org.apache.hadoop.ozone.om.protocolPB.grpc.GrpcClientConstants;
58+
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.S3Authentication;
5859
import org.apache.hadoop.ozone.security.STSTokenIdentifier;
5960
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
6061
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLIdentityType;
@@ -236,9 +237,7 @@ public List<OzoneFileStatus> listStatus(OmKeyArgs args, boolean recursive,
236237
try {
237238
if (isAclEnabled) {
238239
if (isStsS3Request()) {
239-
// We need to be able to tell the difference between being able to download a file and merely seeing the file
240-
// name in a list. Use READ for download ability and LIST (here) for listing.
241-
// When listPrefix is set (original S3 ListObjects prefix), authorize LIST on that prefix for the whole
240+
// When listPrefix is set (original S3 ListObjects prefix), authorize READ on that prefix for the whole
242241
// listing, including FSO traversal where keyName is an internal directory (e.g. userA) under prefix user.
243242
final String listPrefix = args.getListPrefix();
244243
final String keyName = args.getKeyName();
@@ -258,7 +257,7 @@ public List<OzoneFileStatus> listStatus(OmKeyArgs args, boolean recursive,
258257
} else {
259258
aclKey = "*";
260259
}
261-
checkAcls(ResourceType.KEY, StoreType.OZONE, ACLType.LIST, bucket.realVolume(), bucket.realBucket(), aclKey);
260+
checkAcls(ResourceType.KEY, StoreType.OZONE, ACLType.READ, bucket.realVolume(), bucket.realBucket(), aclKey);
262261
} else {
263262
checkAcls(getResourceType(args), StoreType.OZONE, ACLType.READ,
264263
bucket, args.getKeyName());
@@ -304,12 +303,7 @@ public OzoneFileStatus getFileStatus(OmKeyArgs args) throws IOException {
304303

305304
try {
306305
if (isAclEnabled) {
307-
if (isStsS3Request()) {
308-
checkAcls(getResourceType(args), StoreType.OZONE, ACLType.LIST, bucket, args.getKeyName());
309-
} else {
310-
checkAcls(getResourceType(args), StoreType.OZONE, ACLType.READ,
311-
bucket, args.getKeyName());
312-
}
306+
checkAcls(getResourceType(args), StoreType.OZONE, ACLType.READ, bucket, args.getKeyName());
313307
}
314308
metrics.incNumGetFileStatus();
315309
return keyManager.getFileStatus(args, getClientAddress());
@@ -384,7 +378,7 @@ public ListKeysResult listKeys(String volumeName, String bucketName,
384378
final String aclKey = (keyPrefix == null || keyPrefix.isEmpty()) ? "*" : keyPrefix;
385379
captureLatencyNs(
386380
perfMetrics.getListKeysAclCheckLatencyNs(), () -> checkAcls(
387-
ResourceType.KEY, StoreType.OZONE, ACLType.LIST, bucket.realVolume(), bucket.realBucket(), aclKey));
381+
ResourceType.KEY, StoreType.OZONE, ACLType.READ, bucket.realVolume(), bucket.realBucket(), aclKey));
388382
} else {
389383
captureLatencyNs(perfMetrics.getListKeysAclCheckLatencyNs(), () ->
390384
checkAcls(ResourceType.BUCKET, StoreType.OZONE, ACLType.LIST,
@@ -634,7 +628,8 @@ public boolean checkAcls(ResourceType resType, StoreType storeType,
634628
public boolean checkAcls(OzoneObj obj, RequestContext context,
635629
boolean throwIfPermissionDenied) throws OMException {
636630

637-
final RequestContext normalizedRequestContext = maybeAttachSessionPolicyFromThreadLocal(context);
631+
final RequestContext normalizedRequestContext = maybeAttachS3ActionFromThreadLocal(
632+
maybeAttachSessionPolicyFromThreadLocal(context));
638633

639634
if (!captureLatencyNs(perfMetrics::setCheckAccessLatencyNs,
640635
() -> accessAuthorizer.checkAccess(obj, normalizedRequestContext))) {
@@ -692,6 +687,22 @@ private RequestContext maybeAttachSessionPolicyFromThreadLocal(RequestContext co
692687
.build();
693688
}
694689

690+
/**
691+
* Attaches s3 action to RequestContext if an S3Authentication is found in the Ozone Manager thread local,
692+
* and it has an s3 action. Otherwise, returns the RequestContext as it was before.
693+
* @param context the original RequestContext
694+
* @return RequestContext as before or with s3 action embedded
695+
*/
696+
private RequestContext maybeAttachS3ActionFromThreadLocal(RequestContext context) {
697+
final S3Authentication s3Authentication = OzoneManager.getS3Auth();
698+
if (s3Authentication == null || !s3Authentication.hasS3Action()) {
699+
return context;
700+
}
701+
return context.toBuilder()
702+
.setS3Action(s3Authentication.getS3Action())
703+
.build();
704+
}
705+
695706
static String getClientAddress() {
696707
String clientMachine = Server.getRemoteAddress();
697708
if (clientMachine == null) { //not a RPC client

hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@ public void close() {
619619
*/
620620
@VisibleForTesting
621621
OMResponse runCommand(OMRequest request, TermIndex termIndex) {
622+
boolean isS3AuthThreadLocalSet = false;
622623
boolean isStsThreadLocalSet = false;
623624
try {
624625
if (ozoneManager.isSecurityEnabled() && request.hasS3Authentication()) {
@@ -627,6 +628,10 @@ OMResponse runCommand(OMRequest request, TermIndex termIndex) {
627628
STSSecurityUtil.ensureResolvedStsFieldsInvariants(request);
628629

629630
final OzoneManagerProtocolProtos.S3Authentication s3Auth = request.getS3Authentication();
631+
// ThreadLocal carries S3 action for OmMetadataReader.
632+
OzoneManager.setS3Auth(s3Auth);
633+
isS3AuthThreadLocalSet = true;
634+
630635
if (s3Auth.hasSessionToken() && !s3Auth.getSessionToken().isEmpty()) {
631636
// ThreadLocal carries session policy for OmMetadataReader
632637
final STSTokenIdentifier rehydratedTokenIdentifier = new STSTokenIdentifier(
@@ -661,6 +666,9 @@ OMResponse runCommand(OMRequest request, TermIndex termIndex) {
661666
String errorMessage = "Request " + request + " failed with exception";
662667
ExitUtils.terminate(1, errorMessage, e, LOG);
663668
} finally {
669+
if (isS3AuthThreadLocalSet) {
670+
OzoneManager.setS3Auth(null);
671+
}
664672
if (isStsThreadLocalSet) {
665673
OzoneManager.setStsTokenIdentifier(null);
666674
}

0 commit comments

Comments
 (0)