I’ve shown how to set up OpenStack Keystone Federation with SSSD. We know we can set up Horizon with Federation using SAML. Here is how to set up Web Single Sign On (WebSSO) for SSSD and Kerberos.

This is a long one, but I’m trying to include all the steps on one document. Much is a repeat of previous blog posts. However, some details have changed, I want the explanation here to be consistent.

I’m starting with a RHEL 7.1 VM. I tend to use internal Yum repos for packages, to avoid going across the network for Updates, but the general steps should work regardless of update mecahnism. This is, once again, using devstack, as the bits are very fresh for the WebSSO code, and I need to work off master for several projects. We’ll work on an automated version for RDO once the packages are up to date.

I have an IPA server up and running. I want to make this VM the IPA client. Since I’m done with Nova managing my VM config values, I’ll first disable cloud-init; There are many ways to do this, but that is outside the scope of this article.

sudo yum -y update sudo yum -y groupinstall "Development Tools" sudo yum -y install ipa-client sssd-dbus sudo mod_lookup_identity mod_auth_kerb sudo yum -y erase cloud-init

Now to use network manager CLI to set some basics. To list the connections:

$ nmcli c NAME UUID TYPE DEVICE System eth0 5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03 802-3-ethernet eth0

I want this machine to keep its host name, and to keep the DNS server I set:

$ sudo nmcli c edit 5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03 ===| nmcli interactive connection editor |=== Editing existing '802-3-ethernet' connection: '5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03' Type 'help' or '?' for available commands. Type 'describe [ . ]' for detailed property description. You may edit the following settings: connection, 802-3-ethernet (ethernet), 802-1x, ipv4, ipv6, dcb nmcli> goto ipv4 You may edit the following properties: method, dns, dns-search, addresses, gateway, routes, route-metric, ignore-auto-routes, ignore-auto-dns, dhcp-hostname, dhcp-send-hostname, never-default, may-fail, dhcp-client-id nmcli ipv4> set ignore-auto-dns yes nmcli ipv4> set dns 192.168.1.59 nmcli ipv4> set dhcp-hostname horizon.cloudlab.freeipa.org nmcli ipv4> save Connection 'System eth0' (5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03) successfully updated. nmcli ipv4> quit

Set the hostname via

sudo vi /etc/hostname

And lets see what happens when I reboot.

$ sudo reboot Connection to horizon.cloudlab.freeipa.org closed by remote host. Connection to horizon.cloudlab.freeipa.org closed. [ayoung@ayoung530 python-keystoneclient (review/ayoung/access_info_split)]$ ssh cloud-user@horizon.cloudlab.freeipa.org Last login: Wed Apr 1 16:06:27 2015 from 10.10.55.194 [cloud-user@horizon ~]$ hostname horizon.cloudlab.freeipa.org

Let’s see if we can talk to the IPA server:

$ nslookup ipa.cloudlab.freeipa.org Server: 192.168.1.59 Address: 192.168.1.59#53 Name: ipa.cloudlab.freeipa.org Address: 192.168.1.59

$ sudo ipa-client-install WARNING: ntpd time&date synchronization service will not be configured as conflicting service (chronyd) is enabled Use --force-ntpd option to disable it and force configuration of ntpd Discovery was successful! Hostname: horizon.cloudlab.freeipa.org Realm: CLOUDLAB.FREEIPA.ORG DNS Domain: cloudlab.freeipa.org IPA Server: ipa.cloudlab.freeipa.org BaseDN: dc=cloudlab,dc=freeipa,dc=org Continue to configure the system with these values? [no]: yes

Much elided, suffice to say it succeeded. On to devstack

sudo mkdir /opt/stack sudo chown cloud-user /opt/stack/ cd /opt/stack/ git clone https://git.openstack.org/openstack-dev/devstack cd devstack

Now, one workaround. edit the file files/rpms/general and comment out the libyaml-devel package. The functionality is provided by a different package, and that package does not exist in in RHEL7.

... which bc #libyaml-devel gettext # used for compiling message catalogs net-tools java-1.7.0-openjdk-headless # NOPRIME rhel7,f20 java-1.8.0-openjdk-headless # NOPRIME f21,f22

Here is my local.conf file:

[[local|localrc]] ADMIN_PASSWORD=FreeIPA4All DATABASE_PASSWORD=$ADMIN_PASSWORD RABBIT_PASSWORD=$ADMIN_PASSWORD SERVICE_PASSWORD=$ADMIN_PASSWORD SERVICE_TOKEN=$ADMIN_PASSWORD LIBS_FROM_GIT=python-openstackclient,python-keystoneclient

I tried with Django-openstack-auth as well, but it seems devstack does not know to fetch that. I ended up cloning that repo by hand.

Run devstack:

