cloudsoft.io

LDAP Integration

AMP supports the longstanding LDAP standard for both authentication and authorization entitlements, with users defined in LDAP, and the option to define entitlement rules there also.

Authentication

To have users authenticated based on LDAP, use:

brooklyn.webconsole.security.provider=org.apache.brooklyn.rest.security.provider.LdapSecurityProvider
# other settings per brooklyn.cfg reference

The other settings are described here.

Entitlements

To use LDAP to determine what users are authorized to do, you can additionally set:

brooklyn.entitlements.global=io.cloudsoft.amp.entitlements.LdapEntitlementManager
brooklyn.webconsole.ldap.url=ldap://LDAP_SERVER/
brooklyn.webconsole.ldap.realm=AMP
brooklyn.webconsole.ldap.password=PASSWORD

This entitlements scheme requires a new “objectClass” in the LDAP schema, 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 having single User Account with (A) permissions defined specific to that user, and (B) 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 (A) is:

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

while an example of (B) 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