envoyproxy / envoy

Cloud-native high-performance edge/middle/service proxy
https://www.envoyproxy.io
Apache License 2.0
25k stars 4.81k forks source link

Error 'SignatureDoesNotMatch' Occurred while using AWS Request Signing Filter to access an object from AWS S3 #32851

Closed raunakkapoor closed 8 months ago

raunakkapoor commented 8 months ago

Title: Error 'SignatureDoesNotMatch' Occurred while using AWS Request Signing Filter to access an object from AWS S3

Description: I’m using Envoy debug-v1.29.2 along with the AWS request signing filter. The AWS_SIGV4 algorithm is employed to sign the requests. This setup is to access(GET call) AWS S3 objects using https routes.

Filter related configuration:

- name: envoy.filters.http.aws_request_signing
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.aws_request_signing.v3.AwsRequestSigning
    service_name: s3
    signing_algorithm: AWS_SIGV4
    region: 'us-east-1'
    use_unsigned_payload: false
    match_excluded_headers:
      - prefix: x-envoy
      - prefix: x-forwarded
      - exact: x-amzn-trace-id

To use web identity credentials provider I have created layered_runtime and set the envoy.reloadable_features.use_http_client_to_fetch_aws_credentials: true

layered_runtime:
  layers:
    - name: static_layer
      static_layer:
        envoy.reloadable_features.use_http_client_to_fetch_aws_credentials: true

Call to STS is fine and I am able to receive the temporary AWS credentials.

