Keystone SAML Federation on IBM Bluemix Private Cloud
- What is SAML Federation?
- Integrating the IDP and Keystone
- Managing Groups
- Managing Mappings
- Using Horizon
- Using the CLI
- Additional Notes
What is SAML Federation
Security Assertion Markup Language (SAML) is a protocol that allows a user to use Single Sign On (SSO) with an Identity Provider (IDP) and a Service Provider (SP). When a user logs in, they give their credentials to the IDP. The IDP checks their credentials and a SAML Assertion with the user’s attributes is created. That assertion is then passed to the SP and the SP verifies that it was sent by the IDP. Since the SP trusts assertions from the IDP, the user never has to send their credentials to the SP.
Keystone will act as the SP and will authenticate users by receiving
assertions from an IDP. In this documentation, we will use a fictitious
SAML IDP called Company XYZ IDP
.
Please note that any administrative commands on this page requires
the user to have cloud_admin
access.
Integrating the IDP and Keystone
A Bluemix Private Cloud operator will need to work with a Company XYZ IDP
integration
engineer to setup both systems to work together.
A Bluemix Private Cloud operator will need the following:
- IDP Metadata URL Endpoint (We currently only support retrieving Metadata through an URL and not a file)
- IDP entity ID from the IDP Metadata
- User attributes - This is the SAML user attributes that get passed by the IDP. This can be found in the IDP documentation or IDP server side logs.
- Default/Inital Mapping - The documentation on mappings can be found here: http://docs.openstack.org/developer/keystone/federation/federated_identity.html#mapping-combination
The Company XYZ IDP
integration engineer may need the following from the Bluemix Private Cloud Operator:
- Service Provider Metadata URL
- Service Provider redirect URL (Also known as the callback url, this is a whitelisted value that the IDP will allow the redirect to go to)
The Bluemix Private Cloud operator should work with the integration engineer to create the initial/default mapping. The mapping is how the remote users on the IDP are mapped to local groups on the SP. The rest of this document may help clarify what a mapping is and how it’s used. The integration engineer will also need cloud_admin access. This may be the cloud_admin
user or a user with the role of the same name.
The user attributes should be given with the mapping name. For example the attributes may be given as:
attributes:
- name: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
id: email
- name: http://schemas.xmlsoap.org/claims/Group
id: group
The name given is what the name is inside the SAML assertion. The id is what will be used in the Keystone mappings.
After the Bluemix Private Cloud operator is finished integrating Company XYZ IDP
with
Keystone, a mapping called mapping-for-company-xyz-idp
should be
created (or a similar mapping name with the IDP name in it).
The steps for managing the mapping is:
1. Manage groups (Federated users will the same access rights as the group 2. Manage the mappings
Managing Groups
Keystone Federation works by federating Identity Provider users to a group on the Service Provider. The group is granted access by assigning that group a role on a project. The available roles are as follows:
project_admin
- Theadmin
role for the project.cloud_admin
- Theadmin
role for the cloud instance._member_
- A member of a project.heat_stack_owner
- An owner for heat stacks.
To list groups:
$ openstack group list
To create a group:
$ openstack group create --help
$ openstack group create <new group name here>
To list roles:
$ openstack role list
To list role assignments:
$ openstack role assignment list --name
To list projects:
$ openstack project list
For help looking at adding role assignment:
$ openstack role add --help
To add a role assignment for a group to have a role on a project:
$ openstack role add <role> --project <project> --group <group>
Federated users are mapped into groups on the Service Provider.
Here’s the order of operations for granting access for groups:
1. First, create or edit the group. 2. Then, create or edit the role assignments for groups on projects.
Example 1. Creating a company_xyz_admin_group
group.
$ source cloud_adminrc # Your openstack credentials file
$ openstack group list
$ openstack group create company_xyz_admin_group --or-show
$ openstack role assignment list --name --group company_xyz_admin_group
Next, create the role assignment if it doesn’t exist.
These commands add the role cloud_admin
for the group cloud_admin
for the “demo” project.
$ openstack role add cloud_admin --group company_xyz_admin_group --project demo
$ openstack role assignment list --name --group company_xyz_admin_group
After executing the openstack role assignment list
command you should see an output similar to this one:
+-------------+------+---------------------------------+--------------+--------+-----------+
| Role | User | Group | Project | Domain | Inherited |
+-------------+------+---------------------------------+--------------+--------+-----------+
| cloud_admin | | company_xyz_admin_group@Default | demo@Default | | False |
+-------------+------+---------------------------------+--------------+--------+-----------+
Example 2. Creating a group with member access.
$ source cloud_adminrc #Your openstack credentials file
$ openstack group list
$ openstack group create company_xyz_member_group --or-show
$ openstack role assignment list --name --group company_xyz_member_group
$ openstack role add _member_ --group company_xyz_member_group --project demo
$ openstack role assignment list --name --group company_xyz_member_group
After entering the openstack role assignment list
command you should see an output similar to this one:
+----------+------+----------------------------------+--------------+--------+-----------+
| Role | User | Group | Project | Domain | Inherited |
+----------+------+----------------------------------+--------------+--------+-----------+
| _member_ | | company_xyz_member_group@Default | demo@Default | | False |
+----------+------+----------------------------------+--------------+--------+-----------+
Managing Mappings
Below we are using the Company XYZ IDP
and a mapping for it already exists.
A user with cloud_admin
access (user or role) will be able to edit
the mapping. The user will not be able to create or delete mappings.
Mappings can be managed on Horizon in the Identity Dashboard -> Federation -> Mappings -> Edit
The openstack mapping list
command shows a list of mappings. It should show the mapping-for-company-xyz-idp
mapping.
The openstack mapping show mapping-for-company-xyz-idp -f json
command shows the details of the given mapping. It should contain a json
listing of the rules for the mapping.
The openstack mapping set mapping-for-company-xyz-idp --rules newmappings.json
command updates and sets the mapping.
The mappings file should follow the format given here:
[
{
"local": [
<group>
],
"remote": [
<condition>
]
}
]
The rules file should contain a list of rules that map the remote users to a local group. The local section contains a rule specifying the group that the remote users should map to. The local group is required.
{
"group": {
"domain": {
"name": "Default"
},
"name": "company_xyz_admin_group"
}
}
The remote section includes rules that specify which remote users should be allowed access.
{
"type": "group",
"any_one_of": [
"admin_group"]
}
The possible user attributes types that can be given in the remote rules are these:
The possible actions to filter on remote attributes are:
empty
any_ony_of
not_any_of
blacklist
whitelist
To set the username we can use {0} in the local section to indicate to use a field from the remote section. Then we can additional information to the new federated username. We can reference the remote section in the local section by index (“{i}”, where i is the index of the remote field):
[
{
"local": [
{
"user": {
"name": "CompanyXYZ/{0}"
}
},
...
],
"remote": [
{
"type": "email"
},
...
]
}
]
Here are some example scenarios:
Example 1.
Here we are mapping users from the CompanyXYZ IDP and allowing any user
from the admin_group
. The user’s email address will be used to create
a user on Keystone. If the user’s email is test@test.ibmcloud.com
, then
the resulting username would be CompanyXYZ/test@test.ibmcloud.com
.
[
{
"local": [
{
"user": {
"name": "CompanyXYZ/{0}"
}
},
{
"group": {
"domain": {
"name": "Default"
},
"name": "company_xyz_admin_group"
}
}
],
"remote": [
{
"type": "email"
},
{
"type": "group",
"any_one_of": [
"admin_group"
]
}
]
}
]
Example 2. Multiple Groups
In the following example we are using
[
{
"local": [
{
"user": {
"name": "CompanyXYZ/{0}"
}
},
{
"group": {
"domain": {
"name": "Default"
},
"name": "company_xyz_admin_group"
}
}
],
"remote": [
{
"type": "email"
},
{
"type": "group",
"any_one_of": [
"admin_group"
]
}
]
},
{
"local": [
{
"user": {
"name": "CompanyXYZ/{0}"
}
},
{
"group": {
"domain": {
"name": "Default"
},
"name": "company_xyz_member_group"
}
}
],
"remote": [
{
"type": "email"
},
{
"type": "group",
"any_one_of": [
"member_group"
]
}
]
}
]
Using Horizon
The OpenStack dashboard on the Identity Provider should have a drop-down menu labeled Authenticate with SSO.
- The dropdown should show a label for
Company XYZ IDP
. Select the IDP and click Connect. - You will then be redirected to the IDP log in page.
- After successfully authenticating with the IDP, you should then redirected to the horizon page.
Using the CLI
Note: The Command Line Interface (CLI) client can only work with IDPs that satisfy these conditions: 1. The IDP supports the SAML ECP protocol 2. The IDP supports basic authentication on a GET
The CLI offers two types of plugins: 1. v3unscopedsaml (Grab an unscoped token) 2. v3scopedsaml (Use a token to perform scoped actions)
1. Unscoped Example:
Let’s create a file that we can source in our environment to use SAML:
# This file is called companyxyz_saml_rc
export OS_IDENTITY_API_VERSION=3
export OS_PASSWORD=CompanyXYZPassword #replace this
export OS_AUTH_URL=https://<BB CLOUD FQDN>:5000/v3 #replace this
export OS_USERNAME=CompanyXYZUser #replace this
export OS_IDENTITY_PROVIDER=company-xyz-idp #replace this
export OS_PROTOCOL=saml2
export OS_AUTH_TYPE=v3unscopedsaml
export OS_IDENTITY_PROVIDER_URL=https://CompanyXYZ.example/idp/profile/SAML2/SOAP/ECP #replace this
After creating the file and editing the options we can run the following command to get an unscoped token and record the ID:
$ source companyxyz_saml_rc
$ openstack token issue
2. Scoped Example:
Now that we a have an unscoped token, we can scope to a project and list the servers on that project
openstack server list --os-auth-type v3scopedsaml --os-token <os-token-id> --os-project-id <project-id>
Using the Python API Libraries
Note: The Python API Libraries can only work with IDPs that satisfy these conditions: 1. The IDP supports the SAML ECP protocol 2. The IDP supports basic authentication on a GET
The following code uses the SAML auth plugin to authenticate with the IDP, send the SAML assertion to Keystone and retrieve an unscoped token. The unscoped token is then used to get a project scoped token. Then the script will list servers on that project.
This file is saml_list_servers.py:
We will need to source a file before running the script:
# This file is called companyxyz_saml_python_rc
export OS_AUTH_URL=https://<BB CLOUD FQDN>:5000/v3 #replace this
export OS_USERNAME=CompanyXYZUser #replace this
export OS_PASSWORD=CompanyXYZPassword #replace this
export OS_PROJECT_NAME=demo
export OS_IDENTITY_PROVIDER=company-xyz-idp #replace this
export OS_IDENTITY_PROVIDER_URL=https://CompanyXYZ.example/idp/profile/SAML2/SOAP/ECP #replace this
export OS_PROTOCOL=saml2
Then we can run the python script:
source companyxyz_saml_python_rc
python saml_list_servers.py
Additional Notes
Currently an issue exists with federated users and with the trustor/trustee feature in Keystone, which prevents
the federated Heat user from delegating their heat_stack_owner
role.
You’ll need to use a workaround for the trust delegation by assigning the heat_stack_owner
role to the user directly. This is the user that is
created during federated log in. The user’s domain is federated, and it could have the value of None (Federation=None
).
$ openstack user list
The federated user should have a domain of None: openstack user show <federated user>
Here we require the user ID:
$ openstack role add heat_stack_owner --user <federated user ID> --project <project>
Another known issue is that a federated user is presented with the option to change their password in the settings page. They will not be able to change their password.
Also, please note that federated users should not create local users. Federated admins may not be able to change a local user’s password.