External Authorization using Istio, OPA and OpenFGA

External Authorization using Istio, OPA and OpenFGA

I have been looking into multiple solutions around authentication and authorization lately and today we are going to discuss an interesting one.

The Problem: We have a use case where authenticated users need to be authorized before accessing a particular resource in a service and most of this logic should reside outside the actual code. The best case scenario for any development team will be when they'll be able to code their respective services without even bothering about whether an unauthorized request can even reach their systems. Now there are multiple sub-problems involved here, a few noteworthy ones are listed below:

No alt text provided for this image


  1. You'll need a separate authorization service if you want the business logic to be completely separate from your authorization logic.
  2. Even though you'll have a separate authorization service, how are you routing the request to it before it reaches the service?
  3. Generally, you'll find most authorization tools to be dumb stores that are just meant to answer whether user A is authorized to perform an action X on a service M but the requests will never be in this format. You'll have to extract the userIDs, resourceIDs, and actions from somewhere in between all of this.

Some Decisions We Made:

(whoever is reading this, please suggest if you have a better solution for any of these subproblems ??)

  1. For authorization service, we are going to use OpenFGA which is more or less an authorization store based on ReBAC (relationship-based access control system). You can read more about it here: https://openfga.dev/docs/authorization-and-openfga
  2. For routing, we are going to use Istio and OPA
  3. For formatting the requests we are again using OPA.

Note: You can consider this article an extension of Better External Authorization

The Solution:

Before jumping onto exactly what we are doing let's talk about Istio and OPA.

Istio: Istio extends Kubernetes to establish a programmable, application-aware network using the powerful Envoy service proxy. Working with both Kubernetes and traditional workloads, Istio brings standard, universal traffic management, telemetry, and security to complex deployments.

OPA (Open Policy Agent): OPA provides policy-based control for cloud-native environments. We can have flexible, fine-grained control for administrators across our stack using OPA.


Now according to the Istio doc that I mentioned about what we are doing is this:

At runtime,

  1. A request is intercepted by the proxy, and the proxy will send check requests to the external auth service, as configured by the user in the authorization policy.
  2. The external auth service will make the decision whether to allow it or not.
  3. If allowed, the request will continue and will be enforced by any local authorization defined by ALLOW/DENY action.
  4. If denied, the request will be rejected immediately.

And the OPA example policy looks something like this:

package envoy.auth

import input.attributes.request.http as http_request

default allow = false

token = {"valid": valid, "payload": payload} {
    [_, encoded] := split(http_request.headers.authorization, " ")
    [valid, _, payload] := io.jwt.decode_verify(encoded, {"secret": "secret"})
}

allow {
    is_token_valid
    action_allowed
}

is_token_valid {
  token.valid
  now := time.now_ns() / 1000000000
  token.payload.nbf <= now
  now < token.payload.exp
}

action_allowed {
  startswith(http_request.path, base64url.decode(token.payload.path))
}z

        

What the above policy simply means is that we have a rule allow with a default value of false which will be true if the rule is_token_valid and action_allowed are true. The rule is_token_valid checks whether the token is expired or not and the action_allowed rule simply checks whether the path in the JWT token matches the path present in our HTTP request. And if both things check out the request will be authorized.

How we are using OPA:

Our use case is a bit different from this. We can verify the token using Istio itself but what we want OPA to do is extract the context of the request, format it in a way that the FGA store can read, and decide whether to allow or deny the request based on the FGA response.

Now image that our FGA store has a tuple stored like this:

user: user:anne
relation: writer
object: document:42        

The tuple simply means that a user anne can write to a document named 42 and this is what our OPA policy looks like:

package envoy.auth

import input.attributes.request.http as http_request

default allow = false

allow {
    post_document
}

post_document {
    http_request.method == "POST"
    http_request.path == "/document"
    [_, encoded] := split(http_request.headers.authorization, " ")
    payload := io.jwt.decode(encoded)
    document_id := http_request.headers["document_id"]
    user := payload[1]["sub"]
  
    body := {
        "tuple_key": {
            "user": concat("", ["user:", user]),
            "relation": "writer",
            "object": concat("", ["document:", document_id])
        },
        "authorization_model_id": "XXXXXXXXXXXXXXX",
    }
    print("Inside auth")
    resp := http.send({"url": "https://fgastore/stores/storeid/check", "method": "post", "timeout": "3s", "body": body})
    resp.body["allowed"] == true
}        

Now the above policy has only one rule, post_document, and the rule will only be true when the coming request will be a post request with a path /document. Then we are extracting the user from the JWT token and document_id from the header. You can easily replicate this on the query or path parameters too. After that, we are sending a request to the FGA store which will check whether a relationship is present for that particular user, relation, and object combination and if the response body will show allowed == true then the user will be authorized for this request.

What next?

This was just a simple example of how you can use these open-source tools to implement an authorization pipeline that will keep your application logic away from making these decisions. Reading more about these tools will surely help us in resolving more complex cases.

Have a good day and happy coding ??!

Vijayendra Rathod

MSC in Cyber Security | AWS SA & SS | 2X Cyberark | RHCSA | RHCE | VCP 7 | Airwatch | CKA | 3X Hashicorp | MCSA & CCNA

11 个月

Everything happens at network layer and extra opa container required to attach with application

Andres Aguiar

Solving Authorization @ Okta | fga.dev / openfga.dev

1 å¹´

Hi Chaitanya Tyagi! Did you end up implementing this approach? I'm the Product Manager of OpenFGA, we are considering developing a native integration with Istio, and we are looking for people that have experience with the approach. Thanks!

要查看或添加评论,请登录

Chaitanya Tyagi的更多文章

  • Authorization using Casbin in GO

    Authorization using Casbin in GO

    What is Authorization and why do we need it? Imagine that you are the owner of a big store chain. Now to manage your…

    1 条评论
  • Pluggable Authentication Modules (PAM)

    Pluggable Authentication Modules (PAM)

    This is a very introductory article about PAM but I hope that this will kindle a bit of curiosity among the readers on…

  • Using Video and Image Assets Inside a Flutter App

    Using Video and Image Assets Inside a Flutter App

    This article is simply about how to display images and play videos on your flutter app using network or local assets…

  • Creating a Public and Private Subnet in AWS

    Creating a Public and Private Subnet in AWS

    AWS offer highly secure and available network solutions with consistently high performance and global coverage. Today…

  • Deploying Infrastructure Using Terraform

    Deploying Infrastructure Using Terraform

    Terraform enables users to define and provision a datacenter infrastructure using a high-level configuration language…

  • Automate the learning using Docker and Jenkins.

    Automate the learning using Docker and Jenkins.

    Let's automate the learning process using Docker and Jenkins! THE TASK AT HAND: Let's say you have to train a model on…

    3 条评论

社区洞察

其他会员也浏览了