cloudsoft.io

Entitlements

Each REST api operation is authenticated to check if the user has the required privileges.

There is a plugin architecture to allow different entitlement mechanisms to be used. A new entitlements checker implementation can be supplied by implementing brooklyn.management.entitlement.EntitlementManager.

Role Entitlements

Role entitlements can be set globally with the property:

brooklyn.webconsole.security.users=staff1,staff2,itil1,itil2
brooklyn.entitlements.global=io.cloudsoft.amp.entitlements.rbac.PerRoleEntitlementManager

io.cloudsoft.amp.entitlements.rbac.perRole.ContentRole=staff1,staff2
io.cloudsoft.amp.entitlements.rbac.perRole.PlatformRole=itil1,itil2

RBAC Entitlements

Each REST api operation (to list/view items, or to perform changes) is authenticated to check if the user has the required privileges.

There is a plugin architecture to allow different entitlement mechanisms to be used. One mechanism available is io.cloudsoft.amp.entitlements.rbac.PerRoleEntitlementManager. This allows plugins for the various decision points.

Note: these package names may change if the code moves to org.apache.brooklyn. Configuration

This reads from brooklyn.properties, such as:

brooklyn.entitlements.global=io.cloudsoft.amp.entitlements.rbac.PerRoleEntitlementManager
io.cloudsoft.amp.entitlements.rbac.roleCacheExpiryDuration=15m
io.cloudsoft.amp.entitlements.rbac.userToRole=com.acme.amp.rbac.MyCustomRoleResolver
io.cloudsoft.amp.entitlements.rbac.perRole.adminstaff=root
io.cloudsoft.amp.entitlements.rbac.perRole.supportstaff=readonly
io.cloudsoft.amp.entitlements.rbac.perRole.automatons=minimal
io.cloudsoft.amp.entitlements.rbac.perRole.specialpeople=com.acme.amp.rbac.MyCustomEntitlements

The userToRole refers to a class of type io.cloudsoft.amp.entitlements.rbac.RoleResolver, which maps from a user to the role(s) for that user. If a user is in multiple roles, then the user has permission if any of the roles grant that permission.

The roleCacheExpiryDuration is the duration that the roles of a user will be cached for. Note that an extreme (!) way to flush the cache is to “reload properties”, which will replace this EntitlementManager with a new instance.

The perRole has an entry per role name. This value can be to a pre-defined build-in role (i.e. “root”, “readonly” and “minimal”). Alternatively, it can point to a custom brooklyn.management.entitlement.EntitlementManager class. RoleResolver

The io.cloudsoft.amp.entitlements.rbac.RoleResolver is used to map a user to the list of roles for that user.

An instance of the class will be instantiated reflectively. The constructor should have a signature that is one of:

(ManagementContext mgmt, AMPProperties properties)
    (ManagementContext mgmt)
    (AMPProperties properties)
    ()

If the class also implements {@link ManagementContextInjectable}, then the management context will be injected immediately after construction.

Custom EntitlementManager

The RBAC configuration allows one to plugin a custom entitlement manager to be associated with a given role, to meet your exact needs.

The EntitlementManager interface has a single method: isEntitled. This is passed details of the what is being done, and to what, allowing a boolean to be returned to indicate if it is permitted.

LDAP Entitlements

AMP supports LDAP integration for entitlements - i.e. the entitlements rules are stored in LDAP. To use this, you will require the Amp-of-Amps project compiled in (or the JARs in your drop-ins folder), and you must set in your brooklyn.properties:

# requires LDAP used for authorization
brooklyn.webconsole.ldap.url=ldap://LDAP_SERVER/
brooklyn.webconsole.ldap.realm=AMP
brooklyn.webconsole.ldap.password=PASSWORD
# and set the entitlements to be this implementation (or a subclass, if necessary)
brooklyn.entitlements.global=io.cloudsoft.amp.entitlements.LdapEntitlementManager

