Featured image for single sign-on security topics.

In this article, I will demonstrate how workstation users authenticating to Active Directory using the Kerberos protocol can use Simple and Protected GSSAPI Negotiation Mechanism (SPNEGO) tokens with Red Hat's single sign-on (SSO) technology for single sign-on to web applications.

In summary, the authentication flow is as follows:

  1. The user is authenticated with Active Directory over Kerberos and has a Ticket Granting Ticket (TGT) stored locally on the workstation.
  2. The user makes a request from the browser to access the web application.
  3. The web application redirects the browser to Red Hat's SSO.
  4. Red Hat's SSO responds with a 401 HTTP header: WWW-Authenticate: Negotiate

  5. The browser requests a Service Ticket (ST) from the Ticket Granting Service (TGS) part of the KDC and then responds back to Red Hat's SSO and passes the Authorization Negotiate header containing the SPNEGO token.
  6. Red Hat's SSO authenticates the user.
  7. The user is redirected back to the web application and is now allowed access to the protected content.

The source code for this tutorial can be found here: https://github.com/torbjorndahlen/kerberos-keycloak-openshift (use branch rhblog) and here https://github.com/torbjorndahlen/keycloak-openshift-demo-nodejs (use branch rhblog).

Architecture

Figure 1 shows the architecture used in this example, where 3 hosts are being used:

  • Workstation: Contains the kinit client for user authentication to Active Directory and the browser for accessing the secured web application.
  • Opentlc.com: A Red Hat OpenShift cluster containing Red Hat's SSO with a kinit client and the secured web application.
  • Sso-demo.local: A Windows Server with Active Directory and KDC.
The architecture in this example consists of three hosts: Workstation, OpenShift cluster and Widows Server
Figure 1: Architecture with three hosts
Figure 1: Architecture with three hosts.

In this example, the user at some point before trying to access the web app, obtains a TGT using Kinit on the workstation to authenticate to Active Directory. When accessing the web application from the browser, the TGT is used to obtain a ST from the TGS to authenticate to Red Hat's SSO.

The secured web application and Red Hat's SSO with a kinit client are both deployed on OpenShift with in the domain opentlc.com.

The Windows Server contains Active Directory and the KDC in the domain sso-demo.local.

Create a user in Active Directory

Active Directory Domain Services are required for default Kerberos implementations within the domain or forest. The KDC is integrated with Windows Server security services that run on the domain controller. The KDC uses the domain's Active Directory Domain Services database as its security account database.

In this example, I'm using the domain sso-demo.local. A local user called adminuser will be used to authenticate to Active Directory from the workstation later.

