Single sign-on

Single sign-on (Kerberos)

Kerberos is an authentication protocol. It is the default authentication protocol in Windows. The Single Sign-On (SSO) module integrates with Kerberos to let users sign in to an HTTP application with Windows Active Directory credentials. Kerberos doesn’t typically go over HTTP, but an extension to the HTTP protocol, RFC 4559, adds a way to negotiate the protocol via the Www-Authenticate and Authorization HTTP headers. Then Kerberos tickets (identity tokens) are exchanged over the network by wrapping them inside Simple and Protected Negotiate (SPNEGO) tokens, defined by RFC 4178.

HAProxy Enterprise, being in front of the HTTP applications for which you want to enable single sign-on, validates the user’s Kerberos ticket and grants them access. Therefore, the applications themselves do not need to implement Kerberos directly.

To implement single sign-on using Kerberos, we will complete the following steps:

  1. Configure Windows Server.
  2. Configure HAProxy Enterprise.
  3. Configure the user’s PC.

Configure Windows Server Jump to heading

In this section, you will configure Windows Server for Kerberos authentication.

Prerequisites Jump to heading

On Windows Server, please check that the following prerequisites have been met:

  • You’ve installed the Active Directory Domain Services role.
  • You’ve promoted the Windows Server instance to be a domain controller. A domain controller is a Windows Server where you administer Active Directory.
  • You’ve created an Active Directory domain. In this guide, we will use the domain example.com.
  • You’ve installed the DNS Server role.

Create a user account and SPN Jump to heading

With Kerberos, every service gets a unique Service Principal Name (SPN). An SPN identifies a service running on a host. Because HAProxy Enterprise will act as a proxy in front of the applications, we will assign the SPNs to it. In Windows Server, we will create a user account in Active Directory to represent the load balancer and then assign to it an SPN for each load balanced web app. It might seem odd to assign a user account to a load balancer, but this is simply where we will map which services the load balancer can accept authorization requests for. By doing this, we are able to generate a keytab file, which we will store on the load balancer. When a user accesses the service via the load balancer, they present a ticket that the load balancer must decrypt with its keytab to prove that it is authorized by Active Directory.

  1. Log in to your AD domain controller.

  2. Open PowerShell and create a new user with the New-ADUser command. Note that the backtick is the word-wrap operator, allowing you to split the command onto multiple lines. Change the password used in the example:

    powershell
    New-ADUser `
    -Name loadbalancer1 `
    -Description "Load balancer 1 service account" `
    -AccountPassword (ConvertTo-SecureString "P@ssword1" -AsPlainText -Force) `
    -PasswordNeverExpires $true `
    -KerberosEncryptionType AES256 `
    -Enabled $true
    powershell
    New-ADUser `
    -Name loadbalancer1 `
    -Description "Load balancer 1 service account" `
    -AccountPassword (ConvertTo-SecureString "P@ssword1" -AsPlainText -Force) `
    -PasswordNeverExpires $true `
    -KerberosEncryptionType AES256 `
    -Enabled $true
  3. Create a keytab file for the account by calling the ktpass command from PowerShell. The command does two things:

    • Maps the Service Principal Name HTTP/webapp.example.com@EXAMPLE.COM to the AD user account example\loadbalancer1. The load balancer is then authorized to proxy requests for the service webapp.example.com.
    • Generates a keytab file at C:\loadbalancer1.keytab, which contains the service’s key. After you’ve copied this file to the load balancer, the load balancer will be able to decrypt tickets.
    powershell
    ktpass /out C:\loadbalancer1.keytab /princ HTTP/webapp.example.com@EXAMPLE.COM /mapUser example\loadbalancer1 /crypto AES256-SHA1 /pType KRB5_NT_PRINCIPAL /pass +rndPass
    powershell
    ktpass /out C:\loadbalancer1.keytab /princ HTTP/webapp.example.com@EXAMPLE.COM /mapUser example\loadbalancer1 /crypto AES256-SHA1 /pType KRB5_NT_PRINCIPAL /pass +rndPass
    output
    text
    Targeting domain controller: windowsserver.example.com
    Using legacy password setting method
    Successfully mapped HTTP/webapp.example.com to loadbalancer1.
    Key created.
    Output keytab to C:\loadbalancer1.keytab
    Keytab version: 0x502
    keysize 90 HTTP/webapp.example.com@EXAMPLE.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x12 (AES256-SHA1) keylength 32
    output
    text
    Targeting domain controller: windowsserver.example.com
    Using legacy password setting method
    Successfully mapped HTTP/webapp.example.com to loadbalancer1.
    Key created.
    Output keytab to C:\loadbalancer1.keytab
    Keytab version: 0x502
    keysize 90 HTTP/webapp.example.com@EXAMPLE.COM ptype 1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x12 (AES256-SHA1) keylength 32

    In this example:

    Field Description
    /out The keytab to produce.
    /princ The principal name (HTTP/FQDN@REALM).
    /mapUser Maps the principal to the given user account.
    /crypto The cryptosystem to use. AES256-SHA1 means AES256-CTS-HMAC-SHA1-96.
    /pType The principal type. KRB5_NT_PRINCIPAL is the general, recommended ptype.
    /pass Sets the password.
    +rndPass Generates a random password.

    View the Service Principal Name

    To view a user account’s Service Principal Name:

    1. Open Server Manager and go to Tools > Active Directory Users and Computers.
    2. In the Active Directory Users and Computers window, open the View menu and enable Advanced Features.
    3. Expand your domain, select Users, and then double-click the user to view its properties. In our example, we created a user named loadbalancer1.
    4. On the user’s properties, select the Attribute Editor tab.
    5. Scroll to the servicePrincipalName to see its value. In our example, the value is HTTP/webapp.example.com.

Add a DNS record Jump to heading

Add a DNS record for your web app, pointed at your load balancer.

  1. On your AD domain controller, go to Server Manager > Tools > DNS.

  2. Expand Forward Lookup Zones in the tree, right-click on your domain (for example, example.com) and select New Host (A or AAAA).

  3. On the New Host dialog, set the name. For example, webapp.

  4. Set the IP address. For example, use your load balancer’s IP address.

  5. Click Add Host.

    You should see a dialog that says “The host record was successfully created”.

Optional: Disable NTLM Jump to heading

By disabling NTLM altogether you can make your network safer and eliminate the chance that the user’s browser and Windows Server will try to use it.

