Linkerd Auth Plugin


#1

Hello linkerdians,

We are successfully using linkerd to route our gRPC requests to a microservices structure of gRPC servers. However, currently each service needs to individually handle authorizing the request before returning any data by sending the token to the auth service.

We are looking at moving that authentication logic to one central place. Linkerd seems like a logical place for this to happen. Is this assumption correct, or is that the wrong job for what linkerd is trying to solve?

Also, I have been researching linkerd plugins, and that looks like a good place to start. But I want to make sure I don’t reinvent the wheel if there is already an auth linkerd plugin available to use.

If I indeed need to create a custom plugin, would the identifier be the correct choice? I see a plugin classifier example in Java, but not for identifiers. Are there any resources I should be checking out for writing Java identifier plugin, if that is the correct plugin type?

Thanks for your time!

Tom


#2

Hi Tom,

You’re right that Linkerd is a great place for this kind of logic. I’d recommend you implement this as a requestAuthorizer plugin. Our Java plugin tutorial uses a classifier plugin as an example, but all plugins follow the same pattern so making a requestAuthorizer should be very similar.

Authorization tends to be something that everyone does slightly differently so I’m not aware of any auth plugins that anyone has open-sourced. However, if you come up with something that’s general enough to be widely useful and you’re willing to share it, we’d love to see it!

Let me know if you hit any snags during plugin development and we can help get you unstuck.


#3

Thanks Alex! That all makes sense.

I’m working through implementing the requestAuthorizer plugin, but had a few questions. My scala is in its infancy, so I’m a little slow finding the right classes to extend.

I see there is a RequestAuthorizerInitializer, much like the ResponseClassifierInitializer. From what I gather this tells linkerd which class to use to set up its configurations when it sees the configId string in its config.yaml.

I’m not seeing a parallel to the ResponseClassifierConfig class though. Would this simply be a PolymorphicConfig, and how would this go about being constructed?

I’m also at a loss for what my actual authorizer class would need to extend.

I appreciate your feedback. This is a new world for me :slight_smile:


#4

Good question. The class you must extend is H2RequestAuthorizerConfig. To implement this, you will need to implement the mk method which returns a Filter which will be run on each request.


#5

Hi! I recently implemented a RequestAuthorizer plugin in Scala. One thing I noticed, that perhaps you would benefit from knowing is that the callbacks are invoked multiple times. Therefore caching the results in a clever way will probably be of interest for your use case. (I am currently on vacation so I don’t have code/api details att hand, sorry.) I use version 1.4.0 in case Alex would have thought on the multiple invocations.


#6

Thanks @niklasuhrberg! I’m working my way through the basics, but I’ll keep that in mind.

@Alex - that was a great pointer, thanks! I have implemented H2RequestAuthorizerConfig and it’s mk method is returning my Authorizer class (extending filter), but I am having trouble figuring out how to return a Future from the apply method in Java. From what I can gather, I’m getting the request and the next service, I need to make my auth check, then pass the request to the next service or return an auth exception. Possibly that’s the wrong approach?

Thanks for all the help, I can definitely use it :rocket: ! I’ve included a little snippet so you can see where I’ve been hitting a wall:

public class Authorizer extends Filter<Request, Response, Request, Response> {
    private String headerName;

    /**
     * @param headerName the name of the response header to use
     */
    public Authorizer(String headerName) {
        this.headerName = headerName;
    }


    @Override
    public Future<Response> apply(Request request, Service<Request, Response> service) {
        // get auth info from request
        // make auth service call

        // what happens here?
        Response res = (Response) service(request);
        return res;
    }
}

#7

You’ve got the right idea! I’d recommend reading https://twitter.github.io/scala_school/finagle.html for some more background on Futures, Services, and Filters.

service(request) actually returns a Future, so I believe that cast to Response will fail.


#8

@Alex thanks for the resource, that definitely helps give me background.

I ran into an issue with the META-INF file where I named it incorrectly. Switching the file name to io.buoyant.linkerd.RequestAuthorizerInitializer with the contents test.authorizer.AuthorizerInitializer (my initializer) did the trick to let linkerd load it and print my test data.

@niklasuhrberg If you are able to share your plugin, or the auth implementation logic, that would help me a lot getting the idea of implementing the auth structure, I’m getting pretty good at translating Scala to Java :slight_smile:

Now onto implementing the auth checks. Thank you for the help. I’ll keep in touch on the outcome.


#9

Hi Tom, the code I wrote isn’t sharable, sorry. But it’s not a big loss since it has nothing to do with authorization per se. (It’s just that the RequestAuthorizer api fits my usecase.)

BR Niklas

Den tis 17 juli 2018 21:44Tom Mertz buoyant@discoursemail.com skrev:


#10

@niklasuhrberg no problem! Thanks for the pointer though, I appreciated it.

One thing I’m running into is how to resolve my authentication service to make the request and check if the request is indeed allowed. We are using namerd to resolve to services based on grpc paths io.l5d.header.path Is this possible, or do I need to have my auth service at a consistent addres?

Thanks!


#11

@tfmertz you can create a finagle client for making requests to your auth service. You should be able to use any built-in or configured namer in the named destination for the client. e.g. /#/io.l5d.foo/bar/bas.

Does that make sense?


#12

So, I have a gRPC Java client that is making the requests. Would this client be wrapped inside the finagle client?

I also have a quick other question. Is there a standard for linkerd to reject a request? So, it doesn’t make it to the server but instead returns with an “Access Denied”. Or should I be just throwing an exception?

I’m currently doing this:

return Future.exception(new Exception("Access Denied to service route. Please log in"));

Thanks for the help! Very close now


#13

Ah, if you already have a Java client for talking to your auth service, you can just use that.

If you return a Future.exception(new HttpResponseException(rsp)) whatever response you embed in the HttpResponseException will be sent to the caller.


#14

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.