Skip to content

ABAC Guard Details

Tom Mitchell edited this page Feb 24, 2017 · 3 revisions

GENI ABAC Guard Details

This page documents how ABAC-based guards on method invocations on GENI Federation Services (MA, SA, Logging, Credential Store) are implemented.

Introduction

The GENI Federation Services (also known as the Clearinghouse) consist of a set of methods served up at XMLRPC end points. Each method invocation is checked by policy to ensure that the caller of the method is allowed to make that call and receive its results based on the context (arguments, options) of that call.

The rules that determine whether a given caller is authorized to make the call are captured in ABAC credentials and computed using the ABAC logic engine.

To first order, the process of determining whether a user is authorized to make a given call consists of:

  • Collecting a set of assertions about the caller both with respect to and independent of the arguments to the call
  • Collecting a set of policies about what assertions/attributes are required of a caller of a particular method
  • These two collections are provided to the ABAC method who seeks to prove whether the caller is permitted to make this call in this context.
  • If the call is authorized, the call is made and result is returned. If not, an !AuthorizationError is raised.

ABAC Notation

ABAC consists of a set of credentials and a prover. The credentials are a set of signed statements that the signer asserts a relationship between an object and a set.

A typical credential looks like:

SIGNER.SET<-OBJECT # "SIGNER asserts that OBJECT is a member of SET".

or

SIGNER1.SET1<-SIGNER2.SET2 # "SIGNER asserts that anyone that SIGNER2 asserts is a member of SET2 is a member of SET1".

ABAC credentials can be assertions ("I claim this IS true") and policies ("I assert this MUST be true for authorization") but both are of the same essential format.

By convention, we use ME as the name of the current authority (assertions made by me), though any other name could be used instead.

Method subjects

The first step of authorization is extracting a set of 'subjects' from the method invocation. These are extracted from a series of sources:

  • options[match] : Any explicitly named object (referenced by ID or URN) in the options('match'] argument (for lookup)
  • options[fields] : Any explicitly named object (referenced by ID or URN) in the options('fields'] argument (for update, delete, create)
  • arguments: All method arguments are collapsed into a {name : value…} dictionary at the start of the invocation, and by name these may be extracted as subjects (e.g. the update method takes a 'urn' argument)

The following is the set of subject types extracted from the method context:

  • MEMBER_URN: The URN of the member(s) arguments of the invocation
  • SLICE_URN: The URN of the slice(s) arguments of the invocation
  • PROJECT_URN: The URN of the project(s) arguments of the invocation
  • REQUEST_ID: The identifier of a project join request (not a URN but treated as a unique identifier for the same processing)

It is assumed that there will be only one subject TYPE for a given invocation. An !ArgumentError is raised otherwise.

Variable bindings

For a given subject, a set of bindings may or may not be able to be computed.

Binding Name Meaning
$SLICE Bound to the slice argument (if any) of the invocation
$PROJECT Bound to the project argument (if any) of the invocation
$MEMBER Bound to he member argument (if any) of the invocation
$ROLE Bound to the role of the caller in the given slice or project (if any)
$SELF Bound to the URN of the caller
$SHARES_SLICE Bound to "SHARES_SLICE" if the caller and given member argument (if any) share membership in an unexpired slice
$SHARES_PROJECT Bound to "SHARES_PROJECT" if the caller and given member argument (if any) share membership in an unexpired0 project
$PROJECT_LEAD Bound to "PROJECT_LEAD" if the caller is the lead of some unexpired project
$PROJECT_ADMIN Bound to "PROJECT_ADMIN" if the caller is the admin of some unexpired project
$SEARCHING_BY_EMAIL Bound to "SEARCHING_BY_EMAIL" if the caller is searching for member information by email
$SEARCHING_FOR_PROJECT_LEAD_BY_UID Bound to "SEARCHING FOR_PROJECT_LEAD_BY_UID" if the caller is searching for member info of a project lead by member UID
$PENDING_REQUEST_TO_MEMBER Bound to "PENDING_REQUEST_TO_MEMBER" if the caller has a pending project join request for which the member subject (if any) is the lead.
$REQUEST_ROLE Bound to the role the caller has in the project of the given project join request_id (if any)
$REQUESTOR Bound to "REQUESTOR" if the caller is the requestor for the given project join request_id (if any)

ABAC Assertions

An assertion is a statement made by an authority about the caller. We compose assertions from templates (parameterized assertions) and bindings. If the bindings specified in the template are all bound, they are substituted and a complete assertion is is made. If some bindings required of the assertion template are unbound, the assertion is not made.