NTLM is an older and less secure protocol than Kerberos that runs in some Windows Active Directory domains. To authenticate with Kerberos over HTTP, the user’s web browser will exchange a series of messages with Windows Server to negotiate which authentication protocol to use. This is how the Www-Authenticate: Negotiate HTTP header works. Though after this negotiation, the client and server may try to use NTLM instead of Kerberos.

If you see the following error in the SSO debug logs, then try disabling NTLM:

GSS-API error gss_accept_sec_context: An unsupported mechanism was requested

To disable NTLM:

  1. On your AD domain controller, go to Server Manager > Tools > Group Policy Management.
  2. Expand your forest and the domains folder in the Group Policy Management tree.
  3. If you do not yet have a group policy object, right-click on your domain and choose Create a GPO in this domain, and Link it here. Assign the new GPO a name.
  4. Expand your domain, right-click your new policy object, and choose Edit. The Group Policy Management Editor appears.
  5. Under Computer Configuration > Policies > Windows Settings > Security Settings > Local Policies > Security Options, double click the setting named Network security: Restrict NTLM: NTLM authentication in this domain. Set its value to Disable.
  6. Optional: To speed up propagation of the new setting, on the target PC joined to the domain run PowerShell as an administrator and execute the command gpupdate /force.

Configure HAProxy Enterprise Jump to heading

In this section, you will configure HAProxy Enterprise for Kerberos authentication.

Install and configure the module Jump to heading

