Author Sergey KashabaDomains in OpenStack are a method of merging projects or tenants (as they were called in Grizzly) into independent groups. In addition, they allow you to restrict access to a specific domain. For example, as you probably remember, when domains were not applied, a user who was assigned the administrator role in one project was the administrator of the entire cluster and could perform any operations. With the advent of domains, you can assign the user the role of administrator in one domain with administrator privileges only in this domain.
This article will briefly review some situations in which domains can help you organize your projects. Once you understand this, we will proceed to consider how to actually use domains for this purpose.
Motivation
We first came across domains about a year ago when one of our corporate clients requested functionality that is very similar to OpenStack domains. This was part of the Grizzly cycle. We read about it and said: “Yo-ho-ho! We can use domain functionality! ” At that time, it was just introduced.
')
But OpenStack is ... OpenStack, and this functionality was not supported by all the required components. Therefore, we made a patch for Keystone to implement the requested usage scenarios and decided to come back to this later.
After six months, the release of Havana. Since the domain functionality is very useful for the corporate segment, we decided to get proof of the concept using the vanilla Havana release.
We wanted to cover the following usage scenarios:
1. As an IaaS administrator, I should be able to create domains to aggregate tenants (now called projects).
2. As an IaaS administrator, I should be able to add a user as a domain administrator.
3. As a domain administrator, I should be able to:
• Create, delete, move, update and delete projects within this domain. The project list should include only the project of this domain.
• Perform CRUD operations on the user in relation to project roles.
4. LDAP should be used as the source of the username / password.
5. Be sure to use only the REST API. Neither UI nor CLI is required.
This time we did it. Not to say that there were no problems, but the project showed that the domains in the release of Havana, at a minimum, can be applied.
Reade set Go!
We tested domains using the stable / havana branch in Devstack. The localrc file contains only options for providing passwords.
In the process of research, we found out that CLI is only partially ready for the use of domains. It provides full functionality for the average user, but not all for administration (a
description of this problem and the status of its solution can be found
here ).
To use domain functionality, do the following:
1. Change the marker format to UUID (keystone.conf file, [signing] section, token_format = UUID). Glance has
problems with PKI markers (at least with v3 markers), and during authentication, the error is 'body is too large' (“too big body”) when using the default PKI (the problem occurs not only when using domains) .
2. Apply the correct policy.json file for keystone. Domain verification is not supported in the default policy file. We reused the file that comes with the keystone from
git and then
made changes to it .
3. Modify the Nova, Glance-api and paste.registry configuration files in Glance to enable the use of V3.0 authentication by adding 'auth_version = v3.0' to the [filter: authtoken] section. In the test environment, we used only Nova and Glance to start the VM. If you use other services, do not forget to make changes to the corresponding configuration files. If this is not done, the Nova and Glance clients will check the V2 tokens and give the error “Only default domain is allowed over the V2.0” (“Only the default domain can be used in V2.0).
4. Update the keystone endpoints so that the identity service points to v3, not v2.0.
5. Restart the services, the settings of which have been changed.
Note that at the request of the client, we used only the REST interface and did not use CLI / PythonAPI / UI clients.
How to manage domains
Having spent about a week studying domains, I can say that there are two key points for understanding how they work:
1. How the marker is created and, in particular, the token scope.
2. How policies are launched.
In this article, all the examples will be given using curl commands. This is done for two reasons. First, when writing this document, the CLI did not support setting domain-related parameters via the command line. Secondly, and in practice, more importantly, the CLI does not reveal some of the details that are essential for understanding how it works.
Creating a marker
First you need to create a marker, which is quite simple. This is done using the following query:
curl -si -X POST -H “Content-Type: application / json” -d '{“auth”: {“scope”: {“domain”: {“id”: “XXXXX”}}, “identity”: {“Password”: {“user”: {“domain”: {“name”: “default”}, “password”: “qwerty”, “id”: “YYYYYY”}}, “methods”: [“password »]}}}”
127.0.0.1 : 5000 / v3 / auth / tokens | awk '/ X-Subject-Token / {print $ 2}' | tr -d '\ r'
Here you can see the important part of the json file, which is transmitted as a body, the scope. A domain can be either a domain or a project. The marker area is involved in the launch of the policy, so it is important to understand and remember. If interested, you can look at function keystone / auth / controllers.py: AuthInfo._validate_and_normalize_scope_data to find out the code used to create the object, which was then used to generate policy data (policy credentials).
Policy launch
Launching a policy is more difficult. This is the main use case. Fortunately, once you have a clear understanding of how this happens, you will be able to perform most of the domain operations. When launching a policy, three new keywords are used: credential, policy, and target. Now let's see what is the creation and use of each of them.
Credentials (Credentials)
A credential object is a dictionary created on the basis of a marker and includes the area of ​​the resulting marker, as well as the roles assigned to the user.
Policy
A policy is simply a set of rules, united according to the logic of OR / AND. In future releases, it should become more readable, but for now we will use the format currently used. Below are some fragments of the policy as an example of the above.
“Admin_required”: [[“role: admin”]],
“Owner”: [["user_id:% (user_id) s"], ["user_id:% (target.entity.user_id) s"]],
“Identity: get_project”: [[rule: admin_required ”,
"Domain_id:% (target.project.domain_id) s"]],
“Identity: list_projects”: [["rule: admin_required", "domain_id:% (domain_id) s"]],
“Identity: list_user_projects”: [[“rule: owner”], [“rule: admin_required”,
"Domain_id:% (domain_id) s"]],
Everything must be enclosed in square brackets ([]).
The minimum fragment of the rule (you can call it the “atomic” rule) is a string with a colon as the delimiter (':'). It may be:
1. Link to another rule. In this case, the left side is the “rule” and the right side is the name of the rule.
2. The definition of validation logic. In this case, the left side defines the key that is in the credential object, and the right side is something that is in the 'target'. It can also be a constant. This can be expressed as credentials_object [left_side] == right_side% target_object.
Multiple atomic rules can be combined using a comma (,). This means that these rules are combined based on AND logic (for example: [“rule: admin_required”, “domain_id:% (domain_id) s”]). Call it the "rule and".
If there are two sets of rules enclosed in [], then they are combined based on the OR logic. For example, [[“rule: owner”], [“rule: admin_required”]] means that the rule is executed if the owner rule or the rule required by the administrator is executed.
If the rule uses another rule, then the rule enclosed in brackets should be described in the same way.
To summarize: an example of “” identity: list_user_projects ”: [[“ rule: owner ”], [“ rule: admin_required ”,“ domain_id:% (domain_id) s ”]] means“ the list of user projects is allowed only if the rule with the name 'owner' OR is executed and the rule required by the administrator, AND the domain_id from the marker area is equal to the domain id from the target '. Now we will talk about the goal.
Target (Target)
The target object is also a dictionary created based on the following:
• query string (query string) from the query (request);
• launched objects.
For example:
• If we are trying to get information about a project, then the goal is this project.
• If we are trying to assign a user to a project with a specific role, then the goal is the role + project + user.
• If we are trying to list all the projects, the goal value will be empty (At first it made me sad because I thought that some of the operations requested by our clients were not available in the Havana release. But we were lucky, I was wrong.).
• If we try to list all projects using a filter for a specific value (for example, by domains_id), then the goal includes all filters (This was a surprise for me, but it turned out to be very useful).
Request example:
curl -sX GET -H “X-Auth-Token: $ OS_MYTOKEN”
127.0.0.1 : 5000 / v3 / projects? domain_id = $ OS_DOMAIN_ID
In this request, only domain_id is included in the target. The rule that should be included in the policy.json file can be “identity: list_projects”: [[“rule: admin_required”, “domain_id:% (domain_id) s”]].
Knowing all this, you can easily manage access by making changes to the policy.json file.
In addition, in keystone / policy / backends / rules.py: enforce, you can write debugging messages for logging credentials, rules, and goals for more thorough analysis. This method helped me understand what is happening in some use cases.
Cloud Administrator (Cloud admin)
In the existing policy.json file, I did not understand what the rule 'cloud_admin' meant:
“Cloud_admin”: “rule: admin_required and domain_id: admin_domain_id”,
“Identity: get_domain”: “rule: cloud_admin”,
Having done some research, I found out that this is a very sophisticated workaround for determining the 'super administrator' without using a service token. You simply create a domain that is 'super domain', and specify the corresponding ID as the value of admin_domain_id in the rule. For example, in devstack you can use 'default', and specify the default domain as the 'super' administrator.
According to the
discussion of keystone key managers, the concept of a cloud administrator is a temporary solution and will be replaced by
a service administrator concept .
Horizon support
Horizon can also support multiple domains according to blueprints. To do this, you need to make only minor changes either in settings.py or local_setting (which we tested in our laboratory):
OPENSTACK_API_VERSIONS = {
“Identity”: 3
}
OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True
#OPENSTACK_KEYSTONE_URL = “http: //% s: 5000 / v2.0 ″% OPENSTACK_HOST
OPENSTACK_KEYSTONE_URL = “http: //% s: 5000 / v3 ″% OPENSTACK_HOST
Having made these changes, you will receive an additional input field at the login stage to indicate the domain name. Unfortunately, admin actions (such as domain management and assignments) are not available on Horizon when applied to a keystone V3 policy (
link ). However, thanks to this, a regular user can at least use the UI instead of sending CURL commands.
Command Line Interface (CLI)
In mid-January 2014, domain support was added to the CLI, which now supports the following additional set of options:
–Os-domain-name <auth-domain-name>
Domain name of the requested domain level authorization scope (Environment: OS_DOMAIN_NAME).
–Os-domain-id <auth-domain-id>
Domain ID of the requested domain level authorization scope (Environment: OS_DOMAIN_ID).
–Os-user-domain-name <auth-user-domain-name>
Domain name of the user (Environment: OS_USER_DOMAIN_NAME).
–Os-user-domain-id <auth-user-domain-id>
User domain ID (Environment: OS_USER_DOMAIN_ID).
–Os-project-domain-name <auth-project-domain-name>
The domain name of the project that is the requested domain level authorization scope (Environment: OS_PROJECT_DOMAIN_NAME).
–Os-project-domain-id <auth-project-domain-id>
Project domain ID, which is the requested domain-level authorization scope (Environment: OS_PROJECT_DOMAIN_ID).
With the help of these parameters, we can set the marker area, project domain and user domain.
We summarize everything said together
By applying all the above changes, you can perform all the required functions. Below is a list of curl commands. I remind you that I needed to ensure the launch of ONLY REST requests in order for the functionality to work during this testing.
Prerequisites:
For convenient analysis of json responses, I used the jq tool.
sudo apt-get install jq
We also created users (from LDAP): user0, demo
Example:
The test is as follows:
#export start variables.
export OS_AUTH_URL = http: //127.0.0.1: 5000 / v3
export OS_SERVICE_TOKEN = openstack
#list domains
curl -sX GET -H “X-Auth-Token: $ OS_SERVICE_TOKEN”
127.0.0.1 : 35357 / v3 / domains | jq '.domains'
#or
openstack –os-identity-api-version 3 –os-url
127.0.0.1 : 35357 / v3 –os-token openstack domain list
#create domain
curl -sX POST -H “X-Auth-Token: $ OS_SERVICE_TOKEN” -H “Content-Type: application / json” -d '{“domain”: {“enabled”: true, “name”: “dom0 ″} } '
127.0.0.1 : 35357 / v3 / domains | jq '.'
#list show domain0
curl -s -X GET -H “X-Auth-Token: $ OS_SERVICE_TOKEN”
127.0.0.1 : 35357 / v3 / domains? name = dom0 | jq '.domains'
#assign user as an domain admin
#get user id
curl -sX GET -H “X-Auth-Token: $ OS_SERVICE_TOKEN”
127.0.0.1 : 35357 / v3 / users? name = user0 | jq '.users'
export OS_USER_ID = curl -sX GET -H "X-Auth-Token: $ OS_SERVICE_TOKEN"
127.0.0.1 : 35357 / v3 / users? name = user0 | jq '.users []. id' | tr -d '"'
curl -sX GET -H “X-Auth-Token: $ OS_SERVICE_TOKEN”
127.0.0.1 : 35357 / v3 / domains? name = dom0 | jq '.domains'
export OS_DOMAIN_ID = curl -sX GET -H "X-Auth-Token: $ OS_SERVICE_TOKEN"
127.0.0.1 : 35357 / v3 / domains? name = dom0 | jq '.domains []. id' | tr -d '"'
curl -sX GET -H “X-Auth-Token: $ OS_SERVICE_TOKEN”
127.0.0.1 : 35357 / v3 / roles? name = admin | jq '.roles'
curl -sX PUT -H “X-Auth-Token: $ OS_SERVICE_TOKEN”
127.0.0.1 : 35357 / v3 / domains / $ OS_DOMAIN_ID / users / $ OS_USER_ID / roles / bc485df1732140928ad44804a1c9b546
curl -sX GET -H “X-Auth-Token: $ OS_SERVICE_TOKEN”
127.0.0.1 : 35357 / v3 / domains / $ OS_DOMAIN_ID / users / $ OS_USER_ID / roles / | jq '.roles'
#Authenticate as an domain admin
export OS_MYTOKEN = curl -si -X POST -H "Content-Type: application / json" -d "{\" auth \ ": {\" scope \ ": {\" domain \ ": {\" id \ " : \ "$ OS_DOMAIN_ID \"}}, \ "identity \": {\ "password \": {\ "user \": {\ "domain \": {\ "name \": \ "default \"} , \ "Password \": \ "qwerty \", \ "id \": \ "$ OS_USER_ID \"}}, \ "methods \": [\ "password \"]}}} "
127.0.0.1 : 5000 / v3 / auth / tokens | awk '/ X-Subject-Token / {print $ 2}' | tr -d '\ r'
#create project
curl -sX POST -H “X-Auth-Token: $ OS_MYTOKEN” -H “Content-Type: application / json” -d “{\” project \ ”: {\” name \ ”: \” dom0p0 \ ”, \ ”Enabled \”: true, \ ”domain_id \”: \ ”$ OS_DOMAIN_ID \”, \ ”description \”: \ ”\”}} ”
127.0.0.1 : 5000 / v3 / projects | jq '.'
curl -sX GET -H “X-Auth-Token: $ OS_MYTOKEN”
127.0.0.1 : 5000 / v3 / projects? domain_id = $ OS_DOMAIN_ID \ & name = dom0p0 | jq '.projects'
export OS_PROJECT_ID = curl -sX GET -H "X-Auth-Token: $ OS_MYTOKEN"
127.0.0.1 : 5000 / v3 / projects? domain_id = $ OS_DOMAIN_ID \ & name = dom0p0 | jq '.projects []. id' | tr -d '"'
#add user to project
curl -sX GET -H “X-Auth-Token: $ OS_MYTOKEN”
127.0.0.1 : 35357 / v3 / users? name = demo | jq '.users'
export OS_DEMOUSER_ID = curl -sX GET -H "X-Auth-Token: $ OS_MYTOKEN"
127.0.0.1 : 35357 / v3 / users? name = demo | jq '.users []. id' | tr -d '"'
curl -sX GET -H “X-Auth-Token: $ OS_MYTOKEN”
127.0.0.1 : 35357 / v3 / roles? name = Member | jq '.roles'
curl -X PUT -H “X-Auth-Token: $ OS_MYTOKEN”
127.0.0.1 : 5000 / v3 / projects / $ OS_PROJECT_ID / users / $ OS_DEMOUSER_ID / roles / c861b57d824d4619a739823d863064b6
curl -X GET -H “X-Auth-Token: $ OS_MYTOKEN”
127.0.0.1 : 5000 / v3 / projects / $ OS_PROJECT_ID / users / $ OS_DEMOUSER_ID / roles / | jq '.roles'
#authenticate as a user
export OS_DEMOTOKEN_ID = curl -si -X POST -H "Content-Type: application / json" -d "{\" auth \ ": {\" scope \ ": {\" project \ ": {\" id \ " : \ "$ OS_PROJECT_ID \", \ "domain \": {\ "id \": \ "$ OS_DOMAIN_ID \"}}}, \ "identity \": {\ "password \": {\ "user \" : {\ "Domain \": {\ "name \": \ "default \"}, \ "password \": \ "openstack \", \ "id \": \ "$ OS_DEMOUSER_ID \"}}, \ "Methods \": [\ "password \"]}}} "
127.0.0.1 : 5000 / v3 / auth / tokens | awk '/ X-Subject-Token / {print $ 2}' | tr -d '\ r'
#run the VM
curl -si -X POST -H “X-Auth-Token: $ OS_DEMOTOKEN_ID” -H “Content-Type: application / json” -d '{“server”: {“flavorRef”: “1 ″,“ name ”: “Ksn”, “imageRef”: “c0843d76-0cc8-4a19-a9e3-169afd3aace2 ″}} '
127.0.0.1 : 8774 / v2 / $ OS_PROJECT_ID / servers
Open questions
In general, this can be called a success, but we still have a couple of unresolved issues:
1. It is not clear whether Horizon can manage administrator operations when applying a V3 policy to
keystone .
2. It is impossible to delete a domain if you assigned a domain project to a user from another domain.
The problem .
findings
Summarizing, we can say that to use the domain functionality in Horizon, you must first make sure that you are using Keystone API version V3 and that you have changed the authentication scheme to UUID. Then you need to create tokens, then create and run the appropriate policies. By doing this, you can use the commands in the list to create and use domains, or you can use Horizon or new CLI settings.
Original article
in English .