Skip to content

Remove the use of the legacy signer #4941

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Feb 27, 2024
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
5 changes: 0 additions & 5 deletions services/docdb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,6 @@
<artifactId>aws-query-protocol</artifactId>
<version>${awsjavasdk.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>profiles</artifactId>
<version>${awsjavasdk.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>http-auth-aws</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,20 @@

package software.amazon.awssdk.services.docdb.internal;

import static software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute.AWS_CREDENTIALS;
import static software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME;

import java.net.URI;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.concurrent.CompletableFuture;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.CredentialUtils;
import software.amazon.awssdk.auth.signer.Aws4Signer;
import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute;
import software.amazon.awssdk.auth.signer.params.Aws4PresignerParams;
import software.amazon.awssdk.awscore.AwsExecutionAttribute;
import software.amazon.awssdk.awscore.endpoint.DefaultServiceEndpointBuilder;
import software.amazon.awssdk.core.Protocol;
import software.amazon.awssdk.core.SdkRequest;
import software.amazon.awssdk.core.SelectedAuthScheme;
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
import software.amazon.awssdk.core.client.config.SdkClientOption;
import software.amazon.awssdk.core.exception.SdkClientException;
Expand All @@ -40,7 +39,13 @@
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
import software.amazon.awssdk.http.auth.aws.signer.AwsV4FamilyHttpSigner;
import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner;
import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption;
import software.amazon.awssdk.http.auth.spi.signer.HttpSigner;
import software.amazon.awssdk.http.auth.spi.signer.SignRequest;
import software.amazon.awssdk.http.auth.spi.signer.SignedRequest;
import software.amazon.awssdk.identity.spi.Identity;
import software.amazon.awssdk.protocols.query.AwsQueryProtocolFactory;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.docdb.model.DocDbRequest;
Expand Down Expand Up @@ -79,49 +84,39 @@ public interface PresignableRequest {

private final Class<T> requestClassToPreSign;

private final Clock signingOverrideClock;
private final Clock signingClockOverride;

protected RdsPresignInterceptor(Class<T> requestClassToPreSign) {
this(requestClassToPreSign, null);
}

protected RdsPresignInterceptor(Class<T> requestClassToPreSign, Clock signingOverrideClock) {
protected RdsPresignInterceptor(Class<T> requestClassToPreSign, Clock signingClockOverride) {
this.requestClassToPreSign = requestClassToPreSign;
this.signingOverrideClock = signingOverrideClock;
this.signingClockOverride = signingClockOverride;
}

@Override
public final SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context,
ExecutionAttributes executionAttributes) {
ExecutionAttributes executionAttributes) {
SdkHttpRequest request = context.httpRequest();
SdkRequest originalRequest = context.request();
if (!requestClassToPreSign.isInstance(originalRequest)) {
return request;
}

if (request.firstMatchingRawQueryParameter(PARAM_PRESIGNED_URL).isPresent()) {
return request;
PresignableRequest presignableRequest = toPresignableRequest(request, context);
if (presignableRequest == null) {
return request.toBuilder().removeQueryParameter(PARAM_SOURCE_REGION).build();
}

PresignableRequest presignableRequest = adaptRequest(requestClassToPreSign.cast(originalRequest));

SelectedAuthScheme<?> selectedAuthScheme = executionAttributes.getAttribute(SELECTED_AUTH_SCHEME);
String sourceRegion = presignableRequest.getSourceRegion();
if (sourceRegion == null) {
return request;
}

String destinationRegion = executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNING_REGION).id();

String destinationRegion = selectedAuthScheme.authSchemeOption().signerProperty(AwsV4HttpSigner.REGION_NAME);
URI endpoint = createEndpoint(sourceRegion, SERVICE_NAME, executionAttributes);
SdkHttpFullRequest.Builder marshalledRequest = presignableRequest.marshall().toBuilder().uri(endpoint);

SdkHttpFullRequest requestToPresign =
marshalledRequest.method(SdkHttpMethod.GET)
.putRawQueryParameter(PARAM_DESTINATION_REGION, destinationRegion)
.removeQueryParameter(PARAM_SOURCE_REGION)
.build();
marshalledRequest.method(SdkHttpMethod.GET)
.putRawQueryParameter(PARAM_DESTINATION_REGION, destinationRegion)
.removeQueryParameter(PARAM_SOURCE_REGION)
.build();

requestToPresign = presignRequest(requestToPresign, executionAttributes, sourceRegion);
requestToPresign = sraPresignRequest(executionAttributes, requestToPresign, sourceRegion);

String presignedUrl = requestToPresign.getUri().toString();

Expand All @@ -140,39 +135,93 @@ public final SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context,
*/
protected abstract PresignableRequest adaptRequest(T originalRequest);

private SdkHttpFullRequest presignRequest(SdkHttpFullRequest request,
ExecutionAttributes attributes,
String signingRegion) {
/**
* Converts the request to a PresignableRequest if possible.
*/
private PresignableRequest toPresignableRequest(SdkHttpRequest request, Context.ModifyHttpRequest context) {
SdkRequest originalRequest = context.request();
if (!requestClassToPreSign.isInstance(originalRequest)) {
return null;
}
if (request.firstMatchingRawQueryParameter(PARAM_PRESIGNED_URL).isPresent()) {
return null;
}
PresignableRequest presignableRequest = adaptRequest(requestClassToPreSign.cast(originalRequest));
String sourceRegion = presignableRequest.getSourceRegion();
if (sourceRegion == null) {
return null;
}
return presignableRequest;
}

/**
* Presign the provided HTTP request using SRA HttpSigner
*/
private SdkHttpFullRequest sraPresignRequest(ExecutionAttributes executionAttributes, SdkHttpFullRequest request,
String signingRegion) {
SelectedAuthScheme<?> selectedAuthScheme = executionAttributes.getAttribute(SELECTED_AUTH_SCHEME);

Aws4Signer signer = Aws4Signer.create();
Aws4PresignerParams presignerParams = Aws4PresignerParams.builder()
.signingRegion(Region.of(signingRegion))
.signingName(SERVICE_NAME)
.signingClockOverride(signingOverrideClock)
.awsCredentials(resolveCredentials(attributes))
.build();

return signer.presign(request, presignerParams);
Instant signingInstant;
if (signingClockOverride != null) {
signingInstant = signingClockOverride.instant();
} else {
signingInstant = Instant.now();
}
// A fixed signing clock is used so that the current time used by the signing logic, as well as to
// determine expiration are the same.
Clock signingClock = Clock.fixed(signingInstant, ZoneOffset.UTC);
Duration expirationDuration = Duration.ofDays(7);
return doSraPresign(request, selectedAuthScheme, signingRegion, signingClock, expirationDuration);
}

private <T extends Identity> SdkHttpFullRequest doSraPresign(SdkHttpFullRequest request,
SelectedAuthScheme<T> selectedAuthScheme,
String signingRegion,
Clock signingClock,
Duration expirationDuration) {
CompletableFuture<? extends T> identityFuture = selectedAuthScheme.identity();
T identity = CompletableFutureUtils.joinLikeSync(identityFuture);

// Pre-signed URL puts auth info in query string, does not sign the payload, and has an expiry.
SignRequest.Builder<T> signRequestBuilder = SignRequest
.builder(identity)
.putProperty(AwsV4FamilyHttpSigner.AUTH_LOCATION, AwsV4FamilyHttpSigner.AuthLocation.QUERY_STRING)
.putProperty(AwsV4FamilyHttpSigner.EXPIRATION_DURATION, expirationDuration)
.putProperty(HttpSigner.SIGNING_CLOCK, signingClock)
.request(request)
.payload(request.contentStreamProvider().orElse(null));
AuthSchemeOption authSchemeOption = selectedAuthScheme.authSchemeOption();
authSchemeOption.forEachSignerProperty(signRequestBuilder::putProperty);
// Override the region
signRequestBuilder.putProperty(AwsV4HttpSigner.REGION_NAME, signingRegion);
HttpSigner<T> signer = selectedAuthScheme.signer();
SignedRequest signedRequest = signer.sign(signRequestBuilder.build());
return toSdkHttpFullRequest(signedRequest);
}

private AwsCredentials resolveCredentials(ExecutionAttributes attributes) {
return attributes.getOptionalAttribute(SELECTED_AUTH_SCHEME)
.map(selectedAuthScheme -> selectedAuthScheme.identity())
.map(identityFuture -> CompletableFutureUtils.joinLikeSync(identityFuture))
.filter(identity -> identity instanceof AwsCredentialsIdentity)
.map(identity -> {
AwsCredentialsIdentity awsCredentialsIdentity = (AwsCredentialsIdentity) identity;
return CredentialUtils.toCredentials(awsCredentialsIdentity);
}).orElse(attributes.getAttribute(AWS_CREDENTIALS));
private SdkHttpFullRequest toSdkHttpFullRequest(SignedRequest signedRequest) {
SdkHttpRequest request = signedRequest.request();

return SdkHttpFullRequest.builder()
.contentStreamProvider(signedRequest.payload().orElse(null))
.protocol(request.protocol())
.method(request.method())
.host(request.host())
.port(request.port())
.encodedPath(request.encodedPath())
.applyMutation(r -> request.forEachHeader(r::putHeader))
.applyMutation(r -> request.forEachRawQueryParameter(r::putRawQueryParameter))
.removeQueryParameter(PARAM_SOURCE_REGION)
.build();
}

private URI createEndpoint(String regionName, String serviceName, ExecutionAttributes attributes) {
Region region = Region.of(regionName);

if (region == null) {
throw SdkClientException.builder()
.message("{" + serviceName + ", " + regionName + "} was not "
+ "found in region metadata. Update to latest version of SDK and try again.")
+ "found in region metadata. Update to latest version of SDK and try again.")
.build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"useSraAuth": true,
"enableGenerateCompiledEndpointRules": true,
"verifiedSimpleMethods" : [
"describeDBClusterParameterGroups",
"describeDBClusterSnapshots",
Expand Down
Loading