With IBM Security Verify Access 10.0.4 came the possibility to use LUA scripting for HTTP Transformation rules, in addition to the existing XML HTTP Transformation rules.

This is a huge improvement!

Creating transformations using LUA scripts is a lot more intuitive than the XML transformation rules, and there’s also new possiblities coming with the LUA transformation rules.

You can now also modify the body of the http response, and it’s possible to connect to external sources during the execution of a transformation rule.

And there’s a test program available, so you can test your lua scripts before deploying them to WebSEAL (or IAG, for that matter) !

Documentation

The ISVA WebSEAL documentation itself is limited, but there’s more examples at these locations:

Lua transformation rule using http.requests

So let’s try this out.

This transformation rule demonstrates the concept of storing configuration for a LUA transformation rule in an external (or internal) webserver. This has offers the possibility to give control to a 3rd party to modify the rules on when the transformation has to execute (in this example : for which url patterns).

A bonus is that there’s no need to restart WebSeal when the configuration changes (like you would have to do when changing the actual transformation rule).

External configuration

So I’ve put a json file on a webserver ( a json array of dictionaries ).

[
{"url": "index.html"},
{"url": "testerdetest"} 
]

The redis-lua module is also available, so you could also store configuration in a Redis server in a similar vein.

The example below doesn’t actually do anything visibly, so you need to use the trace setting pdweb.http.transformation (minimum at 5), to look at the output in WebSEAL.

Transformation rule

You can edit the configuri parameter to point to your own local webserver. In any case, the file is available from this website too.

Save the script in the current directory as load_from_http.lua (for the test)

local request = require "http.request"
local cjson = require "cjson"
 
local configuri = "https://www.gwbasics.be/requestmatch.json"
local headers, stream = assert(request.new_from_uri(configuri):go())
 
if (headers == nil) or (not headers) then
    Control.returnErrorPage( "1. Could not load configuration data from: "..configuri )
    return
end
 
local body = assert(stream:get_body_as_string())
 
if headers:get ":status" ~= "200" then
    Control.returnErrorPage( "2. http error during loading of: "..configuri )
    return
end
 
if (body == nil) or (not body) then
    Control.returnErrorPage( "3. http error during loading of: "..configuri )
    return
end
 
Control.trace(5, string.format(body))
 
local thejsonbody = cjson.decode(body)
 
local req = HTTPRequest.getURL()
 
for i,v in ipairs(thejsonbody) do
 
    Control.trace(5, "Access url table element : "..v["url"])
 
    if string.find(req, v["url"]) then
        HTTPRequest.setHeader("X-LUA-Processed", "Tom Bosmans")
        Control.trace(5, "Found match "..req)
    end
end

The lua script reads the configuration data and then matches the current url of the request in WebSEAL with the url value. If there’s a match, a header X-LUA-Processed is added to be sent to the backend (useless, I know, but this merely shows the concept).

Update: lua-http does not specifically support TLS, and ssl enabled connections will only work when you have a certificate signed by a global public CA.

Test the rule

Prepare context.yaml

You can generate a context.yaml using dumpContext (in lua script), or by creating one yourself.

You can start from a generated context:

podman run --rm --entrypoint iag_lua_transformation_test icr.io/ibmappgateway/ibm-application-gateway:22.07 -t

This is the output, and you can edit it as you like.

http:
  request:
    method:   <method>
    url:      <url>
    version:  <version>
    protocol: <protocol>
    body:     <body>
    headers:
      - name:  <hdr-name>
        value: <hdr-value>
    cookies:
      - name:  <cookie-name>
        value: <cookie-value>
  response:
    version:  <version>
    body:     <body>
    status:
      code:    <code>
      message: <message>
    headers:
      - name:  <hdr-name>
        value: <hdr-value>
    cookies:
      - name:  <cookie-name>
        value: <cookie-value>
session:
  id:       <id>
  username: <username>
  credential:
    attributes:
      - name:  <attribute-name>
        value: <attribute-value>

This is the context.yaml I’m using for this test :

http:
  request:
    method: "GET"
    url:    "/junction1/index.html"
    headers:
      - name:  "User-Agent"
        value: "curl"
      - name:  "Host"
        value: "www.ibm.com"
credential:
  attribute:
    - name:  "AZN_CRED_PRINCIPAL_NAME"
      value: "testuser"

Save it in the current directory, together with the .lua file.

Run the test

You should now have 2 files in the current directory:

  • load_from_http.lua ( the actual lua script )
  • context.yaml (the webseal context)
podman run --rm -v ${PWD}:/lua_test:z --entrypoint iag_lua_transformation_test \
 icr.io/ibmappgateway/ibm-application-gateway:22.07 -c /lua_test/context.yaml -s /lua_test/load_from_http.lua

Note: in podman, the :z option in the volume mount seems necessary. The error message is not particularly helpful, but if you get the message below, it’s likely the volume mount files are not accessible by the container. DPWWA3301E A failure occurred while parsing the YAML file: /var/iag/staging/context.yaml (bad file) The reason is that the files actually should have system_u:object_r:container_file_t:s0 as selinux context. If you run the above podman command once with the :z option, the selinux context is updated and :z is no longer necessary.

If all goes well, you get this output:

Lua trace (level: 5): [
{"url": "index.html"},
{"url": "testerdetest"} 
]

Lua trace (level: 5): Access url table element : index.html
Request header: set (X-LUA-Processed,Tom Bosmans)
Lua trace (level: 5): Found match /gwbasics/index.html
Lua trace (level: 5): Access url table element : testerdetest

Interactive

If you still run into problems, you can also run the iag_lua_transformation_test command interactively:

podman run --rm -it --entrypoint bash icr.io/ibmappgateway/ibm-application-gateway:22.07
[ivmgr@76f3f6ef8a69 /]$ cd /var/iag/staging/
[ivmgr@76f3f6ef8a69 staging]$ ls
[ivmgr@76f3f6ef8a69 staging]$ vi rule.lua
[ivmgr@76f3f6ef8a69 staging]$ vi context.yaml
[ivmgr@76f3f6ef8a69 staging]$ iag_lua_transformation_test -c context.yaml -s rule.lua

WebSeal configuration

webseald.conf

Once you’ve tested the rule, you can deploy it in WebSEAL.

Edit the webseald.conf, and add a request-match entry.

loadfromhttp = load_from_http.lua

[http-transformations:loadfromhttp]
request-match = request:* /isam/*

NOTE: Make sure to limit the scope here (limit when the lua transformation rule is executed). There is going to be a relatively big hit on performance, since the http call is executed every time the transformation rule is called.

Error page

By adding a page named errors/c/38b9a4b2.html to the management root of the instance, you create a custom error page for these transformations. You can use the macro %FAILREASON% to show the error messages you generate in the lua script using Control.returnErrorPage.

<html>
<head>
    <title>ERROR during transformation</title>
</head>
<body>
 
<h1>Error during execution of a transformation rule</h1>
 
%FAILREASON%
</body>
</html>

It’s easy to verify this, for instance by using a non-existing hostname for the configuri variable.