OpenStack can be seen as a set of projects which combined into a configuration can deliver a management solution for different use-cases. However, several of these projects can also exist on their own to provide a certain functionality, such as Keystone, Ironic, etc. Some of these I will describe in articles on this blog, but let's start with one of the most important one's that deal with Authenticatioin, namely Keystone.
Keystone
Keystone is one of the core projects of OpenStack and is responsible for providing Identity. It offersauthentication, authorization and service discovery mechanisms via HTTP. Especially this last property, offering the API to be accessed by HTTP as a ReSTful interface offers a lot of opportunities for re-use. More about the Keystone project can be found on the project homepage.
Containerized
To simplify the deployment process, I have containerized Keystone. The image can be found at GitLab and the Docker Registry.
Dockerfile
The image is based on a standard CentOS 7 cloud image. To install Keystone, I used the packaged version from the RDO project. After an update, it install the repository information pointing to mitaka, and then it install the Keystone service, some useful utils to use with OpenStack and the SELinux configuration files.
RUN yum update -y && \
yum install -y centos-release-openstack-mitaka && \
yum install -y openstack-keystone openstack-utils openstack-selinux && \
yum clean all
After this we expose the ports that Keystone uses for interaction.
EXPOSE 5000
EXPOSE 35357
As the CMD
it will run a simple initialization script. It takes care to setup
an admin token and use a MySQL connection. It then starts keystone-manage
to
provision the database with the schemas. And finally, it will use keystone-all
to run the services on the exposed ports.
1 2 3 4 5 6 7 8 | #!/bin/sh
ADMIN_TOKEN=${ADMIN_TOKEN-`openssl rand -hex 10`}
openstack-config --set /etc/keystone/keystone.conf DEFAULT admin_token $ADMIN_TOKEN
openstack-config --set /etc/keystone/keystone.conf DEFAULT use_stderr True
openstack-config --set /etc/keystone/keystone.conf database connection ${CONNECTION-mysql://keystone:password@dbhost/keystone}
su keystone -s /bin/sh -c "keystone-manage db_sync"
/usr/bin/keystone-all
|
In the rest of the article I will describe how to use this image to setup the test environment. The first component you will need to use Keystone is the database to store the credentials and other relevant information.
Setup database
As you saw in the Dockerfile
, we specified a MySQL connection. To make it
easy to deploy, we will also be using a container for this database server. I
prefer to use MariaDB, as this is also what normally would come with CentOS. For
this article I will be using MariaDB version 10.1.
To start the container perform the following command:
$ docker run -d --name keystone-database -e MYSQL_ROOT_PASSWORD=secrete mariadb:10.1
This will pull the container and start a named instance as keystone-database
.
The root
password to configure the database is set using the environment
variable to secrete
.
Using this password and container name, we will create a database for Keystone and grant priviliges:
$ docker exec keystone-database mysql -psecrete -e "create database keystone;"
$ docker exec keystone-database mysql -psecrete -e "GRANT ALL ON keystone.* TO 'keystone'@'%' IDENTIFIED BY 'password';"
$ docker exec keystone-database mysql -psecrete -e "GRANT ALL ON keystone.* TO 'keystone'@'localhost' IDENTIFIED BY 'password';"
$ docker exec keystone-database mysql -psecrete -e "flush privileges;"
After this, all the setting for the database are done and we can just leave it running without further configuration.
Generate token for API usage
Just like MariaDB, Keystone can use a token to perform the initial configuration. It is preferred to make this admin token something that is random and not easily guessable. A random token can be generated for instance with:
$ export TOKEN=$(openssl rand -hex 10)
In the rest of the article, this token will be used.
Start container
After the database has been setup, and a token has been decided, we can start the Keystone services.
$ docker run -d --link keystone-database:dbhost -e ADMIN_TOKEN=$TOKEN -p 5000:5000 --name keystone-server gbraad/openstack-keystone:mitaka
This will link the keystone container to the database container. If all goes
well, the service will now be available on the exposed ports. The container
instance will be identified by the name keystone-server
.
As mentioned earlier, when the container gets started, it will provision the database with the schemas that are needed.
In the following segment we will configure Keystone.
Create service entry
To finalize the Keystone configuration, we will insert the service and endpoints for the Identity service.
$ docker exec keystone-server keystone --os-token $TOKEN --os-endpoint http://localhost:35357/v2.0/ service-create --name=keystone --type=identity --description="Keystone Identity Service"
$ docker exec keystone-server keystone --os-token $TOKEN --os-endpoint http://localhost:35357/v2.0/ endpoint-create --service keystone --publicurl 'http://localhost:5000/v2.0' --adminurl 'http://localhost:35357/v2.0' --internalurl 'http://localhost:5000/v2.0'
Note that these command are performed inside the keystone container.
Create admin user
To allow access to keystone without using the admin token, we will need to
create a user. The following commands will create a user named admin
with
the password password
and add this to a tenant called admin
.
$ docker exec keystone-server keystone --os-token $TOKEN --os-endpoint http://localhost:35357/v2.0/ user-create --name admin --pass password
$ docker exec keystone-server keystone --os-token $TOKEN --os-endpoint http://localhost:35357/v2.0/ role-create --name admin
$ docker exec keystone-server keystone --os-token $TOKEN --os-endpoint http://localhost:35357/v2.0/ tenant-create --name admin
$ docker exec keystone-server keystone --os-token $TOKEN --os-endpoint http://localhost:35357/v2.0/ user-role-add --user admin --role admin --tenant admin
More detailed setups are possible, and for this I will refer you to the Keystone documentation.
Getting a user token
To verify Keystone works, we will retrieve a token. First you need to know the IP address that has been assigned to your container.
$ docker inspect --format="{{.NetworkSettings.IPAddress}}" keystone-server
172.17.0.6
This can be helpful to communicate between different containers on the same Docker network. You could create a new container, or pull my OpenStack client container.
Using the OpenStack client
Using the OpenStack client you can retrieve a token using the following configuration:
keystonerc
OS_IDENTITY_API_VERSION=3
OS_AUTH_URL=http://172.17.0.6:5000/v3
OS_USERNAME=admin
OS_PASSWORD=password
OS_PROJECT_NAME=admin
OS_USER_DOMAIN_NAME=Default
OS_PROJECT_DOMAIN_NAME=Default
When you use the following commands you will be given a token:
$ source keystonerc
$ openstack token issue
Using cURL
Since Keystone uses a HTTP interface, you can also retrieve the token using the
cURL command. When using the -i
parameter, cURL will return all the headers
from the response. This will include the X-SUBJECT-TOKEN
we want.
$ curl -i -H "Content-Type: application/json" -d '
{ "auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"name": "admin",
"domain": { "name": "Default" },
"password": "password"
}
}
},
"scope": {
"project": {
"name": "admin",
"domain": { "name": "Default" }
}
}
}
}' http://172.17.0.6:5000/v3/auth/tokens
A response will follow. You need to set the value of X-Subject-Token
to
OS_TOKEN
, or use in cURL as the X-AUTH-TOKEN
header. More about this can
be found in the Keystone documentation: API Examples using cURL.
How this was used for development
For the Commissaire project I added Keystone functionality using two methods; using password and token. The above described container was used to allow easy deployment and testing.
As I decided not to introduce unnecessary libraries as dependencies, the communication from Commissaire happens over the HTTP ReST interface. To authenticate using a basic authorization, using the password method, we only need to construct a simple JSON object:
{ "auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"name": "admin",
"domain": { "name": "Default" },
"password": "password"
}
}
},
"scope": {
"project": {
"name": "admin",
"domain": { "name": "Default" }
}
}
}
}
In code we described this with:
headers = {'Content-Type': 'application/json'}
body = {'auth': {'identity': {}}}
ident = body['auth']['identity']
ident['methods'] = ['password']
ident['password'] = {'user': {
'name': user,
'password': passwd,
'domain': {'name': self.domain}}}
To perform the authentication, we send a request:
response = requests.post(
self.url,
data=json.dumps(body),
headers=headers)
to the endpoint exposed at: http://172.17.0.6:5000/v3/auth/tokens
. When the
response includes the header X-Subject-Token
the authentication succeeded,
else we failed and send a 403 as status code.
if 'X-Subject-Token' in response.headers:
return True
# Forbid by default
return False
The Implementation for authentication using a token is quite similar, instead
we will take the X-Auth-Token
from the request we received and verify this
against Keystone using the following request:
{
"auth": {
"identity": {
"methods": [
"token"
],
"token": {
"id": "faa166abf235430e81c9fa12ad248533"
}
}
}
}
Just like in the previous example, the endpoint is:
http://172.17.0.6:5000/v3/auth/tokens
. If the authentication is succesful
the response will contain the X-Subject-Token
header.
Conclusion
Keystone is powerful service to provide authentication for your infrastructure. As you can see from this example, using a containerized (composed) environment, it is easy to get a basic setup running. And using the ReST API it is also very simple to handle authentication outside of your own application. In future articles more advanced scenarios will be given.
At the moment, password authentication using Keystone and Commissaire is possible and hopefully soon we will also have integrated the token based authentication.
If you have comments or suggestions, please leave them below or consider sending a message to me on Twitter: @gbraad.
Comments
comments powered by Disqus