Perform these steps on the load balancer node.

  1. Install the SSO module according to your platform:

    nix
    sudo apt-get install hapee-extras-spoa-sso
    nix
    sudo apt-get install hapee-extras-spoa-sso
    nix
    sudo yum install hapee-extras-spoa-sso
    nix
    sudo yum install hapee-extras-spoa-sso
    nix
    sudo zypper install hapee-extras-spoa-sso
    nix
    sudo zypper install hapee-extras-spoa-sso
    nix
    sudo pkg install hapee-extras-spoa-sso
    nix
    sudo pkg install hapee-extras-spoa-sso

    It installs the following files:

    File Description
    /opt/hapee-extras/bin/hapee-krb-srv Defines the SSO Kerberos service program.
    /etc/hapee-extras/hapee-krb-srv-spoe.cfg Defines how the load balancer passes data via the Stream Processing Offload Protocol to the SSO Kerberos service. Typically, you will not need to edit this file.
    /etc/hapee-extras/hapee-krb-srv.cfg Gives an example portion of an HAProxy Enterprise load balancer configuration that you can copy-paste.
    /etc/hapee-extras/hapee-sso-spoe.cfg Not used in this setup.
    /etc/hapee-extras/hapee-sso.cfg Not used in this setup.
    /etc/hapee-extras/sample/krb-srv.ini Sets the location of your keytab file for each application.
    /etc/hapee-extras/sample/krb-srv.map Maps HTTP Host headers to applications.
    /etc/hapee-extras/sample/sso.ini Not used in this setup.
    /etc/hapee-extras/sample/sso.map Not used in this setup.
    /etc/default/hapee-extras-sso Defines the default settings for the hapee-extras-spoa-krb-srv service. The file sets the KRBSRV_OPTIONS environment variable.
  2. Copy the keytab file from Windows Server to the load balancer. For example, save it as /etc/hapee-extras/keytabs/loadbalancer1.keytab.

    nix
    sudo mkdir -p /etc/hapee-extras/keytabs
    sudo cp ~/loadbalancer1.keytab /etc/hapee-extras/keytabs/
    nix
    sudo mkdir -p /etc/hapee-extras/keytabs
    sudo cp ~/loadbalancer1.keytab /etc/hapee-extras/keytabs/
  3. Set the owner of the keytab file:

    nix
    sudo chown hapee-sso:hapee /etc/hapee-extras/keytabs/loadbalancer1.keytab
    nix
    sudo chown hapee-sso:hapee /etc/hapee-extras/keytabs/loadbalancer1.keytab
  4. Set the mode of the keytab file:

    nix
    sudo chmod 600 /etc/hapee-extras/keytabs/loadbalancer1.keytab
    nix
    sudo chmod 600 /etc/hapee-extras/keytabs/loadbalancer1.keytab
  5. Copy the files krb-srv.ini and krb-srv.map from the sample directory to /etc/hapee-extras:

    nix
    sudo cp /etc/hapee-extras/sample/krb-srv.* /etc/hapee-extras/
    nix
    sudo cp /etc/hapee-extras/sample/krb-srv.* /etc/hapee-extras/
  6. Edit /etc/hapee-extras/krb-srv.ini. Add a domain section for your application and set the path to the keytab file. Also add an application section that references the domain section.

    krb-srv.ini
    ini
    [domain:webapp.example.com]
    krb_keytab_file = /etc/hapee-extras/keytabs/loadbalancer1.keytab
    [webapp]
    domain = webapp.example.com
    krb-srv.ini
    ini
    [domain:webapp.example.com]
    krb_keytab_file = /etc/hapee-extras/keytabs/loadbalancer1.keytab
    [webapp]
    domain = webapp.example.com
  7. Edit /etc/hapee-extras/krb-srv.map. Add a line that maps the HTTP Host header that you expect with the domain and app.

    krb-srv.map
    text
    # Host header # doman / app
    webapp.example.com webapp.example.com/webapp
    krb-srv.map
    text
    # Host header # doman / app
    webapp.example.com webapp.example.com/webapp
  8. Edit your main load balancer configuration file, /etc/hapee-3.0/hapee-lb.cfg.

    • Add the HA_SSO_SCOPE variable to the global section:

      haproxy
      global
      setenv HA_SSO_SCOPE "sess"
      haproxy
      global
      setenv HA_SSO_SCOPE "sess"
    • Add the SSO Kerberos lines to the frontend for which you want to enable single sign-on (be sure to update the crt argument specifying the certificate file path):

      hapee-lb.cfg
      haproxy
      frontend www
      bind :80
      bind *:443 crt /path/to/certs/cert.pem SSL
      http-request redirect scheme https unless { ssl_fc }
      http-response set-header Strict-Transport-Security max-age=63072000
      #--------------------------------
      # SSO Kerberos section
      #--------------------------------
      option http-buffer-request
      filter spoe engine spoe_krb_srv config /etc/hapee-extras/hapee-krb-srv-spoe.cfg
      # HTTP REQUEST
      # set sso_app and sso_domain variables before calling the SPOA
      http-request set-var(txn.sso_domain) req.hdr(Host),map(/etc/hapee-extras/krb-srv.map),word(1,'/')
      http-request set-var(txn.sso_app) req.hdr(Host),map(/etc/hapee-extras/krb-srv.map),word(2,'/')
      # set sess.sso_cookie variable with the cookie set by the browser
      http-request set-var(txn.sso_cookie) req.cook(sso_cookie) if { req.cook(sso_cookie) -m found }
      # The authorization header might be sent on the first request of a session,
      # depending on the value set in "HA_SSO_SCOPE" variable we could accept a request if
      # a previous request was allowed in the current session.
      http-request return status 401 hdr www-authenticate Negotiate unless { var("$HA_SSO_SCOPE.sso.action") -m found } || { req.hdr(authorization) -m found }
      http-request send-spoe-group spoe_krb_srv grp-sso-check-token
      http-request deny if { var("$HA_SSO_SCOPE.sso.action") -m str dny }
      http-request deny if ! { var("$HA_SSO_SCOPE.sso.action") -m str alw }
      # The agent can extract a display name from a validated Kerberos token
      http-request del-header X-SSO-LOGIN
      http-request set-header X-SSO-LOGIN %[var("$HA_SSO_SCOPE.sso.sso_login")] if { var("$HA_SSO_SCOPE.sso.sso_login") -m found }
      # If the SPOA has set "$HA_SSO_SCOPE.sso.www_authenticate" or "$HA_SSO_SCOPE.sso.sso_set_cookie" variables, we set the header accordingly
      http-response add-header Set-Cookie sso_cookie=%[var("$HA_SSO_SCOPE.sso.sso_set_cookie")];\ path=/;\ domain=%[var("$HA_SSO_SCOPE.sso.sso_set_cookie_domain")]; if { var("$HA_SSO_SCOPE.sso.sso_set_cookie") -m found }
      http-response add-header www-authenticate %[var("$HA_SSO_SCOPE.sso.www_authenticate")] if { var("$HA_SSO_SCOPE.sso.www_authenticate") -m found }
      # BACKENDS
      default_backend sso_err
      use_backend sso_err if { var("$HA_SSO_SCOPE.sso.action") -m str err } # we got an error from the SPOA
      use_backend %[var("$HA_SSO_SCOPE.sso.backend")] if { var("$HA_SSO_SCOPE.sso.action") -m str alw } { var("$HA_SSO_SCOPE.sso.backend") -m found }
      hapee-lb.cfg
      haproxy
      frontend www
      bind :80
      bind *:443 crt /path/to/certs/cert.pem SSL
      http-request redirect scheme https unless { ssl_fc }
      http-response set-header Strict-Transport-Security max-age=63072000
      #--------------------------------
      # SSO Kerberos section
      #--------------------------------
      option http-buffer-request
      filter spoe engine spoe_krb_srv config /etc/hapee-extras/hapee-krb-srv-spoe.cfg
      # HTTP REQUEST
      # set sso_app and sso_domain variables before calling the SPOA
      http-request set-var(txn.sso_domain) req.hdr(Host),map(/etc/hapee-extras/krb-srv.map),word(1,'/')
      http-request set-var(txn.sso_app) req.hdr(Host),map(/etc/hapee-extras/krb-srv.map),word(2,'/')
      # set sess.sso_cookie variable with the cookie set by the browser
      http-request set-var(txn.sso_cookie) req.cook(sso_cookie) if { req.cook(sso_cookie) -m found }
      # The authorization header might be sent on the first request of a session,
      # depending on the value set in "HA_SSO_SCOPE" variable we could accept a request if
      # a previous request was allowed in the current session.
      http-request return status 401 hdr www-authenticate Negotiate unless { var("$HA_SSO_SCOPE.sso.action") -m found } || { req.hdr(authorization) -m found }
      http-request send-spoe-group spoe_krb_srv grp-sso-check-token
      http-request deny if { var("$HA_SSO_SCOPE.sso.action") -m str dny }
      http-request deny if ! { var("$HA_SSO_SCOPE.sso.action") -m str alw }
      # The agent can extract a display name from a validated Kerberos token
      http-request del-header X-SSO-LOGIN
      http-request set-header X-SSO-LOGIN %[var("$HA_SSO_SCOPE.sso.sso_login")] if { var("$HA_SSO_SCOPE.sso.sso_login") -m found }
      # If the SPOA has set "$HA_SSO_SCOPE.sso.www_authenticate" or "$HA_SSO_SCOPE.sso.sso_set_cookie" variables, we set the header accordingly
      http-response add-header Set-Cookie sso_cookie=%[var("$HA_SSO_SCOPE.sso.sso_set_cookie")];\ path=/;\ domain=%[var("$HA_SSO_SCOPE.sso.sso_set_cookie_domain")]; if { var("$HA_SSO_SCOPE.sso.sso_set_cookie") -m found }
      http-response add-header www-authenticate %[var("$HA_SSO_SCOPE.sso.www_authenticate")] if { var("$HA_SSO_SCOPE.sso.www_authenticate") -m found }
      # BACKENDS
      default_backend sso_err
      use_backend sso_err if { var("$HA_SSO_SCOPE.sso.action") -m str err } # we got an error from the SPOA
      use_backend %[var("$HA_SSO_SCOPE.sso.backend")] if { var("$HA_SSO_SCOPE.sso.action") -m str alw } { var("$HA_SSO_SCOPE.sso.backend") -m found }
    • Ensure that the name of the backend for your application matches the name of the application section in krb-srv.ini. In our example, krb-srv.ini contained [webapp], so we would name the backend webapp:

      hapee-lb.cfg
      haproxy
      backend webapp
      balance roundrobin
      server web1 127.0.0.1:3000 check
      hapee-lb.cfg
      haproxy
      backend webapp
      balance roundrobin
      server web1 127.0.0.1:3000 check
    • Add the following two backend sections:

      hapee-lb.cfg
      haproxy
      backend sso_err
      mode http
      backend spoa-backend
      mode tcp
      timeout server 1m
      server sso-daemon 127.0.0.1:12345 inter 1s check
      hapee-lb.cfg
      haproxy
      backend sso_err
      mode http
      backend spoa-backend
      mode tcp
      timeout server 1m
      server sso-daemon 127.0.0.1:12345 inter 1s check
  9. Reload the load balancer:

    nix
    sudo systemctl reload hapee-3.0-lb
    nix
    sudo systemctl reload hapee-3.0-lb
  10. Enable and start the service:

    nix
    sudo systemctl enable hapee-extras-spoa-krb-srv
    sudo systemctl start hapee-extras-spoa-krb-srv
    nix
    sudo systemctl enable hapee-extras-spoa-krb-srv
    sudo systemctl start hapee-extras-spoa-krb-srv
  11. The service’s status should indicate that it is now configured for the new application.

    nix
    sudo systemctl status hapee-extras-spoa-krb-srv
    nix
    sudo systemctl status hapee-extras-spoa-krb-srv
    output
    text
    hapee systemd[1]: Starting hapee-extras-spoa-krb-srv.service - HAPEE SPOA-KRB-SRV: HAPEE SSO solution...
    hapee hapee-krb-srv[4955]: [00] Configured domains:
    hapee hapee-krb-srv[4955]: 1713465556.954921 [00] Configured domains:
    hapee hapee-krb-srv[4955]: 1713465556.954951 [00] domain:webapp.example.com (1 app)
    hapee hapee-krb-srv[4955]: [00] domain:webapp.example.com (1 app)
    hapee hapee-krb-srv[4956]: 1713465556.956454 [00] SSO Server will start in background, pid=4956
    hapee hapee-krb-srv[4956]: [00] SSO Server will start in background, pid=4956
    hapee systemd[1]: Started hapee-extras-spoa-krb-srv.service - HAPEE SPOA-KRB-SRV: HAPEE SSO solution.
    hapee HAProxy-sso[4956]: [00] SSO server is ready and listening on port 12345
    output
    text
    hapee systemd[1]: Starting hapee-extras-spoa-krb-srv.service - HAPEE SPOA-KRB-SRV: HAPEE SSO solution...
    hapee hapee-krb-srv[4955]: [00] Configured domains:
    hapee hapee-krb-srv[4955]: 1713465556.954921 [00] Configured domains:
    hapee hapee-krb-srv[4955]: 1713465556.954951 [00] domain:webapp.example.com (1 app)
    hapee hapee-krb-srv[4955]: [00] domain:webapp.example.com (1 app)
    hapee hapee-krb-srv[4956]: 1713465556.956454 [00] SSO Server will start in background, pid=4956
    hapee hapee-krb-srv[4956]: [00] SSO Server will start in background, pid=4956
    hapee systemd[1]: Started hapee-extras-spoa-krb-srv.service - HAPEE SPOA-KRB-SRV: HAPEE SSO solution.
    hapee HAProxy-sso[4956]: [00] SSO server is ready and listening on port 12345

