IBM Verify

 View Only

Creating a Lua-based authentication module with IBM Security Verify Access

By Scott Exton posted Tue December 12, 2023 04:34 PM

  

Introduction

For many years IBM Security Verify Access (ISVA) has provided an external authentication mechanism to allow customers to implement their own authentication logic.  Originally, while the product was sold as software, a customer could implement a C based authentication mechanism, referred to as a 'Cross-Domain Authentication Service' (CDAS), which was loaded directly into the process space of the WebSEAL instance.  In more recent times custom authentication information could be passed back to WebSEAL, from a Junctioned web server, through the 'External Authentication Interface' (EAI) as HTTP headers.  With the introduction of 'Lua' based HTTP transformation rules, we have reached the next evolution of custom authentication within WebSEAL.  You can now create a Lua based authentication module which resides within the WebSEAL process itself.

This blog article will walk you through the steps required to create your own Lua based authentication module.  Two examples will be provided, one which calls out to an LDAP server to authenticate the user and provide user data, and another which establishes an authenticated user session based on information contained within a client certificate.  The main advantage of the Lua based authentication module over a traditional EAI based application is that everything is hosted locally within WebSEAL and there is no need to host an additional application outside of WebSEAL.

Note: This article has been written for v10.0.7 of IBM Security Verify Access.  This release contains extensions to the Lua support which make the writing of a Lua based authentication module easier.

Pre-Requisites

As a pre-requisite to the steps contained in this article, you must have an ISVA environment which has been installed and configured (either the virtual appliance or containers), and a WebSEAL instance already created.

In addition to this, if you are following the example which performs authentication against an LDAP server, you must already have an LDAP server available.  If you are following the example which uses data from a client certificate to establish a user session the WebSEAL server must already be configured to accept the client certificate.

Lua Scripts

This article contains two simple Lua scripts, one which will perform authentication against a remote LDAP server, and another which will establish a user session based on data found within a client certificate.

LDAP Authentication

The following script is used to authenticate against a remote LDAP server and generate a user session which contains attributes from the LDAP server.  It makes use of the lualdap and urlencode Lua modules which are pre-installed with ISVA.  At a high level the authentication module will:

  1. Extract the username and password fields from the login form.
  2. Perform an authentication against the remote LDAP server by binding to the server.  
  3. Retrieve additional user attributes from the LDAP server.
  4. Establish a new user session with the provided 'external' username and attributes.

Note: The LDAP URL and DN within the script will need to be replaced with those that match your LDAP server environment.

local lualdap   = require "lualdap"
local urlencode = require "urlencode"
 
-- Connect to the LDAP server.  The LDAP URL which is being
-- used must be changed to map the LDAP URL of your LDAP
-- server.
local ld, err = lualdap.initialize("ldaps://ldap.ibm.com:636")
 
if (ld == nil) then
    Control.returnErrorPage(err)
    return
end
 
-- Retrieve the username and password fields from the request body.
local body = HTTPRequest.getBody()
if (body == nil) then
    Control.returnErrorPage("No POST data was found!")
    ld:close()
    return
end
 
local username = nil
local password = nil
 
for k, v in string.gmatch(body, "(%w+)=(%w+)") do
    if (k == "username") then
        username = urlencode.decode_url(v)
    elseif (k == "password") then
        password = urlencode.decode_url(v)
    end
end
 
if (username == nil or password == nil) then
    Control.returnErrorPage("The required form fields were not found!")
    ld:close()
    return
end
 
-- Construct our expected user DN.  Note that this must
-- be changed to match the DN of your LDAP server.
local userDn = string.format("cn=%s,o=ibm,c=us", username)
 
-- Authenticate to the LDAP server.
local rc, err = ld:bind_simple(userDn, password)
 
if (rc == nil) then
    Control.returnErrorPage(err)
    ld:close()
    return
end
 
-- Add the full user DN as an attribute.
Authentication.setAttribute("my_user_dn", userDn)
 
