envoyproxy / envoy

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

Generic auth filter #779

Closed xiaomi-tc closed 6 years ago

xiaomi-tc commented 7 years ago

Just like in nginx, I can set ngx_http_auth_request_module module to implement authorization when clients connecting in with a bypass authorization server. Does envoy support this or will support? If not ,what shall I do?

mattklein123 commented 7 years ago

I'm sorry but I don't understand what you are asking. Can you clarify what feature you want and/or what changes are required to what is currently implemented?

PiotrSikora commented 7 years ago

Translation: ngx_http_auth_request_module does a lookaside HTTP request, result of which (2xx or 403) decides whether or not the request should be processed / proxied.

mattklein123 commented 7 years ago

Yes, async request processing is possible in a filter via the AsyncClient mechanism. We have 2 auth filters at Lyft that work this way. There are other examples in the public code base. Beyond that, I'm not sure if this is an ask for a particular auth protocol being implemented.

markschmid commented 7 years ago

We also use the ngx_http_auth_request_module mechanism of nginx. In case of a positive auth result (2xx), we inject custom HTTP headers (e.g. user id etc.) for the service to work with. Is custom header injection based on the result of a subrequest possible in envoy? Any chance such a generic "auth subrequest" filter could be one of the standard filters of envoy?

jwfang commented 7 years ago

+1 for this.

it's nice to have a generic auth filter

jwfang commented 7 years ago

@markschmid

you can take a look at the istio project's mixer filter, from their istio/proxy project.

mattklein123 commented 7 years ago

@jwfang @markschmid if there is a generic way to do an auth filter that is useful to folks, I'm totally open to it. Feel free to drop a proposed design here. We have multiple auth filters internally at Lyft, but they contain Lyft specific functionality so it's not reasonable to open source them. I will reopen this issue to track seeing if there is a way to do a generic filter.

markschmid commented 7 years ago

Thanks!

OK so I propose the following generic authentication mechanism for Envoy, implemented as a filter. It would basically mimic the "client authorization based on the result of a subrequest" mechanism of the nginx ngx_http_auth_request_module module [1], which is, in a nutshell:

Authenticate each request with an external auth service. To perform authentication, an HTTP subrequest is sent to an external auth service where the subrequest is verified. If the subrequest returns a 2xx response code, access is allowed, if it returns 401 or 403, access is denied. This generic way of authentication would allow for implementing various authentication schemes.

This is the proposed mechanism step by step:

  1. Envoy receives a request (HTTP)
  2. In order to authenticate the request, Envoy performs an HTTP subrequest to an arbitrary auth service which verifies the subrequest
  3. If the auth service responds with a 2XX status code, access is allowed. Envoy further "processes" the original request (e.g. apply next filter, proxy onward etc.)
  4. Optionally: Inject HTTP header parameters received by the subrequest response into the original request (e.g. to supply user identity or other data)
  5. If the auth service responds with status codes 401 or 403, access is denied. Envoy sends a HTTP reponse to the original request with status code 401 (or 403 respectively).
  6. Any other status code sent by the auth service is considered an error

Possible filter configuration

{
  "type": "decoder", // unsure, maybe "both"?
  "name": "auth_request",
  "config": {
    "inject_headers": ["X-Authrequest-Userid", "X-Authrequest-Foo", "X-Authrequest-Bar", ...], // headers received from the auth service response that need to be injected into the verified request
    "timeout": "...", // maybe a configurable timeout in ms for the auth service response (?)
  }
}

Notes:

Additional reading about the nginx module in question can be found in [2] and finally, I think, the repository containing the source of that nginx module is found in [3] I hope this makes at least some sense. As I've only just started looking into envoy a few days ago, I might have missed existing options/concepts etc.

In any case, I'd be happy to further clarify or help in any other way for getting such a filter in envoy!

[1] http://nginx.org/en/docs/http/ngx_http_auth_request_module.html [2] https://www.nginx.com/resources/admin-guide/restricting-access-auth-request/ [3] https://github.com/PiotrSikora/ngx_http_auth_request_module

mattklein123 commented 7 years ago

In general this seems reasonable to me (there are some details to sort out in terms of what should be configurable, etc.). If someone wants to sign up to implementing the filter let me know. It's not difficult and can be modeled after existing filters.

jwfang commented 7 years ago

@markschmid good job

@mattklein123 while we are at it, should we be a little more ambitious about these ? in my understanding, envoy should shift system level functions from user service as much as possible, authorization is certainly one of them.

i looked at https://github.com/istio/proxy a while back ago, their mixer/transcoding filters are really interesting. transcoding soon will be in core envoy ( thanks ), so let's concentrate on mixer.

the mixer filter has three hook, Check/Quota/Report, which are called at different stage of quest processing. here is its api definitions: https://github.com/istio/api/blob/master/mixer/v1/service.proto . it does more than authorization and has a more generic hook points.

i wonder if there is any chance we can collaborate on this generic structure ?

cc @kyessenov @htuch if you are interested.

kyessenov commented 7 years ago

If you want a generic out-of-process auth validation, then I suggest using mixer filter. We have already modified envoy to insert a precondition check over gRPC to a golang "Mixer" server that further dispatches it to a set of plugins. There are details to be worked out regarding caching and TTLs that would need to be solved in your architecture as well.

mattklein123 commented 7 years ago

I've mentioned this elsewhere but medium/long term I would like to move mixer filter into envoy repo and the API into envoy-api repo. Can continue to discuss with @kyessenov and @louiscryan.

I would suggest not polluting this issue since I think that some people would use a basic HTTP auth filter also.

