Note
This feature is experimental and unsupported in Liberty.
Keystone must be running in a web container with https enabled; tests have been done with Apache/2.4.7 running on Ubuntu 14.04 . Please refer to running-keystone-in-httpd and apache-certificate-and-key-installation as references for this setup.
To enable X.509 tokenless authorization, SSL has to be enabled and configured in the Apache virtual host file. The Client authentication attribute SSLVerifyClient should be set as optional to allow other token authentication methods and attribute SSLOptions needs to set as +StdEnvVars to allow certificate attributes to be passed. The following is the sample virtual host file used for the testing.
<VirtualHost *:443>
WSGIScriptAlias / /var/www/cgi-bin/keystone/main
ErrorLog /var/log/apache2/keystone.log
LogLevel debug
CustomLog /var/log/apache2/access.log combined
SSLEngine on
SSLCertificateFile /etc/apache2/ssl/apache.cer
SSLCertificateKeyFile /etc/apache2/ssl/apache.key
SSLCACertificatePath /etc/apache2/capath
SSLOptions +StdEnvVars
SSLVerifyClient optional
</VirtualHost>
The following options can be defined in keystone.conf:
This is a sample configuration for two trusted_issuer and a protocol set to x509.
[tokenless_auth]
trusted_issuer = emailAddress=mary@abc.com,CN=mary,OU=eng,O=abc,L=San Jose,ST=California,C=US
trusted_issuer = emailAddress=john@openstack.com,CN=john,OU=keystone,O=openstack,L=Sunnyvale,ST=California,C=US
protocol = x509
Like federation, X.509 tokenless authorization also utilizes the mapping mechanism to formulate an identity. The identity provider must correspond to the issuer of the X.509 SSL client certificate. The protocol for the given identity is x509 by default, but can be configurable.
In order to create an IdP, the issuer DN in the client certificate needs to be provided. The following sample is what a generic issuer DN looks like in a certificate.
E=john@openstack.com
CN=john
OU=keystone
O=openstack
L=Sunnyvale
S=California
C=US
The issuer DN should be constructed as a string that contains no spaces and have the right order separated by commas like the example below. Please be aware that emailAddress and ST should be used instead of E and S that are shown in the above example. The following is the sample Python code used to create the IdP ID.
import hashlib
issuer_dn = 'emailAddress=john@openstack.com,CN=john,OU=keystone,
O=openstack,L=Sunnyvale,ST=California,C=US'
hashed_idp = hashlib.sha256(issuer_dn)
idp_id = hashed_idp.hexdigest()
print(idp_id)
The output of the above Python code will be the IdP ID and the following sample curl command should be sent to keystone to create an IdP with the newly generated IdP ID.
curl -k -s -X PUT -H "X-Auth-Token: <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"identity_provider": {"description": "Stores keystone IDP identities.","enabled": true}}' \
https://<HOSTNAME>:<PORT>/v3/OS-FEDERATION/identity_providers/<IdP ID>
A mapping needs to be created to map the Subject DN in the client certificate as a user to yield a valid local user if the user’s type defined as local in the mapping. For example, the client certificate has Subject DN as CN=alex,OU=eng,O=nice-network,L=Sunnyvale, ST=California,C=US, in the following examples, user_name will be mapped to``alex`` and domain_name will be mapped to nice-network. And it has user’s type set to local. If user’s type is not defined, it defaults to ephemeral.
Please refer to mod_ssl for the detailed mapping attributes.
{
"mapping": {
"rules": [
{
"local": [
{
"user": {
"name": "{0}",
"domain": {
"name": "{1}"
},
"type": "local"
}
}
],
"remote": [
{
"type": "SSL_CLIENT_S_DN_CN"
},
{
"type": "SSL_CLIENT_S_DN_O"
}
]
}
]
}
}
When user’s type is not defined or set to ephemeral, the mapped user does not have to be a valid local user but the mapping must yield at least one valid local group. For example:
{
"mapping": {
"rules": [
{
"local": [
{
"user": {
"name": "{0}",
"type": "ephemeral"
}
},
{
"group": {
"id": "12345678"
}
}
],
"remote": [
{
"type": "SSL_CLIENT_S_DN_CN"
}
]
}
]
}
}
The following sample curl command should be sent to keystone to create a mapping with the provided mapping ID. The mapping ID is user designed and it can be any string as opposed to IdP ID.
curl -k -s -H "X-Auth-Token: <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"mapping": {"rules": [{"local": [{"user": {"name": "{0}","type": "ephemeral"}},{"group": {"id": "<GROUPID>"}}],"remote": [{"type": "SSL_CLIENT_S_DN_CN"}]}]}}' \
-X PUT https://<HOSTNAME>:<PORT>/v3/OS-FEDERATION/mappings/<MAPPING ID>
The name of the protocol will be the one defined in keystone.conf as protocol which defaults to x509. The protocol name is user designed and it can be any name as opposed to IdP ID.
A protocol name and an IdP ID will uniquely identify a mapping.
The following sample curl command should be sent to keystone to create a protocol with the provided protocol name that is defined in keystone.conf.
curl -k -s -H "X-Auth-Token: <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"protocol": {"mapping_id": "<MAPPING ID>"}}' \
-X PUT https://<HOSTNAME>:<PORT>/v3/OS-FEDERATION/identity_providers/<IdP ID>/protocols/<PROTOCOL NAME>
In order to use auth_token middleware as the service client for X.509 tokenless authorization, both configurable options and scope information will need to be setup.
The following configurable options in auth_token middleware should set to the correct values:
The scope information will be passed from the headers with the following header attributes to:
Once the above configurations have been setup, the following curl command can be used for token validation.
curl -v -k -s -X GET --cert /<PATH>/x509client.crt \
--key /<PATH>/x509client.key \
--cacert /<PATH>/ca.crt \
-H "X-Project-Name: <PROJECT-NAME>" \
-H "X-Project-Domain-Id: <PROJECT-DOMAIN-ID>" \
-H "X-Subject-Token: <TOKEN>" \
https://<HOST>:<PORT>/v3/auth/tokens | python -mjson.tool