envoyproxy / envoy

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

GCP Authentication Filter should be able to use the token endpoint to inject access token #28866

Open sidharthramesh opened 1 year ago

sidharthramesh commented 1 year ago

Title: GCP Authentication Filter should be able to use the token endpoint to inject access token

Description: The GCP Authentication filter injects the id_token (JWT) from http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity into the Authorization header of the request.

However, some Google services seem to expect the access_token from http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token instead. One example is the https://healthcare.googleapis.com APIs.

When the URI is changed to the token endpoint, the response from computeMetadata/v1/instance/service-accounts/default/token is JSON:

 {"access_token":"ya29.c.redacted","expires_in":3400,"token_type":"Bearer"}

The GCP Authentication filter seems to inject the JSON response directly like so:

Authorization: Bearer {\"access_token\":\"ya29.c.redacted\",\"expires_in\":3400,\"token_type\":\"Bearer\"}

The correct access token can be retrieved using the following curl / jq commands:

curl \ 
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" \
-H "Metadata-Flavor: Google" | jq -r '.access_token'

and the expected header is:

Authorization: ya29.c.redacted

Currently, I am getting over this by using a Lua filter in tandem with the GCP Authentication filter like so to strip the access_token:

http_filters:
  - name: 'envoy.filters.http.gcp_authn'
    typed_config:
      '@type': type.googleapis.com/envoy.extensions.filters.http.gcp_authn.v3.GcpAuthnFilterConfig
      http_uri:
        uri: 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token?audience=[AUDIENCE]'
        cluster: 'gcp_authn'
        timeout: 10s
  #  Lua filter to extract the access token from the GCP metadata server response
  - name: envoy.filters.http.lua
    typed_config:
      '@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
      inline_code: |
        function envoy_on_request(request_handle)
            local auth_header = request_handle:headers():get("Authorization")

            if auth_header then
                local access_token = string.match(auth_header, '"access_token":"([^"]+)"')
                if access_token then
                    request_handle:headers():replace("Authorization", "Bearer " .. access_token)
                end
            end
        end
  - name: envoy.filters.http.router
    typed_config:
      '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

However, it'll be great to have this as a configuration option in the GCP Authentication Filter to automatically use the access_token / manipulate the response body using JSON path expressions to inject the right header.

htuch commented 1 year ago

@tyxia @yanavlasov

tyxia commented 1 year ago

@sidharthramesh Thanks for your interest in gcp_authn_filter!

This filter currently only support id_token. The support for access_token is definitely good to have. It was not implemented because we don't have use case for this for now. However,this filter was designed with extensibility in mind (as you can see the tokenCache is template by tokenType) and access_token has been scoped as future work in my original design. This support was also discussed here.

I can check with team again but I guess most likely we don't have bandwidth for this at the moment. If you want to contribute to this filter, that will be very welcomed. I can help with any questions, code review and so on.

sidharthramesh commented 1 year ago

Thank you @tyxia! We don’t see this as an immediate blocker because of the Lua filter workaround.

However, I’ll check with my team and let you know if there’s any bandwidth on our side to contribute.

Thank you again for all the good work!

kanurag94 commented 1 year ago

@tyxia @sidharthramesh I can try to help implement this with some guidance if you folks are occupied.

From what I see in the current implementation on the first pass, we would just need to extend the logic to be able to parse access_token correctly from response (oauth2 type response). Is that correct? Is there an existing design doc to extend it to access_token?

tyxia commented 1 year ago

@kanurag94 Thanks for your interest in contribution! I will be happy to help here.

I think the first step is request path (rather than response). Currently, this filter only supports query of id_token from metadata server. Access token will be queried by different command/request. Here is the doc about more details. @sidharthramesh probably could also share more details since his team has retrieved access token?

Once request path is done, we can then work on response path.

Then next step is using cache to store the valid token avoid redundant query when token is still valid

kanurag94 commented 1 year ago

Thanks @tyxia for the detailed steps.

I could see that in this issue and on https://github.com/envoyproxy/envoy/issues/24612#issue-1501303743 -- users have been able to use the same URL with instead of requesting identity they query for token.

Would love to know your thoughts on this.

tyxia commented 1 year ago

Hi @kanurag94 Could you please specify which API you want to change? Thanks

One thing to note: since this filter has been used widely, the API change should be introduced backwards/forwards compatible fashion. I.e no breaking api change