Monday, August 22, 2016

Apache Fortress: The easiest way to get full Role-based Access Control in Openstack Keystone

Role-based access control is not a generic term!

Role-Based Access Control was introduced as a formal model by David Ferraiolo and Richard Kuhn almost 25 years ago.
A role based access control (RBAC) policy bases access control decisions on the functions a user is allowed to perform within an organization. The users can not pass access permissions on to other users at their discretion.
Ten years later the same authors proposed RBAC as a formal standard in this paper with functional specs in Z-notation.
In 2004 the RBAC formal model was adopted by ANSI as a specification - INCITS 359.
RBAC model four components:
  1. Core RBAC
  2. Hierarchical RBAC
  3. Static Separation of Duty Relations
  4. Dynamic Separation of Duty Relations
Core RBAC includes sets of five basic data elements called users (USERS), roles (ROLES), objects (OBS), operations (OPS), and permissions (PRMS).
The RBAC model as a whole is fundamentally defined in terms of individual users being assigned to roles and permissions being assigned to roles. As such, a role is a means for naming many-to-many relationships among individual users and permissions. In addition, the core RBAC model includes a set of sessions (SESSIONS) where each session is a mapping between a user and an activated subset of roles that are assigned to the user.
So, ANSI RBAC Object Model has six basic elements:
  1. User – human or machine entity
  2. Role – a job function within an organization
  3. Object – maps to system resources
  4. Operation – executable image of program
  5. Permission – approval to perform an Operation on one or more Objects
  6. Session – contains set of activated roles for User






Role hierarchies are commonly included as a key aspect of RBAC models and are often included as part of RBAC product offerings. Hierarchies are a natural means of structuring roles to reflect an organization’s lines of authority and responsibility.
Role hierarchies define an inheritance relation among roles. Role r1 ”inherits” role r2 if all privileges of r2 are also privileges of r1. For some distributed RBAC implementations, role permissions are not managed centrally, while the role hierarchies are. For these systems, role hierarchies are managed in terms of user containment relations: role r1 “contains” role r2 if all users authorized for r1 are also authorized for r2. Note, however, that user containment implies that a user of r1 has (at least) all the privileges of r2, while the permission inheritance for r1 and r2 does not imply anything about user assignment.
RBAC standard recognizes two types of role hierarchies—general role hierarchies and limited role hierarchies. General role hierarchies provide support for an arbitrary partial order to serve as the role hierarchy, to include the concept of multiple inheritances of permissions and user membership among roles. Limited role hierarchies impose restrictions resulting in a simpler tree structure (i.e., a role may have one or more immediate ascendants, but is restricted to a single immediate descendent).



Constrained RBAC adds Separation of Duty relations to the RBAC model. Separation of duty relations are used to enforce conflict of interest policies that organizations may employ to prevent users from exceeding a reasonable level of authority for their positions.
Static Separation of Duties means that if a user is assigned to one role , the user is prohibited from being a member of a second role. From a policy perspective, static constraint relations provides a powerful means of enforcing conflict of interest and other separation rules over sets of RBAC elements. Static constraints generally place restrictions on administrative operations that have the potential to undermine higher-level organizational Separation of Duty policies.
Although real world examples of this SSD policy exist, this definition is overly restrictive in two important aspects. The first aspect being the size of the set of roles in the SSD and the second being the combinati on of roles in the set for which user assignment is restricted. In the RBAC model SSD is defined with two arguments—a role set that includes two or more roles and cardinality greater than one indicating a combination of roles that would constitute a violation of the SSD policy. For example, an organization may require that no one user may be assigned to three of the four roles that represent the purchasing function.






