Skip to content

Overview

Carolina Fernández edited this page May 15, 2013 · 2 revisions

Introduction

PyPElib is a small library that provides programmers with abstractions so they can build Policy Engine(s) within a certain scope of action. It is part of the OFELIA Control Framework software, which has been developed within joint activities between OFELIA and FIBRE EU-BR FP7 projects.

It supports:

  • Rule-based policy enforcement
  • Rule-based action triggering, logging
  • Multiple scope support within an application
  • Multi-syntax support
  • Flexible syntax customization ( Resolver and mappings )
  • Multiple persistence back-ends

The aim of the library has been to encapsulate the basic policy engine code and to be used in several different software components of the CF --therefore allowing to easily customize the behavior for each of those components-- but maintaining a common policy management base code.

Quick overview

The library is meant to provide Rule-based policy enforcement mechanisms; one could easily find similarities with IPtables. The main interface for the programmer to do so is using so-called RuleTable class.

####RuleTable

Each RuleTable instance is meant to encapsulate a set of policies for a certain scope (e.g. processing interface requests). The result of an evaluation will always be a True/False value. Each table has a set of rules and a default policy (DENY/ACCEPT) in case no rules match.

The following diagram exposes a simple workflow:

Simplified Policy Engine

evaluate()'s RuleTable method expects an object as input, which generally encapsulates the basic information which needs to be checked (typical examples, requests, RSPECs...), and verifies against the ruleset (in order). However, rules may interact with other classes/functions or modules of the application to obtain extra information (see Condition and mappings for more information).

Rules can be modified, reordered or deleted on the fly and --if required-- persisted using a back-end.

####Rule

Rules are internal objects used to represent a specific policy. In general they are not meant to be used by the users of the library but rather used through a parser/crafter to construct such objects.

There are fundamentally two types of rules:

  • Terminal rules: which upon a match of the condition, break the rule lookup loop and return Rule's ACCEPT/DENY value.
  • Non-terminal rules (a.k.a action rules): they do not break the rule lookup loop in case of match, and they are meant to perform actions without returning any value (a typical example is for logging and statistical purposes).

####Conditions, Resolver, Mappings and parsing modules

Conditions, as rules, are internal objects which in general are not meant to be used by the users of the library, but rather used through a parser/crafter to construct such objects. Each rule must have a condition which might be simple (e.g. A<5) or complex, containing sub-conditions.

However, is interesting to expose how using the mappings --and regardless of the parsing module-- syntax can be customized and adapted without touching the parsing modules. Consider the following rule definition, specifically the condition clause (RegexParser):

if  vm.memory > 256  then DENY denyMessage Memory limit is 256MB

Indeed one can use this simple notation to access vm.memory field, without having to explicitly modify the parser, add special parsing routines and so on thanks to the mappings and the Resolver class.

Consider the following mappings for the example above:

_mappings = {
"vm.name":"metaObj['name']",  #metaObj is the object passed to evaluate() routine of RuleTable 
"vm.memory":"metaObj['memory']",
#...
}

Mappings are used as follows to instantiate a RuleTable:

myTable = RuleTable("My Engine",_mappings,defaultParser=RegexParser, pType=False)

Note that you can easily point these keywords to any object contained in the metaObj, any static value or any global function that returns required value, without touching the parsing modules (parser treats those keywords as strings, bypassing them):

_mappings = {
"vm.name":"metaObj['name']",
"vm.memory":"metaObj['memory']",
"project.vms":"app.models.Project.getVms" #function must be defined as "def getVms(metaObj)"
"staticValue": "10",
#...
}

###Supported syntax parsers

Current supported parsers:

  • RegexParser: simple regular expression-based parser

####RegexParser

Syntax is as follows:

if <[not] cond> then <return_value> [nonterminal] [do <action>] [denyMessage <error_message>] [#<rule_comment>]
  • <return_value>: must be accept or deny
  • <cond>: must be a simple or complex condition
    • Simple conditions:
      • Operators:
        • Comparison: <, >,<=,>=,= or !=
        • Ranges: in range [<rangeBoundaries>] or in range {<rangeBoundaries>} (boundaries included or excluded)
        • Collection: in collection [<itemsCollection>]
    • Complex conditions
      • Operators: boolean&& or ||
      • Optional not particle, negates condition

Optional parameters:

  • nonterminal: Rule is nonterminal (a.k.a. action rules). An action must be specified
  • do <action>: action must be a keyword resolvable by Resolver
  • denyMessage <error_message>: text to be displayed when action is denied, where action must be a keyword to be resolved by Resolver
  • #<rule_comment>: comment to be displayed only for debugging while using pypelib

Examples (not in order):

if ( vm.RAM < 128 ) then accept
if not request.user in collection {user1,user2,user3} then deny do log # Deny requests from user1, 2 and 3
if  not vm.RAM in range [64,256] then deny  # Accept only VMs with RAM between 64 and 256Mb
if ( vm.RAM > 128 ) && (user.id = myUser) then deny <denyMessage> # Specific user filter

Supported backends

  • RAWFile: raw file serialization backend (mainly to use in testing environment)
  • Django: Django SQL based backend (Django framework is required to use this driver)
    • Note: when using this back-end make sure to modify the 'Meta' information for both Rule and RuleTable models:
      • app_label (your database name)
      • db_table (your table name)

Example

The example contained in the package emulates a simple entry point interface, in which certain policies need to be applied. The example emulates a couple of queries and plays with the policy table.

Main files:

  • main.py: emulates the interface client
  • interface.py: defines interface function/method (e.g. an XMLRPC method)
  • MyPolicyEngine.py: contains the RuleTable instance. An application may have multiple engines or RuleTable instances
  • database/: database contents

To run the example simply execute:

apt-get install python-pyparsing
cd pypelib/example
python main.py
Clone this wiki locally