Monday, May 23, 2016

Kerberos and Keystone: How to integrate Kerberos with OpenLDAP backend in OpenStack Keystone

Kerberos is a network authentication protocol. It is designed to provide strong authentication for client/server applications by using secret-key cryptography.
This post describes how to set up OpenStack Keystone with external authentication - Kerberos with OpenLDAP backend. The post also contains results of the research about using Kerberos not just for authentication but also for authorization by storing additional data (like groups, roles, etc.) in Kerberos ticket.
This post describes how to set up following configuration:

What is the Kerberos

Kerberos was created by MIT as a solution to network security problems. The Kerberos protocol uses strong cryptography so that a client can prove its identity to a server (and vice versa) across an insecure network connection. After a client and server has used Kerberos to prove their identity, they can also encrypt all of their communications to assure privacy and data integrity as they go about their business.
Suppose you want to access a server on another computer. You know that this server requires a Kerberos "ticket" before it will honor your request.
To get your ticket, you first request authentication from the Authentication Server (AS). The Authentication Server creates a "session key" (which is also an encryption key) basing it on your password (which it can get from your user name) and a random value that represents the requested service. The session key is effectively a "ticket-granting ticket."
You next send your ticket-granting ticket to a ticket-granting server (TGS). The TGS may be physically the same server as the Authentication Server, but it's now performing a different service.The TGS returns the ticket that can be sent to the server for the requested service.
The service either rejects the ticket or accepts it and performs the service.
Because the ticket you received from the TGS is time-stamped, it allows you to make additional requests using the same ticket within a certain time period (typically, eight hours) without having to be reauthenticated. Making the ticket valid for a limited time period make it less likely that someone else will be able to use it later.
The protocol is described in detail below.
How to install Kerberos with OpenLDAP
Install OpenLDAP
  1. Install packages
sudo apt-get update
sudo apt-get install slapd ldap-utils
  1. Reconfigure to set admin user
sudo dpkg-reconfigure slapd
  1. Generate password:
  1. Create conf.ldif, use generated your password for olcRootPW (separate request for each block)
dn: olcDatabase={0}config,cn=config
changetype: modify
add: olcRootDN
olcRootDN: cn=admin,cn=config
dn: olcDatabase={0}config,cn=config
changetype: modify
add: olcRootPW
olcRootPW: "{SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU"
dn: olcDatabase={0}config,cn=config
changetype: modify
delete: olcAccess
  1. Create cn=admin,cn=config
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f conf.ldif
Install Kerberos
Each server in a Kerberos authentication realm must be assigned a Fully Qualified Domain Name (FQDN) that is forward-resolvable.
If the server does not already have a FQDN assigned to it and DNS services are not available, name resolution can be implemented by editing the local hosts file (/etc/hosts) on each server and client.
  1. To load the schema into LDAP, on the LDAP server install the krb5-kdc-ldap package. From a terminal enter
sudo apt-get install krb5-kdc-ldap
  1. Next, extract the kerberos.schema.gz file
sudo gzip -d /usr/share/doc/krb5-kdc-ldap/kerberos.schema.gz
sudo cp /usr/share/doc/krb5-kdc-ldap/kerberos.schema /etc/ldap/schema/
  1. –°reate a configuration file named schema_convert.conf, or a similar descriptive name, containing the following lines
include /etc/ldap/schema/core.schema
include /etc/ldap/schema/collective.schema
include /etc/ldap/schema/corba.schema
include /etc/ldap/schema/cosine.schema
include /etc/ldap/schema/duaconf.schema
include /etc/ldap/schema/dyngroup.schema
include /etc/ldap/schema/inetorgperson.schema
include /etc/ldap/schema/java.schema
include /etc/ldap/schema/misc.schema
include /etc/ldap/schema/nis.schema
include /etc/ldap/schema/openldap.schema
include /etc/ldap/schema/ppolicy.schema
include /etc/ldap/schema/kerberos.schema
  1. Create a temporary directory to hold the LDIF files
mkdir /tmp/ldif_output
  1. Now use slapcat to convert the schema files
slapcat -f schema_convert.conf -F /tmp/ldif_output -n0 -s \
"cn={12}kerberos,cn=schema,cn=config" > /tmp/cn=kerberos.ldif
  1. Edit the generated /tmp/cn\=kerberos.ldif file, changing the following attributes
dn: cn=kerberos,cn=schema,cn=config
cn: kerberos
  1. And remove the following lines from the end of the file