In order for clients on the workstation and on OpenShift to access the KDC, the firewall protecting the Windows Server where Active Directory is deployed  needs to allow UDP requests to the port the KDC is running. (The default is port 88; other ports may be specified in the KDC's kdc.conf file.).

Figure 2 shows how to create a user in Active Directory. The user sso-sso-demo in the domain sso-demo.local will be representing the host where Red Hat's single sign-on technology is deployed.

Create a user representing the RHSSO host in Active Directory
Figure 2: Create a user in Active Directory
Figure 2: Create a user in Active Directory.

Configure encryption types allowed for Kerberos

In order to access Kerberos from the kinit client, the allowed encryption types needs to be configured. This configuration is located in Local Security Policy Local Policies Security Options Network Security Configure encryption types allowed for Kerberos.

After selecting this configuration property, the allowed encryption types can be configured as is shown in Figure 3.

Allowed encryption types
Figure 3: Select allowed encryption types for Kerberos
Figure 3: Select allowed encryption types for Kerberos.

These settings should match the settings in the configuration file used for kinit (krb5.conf) on the workstation and in the SSO container on OpenShift. In this example I've only allowed AES256_HMAC_SHA1.

Generate a keytab

A Kerberos keytab file is used by Red Hat's SSO to enable Kerberos based user federation. Before the keytab is generated, the Service Principal Name (SPN) for  Active Directory User Account must be set. Go back to the Windows Server admin console and run the following command:

C:\> setspn -S HTTP/sso-sso-demo.apps.<your domain>.opentlc.com sso-sso-demo

The Service Principal Name (SPN) must include the protocol (HTTP in this example) and also the full route to Red Hat's SSO, and be mapped to the previously created user in AD.

On Windows Server ktpass.exe is used to create the keytab. Use ktpass -? to list all available options.

The SPN together with the Kerberos realm SSO-DEMO.LOCAL is used as principal when generating the keytab for SSO. Note that the value of the -mapUser parameter must be the full name of the user as registered in AD.

C:\> ktpass -out sso-sso-demo.keytab -princ HTTP/sso-sso-demo.apps.<your domain>.opentlc.com@SSO-DEMO.LOCAL -mapUser sso -mapOp set -pass my_password -crypto AES256-SHA1 -ptype KRB5_NT_PRINCIPAL

Copy the keytab file (in this example: sso-sso-demo.keytab) to the workstation from where you will deploy Red Hat's SSO on OpenShift—for example, using Remote Desktop.

On the workstation, after copying, inspect the contents of the keytab file:

% ktutil -k sso-sso-demo.keytab list
sso-sso-demo.keytab:

Vno  Type                     Principal                                                                         

  6  aes256-cts-hmac-sha1-96  HTTP/sso-sso-demo.apps.<your domain>.opentlc.com@SSO-DEMO.LOCAL 

Deploy SSO on OpenShift

Create a project to contain the Red Hat SSO instance:

% oc new-project sso-demo

Add the view role to the default service account:

% oc policy add-role-to-user view system:serviceaccount:$(oc project -q):default

To generate an image puller secret to allow OpenShift to pull images from registry.redhat.io, you need to create a registry server account and the create a secret for OpenShift. The secret will look similar to:

apiVersion: v1
kind: Secret
metadata:
  name: 10000000---my--pull-secret
data:
  .dockerconfigjson: nnnn....
type: kubernetes.io/dockerconfigjson

Then create the secret in OpenShift:

% oc create -f my_user--secret.yaml -n sso-demo

In order to connect to KDC, Red Hat's SSO needs to be deployed with kinit running in the same container. To achieve this, I'm creating a custom image for SSO where kinit and the krb5.conf file are added. In addition, the previously created keytab is also included in the image.

Figure 4 shows a Dockerfile that was used to build the modified SSO image.

Dockerfile for building a custom RHSSO image also containing kinit and the keytab
Figure 4: Dockerfile
Figure 4: Dockerfile.

The path to the krb5.conf configuration file for kinit is included in the Dockerfile. The file krb5.conf contains configuration for kinit and includes the Kerberos realm defined in Active Directory. In this case, the domain in Active Directory is sso-demo.local and the Kerberos realm is SSO-DEMO.LOCAL. The allowed encryption types should be listed in krb5.conf. In the file https://github.com/torbjorndahlen/kerberos-keycloak-openshift/blob/rhblog/krb5.conf you'll find an example configuration that you should modify to fit your environment.

Also the keytab file generated for Red Hat's SSO in Windows Server is included in the Dockerfile. Finally, the krb5-workstation tools (kinit, klist, ktutils, etc.) are also included. 

Using this Dockerfile, a custom image containing Red Hat's SSO and krb5-workstation is built to deploy kinit in the same container as SSO.

% oc new-build https://github.com/torbjorndahlen/kerberos-keycloak-openshift#rhblog \
> --strategy=docker \
> --name='sso76-openshift-rhel8-krb5'

In this example, I'm using one of the standard Red Hat single sign-on templates including support for HTTPS to deploy the custom SSO image. The template is imported to OpenShift as follows:

% oc replace -n openshift --force -f \
> https://raw.githubusercontent.com/jboss-container-images/redhat-sso-7-openshift-image/sso76-dev/templates/reencrypt/ocp-4.x/sso76-ocp4-x509-https.json

Red Hat's SSO is then deployed using the template with the standard image from registry.redhat.io.

% oc new-app --template=sso76-ocp4-x509-https \
> -p APPLICATION_NAME=sso \
> -p SSO_ADMIN_USERNAME=admin  \
> -p SSO_ADMIN_PASSWORD=secret \
> -p IMAGE_STREAM_NAMESPACE=sso-demo

In order to use the custom image, the deployment config is patched as follows:

% oc patch dc/sso --type=json -p '[{"op": "replace", "path": "/spec/triggers/0/imageChangeParams/from/name", "value": "sso76-openshift-rhel8-krb5:latest"}]'
% oc rollout latest dc/sso

Verify that the correct route has been created:

% oc get route
NAME   HOST/PORT                                                      PATH   SERVICES   PORT    TERMINATION   WILDCARD
sso    sso-sso-demo.apps.<your domain>.opentlc.com          sso        <all>   reencrypt     None

Verify that the hostname for Red Hat's SSO is the same as was used to create the sso-sso-demo SPN in AD.

Configure Red Hat's SSO

Once deployed, Red Hat's SSO needs to be configured to be able to authenticate users stored in Active Directory with Kerberos.

To start with, we create a realm. Open the SSO Admin console and create a new realm sso-demo. You can also import the realm from https://github.com/torbjorndahlen/kerberos-keycloak-openshift (use branch rhblog).

In order to work with Kerberos, Red Hat's SSO needs to have User Federation with Active Directory set up. Figure 5 shows an example how to set up a federation wit h Active Directory.

Configuration of Active Directory as federation provider in RHSSO
Figure 5: Configure the user federation provider in RHSSO
Figure 5: Configure the user federation provider in Red Hat's SSO.

Since we're using Kerberos, the Kerberos integration also needs to be configured.

Figure 6 shows the configuration with the Kerberos realm as stated in the krb5.conf file.

Configuration of Kerberos integration in RHSSO
Figure 6: Kerberos integration configuration
Figure 6: Kerberos integration configuration.

The SPN created in Active Directory and the location of the keytab file generated for Red Hat's SSO are also included in the configuration. In this example, the secured web application is a simple Node.js application that is deployed on OpenShift.

Figure 7 shows how to create an OIDC client in Red Hat's SSO for the secured web application.

Configuration of the OIDC client representing the secured web app in RHSSO
Figure 7: OIDC client configuration
Figure 7: OIDC client configuration.

Finally, because Kerberos authentication is disabled by default in Red Hat's SSO, it needs to be enabled. You'll find the setting for Kerberos under the Authentication menu in the SSO admin console.

Deploy the web application

The web application is a simple Node.js application that has a /public and a /secured endpoint.

To configure the Node.js application to use the OIDC client in Red Hat's SSO, create a fork of the git repo in

https://github.com/torbjorndahlen/keycloak-openshift-demo-nodejs

Copy the Installation file from the client configuration page in the SSO admin console created from the Node.js application and replace the existing keycloak.json file, and push the changes to the Git repo:

% git clone https://github.com/<your_git_user>/keycloak-openshift-demo-nodejs
% git checkout rhblog

Replace the keycloak.json file and commit the changes:

% git commit -a -m"Updated keycloak.json"
% git push origin rhblog

Build and deploy the Node.js application on OpenShift as follows:

% oc new-app https://github.com/torbjorndahlen/keycloak-openshift-demo-nodejs#rhblog

Then, create a Route to the Node.js app:

% oc expose svc/keycloak-openshift-demo-nodejs

Test the route by calling the public endpoint:

% curl https://keycloak-openshift-demo-nodejs-sso-demo.apps.<your domain>.opentlc.com/public

You should see the response:

{"message":"public"}

Making a call to the secured endpoint should redirect you to Red Hat's SSO:

% curl http://keycloak-openshift-demo-nodejs-sso-demo.apps.<your domain>.opentlc.com/secured
Found. Redirecting to https://sso-sso-demo.apps.<your domain>.opentlc.com/auth/realms/sso-demo/protocol/openid-connect/auth?client_id=service-nodejs&state=35b0f9a0-2e37-483a-84e0-6e421e7bd439&redirect_uri=http%3A%2F%2Fkeycloak-openshift-demo-nodejs-sso-demo.apps.<your domain>.opentlc.com%2Fsecured%3Fauth_callback%3D1&scope=openid&response_type=code

Set up workstation

Finally, the workstation used to authenticate to Active Directory over Kerberos and running the browser to access the secured web application should be configured to send authentication requests to the KDC.

Use the same krb5.conf file that was deployed in Red Hat's SSO to configure kinit on the workstation by copying the krb5.conf file to /etc/krb5.conf for clients on Linux or Mac and C:\windows\krb5.ini for clients running on Windows.

Verify that your kinit set up is correct by logging in to the KDC running on Windows Server:

% kinit adminuser@SSO-DEMO.LOCAL
adminuser@SSO-DEMO.LOCAL's password: 

Verify that a Kerberos TGT has been created using klist:

% klist
Credentials cache: API:C19459DA-A441-41C3-A51D-EFB78F8ED772
        Principal: adminuser@SSO-DEMO.LOCAL
Issued                Expires               Principal
Jul 20 10:00:17 2023  Jul 20 20:00:12 2023  krbtgt/SSO-DEMO.LOCAL@SSO-DEMO.LOCAL

Next, the web browser must be configured to allow SPNEGO to authenticate to the secured web application. Figure 8 shows an example of enabling SPNEGO in Firefox.

Enable SPNEGO in Firefox
Figure 8: Add trusted URI for SPNEGO
Figure 8: Add trusted URI for SPNEGO.

The domain where the web application is deployed is listed at network.negotiate-auth.trusted-uris, starting with a dot and then the domain name. This is a comma separated list and more trusted domains can be added here.

Finally, use the browser to navigate to the /secured endpoint in the web app, you should see the following response: 

{"message":"secured"}

Troubleshooting

  • Verify that the krb5.conf file is located in the correct place on the workstation and contains the same information as in the SSO container.
  • Check that Kerberos is enabled as authentication method for the realm in Red Hat's SSO.
  • Your keycloak.json needs to contain the configuration for your secured web application client in Red Hat's SSO.
  • Make sure a ticket is actually created on the workstation after kinit. Use klist to verify.
  • Verify that the domain_realm in krb5.conf points to the domain where the secured web application is running, both on the workstation and in the SSO container.
  • The browser should be configured to send SPNEGO ticket to the domain where Red Hat's SSO is running. In Firefox, it's network.negotiate-auth.trusted-uris = <RHSSO-domain>. Use [dot][domain-name] in a comma separated list.
  • Check the SSO log for unsupported encryption type, you might need to restrict the allowed encryption types in Active Directory and make the corresponding change in krb5.conf.

References