For example,

ME.IS_$SELF<-CALLER

is a templated assertion: The presence of the binding parameter ($SELF) makes this a template and not a complete assertion. If the parameter $SELF is bound (typically to the caller's URN, e.g. urn:publicid:IDN+ch-mb.gpolab.bbn.com+user+mbrinn), then the assertion can be completed as, e.g.

ME.IS_urn_publicidIDN_ch_mb_gpolab_bbn_com_user_mbrinn<-CALLER

Note that the URN is 'flattened' to change all punctuation (+, :, -) to an underscore to make it conform with ABAC syntax (see below).

Context-free Assertions

The following assertions are attempted to be made for each caller depending on their privileges:

Assertion Meaning
ME.IS_OPERATOR<-CALLER The given caller has operator privileges
ME.IS_PI<-CALLER The given caller has project lead (PI) privileges
ME.IS_AUTHORITY<-CALLER The given caller has authority privileges
ME.IS_$SELF<-CALLER The given operator has the given URN associated with the $SELF binding.

In addition, as a convenience, some assertions are generated to bundle other sets of assertions. Specifically, at initialization time, we generate , for each possible role (LEAD, ADMIN, MEMBER, AUDITOR),

ME.BELONGS_TO_$SLICE<-ME.IS_$ROLE_$SLICE 
ME.BELONGS_TO_$PROJECT<-ME.IS_$ROLE_$PROJECT

This a policy that states ME.BELONGS_TO_$PROJECT is satisfied by any of ME.IS_LEAD_$PROJECT, ME.IS_ADMIN_$PROJECT, etc.

ABAC Policies

Much like assertions, policies are assertions about who can make what method calls. They are typically templates with the binding $METHOD bound to the name of the currently called method. Thus when invoking the 'get_credentials' method,

ME.MAY_$METHOD<-ME.IS_OPERATOR

transforms into

ME.MAY_GET_CREDENTIALS<-ME.IS_OPERATOR

ABAC Proofs

With the subjects and assertion/policy templates extracted for the method invocation, the authorization proceeds as follows:

  • For each subject in the method subjects,
    • Generate bindings based on the given subject
    • Instantiate each assertion and policy template based on these bindings
    • Attempt to prove either of the following ABAC queries:
      • ME.MAY_$METHOD<-CALLER # The caller may call the method independent of context
      • ME.MAY_$METHOD_$SUBJECT<-CALLER # The caller may call the method in this context
    • If no proof can be generated as 'true' for any subject, the entire authorization fails.
    • If all subjects have a successful proof, the entire authorization succeeds.

JSON Representation of ABAC Assertions and Policies

The logic of which assertions and policies are invoked to make authorization decisions for a given method are contained in an external JSON file (one per Federation service, e.g. logging_policy.json, slice_authority_policy.json) which are kept in /etc/geni-chapi.

The format of these files is a list of method names, and for each, a list of ABAC assertion and policy templates, as follows:

{
   "method_name" : {
      "__DOC__" : "documentation about policy for this method",
      "assertions" : [
          "ME.ASSERTION<-CALLER", ...
      ],
     "policies" : [
         "ME.MAY_$METHOD<-ME.IS_OPERATOR", ...
    ]
   }
}

The following is an example of a policy file for the GENI logging service:


{
  "__DOC__" : "ABAC policies for CHAPI Logging service",

  "log_event" : {
    "__DOC__" : [
        "user_id must be self",
        "must belong to slice or project of context (if any)",
        "or is about self and only about self"
    ],
    "assertions" : [
        "ME.IS_$ROLE_$PROJECT<-CALLER",
        "ME.IS_$ROLE_$SLICE<-CALLER",
        "ME.INVOKING_ON_$MEMBER<-CALLER"
    ],
    "policies" : [
        "ME.MAY_$METHOD<-ME.IS_AUTHORITY",
        "ME.MAY_$METHOD<-ME.IS_OPERATOR",
        "ME.MAY_$METHOD_$SLICE<-ME.BELONGS_TO_$SLICE",
        "ME.MAY_$METHOD_$PROJECT<-ME.BELONGS_TO_$PROJECT",
        "ME.MAY_$METHOD<-ME.INVOKING_ON_$SELF"
        ]
  },

  "get_log_entries_by_author" : {
    "__DOC__" : "user_id must be self",
    "policies" : [
        "ME.MAY_$METHOD<-ME.IS_AUTHORITY",
        "ME.MAY_$METHOD<-ME.IS_OPERATOR",
        "ME.MAY_$METHOD_$MEMBER<-ME.IS_$SELF"
       ]
  },

  "get_log_entries_for_context" : {
    "__DOC__" : "Must be member of project of slice",
    "assertions": [
        "ME.IS_$ROLE_$PROJECT<-CALLER",
        "ME.IS_$ROLE_$SLICE<-CALLER",
        "ME.INVOKING_ON_$MEMBER<-CALLER"
    ],
    "policies" : [
        "ME.MAY_$METHOD<-ME.IS_AUTHORITY",
        "ME.MAY_$METHOD<-ME.IS_OPERATOR",
        "ME.MAY_$METHOD<-ME.INVOKING_ON_$SELF",
        "ME.MAY_$METHOD_$SLICE<-ME.BELONGS_TO_$SLICE",
        "ME.MAY_$METHOD_$PROJECT<-ME.BELONGS_TO_$PROJECT"
       ]
  },

  "get_log_entries_by_attributes" : {
    "__DOC__" : "For now, leave open. We do not think anyone uses this",
    "policies" : [
        "ME.MAY_$METHOD<-CALLER"
    ]
  },

  "get_log_entries_by_log_entry" : {
    "__DOC__" : "For now, leave open",
    "policies" : [
        "ME.MAY_$METHOD<-CALLER"
    ]
  }
}


Example

The following is a worked-through example of how ABAC authorization works in this scheme.

Imagine that the caller invokes the Slice Authority call get_credentaials (to receive a slice_credential).

The relevant policies and assertion templates are:

  "get_credentials" :  {
     "__DOC__" : "Slice lead/admin/member or operators can get slice cred",
      "assertions" : [
        "ME.IS_$ROLE_$SLICE<-CALLER"
        ],
     "policies" : [
        "ME.MAY_$METHOD<-ME.IS_OPERATOR",
        "ME.MAY_$METHOD_$SLICE<-ME.IS_LEAD_$SLICE",
        "ME.MAY_$METHOD_$SLICE<-ME.IS_ADMIN_$SLICE",
        "ME.MAY_$METHOD_$SLICE<-ME.IS_MEMBER_$SLICE"
     ]
    }

The call signature for get_credentials is:

    def get_credentials(self, slice_urn, credentials, options):

The 'slice_urn' is selected as the subject of the call (the object on which the method is working. Thus, $SLICE is bound to the slice_urn argument.

The ABAC Guard then tries to bind the variables in the associated templates, namely $ROLE. It computes this from the role that the caller plays in the given slice ($SLICE=slice_urn argument). If the member plays no role, the $ROLE binding is unbound and none of the assertion "ME.IS_$ROLE_$SLICE<-CALLER" is unbound and unasserted.

Depending on the role (LEAD, ADMIN, MEMBER, AUDITOR), the assertion will be asserted, e.g. ME.IS_LEAD_$SLICE<-CALLER (where $SLICE is replaced by a "flattened" version of the URN, see below).

The policies are then instantiated, again replacing $SLICE with the flattening of the slice_urn, and the $METHOD replaced with get_credentials.

In this context, the query ME.MAY_GET_CREDENTIALS<-CALLER will succeed only if the caller has operator privileges. And the query ME.MAY_GET_CREDENTIALS_$SLICE<-CALLER will only succeed if the caller has LEAD, ADMIN or MEMBER privileges on the slice.

Thus the ABAC queries in the context of the instantiated assertions and policies are proven if and only if the caller is authorized by policy to be allowed to get credentials on the given slice.

Appendix : Additional Details

Speaks For

GENI services support the Speaks for protocol. In so doing, it is often not the caller per se whose attributes are computed and whose authorization is determined, but that of the "spoken for" entity. Typically the caller is a tool in a speaks-for context, and the spoken-for entity is the user using the tool.

Flatten URN

The bindings of variables are often to URN's which have syntactic structure that is incompatible with ABAC. As a result, we ''flatten'' the URNs to simple underscore-separated strings by replacing all punctuation in the URN to underscores. E.g.

urn:publicid:IDN+ch-mb.gpolab.bbn.com+user+mbrinn

changes to

urn_publicid_IDN_ch_mb_gpolab_bbn_com_user_mbrinn

This statements (policies or assertions) templates with URN values can be converted into legal ABAC statements.