In the LDAP schema, this entitlements scheme requires a new objectClass, which in this guide we will call acmePermission, with the following attributes (all marked optional):

  • entityTagRegexesForNavigating: means you can navigate to entities with any tag matching any regex (multi-valued LDAP attribute)
  • entityTagRegexesForReading: means you can see sensors+config on entities with any tag matching any regex (multi-valued LDAP attribute)
  • entityTagRegexesForWriting: means you can invoke effectors on entities with any tag matching any regex (multi-valued LDAP attribute)
  • deployAllowed: means you are allowed to deploy new applications (boolean / present or absent)
  • serverInfoAllowed: means you are allowed to see AMP information (boolean / present or absent)
  • root: means you are root, having all the permissions above and all others (boolean / present or absent)

The DIT will contain:

  • an OU (groups) containing Posix Groups, where each group defines
    • zero or more user members
    • an acmePermission object, defining permissions for all members of the group
  • an OU (users) containing User Accounts, where each user defines
    • the password attribute
    • an acmePermission object, defining specific permissions for that user (in addition to all permissions from all groups)

This structure allows to have single User Account with (1) permissions defined specific to that user, and (2) permissions defined on groups of which he/she is a member. As is often recommended for entitlements, these are purely additive: a user will be entitled to access anything which is entitled by any acmePermission object on the user or any of his/her groups.

An example for (1) is:

  • user1 has entityTagRegexesForNavigating: acme.tenant.entity:${user} attribute

while an example of (2) is:

  • administrators group has 2 values for the entityTagRegexesForNavigating attribute: acme.tenant.entity.master and acme.tenant.entity:${user}
  • user2 is memeber of administrators group so user2 inherits the acme.tenant.entity.master to see the AMP blueprint and acme.tenant.entity:${user} to see his own child AMP.

Please see the LDAP command reference section for instructions for configuring LDAP with the acmePermission schema.

Use Cases

Using the LDAP structure described above with the new AMP feature to manage entitlements, it is possible to cover the following scenarios of interest:

Entitlements for master AMP and tenants/children AMP

In order to enforce entitlement on the master AMP, we need to add tags to the entities so that when we create Tenant-Foo AMP at master, we tag Tenant (AMPNode) and Service (AMPMirror) entities as acme.tenant.entity:${user} (where ${user} is replaced with the tenant name). Also we add tag acme.tenant.entity.master to the master blueprint so that all users can navigate through it (to access their tenant) and so that controllers can invoke effectors there. These tags are done by the AMP Master blueprint (no manual steps needed).

In LDAP, we define the following permissions:

  • tenant group:
    • entityTagRegexesForNavigating: acme.tenant.master, acme.tenant.entity:${user} (where ${user} is literal, the substitution done by the permissions engine)
    • entityTagRegexesForReading: acme.tenant.entity:${user}
    • entityTagRegexesForWriting: acme.tenant.entity:${user}
  • admin group:
    • entityTagRegexesFor...: .* (all permissions)
    • deployAllowed
    • serverInfoAllowed
    • root (with this permission the others are redundant)
  • controller group (WIP):
    • entityTagRegexesFor...: acme.tenant.master, acme.tenant.entity:.* (controllers given all rights to access master root node and all tenant entities at master, but not webapps)

Then in AMP, when a tenant logs in to the master, it will enforce:

  • authentication: we look up user/password in ldap
  • entitlements on the tenant AMP:
    • we fetch the acmePermission attributes attached to the user and for those groups of which he/she is a member
    • we evaluate those permissions to determine whether they are allowed to see the tenant using the tags associated to the tenant entity.
    • (we store these permissions in a cache which is invalidated after 15m or whatever refresh time we require)

A tenant can manage everything tagged with acme.tenant.entity:${USER}

Entitlements check on login at Tenant AMP instances, based on identity of which AMP server

This is deferred to the second iteration. Two options are: * Updating LDAP on Tenant AMP creation and installing this at the tenant AMP * A signed secret that the master AMP returns to the user to login to the tenant AMP that she claims to own

In the first iteration access to Tenant AMPs is driven by credentials stored in the Master AMP.

Features
  • Users, credentials, and permissions are stored in one well-known external system
  • Framework for defining entitlements which is already powerful, and easily extensible
Backlog