Optional: Add a second application Jump to heading

To add another application to SSO:

  1. On your Windows Server, add a DNS record for the new application.

    • On your AD domain controller, go to Server Manager > Tools > DNS.
    • Expand Forward Lookup Zones in the tree, right-click on your domain (for example, example.com) and select New Host (A or AAAA).
    • On the New Host dialog, set the name. For example, webapp2.
    • Set the IP address. For example, use your load balancer’s IP address.
    • Click Add Host.
  2. To add more SPNs to the user account to support other web applications, use the /in argument when calling ktpass to append new SPNs:

    powershell
    ktpass /in C:\loadbalancer1.keytab /out C:\loadbalancer1.keytab /princ HTTP/webapp2.example.com@EXAMPLE.COM /mapUser example\loadbalancer1 /crypto AES256-SHA1 /pType KRB5_NT_PRINCIPAL /pass +rndPass
    powershell
    ktpass /in C:\loadbalancer1.keytab /out C:\loadbalancer1.keytab /princ HTTP/webapp2.example.com@EXAMPLE.COM /mapUser example\loadbalancer1 /crypto AES256-SHA1 /pType KRB5_NT_PRINCIPAL /pass +rndPass
    output
    text
    Existing keytab:
    keytab version: 0x502
    keysize 90 HTTP/webapp.example.com@EXAMPLE.COM pType 1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x12 (AES256-SHA1) keylength 32
    Targeting domain controller: windowsserver.example.com
    Using legacy password setting method
    Successfully mapped HTTP/webapp2.example.com to loadbalancer1.
    Key created.
    Output keytab to C:\loadbalancer1.keytab:
    Keytab version: 0x502
    keysize 90 HTTP/webapp.example.com@EXAMPLE.COM ptype1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x12 (AES256-SHA1) keylength 32
    keysize 91 HTTP/webapp2.example.com@EXAMPLE.COM ptype1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x12 (AES256-SHA1) keylength 32
    output
    text
    Existing keytab:
    keytab version: 0x502
    keysize 90 HTTP/webapp.example.com@EXAMPLE.COM pType 1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x12 (AES256-SHA1) keylength 32
    Targeting domain controller: windowsserver.example.com
    Using legacy password setting method
    Successfully mapped HTTP/webapp2.example.com to loadbalancer1.
    Key created.
    Output keytab to C:\loadbalancer1.keytab:
    Keytab version: 0x502
    keysize 90 HTTP/webapp.example.com@EXAMPLE.COM ptype1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x12 (AES256-SHA1) keylength 32
    keysize 91 HTTP/webapp2.example.com@EXAMPLE.COM ptype1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x12 (AES256-SHA1) keylength 32
  3. Copy the keytab file to the load balancer.

  4. Edit /etc/hapee-extras/krb-srv.ini. Add a domain section for your application and set the path to the keytab file. Also add an application section that references the domain section.

    krb-srv.ini
    ini
    [domain:webapp.example.com]
    krb_keytab_file = /etc/hapee-extras/keytabs/loadbalancer1.keytab
    [webapp]
    domain = webapp.example.com
    [domain:webapp2.example.com]
    krb_keytab_file = /etc/hapee-extras/keytabs/loadbalancer1.keytab
    [webapp2]
    domain = webapp2.example.com
    krb-srv.ini
    ini
    [domain:webapp.example.com]
    krb_keytab_file = /etc/hapee-extras/keytabs/loadbalancer1.keytab
    [webapp]
    domain = webapp.example.com
    [domain:webapp2.example.com]
    krb_keytab_file = /etc/hapee-extras/keytabs/loadbalancer1.keytab
    [webapp2]
    domain = webapp2.example.com
  5. Edit /etc/hapee-extras/krb-srv.map. Add a line that maps the HTTP Host header that you expect with the domain and app.

    krb-srv.map
    text
    # Host header # doman / app
    webapp.example.com webapp.example.com/webapp
    webapp2.example.com webapp2.example.com/webapp2
    krb-srv.map
    text
    # Host header # doman / app
    webapp.example.com webapp.example.com/webapp
    webapp2.example.com webapp2.example.com/webapp2
  6. Restart the SSO Kerberos service:

    nix
    sudo systemctl restart hapee-extras-spoa-krb-srv
    nix
    sudo systemctl restart hapee-extras-spoa-krb-srv

    The service’s status should indicate that it is now configured for the new application.

    nix
    sudo systemctl status hapee-extras-spoa-krb-srv
    nix
    sudo systemctl status hapee-extras-spoa-krb-srv
    output
    text
    hapee hapee-krb-srv[6283]: 1713397718.771858 [00] Configured domains:
    hapee hapee-krb-srv[6283]: 1713397718.771899 [00] domain:webapp.example.com (1 app)
    hapee hapee-krb-srv[6283]: 1713397718.771905 [00] domain:webapp2.example.com (1 app)
    output
    text
    hapee hapee-krb-srv[6283]: 1713397718.771858 [00] Configured domains:
    hapee hapee-krb-srv[6283]: 1713397718.771899 [00] domain:webapp.example.com (1 app)
    hapee hapee-krb-srv[6283]: 1713397718.771905 [00] domain:webapp2.example.com (1 app)
  7. Edit your main load balancer configuration file, /etc/hapee-3.0/hapee-lb.cfg. Ensure that the name of the backend for your application matches the name of the application section in krb-srv.ini. For example, webapp2:

    hapee-lb.cfg
    haproxy
    backend webapp2
    balance roundrobin
    server web1 127.0.0.1:3001 check
    hapee-lb.cfg
    haproxy
    backend webapp2
    balance roundrobin
    server web1 127.0.0.1:3001 check
  8. Reload the load balancer:

    nix
    sudo systemctl reload hapee-3.0-lb
    nix
    sudo systemctl reload hapee-3.0-lb

