cloudstateio / cloudstate

Distributed State Management for Serverless
https://cloudstate.io
Apache License 2.0
762 stars 97 forks source link

ACLs #280

Open jroper opened 4 years ago

jroper commented 4 years ago

Let's say you have an event handler:

service MyReadSideProcessor {
  rpc HandleItemAdded(ItemAdded) returns google.Empty {
    option (cloudstate.eventing) {
      in: {
        event_log: "shopping-cart"
      };
    };
  };
  rpc HandleItemRemoved(ItemRemoved) returns google.Empty {
    option (cloudstate.eventing) {
      in: {
        event_log: "shopping-cart"
      };
    };
  };
};

Now, these methods you expect to be invoked by the event log, but Cloudstate will allow anything to invoke them via gRPC, allowing any service to effectively inject events that didn't come from the event log. This is obviously not desirable, and in fact in some contexts would be a major security concern. We need to provide a way to make these methods effectively private, my suggestion is that we do this by introducing an Access Control List (ACL) mechanism.

To start with, ACLs may just be used to mark methods as private as not, but in future, we could use them to inject opaque token based authentication, and JWT based authentication and authorization, and other mechanisms eg TLS principal assertions etc. That's way out of scope for now, but the point is, we want to build a mechanism that would be flexible enough to support this in future should we add those features.

Here's an example of what this could look like:

service MyReadSideProcessor {
  // This declares a single ACL for the whole service
  option (cloudstate.acl) {
    // allow is an enum with two values, ALL or NONE
    allow: NONE
  }

  rpc HandleItemAdded(ItemAdded) returns google.Empty {
    option (cloudstate.eventing) {
      in: {
        event_log: "shopping-cart"
      };
    };
  };
  rpc HandleItemRemoved(ItemRemoved) returns google.Empty {
    option (cloudstate.eventing) {
      in: {
        event_log: "shopping-cart"
      };
    };
  };
};

For future compatibility, here's an example of what we might do for implementing JWT based authorization using claims on entity keys:

option (cloudstate.acl) {
  allow: JWT
  claim {
    entity_key = "ent"
  }
}
sleipnir commented 4 years ago

I really liked this suggestion @jroper

viktorklang commented 4 years ago

@jroper Are there any prior art we can look at for this feature?

sleipnir commented 4 years ago

I think this should be included in the PR https://github.com/cloudstateio/cloudstate/pull/353

jroper commented 4 years ago

@viktorklang Not that I've been able to find.