Dynamic Separation of duty (DSD) relations, like SSD relations, are intended to limit the permissions that are available to a user. However, DSD relations differ from SSD relations by the context in which these limitations are imposed. SSD relations define and place constraints on a user’s total permission space. The RBAC model component defines DSD properties that limit the availability of the permissions over a user’s permission space by placing constraints on the roles that can be activated within or across a user’s sessions. DSD properties provide extended support for the principle of least privilege in that each user has different levels of permission at different times, depending on the role being performed. These properties ensure that permissions do not persist beyond the time that they are required for performance of duty.
DSD allows a user to be authorized for two or more roles that do not create a conflict of interest when acted in independently, but produce policy concerns when activated simultaneously. For example, a user may be authorized for both the roles of Cashier and Cashier Supervisor, where the supervisor is allowed to acknowledge corrections to a Cashier’s open cash drawer. If the individual acting in the role Cashier attempted to switch to the role Cashier Supervisor, RBAC would require the user to drop the Cashier role, and thereby force the closure of the cash drawer before assuming the role Cashier Supervisor. As long as the same user is not allowed to assume both of these roles at the same time, a conflict of interest situation will not arise.



Apache Fortress

Apache Fortress is a standards-based access management system, written in Java, that provides role-based access control, delegated administration and password policy services with LDAP.
It's released under terms of the Apache License 2.0.
Apache Fortress has the following components:
  • Core - Java Access Management SDK
  • Realm - Java EE security for Apache Tomcat
  • Rest - HTTP protocol wrappers for the APIs
  • Web - HTML pages for the APIs


 

Apache Fortress is ANSI INCITS 359 compliant. This is very important thing to understand: RBAC is not a generic term.  It refers to a specification, ANSI INCITS 359, with explicit instructions to implement.
There is more to RBAC than assigning users to groups and applying ACL policies within directories or databases. RBAC systems provide selective Role activation/deactivation, role hierarchies, and constraints over separation of duty. The RBAC component provides APIs to add, update, delete, and search the directory data.
Shawn McKinney says:
I hear this kind of statement all the time:  ‘We use Roles/Groups for access control in our systems and applications so we’re RBAC’.
My response is an emphatic:  ‘No – using roles for access control is not Role-Based Access Control!’
The first is an anti-pattern, the second a best practice.  Let me explain.
RBAC is not a generic term.  It refers to a specification, ANSI INCITS 359, with explicit instructions to implement.
To be considered RBAC compliant an access management system, at a minimum, supports the following entities: Users, Roles, Permissions (mapping between objects and operations) and, most important – Sessions.
Sessions are the context for which role activation occurs.  Just because a role has been assigned, does not mean it should be included into a particular access control decision.
This is in keeping with the principle of least privilege:  Only give the user the power needed to complete their job at any particular point in time – and no more.
Doing this satisfies a multitude of requirements and we’ll talk about some in future posts.
So unless your access control system supports the session contextual object – stop calling it RBAC.
But there’s much more to it than that.  RBAC specification defines the interfaces necessary for CRUD and interrogation operations. 

Apache Fortress integration with the OpenStack

Keystone Configuration
Configure OpenStack Keystone to use the ldap identity driver. Edit /etc/keystone/keystone.conf:

[identity]
driver = ldap
Also make a basic ldap configuration.
This configuration denies to create/update/delete users or groups. It is more common production case when the Keystone is allowed only to read from the LDAP.

[ldap]
url = ldap://<ip or hostname>
user = cn=Manager,dc=openldap,dc=org
password = secret
suffix = dc=openldap,dc=org
query_scope = sub

user_tree_dn = ou=People,dc=openldap,dc=org
user_objectclass = inetOrgPerson
user_id_attribute = uid
user_name_attribute = sn
user_mail_attribute = mail
user_pass_attribute = userPassword
user_allow_create = false
user_allow_update = false
user_allow_delete = false

group_tree_dn = ou=Groups,dc=openldap,dc=org
group_objectclass = groupOfNames
group_id_attribute = cn
group_name_attribute = cn
group_member_attribute = member
group_allow_create = false
group_allow_update = false
group_allow_delete = false
Restart Keystone.
OpenStack Keystone uses the oslo.policy to manage permissions.
Check your /etc/keystone/policy.json file:

