Note: This solution is only applicable to version 10.x, version 11 doesn't use advCustHelp.

The default logon page for the Access Policy Manager module is pretty basic, particularly so if only the minimal username and password is configured.  However, APM is wildly flexible.  In this tech tip, I’ll cover customizing the logon page by adding a dropdown box in addition to the standard username and password fields.

Introduction

Background Information

The goal here is to provide access to multiple web applications behind APM through the use of an admin-defined dropdown menu and different LTM pools for each web application. We will be generating the list dynamically through the use of data groups so there will be no need to manually edit the iRule code each time an admin decides to add another option.

Solution Overview

Combining advanced customization, data groups, and iRules, we can dynamically generate html code for each key value pair in the data group. We simply add a session variable in the logon page through advanced customization and insert our html code, generated with iRules, through the session variable. The data group serves as a user friendly way of adding more applications as a layer of indirection.

Create the Access Policy

Before creating the custom logon page, an access policy needs to be defined.  This can be done by utilizing the Device Wizards option under the Templates and Wizards main tab.  Select Web Application Access Management for Local Traffic Virtual Servers as shown below in Figure 1.

Follow the steps in the wizard (AAA server, virtual IP, pool info, etc) to get to the summary page shown below in Figure 2.  Before clicking finished, enter the Visual Policy Editor to make a few changes.

Immediately after the start box in the VPE, add an iRule Event as show in Figure 3.  This will trigger the iRule event ACCESS_POLICY_AGENT_EVENT in the iRule featured later in this article.

Make sure to assign an ID to the event before saving it (it's significant only in differentiating the use of multiple iRule events).  Next, click on the Logon Page event in the VPE and add a text field immediately after the password field.  Set the Post Variable Name and the Session Variable Name to ‘appname’. (The name is not significant but will need to match a statement in the HTML that will be replaced later in the article. This ensures that the logon page agent will know to expect appname as one of the POST parameters.) In the Logon Page Input Field #3 box, enter ‘Application’ and click save.  See Figure 4 below for details.

Note: The logon page agent will only parse and store POST parameters it knows about.  Also, the 'Application' entry in Logon Page Input Field #3 is entered for the sake of completeness, but is technically unnecessary as the custom HTML will override this.

Finally, update the access policy by clicking Apply Access Policy and finish the Device Wizard.

Customize the Logon Page

Now that the policy is created, login via ssh to the command line interface to complete several steps.

1. Change Directory into the specific policy’s logon directory:

cd /config/customization/advanced/logon/<policy_name>_act_logon_page_ag (where <policy_name> is your policy name. devCenEx_act_logon_page_ag in this case.)

2. Make a copy of the tmp_logon_en.inc file (the name logon_en.inc is significant.)

cp tmp_logon_en.inc logon_en.inc

3. Add group and world read permissions to the logon_en.inc file

chmod a+r logon_en.inc

4. Edit and save the logon_en.inc file, replacing this PHP code with the HTML code below it.   Notice that the label and select tags reference Applications and appname (respectively) from our Logon Page in Figure 4.

PHP (remove)

<?
//------------------------------------------------------------
foreach( $fields_settings as $field_settings )
{
    if( $field_settings["type"] != "none" )
    {
        if( $GLOBALS["label_position"] == "above" ){
?>
    <tr>
        <td colspan=2 class="credentials_table_unified_cell" ><label for="<? print( $field_settings["type"] );

?>"
><? print( $field_settings["caption"] ); ?></label><input type=<? print( $field_settings["type"] ); ?> name=<? print( $field_settings["name"] ); ?> class="credentials_input_<? print( $field_settings["type"] );

?>"
<? print( ( $field_settings["rw"] == 0 ? "disabled" : "" ) ); ?> value="<? print( $field_settings["value"] );

?>"
autocomplete="off"></td> </tr> <? }else{ ?> <tr> <td class="credentials_table_label_cell" ><? print( $field_settings["caption"] ); ?></td> <td class="credentials_table_field_cell"><input type="<? print( $field_settings["type"] );

?>"
name="<? print( $field_settings["name"] );

?>"
class="credentials_input_<? print( $field_settings["type"] );

?>"
<? print( ( $field_settings["rw"] == 0 ? "disabled" : "" ) ); ?> value="<? print( $field_settings["value"] );

?>"
autocomplete="off"></td> </tr> <? } } } //------------------------------------------------------------ ?>
Custom HTML (add)
<tr>
        <td colspan=2 class="credentials_table_unified_cell" ><label for="text">Username</label>
        <input type=text name=username class="credentials_input_text"  value="" autocomplete="off" autocapitalize="off"></td>
    </tr>
    <tr>
        <td colspan=2 class="credentials_table_unified_cell" ><label for="password">Password</label>
        <input type=password name=password class="credentials_input_password"  value="" autocomplete="off" autocapitalize="off"></td>
    </tr>
    <tr>
        <td colspan=2 class="credentials_table_unified_cell" ><label for="text">Applications</label>
          <select name="appname">
            %{session.custom.logon_opt} 
          </select>
       </td>
    </tr>
Note: The HTML above can be further customized if desired.  The important section of code is:

     <select name>=”appname”>

         %{session.custom.logon_opt}

     </select>

The POST variable was created in the Logon page and the session variable will be utilized in the iRule.
 
UPDATE: As an alternative to replacing the php code with html as shown above, the javascript below can be added to the javascript section of the same file:
 
function dropdown(){
  var allTDs = document.getElementsByTagName('td');
  for(i=0; i < allTDs.length; i++) {
    if(allTDs[i].innerHTML.indexOf('appname') > 0 && allTDs[i].innerHTML.indexOf('auth_form') == -1) {
      var replacetext = '<label for="text">Application</label><select name="appname" value="" autocomplete="off">%{session.custom.logon_opt}</select>';
      allTDs[i].innerHTML = replacetext;
    }
  }
}

To ensure this javascript is run, insert the dropdown() function shown above in line 1 of the OnLoad() function defined in the same file.

5. Apply the customizations to the policy

advCustHelp <your policy name> (The following two commands are included with instructions on updated the access policy when this command is run.)

b customization group <your policy_name>_act_logon_page_ag action update

b profile access <your policy name> generation action increment

6. Create a string class for the applications.  The string should be the name of the application to be displayed in the Logon Page and the value should be the pool that hosts the applicable service.  The resulting configuration of the class is shown entered below via bigpipe then via tmsh (See Figure 5 immediatedly after for the GUI entry for the class.)

b class ExampleDataGroup '{
   {
      "OWA" { "intranet-pool" }
      "Share Point" { "sharepoint-pool" }
   }
}'