[2024-03-11 14:55:57.425][1][debug][aws] [source/extensions/common/aws/credentials_provider_impl.cc:613] Getting AWS web identity credentials from STS: sts.us-east-1.amazonaws.com:443
[2024-03-11 14:55:57.425][1][debug][aws] [source/extensions/common/aws/metadata_fetcher.cc:80] fetch AWS Metadata from the cluster sts_token_service_internal at [uri = https://sts.us-east-1.amazonaws.com/]
[2024-03-11 14:55:57.435][1][debug][router] [source/common/router/upstream_request.cc:578] [Tags: "ConnectionId":"0","StreamId":"15944039925918888167"] pool ready
[2024-03-11 14:55:57.435][1][debug][client] [source/common/http/codec_client.cc:141] [Tags: "ConnectionId":"0"] encode complete
[2024-03-11 14:55:57.549][1][debug][router] [source/common/router/router.cc:1506] [Tags: "ConnectionId":"0","StreamId":"15944039925918888167"] upstream headers complete: end_stream=false
[2024-03-11 14:55:57.550][1][debug][http] [source/common/http/async_client_impl.cc:106] async http request response headers (end_stream=false):
':status', '200'
'x-amzn-requestid', '0b97a632-4366-41bc-bad6-e53866af1e5e'
'content-type', 'application/json'
'content-length', '1966'
'date', 'Mon, 11 Mar 2024 14:55:57 GMT'
'x-envoy-upstream-service-time', '123'

[2024-03-11 14:55:57.550][1][debug][client] [source/common/http/codec_client.cc:128] [Tags: "ConnectionId":"0"] response complete
[2024-03-11 14:55:57.550][1][debug][aws] [source/extensions/common/aws/metadata_fetcher.cc:129] onSuccess: fetch AWS Metadata [cluster = sts_token_service_internal]: success
[2024-03-11 14:55:57.550][1][debug][aws] [source/extensions/common/aws/credentials_provider_impl.cc:731] AWS metadata fetch from STS success, calling callback func
[2024-03-11 14:55:57.550][1][debug][aws] [source/extensions/common/aws/credentials_provider_impl.cc:695] Received the following **AWS credentials from STS: AWS_ACCESS_KEY_ID=ASIAW2G6*********, AWS_SECRET_ACCESS_KEY=*****, AWS_SESSION_TOKEN=*******

After that when I am accessing the route to connect to S3 bucket and access an object, it is failing with SignatureDoesNotMatch 403 error.

[2024-03-11 14:57:10.402][17][debug][router] [source/common/router/router.cc:731] [Tags: "ConnectionId":"25","StreamId":"12034230780859130818"] router decoding headers:
':authority', 'something.com'
':path', '/something/index.html'
':method', 'GET'
':scheme', 'https'
'x-forwarded-for', '10.23.236.190'
'x-forwarded-proto', 'https'
'x-forwarded-port', '443'
'x-amzn-trace-id', 'Root=1-65ef1bc6-156a582f440cc84e4287b69e'
'cache-control', 'max-age=0'
'sec-ch-ua', '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"'
'sec-ch-ua-mobile', '?0'
'sec-ch-ua-platform', '"Windows"'
'upgrade-insecure-requests', '1'
'user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
'accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7'
'sec-fetch-site', 'none'
'sec-fetch-mode', 'navigate'
'sec-fetch-user', '?1'
'sec-fetch-dest', 'document'
'accept-encoding', 'gzip, deflate, br, zstd'
'accept-language', 'en-US,en;q=0.9'
'cookie', '_rollup=GA1.2.1122695626.1676847533; SIMI=eyJzdCI6MH0=; xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
'x-envoy-internal', 'true'
'x-request-id', 'e0caffd8-38cd-XXXXXXXXXXXXXXXX'
'x-amz-content-sha256', 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
'x-amz-security-token', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
'x-amz-date', '20240311T145700Z'
'authorization', 'AWS4-HMAC-SHA256 Credential=ASIAW2G6XXXXXXXXXXXXXXX/20240311/us-east-1/s3/aws4_request, SignedHeaders=accept;accept-encoding;accept-language;cache-control;cookie;host;sec-ch-ua;sec-ch-ua-mobile;sec-ch-ua-platform;sec-fetch-dest;sec-fetch-mode;sec-fetch-site;sec-fetch-user;upgrade-insecure-requests;user-agent;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-request-id, Signature=fd32707eddbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
'x-envoy-expected-rq-timeout-ms', '15000'

[2024-03-11 14:57:10.402][17][debug][pool] [source/common/http/conn_pool_base.cc:78] queueing stream due to no available connections (ready=0 busy=0 connecting=0)
[2024-03-11 14:57:10.402][17][debug][pool] [source/common/conn_pool/conn_pool_base.cc:291] trying to create new connection
[2024-03-11 14:57:10.402][17][debug][pool] [source/common/conn_pool/conn_pool_base.cc:145] creating a new connection (connecting=0)
[2024-03-11 14:57:10.402][17][debug][connection] [./source/common/network/connection_impl.h:98] [Tags: "ConnectionId":"26"] current connecting state: true
[2024-03-11 14:57:10.402][17][debug][client] [source/common/http/codec_client.cc:57] [Tags: "ConnectionId":"26"] connecting
[2024-03-11 14:57:10.402][17][debug][connection] [source/common/network/connection_impl.cc:1009] [Tags: "ConnectionId":"26"] connecting to 54.231.197.74:443
[2024-03-11 14:57:10.402][17][debug][connection] [source/common/network/connection_impl.cc:1028] [Tags: "ConnectionId":"26"] connection in progress
[2024-03-11 14:57:10.402][17][debug][connection] [source/common/network/connection_impl.cc:746] [Tags: "ConnectionId":"26"] connected
[2024-03-11 14:57:10.405][17][debug][client] [source/common/http/codec_client.cc:88] [Tags: "ConnectionId":"26"] connected
[2024-03-11 14:57:10.405][17][debug][pool] [source/common/conn_pool/conn_pool_base.cc:328] [Tags: "ConnectionId":"26"] attaching to next stream
[2024-03-11 14:57:10.405][17][debug][pool] [source/common/conn_pool/conn_pool_base.cc:182] [Tags: "ConnectionId":"26"] creating stream
[2024-03-11 14:57:10.406][17][debug][router] [source/common/router/upstream_request.cc:578] [Tags: "ConnectionId":"25","StreamId":"12034230780859130818"] pool ready
[2024-03-11 14:57:10.406][17][debug][client] [source/common/http/codec_client.cc:141] [Tags: "ConnectionId":"26"] encode complete
[2024-03-11 14:57:10.414][17][debug][router] [source/common/router/router.cc:1506] [Tags: "ConnectionId":"25","StreamId":"12034230780859130818"] upstream headers complete: end_stream=false
[2024-03-11 14:57:10.414][17][debug][http] [source/common/http/conn_manager_impl.cc:1869] [Tags: "ConnectionId":"25","StreamId":"12034230780859130818"] encoding headers via codec (end_stream=false):
':status', '403'
{"upstream_cluster":"service_1","bytes_sent":13894,"response_code":403,"x_forwarded_for":"10.23.236.190","request_method":"GET","protocol":"HTTP/1.1","client_address":"10.23.236.190","response_duration":12,"http2_authority":"some-bucket.s3.us-east-1.amazonaws.com","response_code_details":"via_upstream","request_id":"e0caffd8-38cd-4438-bbf5-2bdb2bd38cc4","user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36","upstream_response_time":"11","start_time":"2024-03-11T14:57:10.401Z","request_duration":13,"response_flags":"-","request_path":"/something/index.html","bytes_received":0,"upstream_host":"54.231.197.74:443"}
'x-amz-request-id', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
'x-amz-id-2', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX='
'content-type', 'application/xml'
'date', 'Mon, 11 Mar 2024 14:57:10 GMT'
'server', 'envoy'
'x-envoy-upstream-service-time', '11'
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<AWSAccessKeyId>ASIAW2G6XXXXXXXXXXXXXXX</AWSAccessKeyId>

AWS support: I've sought assistance from AWS premium support and asked for their input. The only thing they could confirm, using backend tools to review the error, was that the error resulted from a malformed requester provided in the authentication information.

The 403 error response indicates that the correct AccessKeyID was used, which was generated after making a call to STS. In this situation, how can we identify the problem? Is there an issue occurring during the request signing process?

Any suggestions for debugging and resolving this issue would be appreciated.

[optional Relevant Links:]

Any extra documentation required to understand the issue. Envoy AWS request signing filter

Note: I have masked or mocked the security related information in the debug logs

raunakkapoor commented 8 months ago

cc @suniltheta

raunakkapoor commented 8 months ago

After reviewing the AWS documentation to understand signed AWS API requests and comparing it with our approach in Envoy, I identified the problem: a discrepancy in the canonical request hash.

To elaborate, I was using a Route53 record (something.com) to front the ALB, which pointed to an EKS ingress>service>pod where Envoy was running. From Envoy, I was connecting to an S3 bucket (some-bucket.s3.us-east-1.amazonaws.com). However, I missed to include the host_rewrite: some-bucket.s3.us-east-1.amazonaws.com in the typed_per_filter_config.envoy.filters.http.aws_request_signing.aws_request_signing.

When Envoy was creating the canonical request hash, it was considering the host value as something.com. In contrast, when AWS was creating the canonical request hash, it was considering the host value as some-bucket.s3.us-east-1.amazonaws.com. This discrepancy led to a signature mismatch.

The correct configuration must include host_rewrite. Without it, the hash of the canonical request will differ, resulting in a 403 error: "The request signature we calculated does not match the signature you provided".

Example of config.yaml

 static_resources:
  listeners:
  - address:
      socket_address:
        address: 0.0.0.0
        port_value: 80
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          codec_type: AUTO
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: app
              domains:
              - "*"
              routes:
              - match:
                  prefix: "/"
                route:
                  cluster: versioned-cluster
                typed_per_filter_config:
                  envoy.filters.http.aws_request_signing:
                    "@type": type.googleapis.com/envoy.extensions.filters.http.aws_request_signing.v3.AwsRequestSigningPerRoute
                    aws_request_signing:
                      service_name: s3
                      region: us-west-1
                      use_unsigned_payload: true
                      host_rewrite: some-bucket.s3.us-east-1.amazonaws.com
                      match_excluded_headers:
                      - prefix: x-envoy
                      - prefix: x-forwarded
                      - exact: x-amzn-trace-id
                    stat_prefix: some-prefix
          http_filters:
          - name: envoy.filters.http.aws_request_signing
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.aws_request_signing.v3.AwsRequestSigning
              service_name: s3
              region: us-west-2
              use_unsigned_payload: true
              host_rewrite: some-bucket.s3.us-east-1.amazonaws.com
              match_excluded_headers:
              - prefix: x-envoy
              - prefix: x-forwarded
              - exact: x-amzn-trace-id
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
  - name: versioned-cluster
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: versioned-cluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 8080

AWS IAM Troubleshooting guide: https://docs.aws.amazon.com/IAM/latest/UserGuide/signature-v4-troubleshooting.html AWS documentation: https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html Envoy: https://github.com/envoyproxy/envoy/blob/v1.29.2/source/extensions/common/aws/signer_base_impl.cc