OpenStack Juno – Active Directory Integration

By | March 1, 2015

This post explains how to setup OpenStack Juno to authenticate against Active Directory. Many organizations use Microsoft Active Directory as their central identity and access management platform. We can configure OpenStack to use Active Directory to authenticate users, and to define projects and roles. This post shows how to setup the necessary objects in Active Directory, and how to configure keystone to use AD as the identity provider.

First we should setup the objects in AD. These will include the users, roles and projects that we would normally store in the keystone database. Here’s a view of the objects we are going to create:

openstack-ad-objects

The object types we are going to create include organizationalUnit (OU) and organizationalRole objects. If you are familiar with the AD GUI tools, you’ll know that Active Directory Users and Computers is the tool that we normally use to create objects in AD, and we can indeed create the OU objects with this tool. The tool falls short however when we try to create the organizationalRole objects. The tool doesn’t expose an interface for creating this type of object because they’re not often used in the Microsoft world. So for these objects, we need to create them using a different tool, ADSIEDIT.

In ADSIEDIT, you can create an object, and select from the list of objectClasses to define any type of object, then enter the mandatory values. You can then add any optional values. For the organizationalRole object, we’ll populate the roleOccupant attribute with the users that we want to map to the role. For organizationalUnit object that we use for projects, we will set the extensionName attribute to TRUE, which will tell OpenStack that the project is enabled. Again, the standard GUI tool false short here, and we can’t set the extensionName in the standard interface (unless you enable advanced view and use the attribute editor tab). I think a quick video would help here:

You can watch a quick YouTube video on how to create these objects here: http://youtu.be/hwJnzZjTYbg

Here is the complete list of objects in text form:

# this is the OpenStack OU (optional - for organizational purposes only)
dn: OU=OpenStack,DC=mydomain,DC=net
objectClass: organizationalUnit
ou: OpenStack

# this is the roles OU (contains global roles)
dn: OU=Roles,OU=OpenStack,DC=mydomain,DC=net
objectClass: organizationalUnit
ou: Roles

# this is the member global role (demo user is a member)
dn: CN=_member_,OU=Roles,OU=OpenStack,DC=mydomain,DC=net
objectClass: organizationalRole
cn: _member_
roleOccupant: CN=demo,CN=Users,DC=mydomain,DC=net

# this is the admin global role (admin and the services are members)
dn: CN=admin,OU=Roles,OU=OpenStack,DC=mydomain,DC=net
objectClass: organizationalRole
cn: admin
roleOccupant: CN=neutron,CN=Users,DC=mydomain,DC=net
roleOccupant: CN=nova,CN=Users,DC=mydomain,DC=net
roleOccupant: CN=glance,CN=Users,DC=mydomain,DC=net
roleOccupant: CN=cinder,CN=Users,DC=mydomain,DC=net
roleOccupant: CN=admin,CN=Users,DC=mydomain,DC=net

# this is the projects OU
dn: OU=Projects,OU=OpenStack,DC=mydomain,DC=net
objectClass: organizationalUnit
ou: Projects

# this is the demo project
dn: OU=demo,OU=Projects,OU=OpenStack,DC=mydomain,DC=net
objectClass: organizationalUnit
ou: demo
extensionName: TRUE

# this is the member role for the demo project
dn: CN=_member_,OU=demo,OU=Projects,OU=OpenStack,DC=mydomain,DC=net
objectClass: organizationalRole
cn: _member_
roleOccupant: CN=demouser,CN=Users,DC=mydomain,DC=net

# this is the admin project
dn: OU=admin,OU=Projects,OU=OpenStack,DC=mydomain,DC=net
objectClass: organizationalUnit
ou: admin
extensionName: TRUE

# this is the admin role for the admin project (admin user is a member)
dn: CN=admin,OU=admin,OU=Projects,OU=OpenStack,DC=mydomain,DC=net
objectClass: organizationalRole
cn: admin
roleOccupant: CN=admin,CN=Users,DC=mydomain,DC=net

# this is the service project
dn: OU=service,OU=Projects,OU=OpenStack,DC=mydomain,DC=net
objectClass: organizationalUnit
ou: service
extensionName: TRUE

# this is the admin role for the service project (services are the members)
dn: CN=admin,OU=service,OU=Projects,OU=OpenStack,DC=mydomain,DC=net
objectClass: organizationalRole
cn: admin
roleOccupant: CN=neutron,CN=Users,DC=mydomain,DC=net
roleOccupant: CN=nova,CN=Users,DC=mydomain,DC=net
roleOccupant: CN=cinder,CN=Users,DC=mydomain,DC=net
roleOccupant: CN=glance,CN=Users,DC=mydomain,DC=net