Configure user PCs Jump to heading

In this section, you will configure users’ Windows PCs for Kerberos authentication.

Update DNS and AD settings Jump to heading

To allow single sign-on in the AD domain, update the user’s PC to use the domain’s DNS server and, optionally, join the computer to the domain.

  1. Update the user’s preferred DNS server to point to Windows Server. That way, they can access internal subdomains like webapp.example.com. On the user’s PC:

    • On the Desktop, click the Start button. Enter Control Panel and press Enter.

    • Navigate to Network and Internet and bring up the properties for your network adapter:

      • Click Network and Sharing Center.
      • Click Change adapter settings.
      • Right-click on your adapter (for example, Ethernet).
      • Select Properties.
      • Click Advanced network settings.
      • Click on your adapter.
      • Next to More adapter options, click Edit.
    • On the adapter’s Properties dialog, double-click Internet Protocol Version 4 (TCP/IPv4) to open the protocol’s properties.

    • Click Advanced, then select the DNS tab.

    • Under DNS server addresses, click Add, then enter the IP address of your Windows Server. Click Add.

    • Click OK to save the change.

    • To verify, open PowerShell and use nslookup to retrieve the IP address for your AD domain controller server. For example:

      powershell
      nslookup windowsserver.example.com
      powershell
      nslookup windowsserver.example.com
      output
      text
      Name: windowsserver.example.com
      Addresses: 192.168.56.60
      output
      text
      Name: windowsserver.example.com
      Addresses: 192.168.56.60
  2. Join the computer to the Active Directory domain.

When the user accesses the web application, for example webapp.example.com, through their browser, they will see a login prompt. They can enter their Active Directory credentials.

SSO Kerberos login prompt

Optional: Integrated authentication Jump to heading

To skip the login prompt and instead enable Microsoft Edge to use the credentials of the logged-in Windows user, enable integrated authentication. This allows the browser to use the credentials from the Windows operating system and skip being prompted. You will set this as a Group Policy so that it applies to all computers joined to the Active Directory domain.

  1. On the Active Directory domain controller, go to the Microsoft Edge website and click the link Download Windows 64-bit Policy to download the archive. Extract the contents of the archive.
  2. In Windows Explorer, navigate to the policies directory for your domain. For example, for the domain example.com, it would be C:\Windows\SYSVOL\sysvol\example.com\Policies. From there, create a directory named PolicyDefinitions.
  3. From the extracted archive, copy the contents of the MicrosoftEdgePolicyTemplates\windows\admx directory into the new PolicyDefinitions directory.
  4. Open Server Manager and go to Tools > Group Policy Management. Expand your forest and domain in the Group Policy Management tree.
  5. If you do not yet have a group policy object, right-click on your domain and choose Create a GPO in this domain, and Link it here. Assign the new GPO a name.
  6. Expand your domain, right-click your new policy object, and choose Edit. The Group Policy Management Editor appears.
  7. Under Computer Configuration > Policies > Administrative Templates, expand the new Microsoft Edge policy definition in the Group Policy Management Editor.
  8. Within HTTP Authentication, edit the setting named Configure list of allowed authentication servers, enable it, and set its value to your load balanced application. For example, webapp.example.com or *.example.com.
  9. Optional: If the setting Supported authentication schemes has been configured, then be sure its value includes negotiate.

To verify that the policy has been rolled out:

  1. On the user’s PC, run PowerShell and execute gpupdate /force.
  2. Open Microsoft Edge and go to edge://policy to see the applied policies. You should see the policy named AuthServerAllowlist set to your target hostname.

Troubleshooting Jump to heading

This section describes how to troubleshoot the SSO Kerberos service.

Validate the configuration Jump to heading

To validate your configuration file:

  • Call hapee-krb-srv with the --check-cfg flag:

    nix
    /opt/hapee-extras/bin/hapee-krb-srv -f /etc/hapee-extras/krb-srv.ini --check-cfg
    nix
    /opt/hapee-extras/bin/hapee-krb-srv -f /etc/hapee-extras/krb-srv.ini --check-cfg
    output
    text
    1713906912.716699 [00] Configured domains:
    1713906912.717762 [00] domain:webapp.example.com (1 app)
    1713906912.717880 [00] Configuration parsed successfully.
    1713906912.717897 [00] Exiting
    output
    text
    1713906912.716699 [00] Configured domains:
    1713906912.717762 [00] domain:webapp.example.com (1 app)
    1713906912.717880 [00] Configuration parsed successfully.
    1713906912.717897 [00] Exiting

Debug logs Jump to heading

