envoyproxy / envoy

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

proposal: add a filter for injecting credentials into outgoing HTTP requests #21851

Open yskopets opened 2 years ago

yskopets commented 2 years ago

Title: Add a filter for injecting credentials into outgoing HTTP requests

Description:

It would be conventient to have a standard filter that can inject credentials into outgoing HTTP requests (as a value of Authorization header).

The most common use cases:

  1. OAuth2 access token credential
  2. basic auth credential
  3. opaque bearer token credential

The primary focus of this proposal is on injecting OAuth2 access token credential.

Proposal:

Add an HTTP filter with the following configuration model:

- name: envoy.filters.network.http_connection_manager
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
    http_filters:
    - name: envoy.filters.http.credentials
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.filters.http.credentials.v3alpha.Injector
        config:
          rules:
          - match:
              ... # HTTP requests to match
            inject:
              credential: { ... } # credential to inject

If the list of rules is empty, the filter will have no effect.

With regards to OAuth2 support:

Usage examples:

Injecting OAuth2 access token

- name: envoy.filters.network.http_connection_manager
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
    http_filters:
    - name: envoy.filters.http.credentials
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.filters.http.credentials.v3alpha.Injector
        config:
          rules:
          - match:
              prefix: /
            inject:
              credential:
                oauth2:
                  token_endpoint:
                    cluster: oauth
                    uri: oauth.com/token
                    timeout: 3s
                  client_credentials:
                    client_id:
                      secret:
                        name: client-id
                        sds_config:
                          path: "/var/run/secret/credentials/oauth2/client-id.yaml"
                    client_password:
                      secret:
                        name: client-password
                        sds_config:
                          path: "/var/run/secret/credentials/oauth2/client-password.yaml"
                  # (Optional)
                  scopes:
                  - "example"

Injecting basic auth credentials

- name: envoy.filters.network.http_connection_manager
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
    http_filters:
    - name: envoy.filters.http.credentials
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.filters.http.credentials.v3alpha.Injector
        config:
          rules:
          - match:
              prefix: /
            inject:
              credential:
                basic:
                  username:
                    secret:
                      name: username
                      sds_config:
                        path: "/var/run/secret/credentials/basic/username.yaml"
                  password:
                    secret:
                      name: password
                      sds_config:
                        path: "/var/run/secret/credentials/basic/password.yaml"

Injecting opaque bearer token credential

- name: envoy.filters.network.http_connection_manager
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
    http_filters:
    - name: envoy.filters.http.credentials
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.filters.http.credentials.v3alpha.Injector
        config:
          rules:
          - match:
              prefix: /
            inject:
              credential:
                generic:
                  prefix: "Bearer "
                  value:
                    secret:
                      name: bearer-token
                      sds_config:
                        path: "/var/run/secret/credentials/generic/bearer-token.yaml"
wbpcode commented 2 years ago

Could you give some more descriptions about the specific scenes?

IMO, this may be useful in the mesh where the mTLS is disabled and authentication is necessary.

mbana commented 2 years ago

We are also interested in this.

We want/need client_credentials: https://auth0.com/docs/get-started/authentication-and-authorization-flow/client-credentials-flow because Envoy supports only authorization_code: https://github.com/envoyproxy/envoy/blob/main/source/extensions/filters/http/oauth2/oauth_client.cc#L25.

Do you have a branch or image I could use for testing, if you're already implemented it, that is.

tedli commented 2 years ago

Seriously, I need this feature, and I believe I'm not alone.

There was an out of maintenance service (A) which runs robustly for years. But the service (B) this old service depends on, got updated, that introduced auth for existing apis. (A calls B) So a proxy is needed to inject credentials into request from A to consume B, because not willing to touch the code of A.

wbpcode commented 2 years ago

Seriously, I need this feature, and I believe I'm not alone.

There was an out of maintenance service (A) which runs robustly for years. But the service (B) this old service depends on, got updated, that introduced auth for existing apis. (A calls B) So a proxy is needed to inject credentials into request from A to consume B, because not willing to touch the code of A.

Sounds reasonable scene.

wbpcode commented 2 years ago

I tagged this issue to help wanted. But based on our contributing policy, we need a maintainer (@envoyproxy/envoy-maintainers) to sponsor this feature. Because we will add a new extension.

Check this https://github.com/envoyproxy/envoy/blob/main/CONTRIBUTING.md#adding-new-extensions for more info.

cc @yskopets

