Horizon WebSSO via SSSD

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:

Horizon  Login Screen set up for Federation  Showing Kerberos by Default

Horizon Login Screen set up for Federation Showing Kerberos by Default


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:

  • 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.

1 thought on “Horizon WebSSO via SSSD

  1. Hi Adam,
    I keep telling this again and again for every blog post of yours, its a real good post.

    your blog has become the first go-to place for all my experiments when it comes to keystone federation, i owe you alot,
    Thanks and keep posting…

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.