boosh commented 7 years ago

Looks like Ambassador already has this. Maybe there's some code there you can use as a base. Specifically this.

markschmid commented 7 years ago

Indeed, thanks for the info! Ambassador seems to be written in Python though. If only I knew C++, I'd implement that filter right away ... :)

boosh commented 7 years ago

@markschmid I've just added a link to the c++ filter. Here it is again.

markschmid commented 7 years ago

@boosh Great, didn't see that one, thanks!

kflynn commented 7 years ago

@markschmid Ambassador's front end is indeed written in Python, but authentication is mediated by an Envoy filter which is (of course) written in C++. @mattklein123, happy to chat with you about what's needed to get that filter folded in to the Envoy core -- it's pretty simple.

mattklein123 commented 7 years ago

@kflynn as long as it is generally useful, has docs, and tests, just do a PR :)

kflynn commented 7 years ago

@mattklein123 That's what I was hoping to hear. ;) OK, should be able to do that next week.

songole commented 7 years ago

Just came across this thread.

We are evaluating between nginx & envoy for transforming HTTP/1.x REST calls to gRPC servers on the backend with support for external auth mechanisms such as Okta. ngnix doesn't support gRPC and auth support in envoy is in the works.

Any further insights on the availability of this feature?

kflynn commented 7 years ago

There's an open PR that I need to get tests written for, but it works. If you're interested, I can get you information about how to test with it.

songole commented 7 years ago

Definitely. Will help with our eval.

kflynn commented 7 years ago

OK — I’ll make sure the PR is up to date and send you notes over the weekend. :)

kflynn commented 7 years ago

@songole I lied -- I haven't created the PR yet. It's currently committed as https://github.com/datawire/envoy/tree/flynn/feature/extauth; you can grab a built Docker image from DockerHub as dwflynn/ambassador-envoy:201710090214.

The extauth filter is an HTTP connection filter, with a config that looks like this:

              {
                "type": "decoder",
                "name": "extauth",
                "config": {
                  "cluster": "auth",
                  "timeout_ms": 5000,
                  "path_prefix": "authtest",
                  "allowed_headers": [
                    "x-auth-test", "x-hurkle"
                  ]
                }
              }

The extauth filter works by taking every incoming request, stripping the body, and forwarding it to the cluster named in the extauth filter. Only the body is dropped: the HTTP verb, path, and headers are all left untouched, except that if a path_prefix is specified in the filter config, it's prepended to the path when forwarding it.

If the auth service responds with an HTTP 200, the request is considered authenticated, and processing continues. If any headers listed in allowed_headers are supplied in the response from the auth service, they're copied into the request in flight.

If the auth service responds with anything other than 200 - note that this includes all the other 2YZ responses - the request fails, and the response from the auth service is handed back as the response to the original request.

That's all there is to it! Yell if you need more information, and we'll get this turned into a proper PR shortly.

songole commented 7 years ago

Thank you. I will check it out.

markschmid commented 7 years ago

Thanks @kflynn , I will also test it in the next few days.

markschmid commented 7 years ago

I've started getting my env ready for the testing. Currently stuck somewhere in-between: How would I configure envoy to apply this filter to all paths e.g. /api/* with the exception of e.g. /api/login? I've tried with two distinct http_connection_manager filters in my listener for port 80, but somehow envoy crashes. I know it's not directly related with the issue but merely general envoy knowledge. Any help greatly appreciated.

kflynn commented 7 years ago

@markschmid The way we would do that is to configure the extauth filter to use an external auth service that simply returned 200 for /api/login, but did an actual auth check for other things under /api. Part of the point of the extauth filter is to have a very simple interface on the Envoy side (the external part might need arbitrarily-complex business logic, after all).

markschmid commented 7 years ago

@kflynn Great, thanks! I'm having an external auth service in place already. I'm gonna implement the path based distinction there now.

markschmid commented 7 years ago

@kflynn Testing done, everything works as expected, awesome! My goal was to replace nginx with envoy for that specific extauth use case and I was now able to do exactly that. Besides the extauth workflow per se, I've also positively tested path_prefix and allowed_headers. Very happy, please let me know if/how I can assist further in order to get a PR asap.

tbird321 commented 7 years ago

Is there any plan on incorporating this into the master branch... it is a very common use case. I would prefer not to have my own customized version of envoy, but instead have this feature in the base code. If you need additional resources for this PR I would also be glad to help.

kflynn commented 7 years ago

@markschmid, @tbird321, totally agreed about getting this into the base product! Here's the issue: we need to write tests for this functionality to get the PR done, and I simply haven't had bandwidth for that. ☹️ That's definitely something where more hands could help out -- if you have any interest, drop me a line at flynn@datawire.io!

kflynn commented 7 years ago

Just to keep everyone in the loop: yup, still moving here. I'm currently doing a bit of cleanup to help out the folks who've so graciously offered to help with tests (thanks!), and Datawire has a guy starting in early November who'll be able to put a lot more cycles on this issue. Onward!

odino commented 6 years ago

Hey @kflynn -- any update on this? Sorry to bother :-P

rkucheka commented 6 years ago

Hi @kflynn , just wondering if there is any update here. Thanks !!!

kflynn commented 6 years ago

After discussions between Envoy folks, Datawire folks, and Tigera folks, we're folding Ambassador's auth functionality into the auth filter brought in by #2417. I've opened #2828 to track that work -- thanks for the interest!

mattklein123 commented 6 years ago

I'm going to close this in favor of https://github.com/envoyproxy/envoy/issues/2828. Please track that issue for further work on generic auth.