aws / aws-sdk-java-v2

The official AWS SDK for Java - Version 2
Apache License 2.0
2.21k stars 853 forks source link

sigv4 for Lattice unsigned-payload not supported for http requests #5103

Open shulin-sq opened 7 months ago

shulin-sq commented 7 months ago

Describe the bug

When making http (not https) requests to lattice, the payload will always be signed because the signing logic will always sign the body when the protocol http and a streaming body is present. This isn't compatible with VPC Lattice which allows for GRPC over HTTP.

Expected Behavior

requests to be signed with 'x-amz-content-sha256: UNSIGNED-PAYLOAD'

Current Behavior

-H 'x-amz-content-sha256: STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD'

Reproduction Steps

      SdkHttpFullRequest request = SdkHttpFullRequest.builder()
          .protocol("HTTP")
          .host("test.com:80")
          .method(SdkHttpMethod.POST)
          .encodedPath("/_status")
          .contentStreamProvider(() - > new ByteArrayInputStream("{\"status\":\"ok\"}".getBytes()))
          .build();

      ExecutionAttributes ea = new ExecutionAttributes();
      ea.putAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS, EnvironmentVariableCredentialsProvider.create().resolveCredentials());
      ea.putAttribute(AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME, "vpc-lattice-svcs");
      ea.putAttribute(S3SignerExecutionAttribute.ENABLE_PAYLOAD_SIGNING, false);

      AwsCrtS3V4aSigner signer = AwsCrtS3V4aSigner.builder()
          .defaultRegionScope(RegionScope.GLOBAL)
          .build();

      var signedRequest = signer.sign(request, ea);
      for (var header: signedRequest.headers().entrySet()) {
        for (var value: header.getValue()) {
          System.out.print(" -H '" + header.getKey() + ": " + value + "'\n");
        }
      }

will print out some headers, notably

 -H 'x-amz-content-sha256: STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD'

Possible Solution

In DefaultAwsCrtS3V4aSigner (and other signers) change logic to something like

    private boolean shouldSignPayload(SdkHttpFullRequest request, ExecutionAttributes executionAttributes) {
        Boolean payloadSigning =
            executionAttributes.getAttribute(S3SignerExecutionAttribute.ENABLE_PAYLOAD_SIGNING);
        if (payloadSigning != null && !booleanValue(payloadSigning)) {
            return false;
        }

        if (!request.protocol().equals("https") && request.contentStreamProvider().isPresent()) {
            return true;
        }

to prioritize user choice

or look at AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME == vpc-lattice-svcs to decide if the request body should be signed.

Additional Information/Context

No response

AWS Java SDK version used

2.21.20

JDK version used

21.0.2

Operating System and version

macOS 14.4.1

debora-ito commented 7 months ago

Hi @shulin-sq

We don't have current plans to support unsigned payloads over HTTP requests - HTTP requests with payloads are signed for security reasons.

If this is something that the VPC Lattice service allows, we need to expose some kind of control mechanism in the SDK signer that the service can use, and it must be something that works across all language SDKs, not only Java. Adding a check in the code to see if the SERVICE_SIGNING_NAME is "vpc-lattice-svcs" is not easy to maintain and is not scalable.

We are in contact with the VPC Lattice team internally, we'll discuss the next steps and I'll post updates here later on.