tmsh create /ltm data-group ExampleDataGroup type string records add { "OWA" { data "intranet-pool" } "Share Point" { data "sharepoint-pool" } }

7. Create the iRule (via iRule editor, GUI, or tmsh)

when ACCESS_POLICY_AGENT_EVENT {
  if {[ACCESS::policy agent_id] eq "GenHtml"} {
    set htmlstr ""
    # Pull data from the data group
    set keys [class names ExampleDataGroup]
    # log local0. "DATA GROUP:: $keys"
    foreach key $keys {
    # log local0. "KEY:: $key"
    # Add a new option in the drop down box for each key
      append htmlstr "<option value=\"$key\">$key</option>"
   }
    # log local0. "HTML STRING:: $htmlstr"
    # Using the session variable we inserted through advanced customization, 
    # we can insert the html code we generated above
    ACCESS::session data set "session.custom.logon_opt" $htmlstr
  }
}
 
when ACCESS_ACL_ALLOWED {
  set appname [ACCESS::session data get "session.logon.last.appname"]
  # log local0. "appname:: $appname"
  set value [class search -value ExampleDataGroup equals $appname]
  # log local0. "value:: $value"
  if {[string length $value] > 0} {
    pool $value
    # log local0. "POOL:: $value"
  }
}

Note that the class name referenced in the iRule should match the actual class name (instead of ExampleDataGroup as shown above)

8. Finally, apply the iRule to the APM virtual server and the process is complete!

tmsh modify /ltm virtual custom_dropdown_vs rules { custom_dropdown_iRule }

Figure 6 below shows the resulting logon page from the customizations performed.  This is just the surface of what can be done to customize the logon page.  Many thanks to F5er ystephie for writing up the documentation steps for this solution.

 

Related Articles