./stack

And wait.

…

Ok, it is done!

Then edit /etc/sssd/sssd.conf and set sssd to start the info pipe services

[sssd] services = nss, sudo, pam, ssh, ifp

And, in the same file, let infopipe know it can respond with a subset of the LDAP values.

[ifp] allowed_uids = apache, root, cloud-user user_attributes = +givenname, +sn, +uid

Ah, forgot to add in a cloud-user user to IPA, as that is what devstack is set to use. I need the ipa client.

$ sudo yum install ipa-admintools $ kinit ayoung $ ipa user-add cloud-user --uid=1000 First name: Cloud Last name: User

And now:

sudo service sssd restart

Test infopipe:

sudo dbus-send --print-reply --system --dest=org.freedesktop.sssd.infopipe /org/freedesktop/sssd/infopipe org.freedesktop.sssd.infopipe.GetUserGroups string:ayoung

Returns

method return sender=:1.17 -> dest=:1.29 reply_serial=2 array [ string "admins" string "ipausers" string "wheel" ]

Now to configure HTTPD. I’m not going to bother with HTTPS for this setup, as it is only proof of concept, and there is a good bit of Horizon to reset if you do HTTPS.

$ sudo yum install mod_lookup_identity mod_auth_kerb

OK…now we start treading new ground. Instead of a whole new Kerberized setup for Keystone, I’m only going to Kerberize the segment protected by Federation. That is

Create a file with the V3 and admin env vars set:

$ cat openrc.v3 . ./openrc export OS_AUTH_URL=http://192.168.1.67:5000/v3 export OS_IDENTITY_API_VERSION=3 export OS_USERNAME=admin

source that and test:

$ openstack project list +----------------------------------+--------------------+ | ID | Name | +----------------------------------+--------------------+ | 07a369b34c6f41948143f6ff75dc81a6 | alt_demo | | 0edb00180b3d4676baf5c39325e0639d | demo | | 18c3137a9a4e4266adb3b143c0d62ac3 | service | | 64378db96ab845dd8346ce0bcff9709d | admin | | 9a08ef972d7f4ef9a52190085b6b25d0 | invisible_to_admin | +----------------------------------+--------------------+

create the groups and mappings for Federation

Here is the contents of mapping.json

[ { "local": [ { "user": { "name": "{0}", "id": "{0}", "domain": {"name": "Default"} } } ], "remote": [ { "type": "REMOTE_USER" } ] }, { "local": [ { "groups": "{0}", "domain": { "name": "Default" } } ], "remote": [ { "type": "REMOTE_USER_GROUPS", "blacklist": [] } ] } ]

openstack group create admins openstack group create ipausers openstack role add --project demo --group ipausers member openstack identity provider create sssd openstack mapping create --rules /home/cloud-user/mapping.json kerberos_mapping openstack federation protocol create --identity-provider sssd --mapping kerberos_mapping kerberos openstack identity provider set --remote-id SSSD sssd

Get the Keytab

$ ipa service-add HTTP/horizon.cloudlab.freeipa.org ---------------------------------------------------------------------- Added service "HTTP/horizon.cloudlab.freeipa.org@CLOUDLAB.FREEIPA.ORG" ---------------------------------------------------------------------- Principal: HTTP/horizon.cloudlab.freeipa.org@CLOUDLAB.FREEIPA.ORG Managed by: horizon.cloudlab.freeipa.org $ ipa-getkeytab -s ipa.cloudlab.freeipa.org -k /tmp/openstack.keytab -p HTTP/horizon.cloudlab.freeipa.org Keytab successfully retrieved and stored in: /tmp/openstack.keytab $ sudo mv /tmp/openstack.keytab /etc/httpd/conf $ sudo chown apache /etc/httpd/conf/openstack.keytab $ sudo chmod 600 /etc/httpd/conf/openstack.keytab

Enable Kerberos for Keystone. In /etc/httpd/conf.d/keystone.conf

Listen 5000 Listen 35357 #enable modules for Kerberos and getting id from sssd LoadModule lookup_identity_module modules/mod_lookup_identity.so LoadModule auth_kerb_module modules/mod_auth_kerb.so <VirtualHost *:5000> WSGIDaemonProcess keystone-public processes=5 threads=1 user=cloud-user display-name=%{GROUP} WSGIProcessGroup keystone-public WSGIScriptAlias / /var/www/keystone/main WSGIApplicationGroup %{GLOBAL} WSGIPassAuthorization On <IfVersion >= 2.4> ErrorLogFormat "%{cu}t %M" </IfVersion> ErrorLog /var/log/httpd/keystone.log CustomLog /var/log/httpd/keystone_access.log combined #This tells WebSSO what IdP to use SetEnv IDP_ID SSSD #Protect the urls that have kerberos in their path with Kerberos. <location ~ "kerberos" > AuthType Kerberos AuthName "Kerberos Login" KrbMethodNegotiate on KrbMethodK5Passwd off KrbServiceName HTTP KrbAuthRealms CLOUDLAB.FREEIPA.ORG Krb5KeyTab /etc/httpd/conf/openstack.keytab KrbSaveCredentials on KrbLocalUserMapping on Require valid-user # SSLRequireSSL LookupUserAttr mail REMOTE_USER_EMAIL " " LookupUserGroups REMOTE_USER_GROUPS ";" </location>