-- Grab any additional user attributes which should be stored in the
-- authenticated credential.  In this instance we are storing the 'sn'
-- attribute.
for dn, attribs in ld:search { base=userDn, scope="base", attrs="sn" } do
    for name, values in pairs (attribs) do
        if type(values) == "string" then
            local hdrname = "my_"..name
            Authentication.setAttribute(hdrname, values)
        end
    end
end
 
ld:close()
 
-- Set the authentication response data.
Authentication.setUserIdentity(username, true) 

Client Certificate Authentication

The following script is used to establish an authenticated user session from the fields contained in a provided client certificate.  At a high level the authentication module will:

  1. Construct the ISVA user identity by removing the suffix from the CN in the certificate.
  2. Establish a new user session with the provided ISVA username and attributes from the certificate.
-- Set the user identity from the CN, using the value up until the
-- first dot ('.').
cn = string.match(Client.getCertificateField("SubjectCN"), '^[^.]*')
Authentication.setUserIdentity(cn, false)

-- Add the serial number as an attribute.
Authentication.setAttribute("serial-number", Client.getCertificateField("SerialNumber"))

Configuration

Lua Script

In order to create the Lua script, using the LMI, select 'Web' and then 'HTTP Transformation'.  Click on 'New', and then name the script 'auth.lua', and ensure that you select 'Lua' as the template.  Click on 'OK' to create the template script.

 

Select the script, and then click on 'Edit' to edit the contents of the script.  Replace the current contents of the script with the desired script from this article, remembering to update the script to match your environment.  Click on 'Validate' to validate the contents of the script, and then click on 'OK' to save the script once it has been successfully validated.


Edit the WebSEAL configuration file (Web -> Reverse Proxy : Manage -> Configuration -> Edit Configuration File) and add the new Lua script to the WebSEAL configuration with the following configuration entries:

[http-transformations]
pkmslogin.lua = auth.lua

[http-transformations:pkmslogin.lua]
request-match = postazn:POST /pkmslogin.lua*
request-match = postazn:GET /pkmslogin.lua*

This will configure WebSEAL so that after the authorization stage the Lua script will be invoked for requests for the '/pkmslogin.lua' URL.

Click on the 'Save' button to save the changes.

EAI

A Lua based authentication module still uses the EAI capability of WebSEAL to generate the user session.  The main difference however between a Lua based authentication module and a traditional EAI application is that the authentication information is provided by the Lua script instead of being provided via HTTP headers.

The following configuration entries should be added to the WebSEAL configuration file, using the process for editing the configuration file as described in the previous step, to enable EAI authentication:

[eai]
eai-auth = https

[eai-trigger-urls]
trigger = /pkmslogin.lua*

[certificate]
eai-uri = %lua-eai%

The 'eai-uri' configuration entry is required for the client certificate based authentication so that WebSEAL knows that Lua is being used to provide the authentication information rather than an external application.

Policy

The ISVA policy should be modified so that unauthenticated access is allowed for the '/pkmslogin.lua' resource.  This policy update can be achieved using the LMI, or the 'pdadmin' command.  To use the LMI:

  • Open the 'policy administration' panel in the LMI (Web -> Policy Administration) and then authenticate as the 'sec_master' user.   
  • Select 'ACL' -> 'Search ACLs', and then search for the 'favicon' ACL. This ACL currently allows unauthenticated access and can be re-used.  However, you could also create your own ACL which allows unauthenticated access. 
  • Select the ACL, and then select the 'Attach' tab. 
  • Click on the 'Attach' button and set the protected object path to : '/WebSEAL/<hostname>-<instance-name>/pkmslogin.lua'. 

 

LDAP Trust

