For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Dynamic DNS registration of APM SSL VPN Client to AWS route53

Problem this snippet solves:

This iRules LX code dynamically registers a clients obtained IP from APM when they initiate a VPN tunnel to an AWS route 53 domain.

How to use this snippet:

Import attached tgz(zip will need to be re-packaged to tgz) to a new iRules LX workspace, create an iRule LX plugin, and attached the editRecord iRule to your virtual server.

You can also manually create a new workspace and add the iRules LX iRule and extension with the code below. You will also have to install the aws-sdk module using npm. Eric Flores has a good write-up on how to manage npm, https://devcentral.f5.com/articles/getting-started-with-irules-lx-part-4-npm-best-practices-20426.

iRule Code:

when CLIENT_ACCEPTED {
    ACCESS::restrict_irule_events disable
}
when HTTP_REQUEST {
    if { [ACCESS::policy result] eq "allow" && [HTTP::uri] starts_with "/myvpn?sess="} {
        after 5000 {
            set apmSessionId [ACCESS::session data get session.user.sessionid]
            set apmProfileName [ACCESS::session data get session.access.profile]
            set apmPart [ACCESS::session data get session.partition_id]
            set DNSName "<your zone with trailing dot>"
            set action "UPSERT"
            set name "<your hostname>"
            if {not ($name ends_with ".[string trimright ${DNSName} {.}]")}{
                log -noname local1.err "${apmProfileName}:${apmPart}:${apmSessionId}: proper hostname was not provided ($name): 01490000: A Record will not be updated for this session."
                event disable all
                return
            }
            set TTL 1200
            set ip [ACCESS::session data get "session.assigned.clientip"]
            if {[catch {IP::addr $ip mask 255.255.255.255}]}{
                log -noname local1.err "${apmProfileName}:${apmPart}:${apmSessionId}: proper IP addressed was not assigned ($ip): 01490000: A Record will not be updated for this session."
                event disable all
                return
            }
            set RPC_HANDLE [ILX::init route53]
            if {[catch {set rpc_response [ILX::call $RPC_HANDLE route53_nodejs $DNSName $action $name $TTL $ip]}]}{
                set rpc_response "Check LTM log for execution errors from sdmd.  A Record may not have been updated for this session."
            }
            log -noname local1. "${apmProfileName}:${apmPart}:${apmSessionId}: Client connected: 01490000: Params sent = $DNSName $action $name $TTL $ip\r\nRPC response = $rpc_response"
        }
    }
}
when ACCESS_SESSION_CLOSED {
    set apmSessionId [ACCESS::session data get session.user.sessionid]
    set apmProfileName [ACCESS::session data get session.access.profile]
    set apmPart [ACCESS::session data get session.partition_id]
    set DNSName "<your zone with trailing dot>"
    set action "DELETE"
    set name "<your hostname>"
    if {not ($name ends_with ".[string trimright ${DNSName} {.}]")}{
        log -noname local1.err "${apmProfileName}:${apmPart}:${apmSessionId}: proper hostname was not provided ($name): 01490000: A Record will not be removed for this session."
        event disable all
        return
    }
    set TTL 1200
    set ip [ACCESS::session data get "session.assigned.clientip"]
    if {[catch {IP::addr $ip mask 255.255.255.255}]}{
        log -noname local1.err "${apmProfileName}:${apmPart}:${apmSessionId}: proper IP addressed was not assigned ($ip): 01490000: A Record will not be removed for this session."
        event disable all
        return
    }
    set RPC_HANDLE [ILX::init route53]
    if {[catch {set rpc_response [ILX::call $RPC_HANDLE route53_nodejs $DNSName $action $name $TTL $ip]}]}{
        set rpc_response "Check LTM log for execution errors from sdmd.  A Record may not have been removed for this session."
    }
    log -noname local1. "${apmProfileName}:${apmPart}:${apmSessionId}: Client disconnected: 01490000: Params sent = $DNSName $action $name $TTL $ip\r\nRPC response = $rpc_response"
}

node.js code:

var AWS = require('aws-sdk');
var f5 = require('f5-nodejs'); 
/* AWS Config */
//AWS.config.update({accessKeyId: "<your access key>", secretAccessKey: "<your secret>"});
/* get the Route53 library */
var route53 = new AWS.Route53(); 
/* Create a new rpc server for listening to TCL iRule calls. */
var ilx = new f5.ILXServer();  

/* Start listening for ILX::call and ILX::notify events. */
ilx.listen();  

/* Add a method and expect DNSName, action, name, TTL, and ip parameters and reply with response */
ilx.addMethod('route53_nodejs', function(req, response) {
    var DNSName = req.params()[0];
    var params = {
        DNSName: DNSName
    };
    var action = req.params()[1];
    var name = req.params()[2];
    var TTL = req.params()[3];
    var ip = req.params()[4];

    route53.listHostedZonesByName(params, function(err,data) {
        if (err) {
            //console.log(err, err.stack);
            response.reply(err.toString());
        }
        else if (data.HostedZones[0].Name !== params.DNSName) {
            response.reply(params.DNSName + " is not a zone defined in Route53");
        }
        else {
            var zoneId = data.HostedZones[0].Id;
            var recParams = {
                "HostedZoneId": zoneId,
                "ChangeBatch": {
                    "Changes": [
                        {
                            "Action": action,
                            "ResourceRecordSet": {
                                "Name": name,
                                "Type": "A",
                                "TTL": TTL,
                                "ResourceRecords": [
                                    {
                                        "Value": ip
                                    }
                                ]
                            }
                        }
                    ]
                }
            };
            /* edit records using aws sdk */
            route53.changeResourceRecordSets(recParams, function(err,data) {
                if (err) {response.reply(err.toString());}
                else if (data.ChangeInfo.Status === "PENDING") {response.reply("Record is being updated");}
                else {response.reply(data);}
            });
        }
    });
});

This is also on my github, https://github.com/bepsoccer/iRulesLX

Code :

75409

Tested this on version:

12.1
Updated Jun 06, 2023
Version 2.0
No CommentsBe the first to comment