Make sure Keystone can handle Kerberos. In /etc/keystone/keystone.conf

[auth] methods = external,password,token,oauth1,kerberos kerberos = keystone.auth.plugins.mapped.Mapped

and under federation

[federation] # I tried this first, and it worked. It will have issues when sharing WebSSO with other mechanisms remote_id_attribute = IDP_ID trusted_dashboard = http://horizon.cloudlab.freeipa.org/auth/websso/ sso_callback_template = /etc/keystone/sso_callback_template.html #this is supposed to work but does not yet. [kerberos] remote_id_attribute=IDP_ID

Once this is set, copy the templacte file for the webSSO post response from the Keystone repo to /etc/keystone

cp /opt/stack/keystone/etc/sso_callback_template.html /etc/keystone/

curl --negotiate -u: $HOSTNAME:5000/v3/OS-FEDERATION/identity_providers/sssd/protocols/kerberos/auth

Returns

{"token": {"methods": ["kerberos"], "expires_at": "2015-04-02T21:17:51.054150Z", "extras": {}, "user": {"OS-FEDERATION": {"identity_provider": {"id": "sssd"}, "protocol": {"id": "kerberos"}, "groups": [{"id": "482eb4e6a0c64348845773b506d1db77"}, {"id": "6da803796a4540d48a0aff3b3185edad"}, {"id": "f0bf681ae2e84d1580a7ff54ea49bf27"}]}, "domain": {"id": "Federated", "name": "Federated"}, "id": "ayoung", "name": "ayoung"}, "audit_ids": ["J-wAsamnQ5-NHjXRYHSAbA"], "issued_at": "2015-04-02T20:17:51.054182Z"}}

And to test WebSSO

curl --negotiate -u: $HOSTNAME:5000/v3/auth/OS-FEDERATION/websso/kerberos?origin=http://horizon.cloudlab.freeipa.org/auth/websso/

Returns

Keystone WebSSO redirect

Please wait...



On to Horizon:

Since this is devstack, the config changes go in:

/opt/stack/horizon/openstack_dashboard/local/local_settings.py

I put all my custom settings at the bottom:

WEBSSO_ENABLED = True WEBSSO_CHOICES = ( ("credentials", _("Keystone Credentials")), ("kerberos", _("Kerberos")), ) WEBSSO_INITIAL_CHOICE="kerberos" COMPRESS_OFFLINE=True OPENSTACK_KEYSTONE_DEFAULT_ROLE="Member" OPENSTACK_HOST="horizon.cloudlab.freeipa.org" OPENSTACK_API_VERSIONS = { "identity": 3 } OPENSTACK_KEYSTONE_URL="http://horizon.cloudlab.freeipa.org:5000/v3"

Around here is where I cloned the django-openstack-auth repo and made it available to the system using:

cd /opt/stack/ git clone https://git.openstack.org/openstack/django_openstack_auth cd django_openstack_auth sudo python setup.py develop sudo systemctl restart httpd.service

When you hit Horizon from a browser it should look like this:

There was one outstanding bug that needed to be fixed. I patched this inline. The fix has already merged.

Getting the HOSTNAME right on the host. Removing cloud-init worked for me, although I’ve been assured there are better ways to do that.

The Horizon config needs to use the hostnames, not the IP addresses, for Kerberos to work.

If you do the sssd setup before installing apache HTTPD, and the apache user does not exist, the sssd daemon won’t restart. However, if you forget to add the apache user to the sssd.conf the webserver won’t be able to read from dbus, and thus the REMOTE_USER_GROUPS env var won’t be passed to HTTPD. The error message is

IndexError: tuple index out of range

The keytab needs to be owned by the apache user.

Horizon needs to both use the V3 API explicitly and use the AUTH_URL that ends in /v3. It might be possible to drop the /v3 and depend on discovery, but leaving the v2.0 on there will certainly break auth.

As I had buried in the devstack instructions: edit the file files/rpms/general and comment out the libyaml-devel package. The functionality is provided by a different package, and that package does not exist in in RHEL7.

There was a lot of trial and error in making this work, and the cause of the error is not always clear. Some of the things that tripped me up bpoth the first time and trying to replicate: