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 @@ -29,20 +29,14 @@
/**
* Executes revocation of STS tokens.
*
* <p>This command marks the specified STS temporary access key id as revoked
* by adding it to the OM's revoked STS token table. Subsequent S3 requests
* using the same temporary access key id will be rejected once the revocation
* <p>This command marks the specified STS token as revoked by adding it to the OM's revoked STS token table.
* Subsequent S3 requests using the same session token will be rejected once the revocation
* state has propagated.</p>
*/
@Command(name = "revokeststoken",
description = "Revoke S3 STS token for the given access key id")
description = "Revoke S3 STS token for the given session token")
public class RevokeSTSTokenHandler extends S3Handler {

@Option(names = "-k",
required = true,
description = "STS temporary access key id (for example, ASIA...)")
private String accessKeyId;

@Option(names = "-t",
required = true,
description = "STS session token")
Expand All @@ -62,8 +56,8 @@ protected void execute(OzoneClient client, OzoneAddress address)
throws IOException {

if (!yes) {
out().print("Enter 'y' to confirm STS token revocation for accessKeyId '" +
accessKeyId + "': ");
out().print("Enter 'y' to confirm STS token revocation for sessionToken '" +
sessionToken + "': ");
out().flush();
final Scanner scanner = new Scanner(new InputStreamReader(System.in, StandardCharsets.UTF_8));
final String confirmation = scanner.next().trim().toLowerCase();
Expand All @@ -73,7 +67,7 @@ protected void execute(OzoneClient client, OzoneAddress address)
}
}

client.getObjectStore().revokeSTSToken(accessKeyId, sessionToken);
out().println("STS token revoked for accessKeyId '" + accessKeyId + "'.");
client.getObjectStore().revokeSTSToken(sessionToken);
out().println("STS token revoked for sessionToken '" + sessionToken + "'.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -768,12 +768,11 @@ public AssumeRoleResponseInfo assumeRole(String roleArn, String roleSessionName,

/**
* Revokes an STS token.
* @param accessKeyId The STS accessKeyId (starting with ASIA...)
* @param sessionToken The STS session token
* @param sessionToken The STS sessionToken
* @throws IOException if an error occurs while revoking the STS token
*/
public void revokeSTSToken(String accessKeyId, String sessionToken) throws IOException {
proxy.revokeSTSToken(accessKeyId, sessionToken);
public void revokeSTSToken(String sessionToken) throws IOException {
proxy.revokeSTSToken(sessionToken);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1375,9 +1375,8 @@ AssumeRoleResponseInfo assumeRole(String roleArn, String roleSessionName, int du

/**
* Revokes an STS token.
* @param accessKeyId The STS accessKeyId (starting with ASIA...)
* @param sessionToken The STS session token
* @param sessionToken The STS sessionToken
* @throws IOException if an error occurs while revoking the STS token
*/
void revokeSTSToken(String accessKeyId, String sessionToken) throws IOException;
void revokeSTSToken(String sessionToken) throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2798,8 +2798,8 @@ public AssumeRoleResponseInfo assumeRole(String roleArn, String roleSessionName,
}

@Override
public void revokeSTSToken(String accessKeyId, String sessionToken) throws IOException {
ozoneManagerClient.revokeSTSToken(accessKeyId, sessionToken);
public void revokeSTSToken(String sessionToken) throws IOException {
ozoneManagerClient.revokeSTSToken(sessionToken);
}

private static ExecutorService createThreadPoolExecutor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1194,11 +1194,10 @@ default AssumeRoleResponseInfo assumeRole(String roleArn, String roleSessionName

/**
* Revokes an STS token.
* @param accessKeyId The STS accessKeyId (starting with ASIA...)
* @param sessionToken The STS session token
* @param sessionToken The STS sessionToken
* @throws IOException if an error occurs while revoking the STS token
*/
default void revokeSTSToken(String accessKeyId, String sessionToken) throws IOException {
default void revokeSTSToken(String sessionToken) throws IOException {
throw new UnsupportedOperationException("OzoneManager does not require this to be implemented");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2676,10 +2676,9 @@ public AssumeRoleResponseInfo assumeRole(String roleArn, String roleSessionName,
}

@Override
public void revokeSTSToken(String accessKeyId, String sessionToken) throws IOException {
public void revokeSTSToken(String sessionToken) throws IOException {
final OzoneManagerProtocolProtos.RevokeSTSTokenRequest request =
OzoneManagerProtocolProtos.RevokeSTSTokenRequest.newBuilder()
.setAccessKeyId(accessKeyId)
.setSessionToken(sessionToken)
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2387,8 +2387,7 @@ message AssumeRoleResponse {
}

message RevokeSTSTokenRequest {
required string accessKeyId = 1;
required string sessionToken = 2;
required string sessionToken = 1;
}

message RevokeSTSTokenResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ String getMultipartKeyFSO(String volume, String bucket, String key, String
*
* @return Table.
*/
Table<String, String> getS3RevokedStsTokenTable();
Table<String, Long> getS3RevokedStsTokenTable();


/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public class OmMetadataManagerImpl implements OMMetadataManager,
private TypedTable<String, String> snapshotRenamedTable;
private TypedTable<String, CompactionLogEntry> compactionLogTable;

private TypedTable<String, String> s3RevokedStsTokenTable;
private TypedTable<String, Long> s3RevokedStsTokenTable;

private OzoneManager ozoneManager;

Expand Down Expand Up @@ -489,8 +489,8 @@ protected void initializeOmTables(CacheType cacheType,

compactionLogTable = initializer.get(OMDBDefinition.COMPACTION_LOG_TABLE_DEF);

// temporaryAccessKeyId -> sessionToken
// FULL_CACHE keeps revocations in memory as there are not expected to be many revoked tokens
// sessionToken -> insertionTimeMillis
// FULL_CACHE keeps revocations in memory as there are not expected to be many
s3RevokedStsTokenTable = initializer.get(
OMDBDefinition.S3_REVOKED_STS_TOKEN_TABLE_DEF, CacheType.FULL_CACHE);
}
Expand Down Expand Up @@ -1691,7 +1691,7 @@ public Table<String, CompactionLogEntry> getCompactionLogTable() {
}

@Override
public Table<String, String> getS3RevokedStsTokenTable() {
public Table<String, Long> getS3RevokedStsTokenTable() {
return s3RevokedStsTokenTable;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
* | userTable | /user :- UserVolumeInfo |
* | dTokenTable | OzoneTokenID :- renew_time |
* | s3SecretTable | s3g_access_key_id :- s3Secret |
* | s3RevokedStsTokenTable | sts_access_key_id :- sessionToken |
* | s3RevokedStsTokenTable | sts_session_token :- insertionTimeMillis |
* |------------------------------------------------------------------------|
* }
* </pre>
Expand Down Expand Up @@ -163,11 +163,11 @@ public final class OMDBDefinition extends DBDefinition.WithMap {
S3SecretValue.getCodec());

public static final String S3_REVOKED_STS_TOKEN_TABLE = "s3RevokedStsTokenTable";
/** s3RevokedStsTokenTable: sts_access_key_id :- sessionToken.*/
public static final DBColumnFamilyDefinition<String, String> S3_REVOKED_STS_TOKEN_TABLE_DEF
/** s3RevokedStsTokenTable: sts_session_token :- insertionTimeMillis.*/
public static final DBColumnFamilyDefinition<String, Long> S3_REVOKED_STS_TOKEN_TABLE_DEF
= new DBColumnFamilyDefinition<>(S3_REVOKED_STS_TOKEN_TABLE,
StringCodec.get(),
StringCodec.get());
LongCodec.get());

//---------------------------------------------------------------------------
// Volume, Bucket, Prefix and Transaction Tables:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.audit.OMAction;
import org.apache.hadoop.ozone.om.OzoneManager;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext;
import org.apache.hadoop.ozone.om.request.OMClientRequest;
import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
Expand All @@ -43,18 +42,16 @@
/**
* Handles S3RevokeSTSTokenRequest request.
*
* <p>This request marks an STS temporary access key id as revoked by inserting
* <p>This request marks an STS session token as revoked by inserting
* it into the {@code s3RevokedStsTokenTable}. Subsequent S3 requests
* authenticated with the same STS access key id will be rejected when the
* authenticated with the same STS session token will be rejected when the
* revocation state has propagated.</p>
*/
public class S3RevokeSTSTokenRequest extends OMClientRequest {

private static final Logger LOG = LoggerFactory.getLogger(S3RevokeSTSTokenRequest.class);
private static final Clock CLOCK = Clock.system(ZoneOffset.UTC);

private String originalAccessKeyId;

public S3RevokeSTSTokenRequest(OMRequest omRequest) {
super(omRequest);
}
Expand All @@ -67,29 +64,22 @@ public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
// Get the original (long-lived) access key id from the session token
// and enforce the same permission model that is used for S3 secret
// operations (get/set/revoke). Only the owner of the original access
// key (or an S3 / tenant admin) is allowed to revoke its temporary
// STS credentials.
// key (i.e. the creator of the STS token) or an S3 / tenant admin is allowed
// to revoke its temporary STS credentials.
final String sessionToken = revokeReq.getSessionToken();
final String tempAccessKeyId = revokeReq.getAccessKeyId();
final STSTokenIdentifier stsTokenIdentifier = STSSecurityUtil.constructValidateAndDecryptSTSToken(
sessionToken, ozoneManager.getSecretKeyClient(), CLOCK);
originalAccessKeyId = stsTokenIdentifier.getOriginalAccessKeyId();

// Validate that the Access Key ID in the request matches the one in the token
// to prevent users from revoking arbitrary keys using a valid token.
if (!stsTokenIdentifier.getTempAccessKeyId().equals(tempAccessKeyId)) {
throw new OMException("Access Key ID in request does not match the session token",
OMException.ResultCodes.INVALID_REQUEST);
}
final String originalAccessKeyId = stsTokenIdentifier.getOriginalAccessKeyId();

final OzoneManagerProtocolProtos.UserInfo userInfo = getUserInfo();
final UserGroupInformation ugi = S3SecretRequestHelper.getOrCreateUgi(originalAccessKeyId);
S3SecretRequestHelper.checkAccessIdSecretOpPermission(ozoneManager, ugi, originalAccessKeyId);

final OMRequest.Builder omRequest = OMRequest.newBuilder()
.setRevokeSTSTokenRequest(revokeReq)
.setCmdType(getOmRequest().getCmdType())
.setClientId(getOmRequest().getClientId())
.setUserInfo(getUserInfo());
.setUserInfo(userInfo);

if (getOmRequest().hasTraceID()) {
omRequest.setTraceID(getOmRequest().getTraceID());
Expand All @@ -103,20 +93,20 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, Execut
final OMResponse.Builder omResponse = OmResponseUtil.getOMResponseBuilder(getOmRequest());

final OzoneManagerProtocolProtos.RevokeSTSTokenRequest revokeReq = getOmRequest().getRevokeSTSTokenRequest();
final String accessKeyId = revokeReq.getAccessKeyId();
final String sessionToken = revokeReq.getSessionToken();

// All actual DB mutations are done in the response's addToDBBatch().
final OMClientResponse omClientResponse = new S3RevokeSTSTokenResponse(
accessKeyId, sessionToken, omResponse.build());
sessionToken, omResponse.build());

// Audit log
final Map<String, String> auditMap = new HashMap<>();
auditMap.put(OzoneConsts.S3_REVOKESTSTOKEN_USER, originalAccessKeyId);
final OzoneManagerProtocolProtos.UserInfo userInfo = getOmRequest().getUserInfo();
auditMap.put(OzoneConsts.S3_REVOKESTSTOKEN_USER, userInfo.getUserName());
markForAudit(ozoneManager.getAuditLogger(), buildAuditMessage(
OMAction.REVOKE_STS_TOKEN, auditMap, null, getOmRequest().getUserInfo()));
OMAction.REVOKE_STS_TOKEN, auditMap, null, userInfo));

LOG.info("Marked STS temporary access key '{}' as revoked.", accessKeyId);
LOG.info("Marked STS session token '{}' as revoked.", sessionToken);
return omClientResponse;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

import jakarta.annotation.Nonnull;
import java.io.IOException;
import java.time.Clock;
import java.time.ZoneOffset;
import org.apache.hadoop.hdds.utils.db.BatchOperation;
import org.apache.hadoop.hdds.utils.db.Table;
import org.apache.hadoop.ozone.om.OMMetadataManager;
Expand All @@ -35,22 +37,22 @@
@CleanupTableInfo(cleanupTables = {S3_REVOKED_STS_TOKEN_TABLE})
public class S3RevokeSTSTokenResponse extends OMClientResponse {

private final String accessKeyId;
private static final Clock CLOCK = Clock.system(ZoneOffset.UTC);

private final String sessionToken;

public S3RevokeSTSTokenResponse(String accessKeyId, String sessionToken, @Nonnull OMResponse omResponse) {
public S3RevokeSTSTokenResponse(String sessionToken, @Nonnull OMResponse omResponse) {
super(omResponse);
this.accessKeyId = accessKeyId;
this.sessionToken = sessionToken;
}

@Override
public void addToDBBatch(OMMetadataManager omMetadataManager, BatchOperation batchOperation) throws IOException {
if (accessKeyId != null && getOMResponse().hasStatus() && getOMResponse().getStatus() == OK) {
final Table<String, String> table = omMetadataManager.getS3RevokedStsTokenTable();
if (sessionToken != null && getOMResponse().hasStatus() && getOMResponse().getStatus() == OK) {
final Table<String, Long> table = omMetadataManager.getS3RevokedStsTokenTable();
if (table != null) {
// Store sessionToken as value
table.putWithBatch(batchOperation, accessKeyId, sessionToken);
// Store insertionTimeMillis as value
table.putWithBatch(batchOperation, sessionToken, CLOCK.millis());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public static void validateS3Credential(OMRequest omRequest,
token, ozoneManager.getSecretKeyClient(), CLOCK);

// Ensure the token is not revoked
if (isRevokedStsTempAccessKeyId(stsTokenIdentifier, ozoneManager)) {
if (isRevokedStsToken(token, ozoneManager)) {
LOG.info("Session token has been revoked: {}, {}", stsTokenIdentifier.getTempAccessKeyId(), token);
throw new OMException("STS token has been revoked", REVOKED_TOKEN);
}
Expand Down Expand Up @@ -140,9 +140,9 @@ private static void validateSTSTokenAwsSignature(STSTokenIdentifier stsTokenIden
}

/**
* Returns true if the STS token's temporary access key ID is present in the revoked STS token table.
* Returns true if the STS session token is present in the revoked STS token table.
*/
private static boolean isRevokedStsTempAccessKeyId(STSTokenIdentifier stsTokenIdentifier, OzoneManager ozoneManager)
private static boolean isRevokedStsToken(String sessionToken, OzoneManager ozoneManager)
throws OMException {
try {
final OMMetadataManager metadataManager = ozoneManager.getMetadataManager();
Expand All @@ -152,17 +152,14 @@ private static boolean isRevokedStsTempAccessKeyId(STSTokenIdentifier stsTokenId
throw new OMException(msg, INTERNAL_ERROR);
}

final Table<String, String> revokedStsTokenTable = metadataManager.getS3RevokedStsTokenTable();
final Table<String, Long> revokedStsTokenTable = metadataManager.getS3RevokedStsTokenTable();
if (revokedStsTokenTable == null) {
final String msg = "Could not determine STS revocation: revokedStsTokenTable is null";
LOG.warn(msg);
throw new OMException(msg, INTERNAL_ERROR);
}

// When the STSTokenIdentifier is validated, it ensures the temp access key id is not null/empty
final String tempAccessKeyId = stsTokenIdentifier.getTempAccessKeyId();

return revokedStsTokenTable.getIfExist(tempAccessKeyId) != null;
return revokedStsTokenTable.getIfExist(sessionToken) != null;
} catch (Exception e) {
final String msg = "Could not determine STS revocation because of Exception: " + e.getMessage();
LOG.warn(msg, e);
Expand Down
Loading