Authentication filter

714 views
Skip to first unread message

m...@clicktherapeutics.com

unread,
Apr 25, 2018, 4:59:26 PM4/25/18
to envoy-dev
Hello,

I am trying to figure out the best way to implement an authentication filter that takes an incoming HTTP request with a session token header, makes a call to a gRPC authentication service using that session token to authenticate, and then routes the original request to its destination with data from the authentication service injected into the outgoing request. From what I understand, I can implement a custom filter by building Envoy from source and writing a custom filter. I did however find an ext_authz filter linked in source already but with no documentation, and some proto files for an external_auth filter, also with no documentation. I'm wondering if either of these will be sufficient for my use case, and what the status of these implementations are. Let me first outline the use case clearly below:

1. An envoy listener receives a JSON-over-HTTP request destined for a gRPC service S, potentially with an X-Session-Token header
2. We run the JSON to gRPC transcoder filter to prepare the request to hit S
3. We then run our auth filter
  a. If the X-Session-Token header does not exist, don't run the rest of the filter (but don't fail the request! some requests from the web are legitimately unauthenticated, and thats fine)
  b. If it does exist, make a call to the gRPC authentication service. The authentication service is defined by the following proto:

service Authenticator {
  rpc AuthenticateUser(AuthenticateUserRequest) returns (AuthenticateUserResponse) {}
}

message AuthenticateUserRequest {
  string session_token = 1;
}

message AuthenticateUserResponse {
  string auth_token = 1;
}

In other words, it takes a session token and returns a temporary expiring auth token, used as proof of authentication as the request travels from service to service.

4. We receive our response from the authentication service.
  a. If we receive an error, return an error.
  b. If successful, take the auth_token key from the response and add a corresponding gRPC metadata key "authToken" to the gRPC request

I know that I can always fall back to implementing a custom filter if all else fails, but I want to choose the path of least resistance. Do the ext_authz or external_auth filters allow some or all of this behavior, and if so which parts can they handle? Are there plans to implement this type of functionality if it doesnt currently exist right now?

Matt Klein

unread,
Apr 25, 2018, 6:09:14 PM4/25/18
to m...@clicktherapeutics.com, sau...@tigera.io, spike Curtis, Gabriel Sagula, envoy-dev
+Saurabh/Spike/Gabriel who can comment more on ext_authz. 

In general I think you should be able to use this filter (possibly pending some enhancements that Gabriel is doing). 

The lack of documentation of the filter needs to be rectified so hopefully a combination of Saurabh and Gabriel can make that happen at some point.

Thanks,
Matt

--
You received this message because you are subscribed to the Google Groups "envoy-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to envoy-dev+unsubscribe@googlegroups.com.
To post to this group, send email to envo...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/envoy-dev/df35103a-f314-4170-a31f-dcd660f3b4dd%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--

Gabriel Sagula

unread,
Apr 25, 2018, 8:52:02 PM4/25/18
to Matt Klein, m...@clicktherapeutics.com, sau...@tigera.io, spike Curtis, envoy-dev
Hi! I’ve been working on the AuthZ filter enhancements with the Ambassador API Gateway team and the rest of the community. Except for the part that bypasses the gRPC service in the case of no session token, I think this use case is perfectly doable upon the upcoming changes that are being tracked here: https://github.com/envoyproxy/envoy/pull/3162
Unfortunatey, the current design sends every single request context from the filter to the to the Authz service. However, I think this could be changed by making this header constraint (e.g., session token) configurable.

Let me know if that makes sense.

Gabriel
--
Gabriel Linden Sagula

m...@clicktherapeutics.com

unread,
Apr 26, 2018, 11:36:35 AM4/26/18
to envoy-dev
That sounds great! I think the header constraint (or a more general constraint defining when we want to attempt authentication) could be useful in a lot of cases, since I imagine its pretty common that authenticated and non-authenticated requests pass through the same listener.

I'm still trying to figure out how exactly this envoy filter would be configured for my use case. I can't figure out how this filter would:

1. Create a gRPC call to the auth service using the correct rpc with the correct body. We will need to somehow get the value of the X-Session-Token header and create an AuthenticateUserRequest message out of it. I believe the CheckRequest message allows for defining the call to the auth service, I couldn't figure out how to configure envoy to dynamically construct this request based on the incoming HTTP request.

2. Map the AuthenticateUserResponse message to the outgoing request's metadata (specifically, map the auth_token from the response to an "authToken" gRPC metadata key on the outgoing request. Currently, it looks like PR above automatically takes headers received from the auth service and adds them to the outgoing request's headers. I could move the authToken returned by the auth service from the AuthenticateUserResponse message to the response metadata if that makes it easier (it probably should be designed like that anyways for consistency now that I think about it).

If its not too much trouble could you provide some sort of example configuration that would satisfy our use case?
To unsubscribe from this group and stop receiving emails from it, send an email to envoy-dev+...@googlegroups.com.

To post to this group, send email to envo...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/envoy-dev/df35103a-f314-4170-a31f-dcd660f3b4dd%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Gabriel Linden Sagula
Message has been deleted

gsa...@gmail.com

unread,
Apr 28, 2018, 12:37:03 PM4/28/18
to envoy-dev
Sorry for the delay. I'm not sure if I fully understand your last question. You have to define a cluster first and then configure your filter to talk to it. The filter will send the context of the call to your server which should handle whatever logic is needed. I will add this to the documentation since more people, including my current company, have been asking me how it works. I'm not sure if it will be in the same PR though. Here is a simple example:

http_filters:
- name: envoy.ext_authz
            config:
              grpc_service:
                envoy_grpc:
                  cluster_name: authz_service
              failure_mode_allow: true