The SSO module has startup flags that enable debug-level logs, which can help when diagnosing problems.

  1. Edit the file /etc/default/hapee-extras-sso and change the KRBSRV_OPTIONS line so that it includes one of the following debug flags:

    Flag Description
    --debug-spoe Shows information and events about the Stream Processing Offload Engine (SPOE), which is how the load balancer communicates with the SSO Kerberos service.
    --debug-spoe-variables Shows the SPOE variables that the load balancer passes to the SSO Kerberos service.
    --debug-sso Shows messages about the single sign-on flow, such as whether a client authenticated successfully.
    --debug-krb Show messages related to the load balancer processing the Kerberos protocol. Additional details are saved to a log file in the /tmp directory.
    --debug-krb-err Shows errors that occur while processing the Kerberos protocol.
    --debug-all Enables all debugging flags.
  2. Restart the SSO Kerberos service:

    nix
    sudo systemctl restart hapee-extras-spoa-krb-srv
    nix
    sudo systemctl restart hapee-extras-spoa-krb-srv

In the following sections, we show examples.

debug-spoe Jump to heading

Show information and events about the Stream Processing Offload Engine (SPOE), which is how the load balancer communicates with the SSO Kerberos service.

hapee-extras-sso
text
KRBSRV_OPTIONS="--uid hapee-sso --gid hapee --debug-spoe"
hapee-extras-sso
text
KRBSRV_OPTIONS="--uid hapee-sso --gid hapee --debug-spoe"

Use journalctl to show logs:

nix
sudo journalctl --follow --unit hapee-extras-spoa-krb-srv
nix
sudo journalctl --follow --unit hapee-extras-spoa-krb-srv
output
text
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [00] <1> New Client connection accepted and assigned to worker 01
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> read_frame_cb
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> New Frame of 129 bytes received
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Decode HAProxy HELLO frame
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Supported versions : 2.0
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> HAProxy maximum frame size : 16380
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> HAProxy capabilities : pipelining,async
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> HAProxy supports frame pipelining
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> HAProxy supports asynchronous frame
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> HAProxy engine id : 6fb22acf-84a8-47b2-bece-bfb60a8aa88a
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Encode Agent HELLO frame
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Agent version : 2.0
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Agent maximum frame size : 16380
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Agent capabilities :
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> write_frame_cb
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Frame of 54 bytes sent
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> read_frame_cb
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> New Frame of 2486 bytes received
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Decode HAProxy NOTIFY frame
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> STREAM-ID=10 - FRAME-ID=1 - unfragmented frame received - frag_len=0 - len=2486 - offset=7
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] Process frame messages : STREAM-ID=10 - FRAME-ID=1 - length=2479 bytes
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] Encode Agent ACK frame
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] STREAM-ID=10 - FRAME-ID=1
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> write_frame_cb
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Frame of 601 bytes sent
output
text
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [00] <1> New Client connection accepted and assigned to worker 01
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> read_frame_cb
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> New Frame of 129 bytes received
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Decode HAProxy HELLO frame
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Supported versions : 2.0
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> HAProxy maximum frame size : 16380
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> HAProxy capabilities : pipelining,async
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> HAProxy supports frame pipelining
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> HAProxy supports asynchronous frame
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> HAProxy engine id : 6fb22acf-84a8-47b2-bece-bfb60a8aa88a
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Encode Agent HELLO frame
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Agent version : 2.0
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Agent maximum frame size : 16380
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Agent capabilities :
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> write_frame_cb
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Frame of 54 bytes sent
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> read_frame_cb
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> New Frame of 2486 bytes received
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Decode HAProxy NOTIFY frame
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> STREAM-ID=10 - FRAME-ID=1 - unfragmented frame received - frag_len=0 - len=2486 - offset=7
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] Process frame messages : STREAM-ID=10 - FRAME-ID=1 - length=2479 bytes
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] Encode Agent ACK frame
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] STREAM-ID=10 - FRAME-ID=1
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> write_frame_cb
Apr 23 19:09:14 hapee HAProxy-sso[1118]: [01] <1> Frame of 601 bytes sent

debug-spoe-variables Jump to heading

Show the SPOE variables that the load balancer passes to the SSO Kerberos service.

hapee-extras-sso
text
KRBSRV_OPTIONS="--uid hapee-sso --gid hapee --debug-spoe-variables"
hapee-extras-sso
text
KRBSRV_OPTIONS="--uid hapee-sso --gid hapee --debug-spoe-variables"

Use journalctl to show logs:

nix
sudo journalctl --follow --unit hapee-extras-spoa-krb-srv
nix
sudo journalctl --follow --unit hapee-extras-spoa-krb-srv
output
text
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] SPOA argument: sso_app, size=6
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] SPOA argument: sso_domain, size=20
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] SPOA argument: sso_cookie, size=32
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] SPOA argument: method, size=3
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] SPOA argument: authorization, size=2338
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] ==> SPOE sso-check-token message: sso_domain=webapp.example.com sso_app=webapp sso_cookie=8a1200062d3b7222432f00b47ca051dd authorization=Negotiate YIIGzgYGKwYBBQUCoIIGwjCCBr6gMDAuBgkqhkiC9xIBAgIGCSqGSIb3EgECAgYKKwYBBAGCNwICHgYKKwYBBAGCNwICCqKCBogEggaEYIIGgAYJKoZIhvcSAQICAQBuggZvMIIGa6ADAgEFoQMCAQ6iBwMFACAAAACjggUAYYIE/DCCBPigAwIBBaEPGw1FWEFNUExFLkxPQ0FMoicwJaADAgECoR4wHBsESFRUUBsUd2ViYXBwLmV4YW1wbGUubG9jYWyjggS1MIIEsaADAgESoQMCAQSiggSjBIIEny5K5yHXp3e875oOqZOeTMpZFjOygrCXZcsPVgYZ9kuaaZynUMJIVIQVzwOh0w25bmJlJXLUA3zkuFXvOD9cFTjZbvye2PVSbwyQvRcPPQul0gHd+eJCnGiu6/DxR5UCfvtWmwCQTQ7YucFJHc5wPC1WW/2q6e3DTdTnVuFKdUL6HWDz7YL9OZKQD7Yfo7dsSYRiLc5Fd2/Z+I4exlk5taNYYU2LpPcXS4bZEDpnGRQqPpob7ZDh03IC7ZbJpIaUeZhzv82TEV37K7Su4hXdruFthH/jAcp0GLGC8jbALmClJyeX8KVd5cLnqYILtuZl+VBEr7ym+g64rYIk9ANbG6e9lI70zL0ZMQZBb9N78F/5fppzvK9S3uGblu0RD+6dNKWz8uVO3Df2ExOHxewxOVktynrSbzNBg6/m6ZN49muNXnoBTQbS4rvUj70oscLDK0MeB8ZzcE5CwjzAc1DIVKX6liDRcRXYcj1+01UFTIPhKwweh1Arcj/s925T+xFWjM+b0466p0oH1UDS1IyHxG/sEJqPCE3cEn7RWI4TmlB0Y21oRU3d2ShrO1PRuIKo39Okm28qLq/ieSEdU3a98+FL/xUtxTy1pqwJiJHwIm7duphgxFh+C4RRj4RpsWJcfZ/UPbEoeqlo6xisYXgIxL8IY+0O5gWOkSVHyn6HYdP3iJ9Q30eODenfbVQZpzo7qenYXnWlaOgMlV7PtZXU905Gt8NokVR022qU2IyIe+eF0afkyt1rkfJCDeKte4n47nC66lvVXsOsvWAtwHFHtvQOyiG3f78O4m2ZMv2SxHKA2hRmwF7Pyk34dAAK0RUnOhQvO675W1qY97w7NgYcHPivPv2kn7pMt/OzAdmU7TNUNlARDSRcuwUHZWEYG1LhCEHSWUDhDkYqkP286OEikIkfISu743kiz9fiLl2ojZot6J9wLy1KQlV9wnXG9koYkli9bHP832mswyXUU6VYoEPcZWjqmZwpd9EH9rGxgvkXTR6OY+7c9rFzr61iZTdR0tf42tnXqyzMNU6Bew6qm2xitZlB6saMOlUmllF4hvo56rz3tEk8K+Mu+dgaI3CrEwtnbJOcxaZeWMfnCvi3R6rs6Na0PbREECpveOx94W+LgtRjcFlF/dYjCZV436lP0XKPbLjhaxE8mu/lSWa0eEci7Yrn4izF9QeTM/okeFv+7gg4BB7HVRnK4tV62yoiJIcLleGgB2vH9Dgy1Qw4y6B0isB8hUzV+7OJaD+yQoCAp4EaDddYsjJn7EDJgqx1xZv1XfKZdBJEW1j9dZ/uJLHj2EaJZHSdyzTvZUI441cWBO3W1OEBmOiR7v0lGqpDYcRoUcRzIEimxjHYHhsEWOGkfAD952PvXIGFGMQRmsvMeudhlhqr9BNgnrOedNtaGN8kZAr6N3BREIFw4iS+Oa32/6uKm5YcRo2jZLgrrHzDIEf+VNllmFlLiFRm45kWYA0fvzxB23cXAvffM4A9J6LpjNwFeAMfApNYy7B4jSTHx5/+HFfEWNkFUjb5IPvUr5jll68Bw6y50A+7cW+vp2rlUPaQA7sZjnXLrkP4MHCkggFQMIIBTKADAgESooIBQwSCAT9zUk/z1QRXULlWoaR3DD+t3PLrN0UJOPW0YVKDRpOkNy5e5ZJ071RPVueC9GgcEnkqlGLillrddUpXnp8TgTwG+0Z847UwiwELmCxydxr2+XVM1hCYvLSvByJOfKJPqIcvvld4Xjpa7dt3Knon0x96GKw0go+aJhORM6tz4j/9V6p7Govd0WPE3tQAMyUeyoXu15CdcMAVHoa6Qbf0a5h/HB0nF0fr+yedNciBxJhkID//Ra48HjA4PJxZSf/WVEHIYpSouJqk4cZyzEzrWbOxjQMe6kHoFvEnV/RAF9O0pgv6WWvVg7TMzVWZD9Kn/Rl+ki59ZRcCEYGv7e7sueZuOmAvRuvq6fd+NEpv5RSFyxJtDxqEPUULierxTLZtBB7jnv+x22FbaFEyq0jg0y52s4nq6qlqp7ky6zXrvxGW
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=1 sso_set_cookie=2a9dd981820179a19cf5b8d4bae0862b; HttpOnly; Expires=Wed, 24 Apr 2024 05:14:48 GMT
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=1 sso_set_cookie_domain=
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=1 sso_cookie=2a9dd981820179a19cf5b8d4bae0862b
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=2 backend=webapp
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=2 sso_app=webapp
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=2 sso_domain=webapp.example.com
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=2 www_authenticate=Negotiate oYG1MIGyoAMKAQChCwYJKoZIgvcSAQICooGdBIGaYIGXBgkqhkiG9xIBAgICAG+BhzCBhKADAgEFoQMCAQ+ieDB2oAMCARKibwRtIZy7J4L1xwE98XQeAjHPqDg1V2qWDMvIyhh3x8abTBbgV9l8b69nm9XQ+3a0iVxDWB0Wuzdrm14ff2SeAmRI3WRcWV
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=2 sso_login=myuser@example.com
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=2 action=alw
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var int scope=2 status=0
output
text
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] SPOA argument: sso_app, size=6
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] SPOA argument: sso_domain, size=20
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] SPOA argument: sso_cookie, size=32
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] SPOA argument: method, size=3
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] SPOA argument: authorization, size=2338
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] ==> SPOE sso-check-token message: sso_domain=webapp.example.com sso_app=webapp sso_cookie=8a1200062d3b7222432f00b47ca051dd authorization=Negotiate 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
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=1 sso_set_cookie=2a9dd981820179a19cf5b8d4bae0862b; HttpOnly; Expires=Wed, 24 Apr 2024 05:14:48 GMT
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=1 sso_set_cookie_domain=
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=1 sso_cookie=2a9dd981820179a19cf5b8d4bae0862b
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=2 backend=webapp
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=2 sso_app=webapp
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=2 sso_domain=webapp.example.com
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=2 www_authenticate=Negotiate oYG1MIGyoAMKAQChCwYJKoZIgvcSAQICooGdBIGaYIGXBgkqhkiG9xIBAgICAG+BhzCBhKADAgEFoQMCAQ+ieDB2oAMCARKibwRtIZy7J4L1xwE98XQeAjHPqDg1V2qWDMvIyhh3x8abTBbgV9l8b69nm9XQ+3a0iVxDWB0Wuzdrm14ff2SeAmRI3WRcWV
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=2 sso_login=myuser@example.com
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var str scope=2 action=alw
Apr 23 19:14:48 hapee HAProxy-sso[1151]: [01] Encode SPOE set-var int scope=2 status=0

debug-sso Jump to heading

Show messages about the single sign-on flow, such as whether a client authenticated successfully.