structuralObjectClass: olcSchemaConfig
entryUUID: 18ccd010-746b-102d-9fbe-3760cca765dc
creatorsName: cn=config
createTimestamp: 20090111203515Z
entryCSN: 20090111203515.326445Z#000000#000#000000
modifiersName: cn=config
modifyTimestamp: 20090111203515Z
The attribute values will vary, just be sure the attributes are removed.
  1. Load the new schema with ldapadd
ldapadd -x -D cn=admin,cn=config -W -f /tmp/cn\=kerberos.ldif
  1. Add an index for the krb5principalname attribute
ldapmodify -x -D cn=admin,cn=config -W
Enter LDAP Password
dn: olcDatabase={1}hdb,cn=config
add: olcDbIndex
olcDbIndex: krbPrincipalName eq,pres,sub
modifying entry "olcDatabase={1}hdb,cn=config"
  1. Finally, update the Access Control Lists (ACL)
ldapmodify -x -D cn=admin,cn=config -W
Enter LDAP Password:
dn: olcDatabase={1}hdb,cn=config
replace: olcAccess
olcAccess: to attrs=userPassword,shadowLastChange,krbPrincipalKey by
dn="cn=admin,dc=example,dc=com" write by anonymous auth by self write by * none
add: olcAccess
olcAccess: to dn.base="" by * read
add: olcAccess
olcAccess: to * by dn="cn=admin,dc=example,dc=com" write by * read
modifying entry "olcDatabase={1}hdb,cn=config"
  1. Install the necessary packages, from a terminal enter
sudo apt-get install krb5-kdc krb5-admin-server krb5-kdc-ldap
  1. Edit /etc/krb5.conf adding the following options to under the appropriate sections
   default_realm = EXAMPLE.COM
           kdc =
           admin_server =
           default_domain =
           database_module = openldap_ldapconf