tedli commented 2 years ago

It would be convenient if this can affect at route level. So in an istio mesh, we can create EnvoyFilter cr to patch virtual_host, to tell which route need this, also the workload selector could be used to tell which pod need this.

irab commented 2 years ago

I'm keen to have this feature as well. There's a great use case for an outbound edge proxy, adding some protection against malicious code.

Say for example an internal service A needs to access an external service like GitHub. Current practice is to inject a PAT into the container, which can be retrieved by either code execution on the container or privileged access at a node or cluster level. If we move the PAT token out of the container and/or cluster then we can protect against malicious actors with code execution.

Authorisation could be managed with service account + outbound domain allowlists, with JWT validation?

deva26 commented 2 years ago

We would be very much interested in this feature getting implemented. +100

kyessenov commented 1 year ago

Can this be used to implement STS? E.g. exchange k8s service account for a Google service account when connecting to Google services?

zhaohuabing commented 1 year ago

It seems that STS works like oauth Client Credentials Grant, so I think it would be possible to also include it in this new filter. However, the initial implementation will focus on oauth Client Credentials Grant flow.

pre commented 1 year ago

Our use case: Expose Kubernetes API server's OIDC metadata.

It'd be great to expose the OIDC metadata with Istio/envoy, without having to deploy an extra component to request the OIDC metadata from Kubernetes API using the ServiceAccount's token.

Why? The OIDC metadata needs to be available for AWS IAM for AWS IAM Roles as Service Accounts (IRSA). This comes out-of-the-box in EKS, but in a private Kubernetes installation one needs to jump through hoops.

zhaohuabing commented 1 year ago

Our use case: Expose Kubernetes API server's OIDC metadata.

  • Kubernetes API server's OIDC metadata is not publicly available
  • OIDC metadata can be retrieved with a ServiceAccount's token

It'd be great to expose the OIDC metadata with Istio/envoy, without having to deploy an extra component to request the OIDC metadata from Kubernetes API using the ServiceAccount's token.

Why? The OIDC metadata needs to be available for AWS IAM for AWS IAM Roles as Service Accounts (IRSA). This comes out-of-the-box in EKS, but in a private Kubernetes installation one needs to jump through hoops.

When you talk about 'OIDC metadata', Do you mean ID token?

Sounds this could be implemented as an extension tpye of the credentials injector, or use the existing 'Generic' credential extentension. https://github.com/envoyproxy/envoy/pull/27769

pre commented 1 year ago

When you talk about 'OIDC metadata', Do you mean ID token?

No, I mean the OIDC metadata of the Kubernetes API Server.

With AWS IRSA, one needs to create a new Identity Provider in AWS IAM, and its authentication can be OIDC. Then AWS IAM will retrieve the OIDC metadata of this Identity Provider, which in this case is the Kubernetes API server.

Nowdays Kubernetes API server provides the OIDC metadata out-of-the-box. The problem is, in some case like ours, the metadata endpoint requires authentication. Because AWS IAM requires the OIDC metadata be publicly available, one needs to configure a proxy which provides AWS IAM with the OIDC metadata. In this case this proxy will authenticate to the Kubernetes API server OIDC endpoint using the ServiceAccount's credentials (Bearer token).

It looks like this:

❯ curl https://public-proxy-hostname.example.com/.well-known/openid-configuration
{"issuer":"https://public-proxy-hostname.example.com","jwks_uri":"https://public-proxy-hostname.example.com/jwks","response_types_supported":["id_token"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"]}

And here the issuer matches with the hostname of the metadata endpoint which is required for by AWS IAM Identity Provider configuration.

Now were are deploying our own simple proxy web server implementation for https://public-proxy-hostname.example.com/ but it would be neat if the istio envoy sidecar could be configured to authenticate to the private Kubernetes API server OIDC metadata endpoint. Then we wouldn't need to deploy an extra service only for this purpose.

loewenstein commented 10 months ago

I'd be interested in this as well. Is there already a rough idea for when the work in #30850 might get released?

phlax commented 10 months ago

i should imagine it will be included in the next release

juanmolle commented 4 months ago

It would be convenient if this can affect at route level. So in an istio mesh, we can create EnvoyFilter cr to patch virtual_host, to tell which route need this, also the workload selector could be used to tell which pod need this.

we are interesting in using it as upstream filter. For those apis that could be weight routing.

https://github.com/envoyproxy/envoy/pull/35194