The assumption here is that your user accounts are in the Users container in the root of your Active Directory. This may not be the case. Obviously you need to take note of where your users are and adjust the paths accordingly.

Configuring Keystone

Once the AD objects are created, we can configure keystone to point to AD. Edit the /etc/keystone/keystone.conf and find the [identity] sections. Add the following line in that section:

[identity]

driver = keystone.identity.backends.ldap.Identity

Next find the [ldap] section and add the following lines:

[ldap]

query_scope = sub
url = ldap://192.168.1.253
user = cn=ldapuser,cn=Users,dc=mydomain,dc=net
password = ldapuser01
suffix = dc=mydomain,dc=net
use_dumb_member = True
dumb_member = cn=ldapuser,cn=Users,dc=mydomain,dc=net

user_tree_dn = cn=Users,dc=mydomain,dc=net
user_objectclass = organizationalPerson
user_id_attribute = cn
user_name_attribute = sAMAccountName
user_mail_attribute = mail
user_enabled_attribute = userAccountControl
user_enabled_mask = 2
user_enabled_default = 512
user_attribute_ignore = password,tenant_id,tenants
user_allow_create = False
user_allow_update = False
user_allow_delete = False

tenant_tree_dn = ou=Projects,ou=OpenStack,dc=mydomain,dc=net
tenant_objectclass = organizationalUnit
tenant_id_attribute = ou
tenant_member_attribute = member
tenant_name_attribute = ou
tenant_desc_attribute = description
tenant_enabled_attribute = extensionName
tenant_attribute_ignore = description,businessCategory,extensionName
tenant_allow_create = False
tenant_allow_update = False
tenant_allow_delete = False

role_tree_dn = ou=Roles,ou=OpenStack,dc=mydomain,dc=net
role_objectclass = organizationalRole
role_id_attribute = cn
role_name_attribute = cn
role_member_attribute = roleOccupant
role_allow_create = False
role_allow_update = False
role_allow_delete = False

Note that the AD paths match the locations of the objects we created above.

Configure Neutron

If you’re running neutron, you’ll want to modify the /etc/neutron/neutron.conf file on the node that is running the neutron-server service (typically the controller node). Find the nova_admin_tenant_id and update this to reflect the new ID of the server service. The original ID will be a UUID, but in Active Directory the ID will be the same as the name of the service project (typically service). So change the line to read:

nova_admin_tenant_id = service

Once that’s done, it’s a good idea to restart all of the OpenStack-related services, or simply reboot your whole stack.

Recreate Your Tenant-specific Networks

I had some trouble bringing up instances, they wouldn’t receive a DHCP lease. I found that I had to delete the admin network, subnet and router and recreate them. This is likely due to the fact that the admin tenant ID changed when I reconfigured to use Active Directory.

Caveats

Once you’re using AD, the identity panel in the OpenStack dashboard will be basically read-only. Although you can make a few things work if you try hard enough, it really defeats the purpose of using AD in the fist place. Let the AD administrator define all of the projects and roles.

As I mentioned above, the tenant ID’s will no longer be in UUID form. This isn’t a problem, just something to be aware of. Also, since the tenant ID’s have changed since we switched to AD, any non-shared resources that were tenant-specific may need to be recreated.

 

 

3 thoughts on “OpenStack Juno – Active Directory Integration

    1. Brian Seltzer Post author

      Agreed Lee. Any existing tenants would lose access to their existing instances, networks, volumes, etc when you made the switch. You could avoid this by using the existing tenant ID as the name of the project OU, but I agree, it’s best to switch authentication mechanisms before deploying any tenants.

      Reply
  1. Adam Young

    I wrote the LDAP code for Keystone, and I can say: don’t use the LDAP backend for Assignments. Use the SQL backend for assignemtns. LDAP should be treated as Read Only, and putting projects in there means that you cannot manage them from OpenStack.

    Even for LDAP, you should set the identity backend to SQL, and the use a domain specific backend for LDAP, as can be seen here:

    http://adam.younglogic.com/2014/08/getting-service-users-out-of-ldap/

    If you don’t want users to have to type in the domain, you can configure Horizon to show the domain drop down by editing the local_settings. Enable multidomain support with:

    OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True

    And then change the line below to reflect the domain where the LDAP users get stored
    #OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = ‘Default’

    for example, if you call this the “AD” domain:
    OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = ‘AD’

    Reply

Leave a Reply