- name: envoy.router
            config: {}
  
clusters:
- name: authz_service
    connect_timeout: 0.25s
    type: strict_dns
    lb_policy: round_robin
    http2_protocol_options: {}
    hosts:
- socket_address:
        address: 127.0.0.1
        port_value: 50051

Rama Rao

unread,
Apr 28, 2018, 11:55:08 PM4/28/18
to gsa...@gmail.com, envoy-dev
Sorry, for hijacking this thread, for a little off-topic but a related conversation. Are you thinking of any cache in external auth filter that could avoid going to external auth service for every request? We are also experimenting with external auth service and we think we need one - I just want to understand what you think for your use cases?

Thanks,
Rama

--
You received this message because you are subscribed to the Google Groups "envoy-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to envoy-dev+unsubscribe@googlegroups.com.

To post to this group, send email to envo...@googlegroups.com.

Richard Li

unread,
Apr 29, 2018, 7:25:46 AM4/29/18
to Rama Rao, Gabriel Sagula, envoy-dev

On Sat, Apr 28, 2018 at 11:55 PM, Rama Rao <ramarao...@gmail.com> wrote:
Sorry, for hijacking this thread, for a little off-topic but a related conversation. Are you thinking of any cache in external auth filter that could avoid going to external auth service for every request? We are also experimenting with external auth service and we think we need one - I just want to understand what you think for your use cases?

Yes. (As context, Gabriel has been working on porting the design we used for the Ambassador auth stuff to Envoy upstream so we don't have to maintain our Envoy fork. In practice, we've had lots of folks use the auth with subrequest model without any problems.)

Richard

Rama Rao

unread,
Apr 29, 2018, 7:55:55 AM4/29/18
to Richard Li, Gabriel Sagula, envoy-dev
Cool, thanks! 

Rama Rao

unread,
Apr 29, 2018, 9:12:37 AM4/29/18
to Richard Li, Gabriel Sagula, envoy-dev
Is this the repo you were mentioning about your own fork https://github.com/datawire/ambassador-envoy?
I couldn't find any caching implementation in the filter? Am I looking at the right repo?

Richard Li

unread,
Apr 29, 2018, 7:37:09 PM4/29/18
to Rama Rao, Gabriel Sagula, envoy-dev
Yes. Sorry, I must have been unclear. The Envoy upstream work is based on the repo you just referenced. We have not implemented caching, either in our fork or in upstream Envoy. Part of the reason for this is because we haven't run across a situation (yet) where caching would make a significant difference. Part of it is that we haven't had time. Help is welcome :-).

Rama Rao

unread,
Apr 29, 2018, 11:02:23 PM4/29/18
to Richard Li, Gabriel Sagula, envoy-dev
Ok. Thanks for the details. No problem.

m...@clicktherapeutics.com

unread,
Apr 30, 2018, 9:47:11 AM4/30/18
to envoy-dev
Ok so given the example configuration, I think I understand now how the filter is supposed to work, though I don't think it supports the original use case posted. What is most important for us is that the response from the auth service not simply be used as an indicator of success/failure, but that we are also allowed to use the headers/body of the response to add information to the original request in some way. Let me provide another example to make sure I understand how this filter is intended to work. It is a simplified example of the original example posted at the start of this thread.

1. Original request comes in on an envoy listener. For clarity, lets say its a call like getAccount({ account_id: 100 }), where the former notation means that the getAccount rpc was called with an account_id field. This request also has the following gRPC metadata: { X-Session-Token: $somehash }.

2. Authz filter is run, which simply takes the original request and forwards it to the auth service for processing.

3. The auth service starts processing the request. In this case, auth service receives a call with account_id in the body and X-Session-Token in the metadata; it ignores all but the X-Session-Token data and returns success if that session token is valid and an error if it is not.

4. The authz filter will simply take the response from the auth service and, on error, return the configured error response, or on success, let the filter chain continue.

Is this interpretation correct? If so, I have a few questions:

1. How does the authz filter know which rpc to call on the auth service cluster? The configuration only provides the correct cluster, not the rpc.

2. Won't the auth service reject any call where the body of the incoming request does not match the proto interface for the auth service's rpc? In this example, getAccount has an account_id field in the body, which the auth service's rpc will reject if not defined in its own body. Of course, its infeasible to add every field possible from every possible request to the auth service rpc's request message definition.

3. The auth service returns a response with auth_token in the body. We would like to take this value and insert it into the metadata of the original request. In our example, this means that after the authz filter is complete, our original request looks like: getAccount({ account_id: 100 }) with metadata { X-Session-Token: $somehash, X-Auth_Token: $signedToken }. Is this possible?

4. The request from envoy to auth service seems to be heavily reliant on the format of the original request (the one that the envoy listener originally received). Is there an opportunity to transform this request before making a request to the auth service? In our example, could we, say, take the X-Session-Token and create a request like: authService.authenticateUser({ session_token: $somehash }), where $somehash was taken from the value of the X-Session-Token header?

m...@clicktherapeutics.com

unread,
Jun 4, 2018, 1:23:12 PM6/4/18
to envoy-dev
Hey,

Just wanted to re-open this because I never received a response and this is still very relevant for us. Did my example make sense? I found a ticket that seems to describe exactly the functionality necessary for the original use case described in the first post: https://github.com/envoyproxy/envoy/issues/2828. Specifically, "The auth service needs to be able to modify the request that continues downstream on success." section of that ticket is critical for us, and I did not see a way to do that in the example configuration provided. Is this currently possible and if not, is it actively being worked on / is there a rough estimate as to when we might expect this to be complete?
Reply all
Reply to author
Forward
0 new messages