If you are using the LDAP authentication script you need to configure WebSEAL to trust the remote LDAP server.  This is achieved by editing the WebSEAL certificate key file, and adding the CA certificate of the remote LDAP server as a signer certificate.  In order to do this, via the LMI:

  1. Select 'System' -> 'SSL Certificates', select the WebSEAL certificate database (usually pdsrv) and then select 'Manage' -> 'Edit SSL Certificate Database'.
  2. On the 'Signer Certificates' tab, select 'Manage' -> 'Import' (if you have the CA certificate as a file), or 'Manage' -> 'Load' (if you want to load the CA certificate directly from the LDAP server).

You also need to specifically make the CA certificate available to the Lua scripts.  This is achieved by adding the following configuration entries to the WebSEAL configuration file, using the process for editing the configuration file as described earlier in this article:

[http-transformations:pkmslogin.lua]
lua-ldap-ca-cert-label = ldap.server

Notes:

  • The configuration value (ldap.server) must match the label of the CA certificate in the WebSEAL key database.
  • The hostname of the LDAP server is verified against the SAN and CN of the server certificate.  If this verification fails the Lua script will not be able to communicate with the LDAP server.

Triggering the Flow

You also need to ensure that the '/pkmslogin.lua' resource is requested when authentication is required.  The mechanism to do this is different based on whether you are using the LDAP authentication or client certificate authentication scripts.

LDAP Authentication

For the LDAP based authentication script you need to ensure that the login form, which contains the username and password, is posted to the '/pkmslogin.lua' resource.  The easiest way to do this is to modify the login.html file so that the form action is changed from '/pkmslogin.form' to '/pkmslogin.lua'.  In order to do this, via the LMI:

  1. Select 'Web' -> 'Reverse Proxy', select the WebSEAL instance and then select 'Manage' -> 'Management Root'. 
  2. Expand the 'management' and 'C' directories, select 'login.html' and then select 'File' -> 'Open'.
  3. Replace the '/pkmslogin.form' action with '/pkmslogin.lua' and then save the changes.

When the 'Control.returnErrorPage' function is called from a Lua script the 38b9a4b2.html error page is returned, and the %FAILREASON% macro is set to indicate the reason for the failure.  The 38b9a4b2.html error page should be created from the contents of the login.html file, and the '%ERROR%' macro should be replaced with '%FAILREASON%'.  

In order to do this, via the LMI:

  1. Select 'Web' -> 'Reverse Proxy', select the WebSEAL instance and then select 'Manage' -> 'Management Root'. 
  2. Expand the 'management' and 'C' directories, select 'login.html' and then select 'File' -> 'Open'.
  3. Copy the contents of 'login.html' to the clipboard and then close the file.
  4. Expand the 'errors' and 'C' directories, and then select 'File' -> 'New' -> 'File'.
  5. Copy the contents of login.html into the new file, and replace the '%ERROR%' string with '%FAILREASON%'.
  6. Specify '38b9a4b2.html' as the file name, and then click on 'Save'.

Client Certificate Authentication

For client certificate based authentication you need to enable the local response redirect capability so that login requests are redirected to the '/pkmslogin.lua' resource.  

The following configuration entries should be added to the WebSEAL configuration file, using the process for editing the configuration file as described earlier in this article, to enable local response redirect:

[acnt-mgt]
enable-local-response-redirect = yes

[local-response-redirect]
local-response-redirect-uri = [login] /pkmslogin.lua

Deployment

The final step is to commit the pending changes, publish the configuration (in a containerized environment), and then restart the WebSEAL server.  Once the changes have been applied to the WebSEAL server you should see the authentication Lua script being used to handle authentication when accessing a protected resource.

If there are any issues with the authentication process the Lua script can be modified to include 'print' statements to help debug the script.  The output from any 'print' statements will be sent to the log file for the WebSEAL server.  The official documentation also provides additional advice on how to test and debug a Lua script: https://www.ibm.com/docs/en/sva/10.0.7?topic=transformation-testing-rules.

Conclusion

This article has provided a brief overview on how to create a Lua based authentication module. The main advantage of a Lua based authentication module over a traditional EAI based application is that everything is hosted locally within WebSEAL and there is no need to host an additional application outside of WebSEAL.

0 comments
18 views

Permalink