[domain_realm] = EXAMPLE.COM
   ldap_kerberos_container_dn = dc=example,dc=com
   openldap_ldapconf = {
           db_library = kldap
           ldap_kdc_dn = "cn=admin,dc=example,dc=com"
           # this object needs to have read rights on
           # the realm container, principal container and realm sub-trees
           ldap_kadmind_dn = "cn=admin,dc=example,dc=com"
           # this object needs to have read and write rights on
           # the realm container, principal container and realm sub-trees
           ldap_service_password_file = /etc/krb5kdc/service.keyfile
           ldap_servers = ldaps://
           ldap_conns_per_server = 5

  1. Change, dc=example,dc=com, cn=admin,dc=example,dc=com, and to the appropriate domain, LDAP object, and LDAP server for your network.
To avoid it edit /etc/krb5.conf, and change the section
   ldap_kerberos_container_dn = dc=example,dc=com
   ldap_kerberos_container_dn = cn=krbContainer,dc=example,dc=com
  1. Use the kdb5_ldap_util utility to create the realm
sudo kdb5_ldap_util -D  cn=admin,dc=example,dc=com create -subtrees \
dc=example,dc=com -r EXAMPLE.COM -s -H ldap://
  1. Create a stash of the password used to bind to the LDAP server. This password is used by the ldap_kdc_dn and ldap_kadmin_dn options in /etc/krb5.conf
sudo kdb5_ldap_util -D  cn=admin,dc=example,dc=com stashsrvpw -f \
/etc/krb5kdc/service.keyfile cn=admin,dc=example,dc=com
  1. Install Apache Directory Studio
  1. Create ou=People,dc=example,dc=com
  1. Create user in that tree, for example: uid=steve,ou=people,dc=example,dc=com
  1. Start services
sudo krb5kdc
sudo kadmind
  1. To add a principal using the kadmin.local utility enter
sudo kadmin.local
Authenticating as principal root/admin@EXAMPLE.COM with password.
kadmin.local:  addprinc -x dn="uid=steve,ou=people,dc=example,dc=com" steve
WARNING: no policy specified for steve@EXAMPLE.COM; defaulting to no policy
Enter password for principal "steve@EXAMPLE.COM":
Re-enter password for principal "steve@EXAMPLE.COM":
Principal "steve@EXAMPLE.COM" created.
  1. To test the operation of Kerberos, request a Ticket-Granting Ticket (TGT) with the kinit command, as shown. Any valid Kerberos principal can be substituted for "steve". Omit the realm name from the command if the default_realm directive is properly specified in the/etc/krb5.conf file or DNS SRV records.
  1. Note: The realm name is CASE SENSITIVE.
$ kinit -p steve@EXAMPLE.COM
Password for steve@EXAMPLE.COM: ****
  1. Use the klist command to verify the TGT is valid
$ klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: steve@EXAMPLE.COM
Valid starting Expires         Service principal
01/21/05 10:28:51  01/21/05 20:27:43
Integrate with SASL
We will need the SASL pluggable authentication framework, and the GSSAPI module for the Kerberos implementation in use. Here the MIT implementation is used.
  1. Run the following command on the server
sudo apt-get install sasl2-bin libsasl2-2 libsasl2-modules \
  1. Create principal for LDAP server (change localhost to dns name)
sudo kadmin.local
kadmin: addprinc -randkey ldap/localhost@EXAMPLE.COM
  1. Create keytab
kadmin: ktadd -k /etc/krb5.keytab ldap/localhost@EXAMPLE.COM
  1. Restrict the keytab's file permissions as necessary
  1. Configured the service to access the correct keytab
export KRB5_KTNAME=/path/to/keytab
Test configuration
  1. Start the SASL sample server on the server with the GSSAPI mechanism and the specified service
sasl-sample-server -m GSSAPI -s ldap
  1. In a separate terminal on the server, initialize a credentials cache for a user using kinit
  1. In the same terminal, start the SASL sample client
sasl-sample-client -s ldap -n localhost -u steve
  1. Copy the line beginning with "S: " from the server window to the client window and hit Enter. The client will generate a line beginning with "C: " that must be copy/pasted into the server window. This exchange must be repeated several times. The process should completely with no errors.
  1. Repeat the same process with the sample server running on the server and the sample client running on a host that will need to access the service.
  1. Example
admin@server:~$ sasl-sample-server -m GSSAPI -s ldap
Forcing use of mechanism GSSAPI
Sending list of 1 mechanism(s)
Waiting for client mechanism...
C: R1N...
got 'GSSAPI'
Sending response...
S: YIG...
Waiting for client reply...
got ''
Sending response...
S: YD8G...
Waiting for client reply...
C: YEc...
got '...'
Negotiation complete
Username: steve
Realm: (NULL)
SSF: 56
sending encrypted message 'srv message 1'
Waiting for encrypted message...
got ''
recieved decoded message 'client message 1'
user@server:~$ sasl-sample-client -s ldap -n localhost -u steve
Waiting for mechanism list from server...
recieved 6 byte message
Choosing best mechanism from: GSSAPI
returning OK: steve
Using mechanism GSSAPI
Preparing initial.
Sending initial response...
C: R1N...
Waiting for server reply...
S: YIG...
recieved 153 byte message
Waiting for server reply...
S: YD8...
recieved 65 byte message
Sending response...
C: YEc...
Negotiation complete
Username: user
SSF: 56
Waiting for encoded message...
recieved 77 byte message
recieved decoded message 'srv message 1'
sending encrypted message 'client message 1'
  1. Add the following attributes to the cn=config DIT
#The FQDN of the Kerberos KDC.
olcSaslHost: localhost
#The Kerberos realm name
olcSaslRealm: EXAMPLE.COM
#disallow insecure authentication mechanisms such as plain passwords
olcSaslSecProps: noplain,noactive,noanonymous,minssf=56
#by default, the DN of an authorized Kerberos client takes the form
#uid=<Kerberos principal name>,cn=<Kerberos Realm>,cn=GSSAPI,cn=auth
#adjust the following mappings to match the local configuration as necessary
olcAuthzRegexp: {0}"uid=([^/]*),,cn=GSSAPI,cn=auth" "uid=$1,ou=people,dc=example,dc=com"
olcAuthzRegexp: {1}"uid=host/([^/]*),,cn=gssapi,cn=auth" "cn=$1,ou=hosts,dc=example,dc=com"
#administrative user map, assumes existence of cn=admin,cn=config
olcAuthzRegexp: {2}"uid=ldap/admin,,cn=gssapi,cn=auth" "cn=admin,cn=config"
  1. Add the following lines to /etc/ldap/ldap.conf to use Kerberos authentication as the default authentication mechanism for ldap-utils(e.g. ldapsearch). Replace "EXAMPLE.COM" with the name of your Kerberos realm
  1. To test Kerberos authentication, obtain a Kerberos TGT with kinit, then use the ldapsearch tool with the -Y GSSAPI option to force the use of the GSSAPI authentication mechanism
$ ldapsearch -Y GSSAPI
SASL/GSSAPI authentication started
SASL username: ktest@EXAMPLE.COM
SASL data security layer installed.
# extended LDIF
# LDAPv3
# base <> (default) with scope subtree
# filter: (objectclass=*)
# requesting: ALL
# search result
search: 4
result: 32 No such object
# numResponses: 1

Connect from Apache Directory Studio by GSAPI

How does Keystone work with Keystone
First of all Keystone should be running within Apache HTTPD with special Apache authentication modules to allow Kerberos authentication.
Then Keystone is becoming for Kerberos a simple HTTP service and it very easy to configure everything.
In that case Keystone is just responsible for authorization.
How to configure Keystone with Kerberos
  1. Run Keystone under Apache with mod_auth_kerb module 
    And configure Keystone site like this
<VirtualHost *:5000>
WSGIDaemonProcess keystone-public user=stack display-name=keystone-public
WSGIProcessGroup keystone-public
WSGIImportScript /usr/local/bin/keystone-wsgi-public process-group=keystone-public application-group=%{GLOBAL}
WSGIScriptAlias /krb "/usr/local/bin/keystone-wsgi-public"
WSGIScriptAlias / "/usr/local/bin/keystone-wsgi-public"
<Location "/krb/v3/auth/tokens">
    LogLevel debug
    AuthType Kerberos
    AuthName "Kerberos Login"
    KrbMethodNegotiate on
    KrbMethodK5Passwd off
    KrbServiceName HTTP
    KrbAuthRealms EXAMPLE.COM
    Krb5KeyTab /etc/http.keytab
    KrbLocalUserMapping on
    require valid-user
ErrorLog /var/log/apache2/keystone.log
LogLevel debug
CustomLog /var/log/apache2/keystone_access.log combined
  1. Create principal for HTTP server (change localhost to dns name)
sudo kadmin.local
kadmin: addprinc -randkey HTTP/localhost@EXAMPLE.COM
  1. Create keytab
kadmin: ktadd -k /etc/krb5.keytab HTTP/localhost@EXAMPLE.COM
  1. Copy keytab file to Apache machine.

How does it work step by step

From Authentication to Authorization

Kerberos was created for authentication but there is way to use Kerberos also for authorization.
Kerberos does not itself provide authorization, but V5 Kerberos passes authorization information generated by other services. In this manner, Kerberos can be used as a base for building separate distributed authorization services.
It is important here to understand that there is difference between protocol and implementation between protocol. In general there are two implementations: Microsoft and UNIX-like.
Microsoft extended the base Kerberos protocol to include authorization data (e.g., global group memberships).
Authorization Data field contains authorization data for the client.
In MIT's implementation of the Kerberos protocol, this field normally contains access restrictions. For example, if this were a ticket for a print server sent by a client wishing to print a file, the file name could be included here, restricting the print server's access on behalf of the client to only this file.
In the Microsoft implementation, this field is significant because it contains the user's SID and the SIDs of groups the client belongs to—data which is used to build the client's access token (the privilege attributes for the client).
The Kerberos service does not interpret the contents of this field. Interpretation is left up to the service requesting credentials.

Microsoft Kerberos authorization

Microsoft extended the base Kerberos protocol to include authorization data.To implement Kerberos authorization Microsoft uses Kerberos Privilege Attribute Certificate (PAC).
In the Microsoft implementation KDC generates the PAC, puts the authorization data there and puts the PAC into a TGT. The PAC data are added to a ticket on the KDC level.
So usually it means that KDC gets user-group membership from Active Directory.
Kerberos  ticket can be decrypted only by a KDC and the destination resource server. The client can't decrypt and change its authorization data.
Most non-Microsoft Kerberos implementations ignore the PAC field and its content.
The Kerberos ticket has a fixed size. If a user is a member of a large amount of groups, this size may be exceeded and authentication and group policy processing may fail. Microsoft allows you to adjust the maximum size of a Kerberos ticket using the MaxTokenSize registry parameter.

Is there any way to store Keystone groups and roles in Kerberos ticket?

The quick answer is "No".
First of all Kerberos was created only for authentication and it is not very good idea to use it for authorization.
There is Microsoft implementation of Kerberos which extends the base Kerberos protocol to include authorization data.
In case of Microsoft implementation groups, roles, ... should be stored in Active Directory.
Since Keystone doesn't want to have management for groups and roles in LDAP the idea of using Kerberos for authorization even for Active Directory doesn't make any sense.

No comments:

Post a Comment