hapee-extras-sso
text
KRBSRV_OPTIONS="--uid hapee-sso --gid hapee --debug-sso"
hapee-extras-sso
text
KRBSRV_OPTIONS="--uid hapee-sso --gid hapee --debug-sso"

Use journalctl to show logs:

nix
sudo journalctl --follow --unit hapee-extras-spoa-krb-srv
nix
sudo journalctl --follow --unit hapee-extras-spoa-krb-srv
output
text
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Process SPOE Message 'sso-check-token'
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] ==> Process SPOE Message 'sso-check-token' with 5 args. len left: 2462
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] ==> SPOE sso-check-token message
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Unexpected SPOA argument: method
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [00] Cleaning cookie contexts that have expired...
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [00] Purged 0/0 cookie contexts
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] In handle_authorization()
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Generating a new sso cookie...
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Created new cookie 23dfd3e13b171bc5310bb885db30e0e1
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Adding new domain context for domain webapp.example.com for cookie 23dfd3e13b171bc5310bb885db30e0e1. Expires=2024-04-24 05:17:57
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Storing SSO cookie 23dfd3e13b171bc5310bb885db30e0e1, expires on 2024-04-24 05:17:57
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] AUTH_RESULT=SUCCESS
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Setting action to SSO_ACTION_ALLOW
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Next action=alw msg=(null)
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] ======================= response frame encoded, 594 bytes
output
text
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Process SPOE Message 'sso-check-token'
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] ==> Process SPOE Message 'sso-check-token' with 5 args. len left: 2462
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] ==> SPOE sso-check-token message
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Unexpected SPOA argument: method
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [00] Cleaning cookie contexts that have expired...
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [00] Purged 0/0 cookie contexts
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] In handle_authorization()
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Generating a new sso cookie...
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Created new cookie 23dfd3e13b171bc5310bb885db30e0e1
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Adding new domain context for domain webapp.example.com for cookie 23dfd3e13b171bc5310bb885db30e0e1. Expires=2024-04-24 05:17:57
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Storing SSO cookie 23dfd3e13b171bc5310bb885db30e0e1, expires on 2024-04-24 05:17:57
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] AUTH_RESULT=SUCCESS
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Setting action to SSO_ACTION_ALLOW
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] Next action=alw msg=(null)
Apr 23 19:17:57 hapee HAProxy-sso[1177]: [01] ======================= response frame encoded, 594 bytes

debug-krb Jump to heading

Show messages related to the load balancer processing the Kerberos protocol. Additional details are saved to a log file in the /tmp directory. In the example below, the log file is named /tmp/krb_debug_webapp.example.com.log.

hapee-extras-sso
text
KRBSRV_OPTIONS="--uid hapee-sso --gid hapee --debug-krb"
hapee-extras-sso
text
KRBSRV_OPTIONS="--uid hapee-sso --gid hapee --debug-krb"

Use journalctl to show logs:

nix
sudo journalctl --follow --unit hapee-extras-spoa-krb-srv
nix
sudo journalctl --follow --unit hapee-extras-spoa-krb-srv
output
text
Apr 23 19:19:43 hapee HAProxy-sso[1207]: [00] Logging Kerberos operations to /tmp/krb_debug_webapp.example.com.log
Apr 23 19:19:43 hapee HAProxy-sso[1207]: [00] Token validated for client "myuser@example.com"
output
text
Apr 23 19:19:43 hapee HAProxy-sso[1207]: [00] Logging Kerberos operations to /tmp/krb_debug_webapp.example.com.log
Apr 23 19:19:43 hapee HAProxy-sso[1207]: [00] Token validated for client "myuser@example.com"

Then the file krb_debug_webapp.example.com.log in the /tmp directory contains additional details:

krb_debug_webapp.example.com.log
text
[1207] 1713899983.885398: Decrypted AP-REQ with server principal HTTP/webapp.example.com@EXAMPLE.COM: aes256-cts/5511
[1207] 1713899983.885399: AP-REQ ticket: myuser@EXAMPLE.COM -> HTTP/webapp.example.com@EXAMPLE.COM, session key aes256-cts/C099
[1207] 1713899983.885400: Negotiated enctype based on authenticator: aes256-cts
[1207] 1713899983.885401: Authenticator contains subkey: aes256-cts/A116
[1207] 1713899983.885402: Creating AP-REP, time 1713899983.12, subkey aes256-cts/D7DE, seqnum 255461910
[1257] 1713900224.105711: Decrypted AP-REQ with server principal HTTP/webapp.example.com@EXAMPLE.COM: aes256-cts/5511
[1257] 1713900224.105712: AP-REQ ticket: myuser@EXAMPLE.COM -> HTTP/webapp.example.com@EXAMPLE.COM, session key aes256-cts/D0FA
[1257] 1713900224.105713: Negotiated enctype based on authenticator: aes256-cts
[1257] 1713900224.105714: Authenticator contains subkey: aes256-cts/03DC
[1257] 1713900224.105715: Creating AP-REP, time 1713900223.18, subkey aes256-cts/9481, seqnum 1008188495
krb_debug_webapp.example.com.log
text
[1207] 1713899983.885398: Decrypted AP-REQ with server principal HTTP/webapp.example.com@EXAMPLE.COM: aes256-cts/5511
[1207] 1713899983.885399: AP-REQ ticket: myuser@EXAMPLE.COM -> HTTP/webapp.example.com@EXAMPLE.COM, session key aes256-cts/C099
[1207] 1713899983.885400: Negotiated enctype based on authenticator: aes256-cts
[1207] 1713899983.885401: Authenticator contains subkey: aes256-cts/A116
[1207] 1713899983.885402: Creating AP-REP, time 1713899983.12, subkey aes256-cts/D7DE, seqnum 255461910
[1257] 1713900224.105711: Decrypted AP-REQ with server principal HTTP/webapp.example.com@EXAMPLE.COM: aes256-cts/5511
[1257] 1713900224.105712: AP-REQ ticket: myuser@EXAMPLE.COM -> HTTP/webapp.example.com@EXAMPLE.COM, session key aes256-cts/D0FA
[1257] 1713900224.105713: Negotiated enctype based on authenticator: aes256-cts
[1257] 1713900224.105714: Authenticator contains subkey: aes256-cts/03DC
[1257] 1713900224.105715: Creating AP-REP, time 1713900223.18, subkey aes256-cts/9481, seqnum 1008188495

Do you have any suggestions on how we can improve the content of this page?