The entitlements for the entity/sensors/effectors on the tenant AMP will be addressed on the second iteration.

LDAP command reference

Currently, we added to the ldap schema a acmePermission objectClass with 6 attributes:

entityTagRegexesForNavigating
entityTagRegexesForReading
entityTagRegexesForWriting
deployAllowed
serverInfoAllowed
root

Starting from a new openldap 2.4 instance, it is possible to add the acmePermission objectClass by issuing the following commands:

ldapadd -Q -Y EXTERNAL -H ldapi:/// -f acme.ldif

Details

This ldif file has been created starting from the following acme.schema placed in (/etc/ldap/schema/acme.schema)

attributetype (1.3.6.1.4.1.42.2.27.4.1.30
	NAME 'root'
	DESC 'regex to match entity tag that allows browsing entities'
	EQUALITY booleanMatch
	SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )

attributetype (1.3.6.1.4.1.42.2.27.4.1.31
	NAME 'deployAllowed'
	DESC 'deployAllowed'
	EQUALITY booleanMatch
	SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )

attributetype (1.3.6.1.4.1.42.2.27.4.1.32
	NAME 'serverInfoAllowed'
	DESC 'serverInfoAllowed'
	EQUALITY booleanMatch
	SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )

attributetype ( 1.3.6.1.4.1.42.2.27.4.1.20
	NAME 'entityTagRegexesForNavigating'
	DESC 'regex to match entity tag that allows browsing entities'
	EQUALITY caseExactMatch
	SUBSTR caseIgnoreSubstringsMatch
	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )

attributetype ( 1.3.6.1.4.1.42.2.27.4.1.21
	NAME 'entityTagRegexesForReading'
	DESC 'regex to match entity tag that allows reading entities'
	EQUALITY caseExactMatch
	SUBSTR caseIgnoreSubstringsMatch
	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )

attributetype ( 1.3.6.1.4.1.42.2.27.4.1.22
	NAME 'entityTagRegexesForWriting'
	DESC 'regex to match entity tag that allows writing entities'
	EQUALITY caseExactMatch
	SUBSTR caseIgnoreSubstringsMatch
	SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )

objectclass ( 1.1.2.2.1 NAME 'acmePermission'
    DESC 'permissions for Acme'
    SUP top
	AUXILIARY
	MAY ( root $ deployAllowed $ serverInfoAllowed $ entityTagRegexesForNavigating $
          entityTagRegexesForReading $ entityTagRegexesForWriting ) )

by using slaptest utility.

cd /tmp/ldap
cat > schema_convert.conf <<EOT
include /etc/ldap/schema/core.schema
include /etc/ldap/schema/collective.schema
include /etc/ldap/schema/corba.schema
include /etc/ldap/schema/cosine.schema
include /etc/ldap/schema/duaconf.schema
include /etc/ldap/schema/dyngroup.schema
include /etc/ldap/schema/inetorgperson.schema
include /etc/ldap/schema/java.schema
include /etc/ldap/schema/misc.schema
include /etc/ldap/schema/nis.schema
include /etc/ldap/schema/openldap.schema
include /etc/ldap/schema/ppolicy.schema
include /etc/ldap/schema/ldapns.schema
include /etc/ldap/schema/pmi.schema
include /etc/ldap/schema/acme.schema
EOT
mkdir ldif_output
slapd -f schema_convert.conf -F . 
slapcat -f schema_convert.conf -F ldif_output -n 0 | grep acme,cn=schema
# get the output
slapcat -f schema_convert.conf -F ldif_output -n0 -H \
ldap:///<output> -l cn=acme.ldif

# Edit cn=acme.ldif to arrive at the following attributes:

dn: cn=acme,cn=schema,cn=config
...
cn: acme
Also remove the following lines from the bottom:

structuralObjectClass: olcSchemaConfig
entryUUID: 52109a02-66ab-1030-8be2-bbf166230478
creatorsName: cn=config
createTimestamp: 20110829165435Z
entryCSN: 20110829165435.935248Z#000000#000#000000
modifiersName: cn=config
modifyTimestamp: 20110829165435Z

and finally

sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f cn\=brooklyn.ldif