I have a number of services that are authenticated using OAuth Bearer tokens. I can obtain the tokens using either an OAuth Client Credentials grant or a Resource Owner Credentials Grant.
However I have a number of existing systems that are only capable of making calls authenticated using Mutual TLS authenticated connections.
Rather than updating all the calling applications to be able to obtain OAuth bearer tokens I’d instead like to build a gateway proxy that:
- Receives TLS Authenticated connections
- Uses the subject of the certificate to identify the system actor
- Obtain a token on that system’s behalf using Client Credentials or Resource Owner grant
- Make the call to the underlying service and return the results to the client
Essentially I want to hide the fact that OAuth is in use from the old clients and allow them to work exclusively with Mutal TLS Authentication.
Are there existing reverse proxies or ways or modules for Ngnix, Apache, Envoy or similar HTTP reverse proxies that would achieve this without building an entire proxy?
I’ve found lots of modules that handle the case of setting up Apache and Ngnix to be a OAuth relaying party or resource server using various modules such as
But can’t find any examples of them acting as an OAuth or Open ID Connect client as a proxy for the Mutual TLS authenticated client.
Particularly I want to avoid writing the proxy part. Even if I have to script the actual OAuth interactions. The closest thing I’ve found is this blog post on implementing an OAuth RP in Envoy lua scripts.
I struggle to imagine this is a unique need so I’m wondering if there is any standard implementation of this pattern out there that I haven’t found.
2
Answers
It turns out that this is easy to accomplish with Envoy Proxy. Specifically by making use of the External Authorization HTTP Filters.
Envoy proxy can be configured to do the SSL termination and require a client certificate by setting the Downstream TLS Context on the listener and setting
require_client_certificate
totrue
.It's possible to configure the HTTP Connection Manager Network Filter to set the
x-forwarded-client-cert
header on the request to the upstream service. Specifically setting forward-client-cert-details toSANITIZE_SET
will cause envoy to set the header. What is included in the header can be configured by setting set-current-client-cert-details.But to actually have Envoy do the token exchange we need to configure an External Authorization filter. This allows envoy to call a service with details of the request (including the certificate) and that service can decide if the request is allowed or not. Crucially, when allowed it's able to add headers to the request made to the upstream services which allows it to add the bearer token needed for oauth.
There are both Network Filter and HTTP Filter versions of the External Authorization filter. You must use the HTTP one if you want to add headers to the upstream request.
The resulting envoy config looks like:
The trick then is to implement a GRPC service that implements the External Authorization Protcol to do the token exchange and either reject the request or provide the
Authorization
header to include the bearer token in the upstream request.If you still need this, I built an authorization server that works using a local auth DB. It’s very easy to convert it to call an OIDC and receive a token to be injected.
https://github.com./fams/tlsjwt-gw