{
    "admin_required": "role:admin or is_admin:1",
    "service_role": "role:service",
    "service_or_admin": "rule:admin_required or rule:service_role",
    "owner" : "user_id:%(user_id)s",
    "admin_or_owner": "rule:admin_required or rule:owner",
...
    "default": "rule:admin_required",
...
    "identity:list_regions": "",
...
    "identity:list_users": "rule:admin_required",
...
    "identity:list_groups": "rule:admin_required",
...
Such policy configuration can be illustrated by this diagram:

 policy.png

Apache Fortress Configuration
With the Fortress Web Interface create all entities.
Roles
roles.png
 

In Fortress Web create roles:
  • “keystone_member”
  • “keystone_user” with parent role “keystone_member”
  • “keystone_admin” with parent role “keystone_user”

f_roles.png

Objects
Create an object “Identity”:

 f_obj.png

Permissions
Permission is a pair of object an operation.
Create permissions:
  • Object “Identity” + operation “list_regions”
  • Object “Identity” + operation “list_groups”
  • Object “Identity” + operation “list_users”
Grant permissions for roles:
  • Users with role “keystone_member” can list regions
  • Users with role “keystone_user” can list groups
  • Users with role “keystone_admin” can list users.
Remember that our roles has hierarchy.
So users with role “keystone_user” can also list regions and users with role “keystone_admin” can list regions and list groups.



f_perms.png

Users
  • John Smith has role “keystone_user”
  • Alex Big has role “keystone_admin”
  • Mary Brown has role “keystone_member”

 f_usr.png

Apache Fortress stores all entities in OpenLDAP:

 f_ldap.png

oslo.policy
To use Apache Fortress instead of policy.json file use the patch: https://review.openstack.org/#/c/237521/ for oslo.policy library
Configure oslo.policy to use Apache Fortress. Edit /etc/keystone/keystone.conf file:

fortress_url = http://172.18.66.81:8080/fortress-rest-1.0-RC41-SNAPSHOT
fortress_user = demouser4
fortress_password = passwordu

use_fortress = True
A small change in Keystone is also required. Modify line 41 in keystone/policy/backend/rules.py file:

41:   _ENFORCER = common_policy.create_enforcer(CONF)
Restart Keystone.
Now oslo.policy uses Apache Fortress instead of oslo.policy.
Regarding previous configuration policy for users are:
  • Mary Brown can list regions but she can not list groups and users.
  • John Smith can list regions and groups but he can not list users.
  • Alex Big can list regions, groups and users.
policy_fortress.png


Test for John Smith.
Get Token:

$ curl -i -X POST -H "Content-Type: application/json" \
  -d '{ "auth": {"identity": {"methods": ["password"],"password": {"user": {"name": "Smith","domain": { "id": "default" },"password": "secret"}}}}}' \
  http://localhost:5000/v3/auth/tokens

HTTP/1.1 201 Created
X-Subject-Token: 03d4792b1eeb4e5f92dc2e24123a8bcd
List regions:

$ curl -s -H "X-Auth-Token: 03d4792b1eeb4e5f92dc2e24123a8bcd" \
  http://localhost:5000/v3/regions

{"regions": [{"parent_region_id": null, "id": "RegionOne", "links": {"self": "http://172.18.191.107/identity/v3/regions/RegionOne"}, "description": ""}], "links": {"self": "http://172.18.191.107/identity/v3/regions", "previous": null, "next": null}}
List groups:

$ curl -s -H "X-Auth-Token: 03d4792b1eeb4e5f92dc2e24123a8bcd" \
  http://localhost:5000/v3/groups

{"links": {"self": "http://172.18.191.107/identity/v3/groups", "previous": null, "next": null}, "groups": [{"domain_id": "default", "id": "ldapgroup", "links": {"self": "http://172.18.191.107/identity/v3/groups/ldapgroup"}, "name": "ldapgroup"}]}
List users:

$ curl -s -H "X-Auth-Token: 03d4792b1eeb4e5f92dc2e24123a8bcd" \
  http://localhost:5000/v3/users

{"error": {"message": "You are not authorized to perform the requested action: identity:list_users", "code": 403, "title": "Forbidden"}}
 

Apache Fortress Integration with the Federated OpenStack Keystone

OpenStack provides the ability to use one login for all of an organization's web services, providing a way to seamlessly sign on to each system rather than using different usernames and passwords for each system.
To make this possible, you can configure Openstack Keystone and Horizon for Web Single Sign-on (WebSSO) so the user can log in to the Horizon dashboard using credentials that are authenticated using a remote authentication service.
There is no need to do additional configuration for Apache Fortress integration in case of Federation. Integration for Federated Keystone looks absolutely the same as for usual Keystone.
It is so because of “Shadow User” feature in OpenStack Keystone.
To use local user id (from LDAP) in federated Keystone mapping for identity provider should have  “type”: “local” parameter.

[
    {
        "local": [
            {
                "user": {
                    "name": "{0}",
                    "type": "local",
                    "domain": {"id": "default"}          
                }
            },        
            {
                "group": {            
                    "id": "ldapgroup"
                }
            }      
        ],     
        "remote": [
            {
                "type": "uid"        
            }      
        ]    
    }  
]
In such configuration Keystone maps federated users to the local users during the authentication. Since we use ldap identity Keystone maps federated users to the Apache Fortress users.
Keystone doesn’t create a copy of the federated users. It just maps users and add an original id of the user to the token.
User ‘steve’ has Fortress role ‘keystone_user’:

steve.png

Federation is configured and ‘steve’ can login into the openstack dashboard:

hor_saml.png

Select ‘Security Assertion Markup Language’ method and enter Shibboleth credentials:

hor_shib.png

Regarding mapping Keystone creates token:

{"token":
    {
     "is_domain": false,
     "methods": ["token", "saml2"],
     "roles":
         [
          {
           "id": "56f9feb2caa248b68d016c10c7c791bf",
           "name": "Member"}
         ],
     "is_admin_project": true,
     "project":
         {
          "domain": {"id": "default", "name": "Default"},
          "id": "bab5237ddf69429684f1f639d2b641ba",
          "name": "admin"
         },
     ...
     "user":
         {
          "domain": {"id": "default", "name": "Default"},
          "id": "steve",
          "name": "steve"
         }
     ...
    }
}
Keystone put the user id into the token. In our case this id is an ‘uid’ from OpenLDAP.
oslo_policy gets user id and it is very easy to check permissions for user then. It is only one request:

request_xml = """
    <FortRequest>
        <contextId>HOME</contextId>
        <entity xsi:type="permission" 
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <objName>identity</objName>
            <opName>list_groups</opName>
            <admin>false</admin>
        </entity>
    </FortRequest>"""

requests.post(
'http://172.18.66.80:8080/fortress-rest-1.0-RC41-SNAPSHOT/permUsersAuthzed',
headers={"Accept": "text/xml", "Content-Type": "text/xml"},
data=request_xml,
auth=('admin_user', 'password'))

Conclusions

Role-based access control is not a generic term.  It refers to a specification, ANSI INCITS 359, with explicit instructions to implement.
OpenStack uses json files (policy.json) to set rules for RBAC. But this is not the Role-based access control. Luckily, Apache Fortress is an access management system based on the ANSI RBAC (INCITS 359) standard that can integrate effectively with an OpenStack cloud.
OpenStack integration with Apache Fortress requires a small change in the oslo.policy. This change doesn’t affect current functionality and OpenStack users still can decide what they want to use for the access control.
OpenStack integration with Apache Fortress can be done also for Federated Keystone. This is because Keystone has a ‘shadow users’ feature. Federated users can be mapped to a local users and origin user id from OpenLDAP is stored in the user token.
Apache Fortress has everything OpenStack needs for RBAC:
  • ANSI RBAC implementation. Everything is already created, described, and documented.
  • Apache Fortress stores entities (users, roles, permissions) in OpenLDAP or Active Directory, making it perfect for enterprises who already use LDAP or Active Directory for identity management.
  • Because of Fortress' use of standardized LDAP backends, it is easily integrated with many other enterprise applications.
  • Apache Fortress has RESTful APIs and a web interface.
  • Integration with OpenStack works for federation case.


 


1 comment:

  1. Great write up. Expect some more traffict, as we used this post in a policy discussion in Keystone today (Nov 16, 2016).

    I am not going to recommend that people try and do policy on user IDs. Itm akes delegation almost impossible without impersonation. It is on reason we try to limit policy checks to "role on project"

    Since this came out, Keystone itself has grown implied roles, like Fortress does, which i think was the biggest shortcoming.

    I would like to see the interface between policy and an External PDP be codified. If XML< probably should be XACAML. Something from the JWT world would work, too.

    Thanks again.

    ReplyDelete