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