cancel
Showing results for 
Search instead for 
Did you mean: 

Pulse Secure vADC

Sort by:
This video is a demo of Elastic Application Delivery using the Stingray Services Controller. It shows how monitoring something like throughput or concurrent connections can be used as a trigger for the Services Controller to spin up new instances of Stingray. This type of model can be used when building a very dynamic datacenter. The Flexible Licensing of the Services Controller enables a pool of licensed bandwidth to "follow an application".   Imagine how in an enterprise data center, different workloads are busy at different times of the day. For instance, early in the morning, the VMware View servers may be busy as desktops are booted, but after that "boot storm" they are relatively idle. Then a half hour later something like Microsoft Exchange or Siebel become busy... Well now using the flexible licenses of the Stingray Services Controller, an ADC can spin up during the busy times for one application, then shut down and return its resources to the pool, then as the next application becomes busy, it can bring up a different instance with bandwidth assigned to it, then return its bandwidth when it is done.   This type of model is only available with the Stingray Services Controller.  
View full article
This is an update to the "Simply WURFL" article written in 2009.  Since that time, some of the underlying libraries have changed, especially with regards to logging.   What is WURFL?  WURFL stands for Wireless Universal Resource FiLe.  From the WURFL webpage: "WURFL is a Device Description Repository (DDR), i.e. a framework that enables applications to map HTTP requests to a description of the capability of the mobile device that requests the page."  WURFL is licensed and maintained by ScientiaMobile, Inc. and includes a Java API so it can be used with the Stingray Traffic Manager.  It is up to the user to make sure they comply with the ScientiaMobile WURFL license.  You do not need to know Java to get these examples working since all the necessary class and jar files are either attached to this article or available at the links below, but if you do want to modify the source code, the source files are also attached.  To get started with WURFL you first need to ensure your Stingray Traffic Manager has working Java support and then download the following items.   wurfl Java API : This code has been tested with version 1.4.4.3.  You will need wurfl-1.4.4.3.jar. wurfl: The WURFL respository.  wurfl.zip which can be used as is or extracted as wurfl.xml.  This code has been tested with version 2.3.4. commons-collections: Java interfaces, implementations and utilities.  This code has been tested with version 3.2.1.  You will need commons-collections-3.2.1.jar. commons-lang: Helper utilities.  This code has been tested with version 3.1.  You will need commons-lang3-3.1.jar. sl4j: Logging framework.  This code has been tested with version 1.7.5.  You will need slf4j-api-1.7.5.jar and slf4j-noop-1.7.5.jar or slf4j-simple-1.7.5.jar depending on what you want to see with regards to logging messages.  slf4j-noop-1.7.5.jar will cause all messages to be suppressed while slf4j-simple-1.7.5.jar will cause all messages to appear in the Stingray event log as warnings.   Upload the files specified using the Calalogs > Java UI page on your Stingray Traffic Manager. Now you're all set to experiment with the following examples.   The first sample servlet is a very simple use-case of WURFL, useful as a base for your own servlet or for debugging. The intention is for it to introduce the WURFL API as it fits within the framework of Stingray Java Extensions. WURFL is typically configured using the ConfigListener model, but Stingray doesn't go as far as implementing all the nuts and bolts required by full web applications. Our WURFL servlet must perform the required initialization itself, so an init method has been implemented that sets up a WURFLManager. As much work as possible is done at servlet initialization time. Then all the doGet method needs to do is check the request against the pre-initialised WURFLManager.   The source code and the compiled class for this example, StingrayWURFLInfoServlet are attached to this article.  To compile the code yourself on the Stingray instance, after uploading the specified jar files, you can upload the source file as well and then from the $ZEUSHOME/zxtm/conf/jars directory execute the following command:   javac -cp "$ZEUSHOME/zxtm/lib/*:$ZEUSHOME/zxtm/conf/jars/* StingrayWURFLInfoServlet.java   To compile it on a different machine, you will need the following jar files that were uploaded to Stingray: commons-collections-3.2.1.jar, commons-lang3.3.1.jar, wurfl-1.4.4.3.jar as well as servlet.jar and zxtm-servlet.jar from Stingray.  These are available via links in the Stingray UI under Catalogs > Java.  Please see the Stingray Java Development Guide for more information.   To get the servlet working follow these steps:   Upload the StingrayWURFLInfoServlet.class file using Stingray Catalogs > Java UI page, leave the "Automatically create TrafficScript rule" checkbox ticked If you want devices that are not included in the standard WURFL repository, like desktop browsers, to be detected properly, a patches file can be created and uploaded to Stingray using the Catalogs > Java page.   If this file is uploaded, click on the StingrayWURFLInfoServlet link on the UI page and add the parameter "wurfl_patches" with the value of the name of the patches file, e.g., "web_browsers_patch.xml".  For details on creating a patches file see the ScientiaMobile website. Set up a Virtual Server for testing.  The pool can be set to "discard" since StingrayWURFLInfoServlet will create a response. Associate the auto-generated StingrayWURFLInfoServlet rule with the test Virtual Server as a request rule. Visit the virtual server from different browsers on different devices.   The result should be a page showing some general information about your browser at the top followed by the full table of WURFL capabilities and their values. The following screenshots show the top part of the output using a iPhone and a desktop browser:   Safari on iPhone:     Firefox on Windows:   This is fine as a demo but not particularly useful for a real world application. What we really want is a module that can export capabilities so that they can be used by TrafficScript and other extensions. We also don't want to specifically be a doGet processor, so we'll modify the servlet along the lines described in the article: Writing TrafficScript functions in Java   in Java*</a>   There are a lot of capabilities covered by WURFL, the tables in the above screenshots go on for several pages - more than 500 capabilities in all. So we'll make it possible for TrafficScript to specify what capability fields it wants.   This gives us a servlet that could be used from TrafficScript using a rule such as the following example.  This rule extracts a few values and caches them in the Global Associative Array using the user-agent as the key so that subsequent requests for the same user-agent don't require another look up.   sub checkWURFL() { $ua = http.getHeader( "User-Agent" ); if (!string.length($ua)) return; $markup = data.get("WURFL" . $ua . "preferred_markup"); $datarate = data.get("WURFL" . $ua . "max_data_rate"); $brand = data.get("WURFL" . $ua . "brand_name"); $cookie = data.get("WURFL" . $ua . "cookie_support"); $os = data.get("WURFL" . $ua . "device_os"); $osVersion = data.get("WURFL" . $ua . "device_os_version"); $isWireless = data.get("WURFL" . $ua . "is_wireless_device"); if (string.length($markup)) { log.info("Returning cached values for User-Agent: " . $ua . ", datarate: " . $datarate . ", markup: " . $markup . ", brand: " . $brand . ", cookies: " . $cookie . ", os: " . $os . ", version: " . $osVersion . ", iswireless: " . $isWireless); $1 = $markup; $2 = $datarate; $3 = $brand; $4 = $cookie; $5 = $os; $6 = $osVersion; $7 = $isWireLess; return; } # no cached values for the UA, so run it through WURFL java.run("StingrayWURFLServlet", "max_data_rate", "preferred_markup", "brand_name", "cookie_support", "device_os", "device_os_version", "is_wireless_device"); $markup = connection.data.get("preferred_markup"); $datarate = connection.data.get("max_data_rate"); $brand = connection.data.get("brand_name"); $cookie = connection.data.get("cookie_support"); $os = connection.data.get("device_os"); $osVersion = connection.data.get("device_os_version"); $isWireless = connection.data.get("is_wireless_device"); data.set("WURFL" . $ua . "preferred_markup", $markup); data.set("WURFL" . $ua . "max_data_rate", $datarate); data.set("WURFL" . $ua . "max_data_rate", $datarate); data.set("WURFL" . $ua . "brand_name", $brand); data.set("WURFL" . $ua . "cookie_support", $cookie); data.set("WURFL" . $ua . "device_os", $os); data.set("WURFL" . $ua . "device_os_version", $osVersion); data.set("WURFL" . $ua . "is_wireless_device", $isWireless); log.info("Returning fresh WURFL values for User-Agent: " . $ua . ", datarate: " . $datarate . ", markup: " . $markup . ", brand: " . $brand . ", cookies: " . $cookie . ", os: " . $os . ", version: " . $osVersion . ", iswireless:" . $isWireless); $1 = $markup; $2 = $datarate; $3 = $brand; $4 = $cookie; $5 = $os; $6 = $osVersion; $7 = $isWireless; return; } # simple case to test the checkWURFL function checkWURFL(); $html = "Max Data Rate: " . $2 . "kbps Preferred Markup: " . $1 . " Brand: " . $3 . " OS: " . $5 . " Version: " . $6 . " Cookie Support: " . $4 . " Is Wireless Device: " . $7; http.sendResponse("200", "text/html", $html, "");   The corresponding StingrayWURFLServlet source and compiled class file and slf4j-stingray.jar file are attached to this article.  The slf4j-stingray.jar file includes an implementation to properly direct slf4j messages to the Stingray Event Log. The logging code is documented here: slf4j Logging and Stingray Java Extensions . The slf4j-noop-1.7.5.jar or slf4j-simple-1.7.5.jar file uploaded for the previous example should be deleted.  To control what level of logging to output to the Stingray Event Log, a parameter, "log_level" can be set to a value (case insensitive) of "debug", "info", "warn" or "error".  If no value is set, the default is "warn".   Rather than the doGet method implemented in the StingrayWURFLInfoServlet we now have a simpler service method.   public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String[] args = (String[])request.getAttribute( "args" ); if (args == null || args.length == 0) { throw new ServletException("error: no arguments supplied"); } Device device = manager.getDeviceForRequest(request); Map capabilities = device.getCapabilities(); for (int i = 0; i < args.length; ++i) { String cap = (String)capabilities.get(args[i]); if (cap == null) { Logger.warn("Java Servlet StingrayWURFLServlet: No capability found matching: " + args[i]); } else { ((ZXTMServletRequest)request).setConnectionData( args[i], cap ); } } }   There is much more you could do with the StingrayWURFLServlet Java Extension provided in this article. Think of it as a starting point for developing your own solutions to improve the web browsing experience of your mobile users.   A few examples:   The max_data_rate value retrieved above could be used to reduce image quality or size for people with low bandwidth devices. This would result in a snappier web browsing experience for these people as there would be less data for them to retrieve over their slow links. The preferred_markup value can be used to direct clients to different backend pools based on whether they can handle XHTML, or should be served WML. The streaming_flv value can be checked to see if the device has Flash video support and can thus be sent to your full bells-and-whistles website. A scaled down version could be made for those that only have Flash Lite, which is specified by the value of flash_lite_version. Devices that don't support flash at all (such as the iPhone) can be sent to a plain HTML version of the site, or WML as in the previous bullet point.   Speaking of the iPhone, it doesn't have Flash but its browser does have excellent AJAX support. You know your site is being visited by an iPhone user using the normal iPhone web browser when model_name is "iPhone" and mobile_browser is "Safari". If there are important differences between iPhone OS releases you can also check model_extra_info ordevice_os_version for this detail. For AJAX in general there are a whole set of specific properties: ajax_manipulate_css ajax_manipulate_dom ajax_support_event_listener ajax_support_events   Up to date documentation on all the WURFL capabilities can be found on the WURFL website.   <a title=h   Any of these values can also be passed on to your backend nodes of course. You could add special headers containing the values, a cookie, or a URL argument. You could also cache browser capabilities uniquely to each device with cookies or another method of session tracking, rather than cache the capabilities based solely on the user agent. Then you could offer users the ability to override special mobile device modes.   We would love to hear your ideas and learn how we can help you in this exciting area - the opportunities are practically limitless.
View full article
Why write a health monitor in TrafficScript?   The Health Monitoring capabilities (as described in Feature Brief: Health Monitoring in Traffic Manager) are very comprehensive, and the built-in templates allow you to conduct sophisticated custom dialogues, but sometimes you might wish to resort to a full programming language to implement the tests you need.   Particularly on the Traffic Manager Virtual Appliance, your options can be limited. There's a minimal Perl interpreter included (see Tech Tip: Running Perl code on the Traffic Manager Virtual Appliance), and you can upload compiled binaries (Writing a custom Health Monitor in C) and shell scripts. This article explains how you can use TrafficScript to implement health monitors, and of course with Java Extensions, TrafficScript can 'call out' to a range of third-party libraries as well.   Overview   We'll implement the solution using a custom 'script' health monitor.  This health monitor will probe a virtual server running on the local Traffic Manager (using an HTTP request), and pass it all of the parameters relevant to the health request.   A TrafficScript rule running on the Traffic Manager can perform the appropriate health check and respond with a 'PASS' (200 OK) or 'FAIL' (500 Error) response.   The health monitor script   The health monitor script is straightforward and should not need any customization.  It will take its input from the health monitor configuration.   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #!/bin/sh  exec $ZEUSHOME /perl/miniperl -wx $0 ${1+ "$@" }       if 0;       #!/usr/bin/perl  #line 7       BEGIN{          # Pull in the Traffic Manager libraries for HTTP requests          unshift @INC , "$ENV{ZEUSHOME}/zxtmadmin/lib/perl" , "$ENV{ZEUSHOME}/zxtm/lib/perl" ;  }       use Zeus::ZXTM::Monitor qw( ParseArguments MonitorWorked MonitorFailed Log );  use Zeus::HTMLUtils qw( make_query_string );  use Zeus::HTTP;       my %args = ParseArguments();       my $url = "http://localhost:$args{vsport}$args{path}?" .make_query_string( %args );  my $http = new Zeus::HTTP( GET => $url );  $http ->load();       Log ( "HTTP GET for $url returned status: " . $http ->code() );       if ( $http ->code() == 200 ) {      MonitorWorked();  } else {      MonitorFailed( "Monitor failed: " . $http ->code() . " " . $http ->body() );  }   Upload this to the Monitor Programs of the Extra Files section of the catalog, and then create an "External Program Monitor" based on that script.  You will need to add two more configuration parameters to this health monitor configuration:   vsport: This should be set to the port of the virutal server that will host the trafficscript test path: This is optional - you can use it if you want to run several different health tests from the trafficscript rule   Your configuration should look something like this:   The virtual server   Create an HTTP virtual server listening on the appropriate port number (vsport).  You can bind this virtual server to localhost if you want to prevent external clients from accessing it.   The virtual server should use the 'discard' pool - we're going to add a request rule that always sends a response, so there's no need for any backend nodes.   The TrafficScript Rule   The 'business end' of your TrafficScript health monitor resides in the TrafficScript rule.  This rule is invoked every time the health monitor script is run, and it is given the details of the node which is to be checked.   The rule should return a 200 OK HTTP response if the node is OK, and a different response (such as 500 Error) if the node has failed the test.   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 $path = http.getPath(); # Use 'path' if you would like to publish                           # several different tests from this rule       $ip = http.getFormParam( "ipaddr" );  $port = http.getFormParam( "port" );  $nodename = http.getFormParam( "node" );       # We're going to test the node $nodename on $ip:$port  #  # Useful functions include:  #   http.request.get/put/post/delete()  #   tcp.connect/read/write/close()  #   auth.query()  #   java.run()            sub Failed( $msg ) {      http.sendResponse( 500, "text/plain" , $msg , "" );  }       # Let's run a simple GET  $req = 'GET / HTTP/1.0  Host: www.riverbed.com       ';  $timeout = 1000; # ms  $sock = tcp. connect ( $ip , $port , $timeout );  tcp. write ( $sock , $req , $timeout );  $resp = tcp. read ( $sock , 102400, $timeout );       # Perform whatever tests we want on the response data.   # For example, it should begin with '200 OK'       if ( ! string.startsWith( $resp , "HTTP/1.1 200 OK" ) ) {      Failed( "Didn't get expected response status" );  }       # All good  http.sendResponse( 200, "text/plain" , "" , "" );  
View full article
 UPDATE January 2015: libDNS version 2.1 released! The new release is backward-compatible with previous releases and includes the following bug fixes: Fixed multiple bugs in parsing of DNS records   The libDNS.rts library (written by Matthew Geldert and enhanced by Matthias Scheler) attached below provides a means to interrogate and modify DNS traffic from a TrafficScript rule, and to respond directly to DNS request when desired.   You can use it to:   Modify DNS requests on the fly, rewriting a lookup for one address so that it becomes a lookup for a different one: HowTo: Modify DNS requests using libDNS.rts Intercept and respond directly to DNS requests, informed by TrafficScript logic: HowTo: Respond directly to DNS requests using libDNS.rts Implement a simple DNS responder (avoiding the need for a back-end DNS server) - particularly useful in a Global Server Load Balancing scenario: HowTo: Implement a simple DNS resolver using libDNS.rts   Note: This library allows you to inspect and modify DNS traffic as it is balanced by Stingray.  If you want to issue DNS requests from Stingray, check out the net.dns.resolveHost() and net.dns.resolveIP() TrafficScript functions for this purpose.   Overview   This rule can be used when Stingray receives DNS lookups (UDP or TCP) using a Virtual Server, and optionally uses a pool to forward these requests to a real DNS server:   Typically, a listen IP addresses of the Stingray will be published as the NS record for a subdomain so that clients explicitly direct DNS lookups to the Stingray device, although you can use Stingray software in a transparent mode and intercept DNS lookups that are routed through Stingray using the iptables configuration described in Transparent Load Balancing with Stingray Traffic Manager.   Installation     Step 1:   Configure Stingray with a virtual server listening on port 53; you can use the 'discard' pool to get started:   If you use 'dig' to send a DNS request to that virtual server (listening on 192.168.35.10 in this case), you won't get a response because the virtual server will discard all requests:   $ dig @192.168.35.10 www.zeus.com ; <<>> DiG 9.8.3-P1 <<>> @192.168.35.10 www.zeus.com ; (1 server found) ;; global options: +cmd ;; connection timed out; no servers could be reached   Step 2:   Create a new rule named libDNS.rts using the attached TrafficScript Source:   This rule is used as a library, so you don't need to associate it with a Virtual Server.   Then associate the following request rule with your virtual server:   import libDNS.rts as dns; $request = request.get(); $packet = dns.convertRawDataToObject($request, "udp"); # Ignore unparsable packets and query responses to avoid # attacks like the one described in CVE-2004-0789. if( hash.count( $packet ) == 0 || $packet["qr"] == "1" ) { break; } log.info( "DNS request: " . lang.dump( $packet ) ); $host = dns.getQuestion( $packet )["host"]; $packet = dns.addResponse($packet, "answer", $host, "www.site.com.", "CNAME", "IN", "60", []); $packet["qr"] = 1; log.info( "DNS response: " . lang.dump( $packet ) ); request.sendResponse( dns.convertObjectToRawData($packet, "udp"));   This request rule will catch all DNS requests and respond directly with a CNAME directing the client to look up 'www.site.com' instead:   $ dig @192.168.35.10 www.zeus.com ; <<>> DiG 9.8.3-P1 <<>> @192.168.35.10 www.zeus.com ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<     Documentation   This DNS library provides a set of helper functions to query and modify DNS packets.  Internally, a decoded DNS packet is represented as a TrafficScript datastructure and when you are writing and debugging rules, it may be useful to use log.info( lang.dump( $packet ) ) as in the rule above to help understand the internal structures.   Create a new DNS packet   $packet = dns.newDnsObject(); log.info( lang.dump( $packet ) );   Creates a data structure model of a DNS request/response packet. The default flags are set for an A record lookup. Returns the internal hash data structure used for the DNS packet.   Set the question in a DNS packet   $packet = dns.setQuestion( $packet, $host, $type, $class );   Sets the question in a data structure representation of a DNS packet.   $packet - the packet data structure to manipulate. $host - the host to lookup. $type - the type of RR to request.   Returns the modified packet data structure, or -1 if the type is unknown.     Get the question in the DNS packet   $q = dns.getQuestion( $packet ); $host = $q["host"]; $type = $q["type"]; $class = $q["class"];   Gets the question from a data structure representing a DNS request/response.  Returns a hash of the host and type in the data structure's question section.   Add an answer to the DNS packet   $packet = dns.addResponse($packet, $section, $name, $host, $type, $class, $ttl, $additional);   Adds an answer to the specified RR section:   $packet - the packet data structure to manipulate. $section - name of the section ("answer", "authority" or "additional") to add the answer to $name, $host, $type, $class, $ttl, $additional - the answer data   Returns the modified packet data structure.   Remove an answer from the DNS packet   $packet = dns.removeResponse( $packet, $section, $answer ); while( $packet["additionalcount"] > 0 ) { $packet = dns.removeResponse( $packet, "additional", 0); }   Removes an answer from the specified RR section.   $packet - the packet data structure to manipulate. $section - name of the section ("answer", "authority" or "additional") to remove the entry from $answer - the array position of the answer to remove (0 removes the first).   Returns the modified packet data structure, or -1 if the specified array key is out of range.   Convert the datastructure packet object to a raw packet   $data = dns.convertObjectToRawData( $packet, $protocol ); request.sendResponse( $data );   Converts a data structure into the raw data suitable to send in a DNS request or response.   $packet - data structure to convert. $protocol - transport protocol of data ("udp" or "tcp").   Returns the raw packet data.     Convert a raw packet to  an internal datastructure object   $data = request.get(); $packet = dns.convertRawDatatoObject( $data, $protocol );   Converts a raw DNS request/response to a manipulatable data structure.   $data - raw DNS packet $protocol - transport protocol of data ("udp" or "tcp")   Returns trafficscript data structure (hash).     Kudos   Kudos to Matthew Geldert, original author of this library, and Matthias Scheler, author of version 2.0.
View full article
This guide will walk you through the setup to deploy Global Server Load Balancing on Traffic Manager using the Global Load Balancing feature. In this guide, we will be using the "company.com" domain.     DNS Primer and Concept of operations: This document is designed to be used in conjuction with the Traffic Manager User Guide.   Specifically, this guide assumes that the reader: is familiar with load balancing concepts; has configured local load balancing for the the resources requiring Global Load Balancing on their existing Traffic Managers; and has read the section "Global Load Balancing" of the Traffic Manager User Guide in particular the "DNS Primer" and "About Global Server Load Balancing" sections.   Pre-requisite:   You have a DNS sub-domain to use for GLB.  In this example we will be using "glb.company.com" - a sub domain of "company.com";   You have access to create A records in the glb.company.com (or equivalent) domain; and   You have access to create CNAME records in the company.com (or equivalent) domain.   Design: Our goal in this exercise will be to configure GLB to send users to their geographically closes DC as pictured in the following diagram:   Design Goal We will be using an STM setup that looks like this to achieve this goal: Detailed STM Design     Traffic Manager will present a DNS virtual server in each data center.  This DNS virtual server will take DNS requests for resources in the "glb.company.com" domain from external DNS servers, will forward the requests to an internal DNS server, an will intelligently filter the records based on the GLB load balancing logic.     In this design, we will use the zone "glb.company.com".  The zone "glb.company.com" will have NS records set to the two Traffic IP addresses presented by vTM for DNS load balancing in each data centre (172.16.10.101 and 172.16.20.101).  This set up is done in the "company.com" domain zone setup.  You will need to set this up yourself, or get your DNS Administrator to do it.       DNS Zone File Overview   On the DNS server that hosts the "glb.company.com" zone file, we will create two Address (A) records - one for each Web virtual server that the vTM's are hosting in their respective data centre.     Step 0: DNS Zone file set up Before we can set up GLB on Traffic Manager, we need to set up our DNS Zone files so that we can intelligently filter the results.   Create the GLB zone: In our example, we will be using the zone "glb.company.com".  We will configure the "glb.company.com" zone to have two NameServer (NS) records.  Each NS record will be pointed at the Traffic IP address of the DNS Virtual Server as it is configured on vTM.  See the Design section above for details of the IP addresses used in this sample setup.   You will need an A record for each data centre resource you want Traffic Manager to GLB.  In this example, we will have two A records for the dns host "www.glb.company.com".  On ISC Bind name servers, the zone file will look something like this: Sample Zone FIle     ; ; BIND data file for glb.company.com ; $TTL 604800 @ IN SOA stm1.glb.company.com. info.glb.company.com. ( 201303211322 ; Serial 7200 ; Refresh 120 ; Retry 2419200 ; Expire 604800 ; Default TTL ) @ IN NS stm1.glb.company.com. @ IN NS stm2.glb.company.com. ; stm1 IN A 172.16.10.101 stm2 IN A 172.16.20.101 ; www IN A 172.16.10.100 www IN A 172.16.20.100   Pre-Deployment testing:   - Using DNS tools such as DiG or nslookup (do not use ping as a DNS testing tool) make sure that you can query your "glb.company.com" zone and get both the A records returned.  This means the DNS zone file is ready to apply your GLB logic.  In the following example, we are using the DiG tool on a linux client to *directly* query the name servers that the vTM is load balancing  to check that we are being served back two A records for "www.glb.company.com".  We have added comments to the below section marked with <--(i)--| : Test Output from DiG user@localhost$ dig @172.16.10.40 www.glb.company.com A ; <<>> DiG 9.8.1-P1 <<>> @172.16.10.40 www.glb.company.com A ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19013 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 2, ADDITIONAL: 2 ;; QUESTION SECTION: ;www.glb.company.com. IN A ;; ANSWER SECTION: www.glb.company.com. 604800 IN A 172.16.20.100 <--(i)--| HERE ARE THE A RECORDS WE ARE TESTING www.glb.company.com. 604800 IN A 172.16.10.100 <--(i)--| ;; AUTHORITY SECTION: glb.company.com. 604800 IN NS stm1.glb.company.com. glb.company.com. 604800 IN NS stm2.glb.company.com. ;; ADDITIONAL SECTION: stm1.glb.company.com. 604800 IN A 172.16.10.101 stm2.glb.company.com. 604800 IN A 172.16.20.101 ;; Query time: 0 msec ;; SERVER: 172.16.10.40#53(172.16.10.40) ;; WHEN: Wed Mar 20 16:39:52 2013 ;; MSG SIZE rcvd: 139       Step 1: GLB Locations GLB uses locations to help STM understand where things are located.  First we need to create a GLB location for every Datacentre you need to provide GLB between.  In our example, we will be using two locations, Data Centre 1 and Data Centre 2, named DataCentre-1 and DataCentre-2 respectively: Creating GLB  Locations   Navigate to "Catalogs > Locations > GLB Locations > Create new Location"   Create a GLB location called DataCentre-1   Select the appropriate Geographic Location from the options provided   Click Update Location   Repeat this process for "DataCentre-2" and any other locations you need to set up.     Step 2: Set up GLB service First we create a GLB service so that vTM knows how to distribute traffic using the GLB system: Create GLB Service Navigate to "Catalogs > GLB Services > Create a new GLB service" Create your GLB Service.  In this example we will be creating a GLB service with the following settings, you should use settings to match your environment:   Service Name: GLB_glb.company.com   Domains: *.glb.company.com   Add Locations: Select "DataCentre-1" and "DataCentre-2"   Then we enable the GLB serivce:   Enable the GLB Service Navigate to "Catalogs > GLB Services > GLB_glb.company.com > Basic Settings" Set "Enabled" to "Yes"   Next we tell the GLB service which resources are in which location:   Locations and Monitoring Navigate to "Catalogs > GLB Services > GLB_glb.company.com > Locations and Monitoring" Add the IP addresses of the resources you will be doing GSLB between into the relevant location.  In my example I have allocated them as follows: DataCentre-1: 172.16.10.100 DataCentre-2: 172.16.20.100 Don't worry about the "Monitors" section just yet, we will come back to it.     Next we will configure the GLB load balancing mechanism: Load Balancing Method Navigate to "GLB Services > GLB_glb.company.com > Load Balancing"   By default the load balancing "algorithm" will be set to "Adaptive" with a "Geo Effect" of 50%.  For this set up we will set the "algorithm" to "Round Robin" while we are testing.   Set GLB Load Balancing Algorithm Set the "load balancing algorithm" to "Round Robin"   Last step to do is bind the GLB service "GLB_glb.company.com" to our DNS virtual server.   Binding GLB Service Profile Navigate to "Services > Virtual Servers > vs_GLB_DNS > GLB Services > Add new GLB Service" Select "GLB_glb.company.com" from the list and click "Add Service" Now that we have GLB applied to the "glb.company.com" zone, we can test GLB in action. Using DNS tools such as DiG or nslookup (again, do not use ping as a DNS testing tool) make sure that you can query against your STM DNS virtual servers and see what happens to request for "www.glb.company.com". Following is test output from the Linux DiG command. We have added comments to the below section marked with the <--(i)--|: Step 3 - Testing Round Robin Now that we have GLB applied to the "glb.company.com" zone, we can test GLB in action. Using DNS tools such as DiG or nslookup (again, do not use ping as a DNS testing tool) make sure that you can query against your STM DNS virtual servers and see what happens to request for "www.glb.company.com". Following is test output from the Linux DiG command. We have added comments to the below section marked with the <--(i)--|:   Testing user@localhost $ dig @172.16.10.101 www.glb.company.com ; <<>> DiG 9.8.1-P1 <<>> @172.16.10.101 www.glb.company.com ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17761 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2 ;; QUESTION SECTION: ;www.glb.company.com. IN A ;; ANSWER SECTION: www.glb.company.com. 60 IN A 172.16.2(i)(i)0.100 <--(i)--| DataCentre-2 response ;; AUTHORITY SECTION: glb.company.com. 604800 IN NS stm1.glb.company.com. glb.company.com. 604800 IN NS stm2.glb.company.com. ;; ADDITIONAL SECTION: stm1.glb.company.com. 604800 IN A 172.16.10.101 stm2.glb.company.com. 604800 IN A 172.16.20.101 ;; Query time: 1 msec ;; SERVER: 172.16.10.101#53(172.16.10.101) ;; WHEN: Thu Mar 21 13:32:27 2013 ;; MSG SIZE rcvd: 123 user@localhost $ dig @172.16.10.101 www.glb.company.com ; <<>> DiG 9.8.1-P1 <<>> @172.16.10.101 www.glb.company.com ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9098 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2 ;; QUESTION SECTION: ;www.glb.company.com. IN A ;; ANSWER SECTION: www.glb.company.com. 60 IN A 172.16.1(i)0.100 <--(i)--| DataCentre-1 response ;; AUTHORITY SECTION: glb.company.com. 604800 IN NS stm2.glb.company.com. glb.company.com. 604800 IN NS stm1.glb.company.com. ;; ADDITIONAL SECTION: stm1.glb.company.com. 604800 IN A 172.16.10.101 stm2.glb.company.com. 604800 IN A 172.16.20.101 ;; Query time: 8 msec ;; SERVER: 172.16.10.101#53(172.16.10.101) ;; WHEN: Thu Mar 21 13:32:27 2013 ;; MSG SIZE rcvd: 123   Step 4: GLB Health Monitors Now that we have GLB running in round robin mode, the next thing to do is to set up HTTP health monitors so that GLB can know if the application in each DC is available before we send customers to the data centre for access to the website:     Create GLB Health Monitors Navigate to "Catalogs > Monitors > Monitors Catalog > Create new monitor" Fill out the form with the following variables: Name:   GLB_mon_www_AU Type:    HTTP monitor Scope:   GLB/Pool IP or Hostname to monitor: 172.16.10.100:80 Repeat for the other data centre: Name:   GLB_mon_www_US Type:    HTTP monitor Scope:   GLB/Pool IP or Hostname to monitor: 172.16.20.100:80   Navigate to "Catalogs > GLB Services > GLB_glb.company.com > Locations and Monitoring" In DataCentre-1, in the field labled "Add new monitor to the list" select "GLB_mon_www_AU" and click update. In DataCentre-2, in the field labled "Add new monitor to the list" select "GLB_mon_www_US" and click update.   Step 5: Activate your preffered GLB load balancing logic Now that you have GLB set up and you can detect application failures in each data centre, you can turn on the GLB load balancing algorithm that is right for your application.  You can chose between: GLB Load Balancing Methods Load Geo Round Robin Adaptive Weighted Random Active-Passive The online help has a good description of each of these load balancing methods.  You should take care to read it and select the one most appropriate for your business requirements and environment.   Step 6: Test everything Once you have your GLB up and running, it is important to test it for all the failure scenarios you want it to cover. Remember: failover that has not been tested is not failover...   Following is a test matrix that you can use to check the essentials: Test # Condition Failure Detected By / Logic implemented by GLB Responded as designed 1 All pool members in DataCentre-1 not available GLB Health Monitor Yes / No 2 All pool members in DataCentre-2 not available GLB Health Monitor Yes / No 3 Failure of STM1 GLB Health Monitor on STM2 Yes / No 4 Failure of STM2 GLB Health Monitor on STM1 Yes / No 5 Customers are sent to the geographically correct DataCentre GLB Load Balancing Mechanism Yes / No   Notes on testing GLB: The reason we instruct you to use DiG or nslookup in this guide for testing your DNS rather than using a tool that also does an DNS resolution, like ping, is because Dig and nslookup tools bypass your local host's DNS cache.  Obviously cached DNS records will prevent you from seeing changes in status of your GLB while the cache entries are valid.     The Final Step - Create your CNAME: Now that you have a working GLB entry for "www.glb.company.com", all that is left to do is to create or change the record for the real site "www.company.com" to be a CNAME for "www.glb.company.com". Sample Zone File ; ; BIND data file for company.com ; $TTL 604800 @ IN SOA ns1.company.com. info.company.com. ( 201303211312 ; Serial 7200 ; Refresh 120 ; Retry 2419200 ; Expire 604800 ; Default TTL ) ; @ IN NS ns1.company.com. ; Here is our CNAME www IN CNAME www.glb.company.com.
View full article
Stingray Traffic Manager can run as either a forward or a reverse proxy. But what is a Proxy? A reverse proxy? A forward proxy? And what can you do with such a feature?   Let's try and clarify what all these proxies are. In computing, a Proxy is a service that accepts network connections from clients and then forwards them on to a server. So in essence, any Load Balancer or Traffic Manager is a kind of proxy. Web caches are another example of proxy servers. These keep a copy of frequently requested web pages and will deliver these pages themselves, rather than having to forward the request on to the 'real' server.   Forward and Reverse Proxies   The difference between a 'forward' and 'reverse' proxy is determined by where the proxy is running.   Forward Proxies:  Your ISP probably uses a web cache to reduce its bandwidth costs. In this case, the proxy is sitting between your computer and the whole Internet. This is a 'forward proxy'. The proxy has a limited set of users (the ISP's customers), and can forward requests on to any machine on the Internet (i.e. the web sites that the customers are browsing).   Reverse Proxies: Alternatively, a company can put a web cache in the same data center as their web servers, and use it to reduce the load on their systems. This is a 'reverse proxy'. The proxy has an unlimited set of users (anyone who wants to view the web site), but proxies requests on to a specific set of machines (the web servers running the company's web site). This is a typical role for Traffic Managers - they are traditionally used as a reverse proxy.   Using Stingray Traffic Manager as a Forward Proxy   You may use Stingray Traffic Manager to forward requests on to any other computer, not just to a pre-configured set of machines in a pool. TrafficScript is used to select the exact address to forward the request on to:   pool.use( "Pool name", $ipaddress, $port );   The pool.use() function is used, in the same way as you would normally pick a pool of servers to let Stingray Traffic Manager load balance to. The extra parameters specify the exact machine to use. This machine does not have to belong to the pool that is mentioned; the pool name is there just so Stingray Traffic Manager can use its settings for the connection (e.g. timeout settings, SSL encryption, and so on).   We refer to this technique as 'Forward Proxy mode', or 'Forward Proxy' for short.   What use is a Forward Proxy?   Combined with TrafficScript, the Forward Proxy feature gives you complete control over the load balancing of requests. For example, you could use Stingray Traffic Manager to load balance RDP (Remote Desktop Protocol), using TrafficScript to pick out the user name of a new connection, look the name up in a database and find the hostname of a desktop to allocate for that user.   Forward Proxying also allows Stingray Traffic Manager to be used nearer the clients on a network. With some TrafficScript, Stingray Traffic Manager can operate as a caching web proxy, speeding up local Internet usage. You can then tie in other Stingray Traffic Manager features like bandwidth shaping, service level monitoring and so on. TrafficScript response rules could then filter the incoming data if needed.   Example: A web caching proxy using Stingray Traffic Manager and TrafficScript   You will need to set up Stingray Traffic Manager with a virtual server listening for HTTP proxy traffic. Set HTTP as the protocol, and enable web caching. Also, be sure to disable Stingray's "Location Header rewriting", on the connection management page. Then you will need to add a TrafficScript rule to examine the incoming connections and pick a suitable machine. Here's how you would build such a rule:   # Put a sanity check in the rule, to ensure that only proxy traffic is being received: $host = http.getHostHeader(); if( http.headerExists( "X-Forwarded-For" ) || $host == "" ) { http.sendResponse( "400 Bad request", "text/plain", "This is a proxy service, you must send proxy requests", "" ); } # Trim the leading http://host from the URL if necessary $url = http.getRawUrl(); if ( string.startswith( $url, "http://" ) ) { $slash = string.find( $url, "/", 8 ); $url = string.substring( $url, $slash, -1 ); } http.setPath( string.unescape( $url ) ); # Extract the port out of the Host: header, if it is there $pos = string.find( $host, ":" ); if( $pos >= 0 ) { $port = string.skip( $host, $pos + 1 ); $host = string.substring( $host, 0, $pos - 1 ); } else { $port = 80; } # We need to alter the HTTP request to supply the true IP address of the client # requesting the page, and we need to tweak the request to remove any proxy-specific headers. http.setHeader( "X-Forwarded-For", request.getRemoteIP() ); http.removeHeader( "Range" ); # Removing this header will make the request more cacheable http.removeHeader( "Proxy-Connection" ); # The user might have requested a page that is unresolvable, e.g. # http://fakehostname.nowhere/. Let's resolve the IP and check $ip = net.dns.resolveHost( $host ); if( $ip == "" ) { http.sendResponse( "404 Unknown host", "text/plain", "Failed to resolve " . $host . " to an IP address", "" ); } # The last task is to forward the request on to the target website pool.use( "Forward Proxy Pool", $ip, $port );   Done! Now try using the proxy: Go to your web browser's settings page or your operating system's network configuration (as appropriate) and configure an HTTP proxy.  Fill in the hostname of your Stingray Traffic Manager and the port number of the virtual server running this TrafficScript rule. Now try browsing to a few different web sites. You will be able to see the URLs on the Current Activity page in the UI, and the Web Cache page will show you details of the content that has been cached by Stingray:   'Recent Connections' report lists connections proxied to remote sites   Content Cache report lists the resources that Stingray has cached locally     This is just one use of the forward proxy. You could easily use the feature for other uses, e.g. email delivery, SSL-encrypted proxies, and so on. Try it and see!
View full article
In this article I walk-through a few examples of simple custom monitors written in C that query a MySQL database. Hopefully you will be able to use the example code as the basis of your own monitors.
View full article
Google Analytics i s a great tool for monitoring and tracking visitors to your web sites. Perhaps best of all, it's entirely web based - you only need a web browser to access the analysis services it provides.     To enable tracking for your web sites, you need to embed a small fragment of JavaScript code in every web page. This extension makes this easy, by inspecting all outgoing content and inserting the code into each HTML page, while honoring the users 'Do Not Track' preferences.   Installing the Extension   Requirements   This extension has been tested against Stingray Traffic Manager 9.1, and should function with all versions from 7.0 onwards.   Installation    Copy the contents of the User Analytics rule below. Open in an editor, and paste the contents into a new response rule:     Verify that the extension is functioning correctly by accessing a page through the traffic manager and use 'View Source' to verify that the Google Analytics code has been added near the top of the document, just before the closing </head> tag:     User Analytics rule   # Edit the following to set your profile ID; $defaultProfile = "UA-123456-1"; # You may override the profile ID on a site-by-site basis here $overrideProfile = [ "support.mysite.com" => "UA-123456-2", "secure.mysite.com" => "UA-123456-3" ]; # End of configuration settings # Only process text/html responses $contentType = http.getResponseHeader( "Content-Type" ); if( !string.startsWith( $contenttype, "text/html" )) break; # Honor any Do-Not-Track preference $dnt = http.getHeader( "DNT" ); if ( $dnt == "1" ) break; # Determine the correct $uacct profile ID $uacct = $overrideProfile[ http.getHostHeader() ]; if( !$uacct ) $uacct = $defaultProfile; # See http://www.google.com/support/googleanalytics/bin/answer.py?answer=174090 $script = ' <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(["_setAccount", "' . $uacct . '"]); _gaq.push(["_trackPageview"]); (function() { var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true; ga.src=("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s); })(); </script>'; $body = http.getResponseBody(); # Find the location of the closing '</head>' tag $i = string.find( $body, "</head>" ); if( $i ==-1 ) $i = string.findI( $body, "</head>" ); if( $i ==-1 ) break; # Give up http.setResponseBody( string.left( $body, $i ) . $script . string.skip( $body, $i ));   For some extensions to this rule, check out Faisal Memon's article Google Analytics revisited
View full article
# change this to control debug messages: # 0 -> debug off # 1 -> only log what happens when mappings are reloaded # 2 -> also log what goes on in each request $debug = 0; # The file that we parse is sourced from the "Extra Files" section # of the Traffic Manager and we expect it to be named the same as # the TrafficScript rule (so if your rule is named MyRewrite, the rule will # expect a file called "MyRewrite" in the "extra" directory). # The file we read has 3 elements that are space separated: # Element one is the string RD or RW depending if we are redirecting #   the connection or rewriting it # Element two is the old URL # Element three is the new URL # example: # RD /oldurl http://newsite.com/newurl   # We cannot store the hash-table of mappings in data.get/set because # that is inefficient due to the constant (de-)serialization. # Instead, we employ the following 'flattening' strategy: all direct # mappings (no wildcard or regex) are stored in data.get/set as is. # Wildcards and regexes are sorted by how specific they are (most # specific first) and then stored under the key $file . $idx, where # $idx is their position in the pecking order.  When we do a lookup, # we check the direct mappings first.  If we don't find anything, we # go through the wc/regex keys starting with index 0, counting up. # As long as we keep finding the key, we check for a match.  When we # fail to find the key, we know we've checked all entries and give # up. sub sortAndInsertMappings( $prefix,$mappings,$debug ) {    # the '1' indicates reverse order: we want big numbers first    $sorted = array.sort( hash.keys( $mappings ), 1 );    $upper = array.length( $sorted );    for( $i = 0; $i < $upper; ++$i ) {       $k = $sorted[$i];       $path = string.skip( $k, 2 ); # strip off number of slashes       $key = $prefix . $i;       $value = $path . " " . $mappings[$k];       if( $debug ) { log.info( "Mapping from " . $key . " to '" . $value . "'" ); }       data.set( $key, $value );    } } sub reloadRedirects( $file, $fileTime,$debug ) {    $nonflat_rd = []; # empty hash    $nonflat_rw = [];    # data.reset is really expensive, but could be avoided by actually    # storing an array of 'our' keys under a dedicated entry.  That    # would only be worth it if the number of elements in data that    # are not ours is significant, since then the deserialization    # followed by an iteration over the relevant elements would    # be cheaper than the full scan.    data.reset( $file );    $paths = resource.getlines( $file );    foreach ( $path in $paths ) {       $data = string.split( $path );       if( 3 != array.length( $data ) ) {          log.warn( "Invalid line: " . $line );          continue;       }       $prefix =  $data[0];       $path = $data[1];       $mapping = $data[2];       if( !string.contains( $mapping, "$1" ) && !string.contains( $path, "*" ) ) {          # simple mapping          $k = $file . $prefix . $path;          if( $debug ) { log.info( "Direct mapping from " . $k . " to " . $mapping ); }          data.set( $k, $mapping );       } else {          # wc or regex          # Create a 2-byte binary representation of the number in network          # byte order.  This means we can use alphabetical sorting and still          # end up with an array sorted numerically          $num_slashes = string.intToBytes( string.count( $path, "/" ), 2 );          if( $prefix == "RD" ) {             $nonflat_rd[ $num_slashes . $path ] = $mapping;          } else if ( $prefix == "RW" ) {             $nonflat_rw[ $num_slashes . $path ] = $mapping;          } else {             log.warn( "Invalid prefix: " . $prefix );          }       }    }    sortAndInsertMappings( $file . "RD", $nonflat_rd, $debug );    sortAndInsertMappings( $file . "RW", $nonflat_rw, $debug );    data.set( $file . "-MTIME", $fileTime ); } sub checkPaths( $prefix, $path, $debug ) {    $k = rule.getname() . $prefix . $path;    $mapping = data.get( $k );    if ( string.length( $mapping ) ) {       if( $debug > 1 ) { log.info( "Straight swap from " . $path . " to " . $mapping ); }       return $mapping;    }    for( $i = 0; 1; ++$i ) {       $k = rule.getname() . $prefix . $i;       $data = data.get( $k );       if( $debug > 1 ) { log.info( "Checking key " . $k ); }       if( 0 == string.length( $data ) ) {          if( $debug > 1 ) {             log.info( "No more entries, no match found after " . $i . " entries" );          }          return "";       }       if( $debug > 1 ) { log.info( "Found data " . $data ); }       $arr = string.split( $data );       if( 2 != array.length( $arr ) ) {          log.warn( "Invalid data entry: " . $data );          continue;       }       $match = $arr[0];       $mapping = $arr[1];       if( string.contains( $mapping, "$1" ) ) {          # User needs a regex match /foo/(.*) /bar/$1          if( string.regexmatch( $path, $match ) ) {             if( $debug > 1 ) { log.info( "Regex matched" ); }             return string.regexsub( $path, $match, $mapping );          }       } else if( string.endswith( $match, "*" ) ) {          # Redirect "/foo/*" to /bar          $p = string.drop( $match, 1 );          if( string.startswith( $path, $p ) ) {             if( $debug > 1 ) { log.info( "Wildcard matched" ); }             return string.replace( $path, $p, $mapping );          }       }    }    return ""; } if( $debug > 1 ) { $start_time = sys.time.highres(); } checkMappingsUpToDate(); $path = http.getPath(); $rdPath = checkPaths( "RD", $path, $debug ); if ( string.length( $rdPath ) ) {    if( $debug > 1 ) {       log.info( "Redirecting: " . $path . " to " . $rdPath . "; Elapsed: "          . (sys.time.highres() - $start_time) );     }    http.redirect( $rdPath ); } $rwPath = checkPaths( "RW", $path, $debug ); if ( string.length( $rwPath ) ) {    if( $debug > 1 ) {       log.info( "Rewriting: " . $path . " to " . $rwPath . "; Elapsed: "          . (sys.time.highres() - $start_time) );    }    http.setPath( $rwPath ); } if( $debug > 1 ) {    log.info( "No match, request goes through unchanged; Elapsed: "          . (sys.time.highres() - $start_time) ); } # Since we store the mappings as multiple values, when the file has # changed, we have to delete all mappings from the file and then step # by step populate the map again with the new mappings.  This means # that while we're re-populating, a lookup for a particular value might # incorrectly find neither the old nor the new value.  TrafficScript doesn't # have real locks that guarantee access by only one process.  We can # emulate them closely, however, by guarding write access to the # mappings with a single 0/1 entry. sub checkMappingsUpToDate() {    $pid = sys.getpid();    if( !string.length( data.get( $pid ) ) ) { data.set( $pid, 0 ); }    $file = rule.getname();    if ( resource.exists( $file ) ) {       # we could use 'mtime' as the 'lock' key as well by setting it to       # a 'magic' value to indicate we're updating       $lock_key = $file . "-LOCK";       $mod_key = $file . "-MTIME";       while( 1 ) {          $mtime = data.get( $mod_key );          $fileTime = resource.getMTime( $file );          if ( $mtime == $fileTime ) {             break;          }          if ( !data.get( $lock_name ) ) {             data.set( $lock_name, "1" ); # 'lock'             log.info( $pid . " parsing file" );             reloadRedirects( $file, $fileTime, $debug );             data.remove( $lock_name ); # 'unlock'             break;          } else {             # wait for the other process to reload the file             $waits = data.get( $pid );             data.set( $pid, $waits+1 );             connection.sleep( 2 );          }       }    } }
View full article
Java Extensions are one of the 'data plane' APIs provided by Traffic Manager to process network transactions.  Java Extensions are invoked from TrafficScript using the java.run() function.   This article contains a selection of technical tips and solutions to illustrate the use of Java Extensions.   Basic Language Examples   Writing Java Extensions - an introduction (presenting a template and 'Hello World' application) Writing TrafficScript functions in Java (illustrating how to use the GenericServlet interface) Tech Tip: Prompting for Authentication in a Java Extension Tech Tip: Reading HTTP responses in a Java Extension   Advanced Language Examples   Apache Commons Logging (TODO) Authenticating users with Active Directory and Stingray Java Extensions Watermarking Images with Traffic Manager and Java Extensions Watermarking PDF documents with Traffic Manager and Java Extensions Being Lazy with Java Extensions XML, TrafficScript and Java Extensions Merging RSS feeds using Java Extensions (12/17/2008) Serving Web Content from Traffic Manager using Java Stingray-API.jar: A Java Interface Library for Traffic Manager's SOAP Control API TrafficManager Status - Using the Control API from a Java Extension   Java Extensions in other languages   PyRunner.jar: Running Python code in Traffic Manager Making Traffic Manager more RAD with Jython! Scala, Traffic Manager and Java Extensions (06/30/2009)   More information   Feature Brief: Java Extensions in Traffic Manager Java Development Guide documentation in the Product Documentation
View full article
The following code uses Stingray's Control API to list all the running virtual servers on a cluster. The code is written in Java and uses the Stingray-API.jar library described in this article: Using Stingray's SOAP Control API with Java. listVS.java Make sure to edit the endpoint address (https://username:password@host:9090/soap) so that the username, password and host match the admin interface for your Stingray. import com.zeus.soap.zxtm._1_0.*; import java.security.Security; import java.security.KeyStore; import java.security.Provider; import java.security.cert.X509Certificate; import javax.net.ssl.ManagerFactoryParameters; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactorySpi; import javax.net.ssl.X509TrustManager; public class listVS {    public static void main( String[] args ) {       // Install the all-trusting trust manager       Security.addProvider( new MyProvider() );       Security.setProperty( "ssl.TrustManagerFactory.algorithm", "TrustAllCertificates");       try {          VirtualServerLocator vsl = new VirtualServerLocator();          vsl.setVirtualServerPortEndpointAddress(             " https://username:password@host:9090/soap " );          VirtualServerPort vsp = vsl.getVirtualServerPort();          String[] vsnames = vsp.getVirtualServerNames();          boolean[] vsenabled = vsp.getEnabled( vsnames );          for( int i = 0; i < vsnames.length; i++ ){             if( vsenabled ){                System.out.println( vsnames );             }          }       } catch (Exception e) {          System.out.println( e.toString() );       }    }    /* The following code disables certificate checking.    * Use the Security.addProvider and Security.setProperty    * calls to enable it */    public static class MyProvider extends Provider {       public MyProvider() {          super( "MyProvider", 1.0, "Trust certificates" );          put( "TrustManagerFactory.TrustAllCertificates", MyTrustManagerFactory.class.getName() );       }       protected static class MyTrustManagerFactory extends TrustManagerFactorySpi {          public MyTrustManagerFactory() {}          protected void engineInit( KeyStore keystore ) {}          protected void engineInit(             ManagerFactoryParameters mgrparams ) {}          protected TrustManager[] engineGetTrustManagers() {             return new TrustManager[] { new MyX509TrustManager() };          }       }       protected static class MyX509TrustManager implements X509TrustManager {          public void checkClientTrusted( X509Certificate[] chain, String authType) {}          public void checkServerTrusted( X509Certificate[] chain, String authType) {}          public X509Certificate[] getAcceptedIssuers() { return null; }       }    } } Running the example To build and run the code, you'll first need to do the following: Download axis 1.4 from Index of /dist/ws/axis/1_4. You can unzip the axis-bin package in your working directory, or you can install the jar files permanently (e.g. in the JAVAHOME/jre/lib/ext/ directory). Download the JavaMail library; either unzip the package in your working directory, or install the mail.jar file to the JAVAHOME/jre/lib/ext/ directory.  This package provides class implementations that, though not required, will avoid warning about missing classes Compile and run the example as follows: $ javac -cp Stingray-API.jar:axis-1_4/lib/* listVS.java $ jar -cvfe listVS.jar listVS listVS*.class $ java -cp Stingray-API.jar:axis-1_4/lib/*:javamail-1.4.7/lib/*:listVS.jar listVS Main website Mail servers Test site If you install the Stingray-API, Apache Axis 1.2 and JavaMail libraries in your system classpath, then you don't need to reference them explicity when you build and run this example. Notes The bulk of this code disables client certificate checking. Details of the code and surrounding infrastructure are at http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html. Read more Collected Tech Tips: SOAP Control API examples
View full article
The TrafficScript function http.changeSite() makes it easy to redirect clients from one domain to another.  You can also use it to reliably redirect clients from http to https (or https to http), or from one document tree on a website (e.g. /products) to another (e.g /sales). # Example: Redirect client from www.site.com to www.site.co.uk if( geo.getCountryCode( request.getRemoteIP() ) == "GB" ) {   http.changeSite( "www.site.co.uk" ); } # Example: Force client to https (assuming this rule is attached to an HTTP virtual server) http.changeSite( " https:// " . http.getHostHeader() ); # Example: move client from one tree to another $path = http.getPath(); if( string.startsWith( $path, "/products" ) ) http.changeSite( http.getHostHeader(). "/sales" ); For more fine-grained control of HTTP redirects, you can also use the http.redirect() function. Read more Collected Tech Tips: TrafficScript examples
View full article
When you need to scale out your MySQL database, replication is a good way to proceed. Database writes (UPDATEs) go to a 'master' server and are replicated across a set of 'slave' servers. Reads (SELECTs) are load-balanced across the slaves.   Overview   MySQL's replication documentation describes how to configure replication:   MySQL Replication   A quick solution...   If you can modify your MySQL client application to direct 'Write' (i.e. 'UPDATE') connections to one IP address/port and 'Read' (i.e. 'SELECT') connections to another, then this problem is trivial to solve. This generally needs a code update (Using Replication for Scale-Out).   You will need to direct the 'Update' connections to the master database (or through a dedicated Traffic Manager virtual server), and direct the 'Read' connections to a Traffic Manager virtual server (in 'generic server first' mode) and load-balance the connections across the pool of MySQL slave servers using the 'least connections' load-balancing method: Routing connections from the application   However, in most cases, you probably don't have that degree of control over how your client application issues MySQL connections; all connections are directed to a single IP:port. A load balancer will need to discriminate between different connection types and route them accordingly.   Routing MySQL traffic   A MySQL database connection is authenticated by a username and password. In most database designs, multiple users with different access rights are used; less privileged user accounts can only read data (issuing 'SELECT' statements), and more privileged users can also perform updates (issuing 'UPDATE' statements). A well architected application with sound security boundaries will take advantage of these multiple user accounts, using the account with least privilege to perform each operation. This reduces the opportunities for attacks like SQL injection to subvert database transactions and perform undesired updates.   This article describes how to use Traffic Manager to inspect and manage MySQL connections, routing connections authenticated with privileged users to the master database and load-balancing other connects to the slaves:   Load-balancing MySQL connections   Designing a MySQL proxy   Stingray Traffic Manager functions as an application-level (layer-7) proxy. Most protocols are relatively easy for layer-7 proxies like Traffic Manager to inspect and load-balance, and work 'out-of-the-box' or with relatively little configuration.   For more information, refer to the article Server First, Client First and Generic Streaming Protocols.   Proxying MySQL connections   MySQL is much more complicated to proxy and load-balance.   When a MySQL client connects, the server immediately responds with a randomly generated challenge string (the 'salt'). The client then authenticates itself by responding with the username for the connection and a copy of the 'salt' encrypted using the corresponding password:   Connect and Authenticate in MySQL   If the proxy is to route and load-balance based on the username in the connection, it needs to correctly authenticate the client connection first. When it finally connects to the chosen MySQL server, it will then have to re-authenticate the connection with the back-end server using a different salt.   Implementing a MySQL proxy in TrafficScript   In this example, we're going to proxy MySQL connections from two users - 'mysqlmaster' and 'mysqlslave', directing connections to the 'SQL Master' and 'SQL Slaves' pools as appropriate.   The proxy is implemented using two TrafficScript rules ('mysql-request' and 'mysql-response') on a 'server-first' Virtual Server listening on port 3306 for MySQL client connections. Together, the rules implement a simple state machine that mediates between the client and server:   Implementing a MySQL proxy in TrafficScript   The state machine authenticates and inspects the client connection before deciding which pool to direct the connection to. The rule needs to know the encrypted password and desired pool for each user. The virtual server should be configured to send traffic to the built-in 'discard' pool by default.   The request rule:   Configure the following request rule on a 'server first' virtual server. Edit the values at the top to reflect the encrypted passwords (copied from the MySQL users table) and desired pools:   sub encpassword( $user ) { # From the mysql users table - double-SHA1 of the password # Do not include the leading '*' in the long 40-byte encoded password if( $user == "mysqlmaster" ) return "B17453F89631AE57EFC1B401AD1C7A59EFD547E5"; if( $user == "mysqlslave" ) return "14521EA7B4C66AE94E6CFF753453F89631AE57EF"; } sub pool( $user ) { if( $user == "mysqlmaster" ) return "SQL Master"; if( $user == "mysqlslave" ) return "SQL Slaves"; } $state = connection.data.get( "state" ); if( !$state ) { # First time in; we've just recieved a fresh connection $salt1 = randomBytes( 8 ); $salt2 = randomBytes( 12 ); connection.data.set( "salt", $salt1.$salt2 ); $server_hs = "\0\0\0\0" . # length - fill in below "\012" . # protocol version "Stingray Proxy v0.9\0" . # server version "\01\0\0\0" . # thread 1 $salt1."\0" . # salt(1) "\054\242" . # Capabilities "\010\02\0" . # Lang and status "\0\0\0\0\0\0\0\0\0\0\0\0\0" . # Unused $salt2."\0"; # salt(2) $l = string.length( $server_hs )-4; # Will be <= 255 $server_hs = string.replaceBytes( $server_hs, string.intToBytes( $l, 1 ), 0 ); connection.data.set( "state", "wait for clienths" ); request.sendResponse( $server_hs ); break; } if( $state == "wait for clienths" ) { # We've recieved the client handshake. $chs = request.get( 1 ); $chs_len = string.bytesToInt( $chs ); $chs = request.get( $chs_len + 4 ); # user starts at byte 36; password follows after $i = string.find( $chs, "\0", 36 ); $user = string.subString( $chs, 36, $i-1 ); $encpasswd = string.subString( $chs, $i+2, $i+21 ); $passwd2 = string.hexDecode( encpassword( $user ) ); $salt = connection.data.get( "salt" ); $passwd1 = string_xor( $encpasswd, string.hashSHA1( $salt.$passwd2 ) ); if( string.hashSHA1( $passwd1 ) != $passwd2 ) { log.warn( "User '" . $user . "': authentication failure" ); connection.data.set( "state", "authentication failed" ); connection.discard(); } connection.data.set( "user", $user ); connection.data.set( "passwd1", $passwd1 ); connection.data.set( "clienths", $chs ); connection.data.set( "state", "wait for serverhs" ); request.set( "" ); # Select pool based on user pool.select( pool( $user ) ); break; } if( $state == "wait for client data" ) { # Write the client handshake we remembered from earlier to the server, # and piggyback the request we've just recieved on the end $req = request.get(); $chs = connection.data.get( "clienths" ); $passwd1 = connection.data.get( "passwd1" ); $salt = connection.data.get( "salt" ); $encpasswd = string_xor( $passwd1, string.hashSHA1( $salt . string.hashSHA1( $passwd1 ) ) ); $i = string.find( $chs, "\0", 36 ); $chs = string.replaceBytes( $chs, $encpasswd, $i+2 ); connection.data.set( "state", "do authentication" ); request.set( $chs.$req ); break; } # Helper function sub string_xor( $a, $b ) { $r = ""; while( string.length( $a ) ) { $a1 = string.left( $a, 1 ); $a = string.skip( $a, 1 ); $b1 = string.left( $b, 1 ); $b = string.skip( $b, 1 ); $r = $r . chr( ord( $a1 ) ^ ord ( $b1 ) ); } return $r; }   The response rule   Configure the following as a response rule, set to run every time, for the MySQL virtual server.   $state = connection.data.get( "state" ); $authok = "\07\0\0\2\0\0\0\02\0\0\0"; if( $state == "wait for serverhs" ) { # Read server handshake, remember the salt $shs = response.get( 1 ); $shs_len = string.bytesToInt( $shs )+4; $shs = response.get( $shs_len ); $salt1 = string.substring( $shs, $shs_len-40, $shs_len-33 ); $salt2 = string.substring( $shs, $shs_len-13, $shs_len-2 ); connection.data.set( "salt", $salt1.$salt2 ); # Write an authentication confirmation now to provoke the client # to send us more data (the first query). This will prepare the # state machine to write the authentication to the server connection.data.set( "state", "wait for client data" ); response.set( $authok ); break; } if( $state == "do authentication" ) { # We're expecting two responses. # The first is the authentication confirmation which we discard. $res = response.get(); $res1 = string.left( $res, 11 ); $res2 = string.skip( $res, 11 ); if( $res1 != $authok ) { $user = connection.data.get( "user" ); log.info( "Unexpected authentication failure for " . $user ); connection.discard(); } connection.data.set( "state", "complete" ); response.set( $res2 ); break; }   Testing your configuration   If you have several MySQL databases to test against, testing this configuration is straightforward. Edit the request rule to add the correct passwords and pools, and use the mysql command-line client to make connections:   $ mysql -h zeus -u username -p Enter password: *******   Check the 'current connections' list in the Traffic Manager UI to see how it has connected each session to a back-end database server.   If you encounter problems, try the following steps:   Ensure that trafficscript!variable_pool_use is set to 'Yes' in the Global Settings page on the UI. This setting allows you to use non-literal values in pool.use() and pool.select() TrafficScript functions. Turn on the log!client_connection_failures and log!server_connection_failures settings in the Virtual Server > Connection Management configuration page; these settings will configure the traffic manager to write detailed debug messages to the Event Log whenever a connection fails.   Then review your Traffic Manager Event Log and your mysql logs in the event of an error.   Traffic Manager's access logging can be used to record every connection. You can use the special *{name}d log macro to record information stored using connection.data.set(), such as the username used in each connection.   Conclusion   This article has demonstrated how to build a fairly sophisticated protocol parser where the Traffic Manager-based proxy performs full authentication and inspection before making a load-balancing decision. The protocol parser then performs the authentication again against the chosen back-end server.   Once the client-side and server-side handshakes are complete, Traffic Manager will simply forward data back and fro between the client and the server.   This example addresses the problem of scaling out your MySQL database, giving load-balancing and redundancy for database reads ('SELECTs'). It does not address the problem of scaling out your master 'write' server - you need to address that by investing in a sufficiently powerful server, architecting your database and application to minimise the number and impact of write operations, or by selecting a full clustering solution.     The solution leaves a single point of failure, in the form of the master database. This problem could be effectively dealt with by creating a monitor that tests the master database for correct operation. If it detects a failure, the monitor could promote one of the slave databases to master status and reconfigure the 'SQLMaster' pool to direct write (UPDATE) traffic to the new MySQL master server.   Acknowledgements   Ian Redfern's MySQL protocol description was invaluable in developing the proxy code.     Appendix - Password Problems? This example assumes that you are using MySQL 4.1.x or later (it was tested with MySQL 5 clients and servers), and that your database has passwords in the 'long' 41-byte MySQL 4.1 (and later) format (see http://dev.mysql.com/doc/refman/5.0/en/password-hashing.html)   If you upgrade a pre-4.1 MySQL database to 4.1 or later, your passwords will remain in the pre-4.1 'short' format.   You can verify what password format your MySQL database is using as follows:   mysql> select password from mysql.user where user='username'; +------------------+ | password         | +------------------+ | 6a4ba5f42d7d4f51 | +------------------+ 1 rows in set (0.00 sec)   mysql> update mysql.user set password=PASSWORD('password') where user='username'; Query OK, 1 rows affected (0.00 sec) Rows matched: 1  Changed: 1  Warnings: 0   mysql> select password from mysql.user where user='username'; +-------------------------------------------+ | password                                  | +-------------------------------------------+ | *14521EA7B4C66AE94E6CFF753453F89631AE57EF | +-------------------------------------------+ 1 rows in set (0.00 sec)   If you can't create 'long' passwords, your database may be stuck in 'short' password mode. Run the following command to resize the password table if necessary:   $ mysql_fix_privilege_tables --password=admin password   Check that 'old_passwords' is not set to '1' (see here) in your my.cnf configuration file.   Check that the mysqld process isn't running with the --old-passwords option.   Finally, ensure that the privileges you have configured apply to connections from the Stingray proxy. You may need to GRANT... TO 'user'@'%' for example.
View full article
This Document provides step by step instructions on how to set up Brocade Virtual Traffic Manager for Microsoft IIS.   This document has been updated from the original deployment guides written for Riverbed Stingray and SteelApp software.
View full article
The Enforcer rule used by Stingray Application Firewall (SAF) will pass all requests to the local decider processes for inspection and security. For performance reasons, you may not want to inspect all requests.  For example, if some requests that are processed by your virtual server are sent to a cluster of servers hosting static content, and other requests are sent to a completely separate set of transaction servers, then it may make pragmatic sense to just inspect the requests that are routed to your transaction servers. You can whitelist a request by setting the a connection-local variable 'enforcer.whitelist' to '1'. Example The following rule should be applied to the Virtual Server prior to the SAF Enforcer rule.  It will whitelist requests only if they are using the HTTP "GET" method, do not have a Query String, and the file extension appears in the $fileTypes array #=-SAF Bypass Rule. This needs to be run as a request rule prior to the SAF Enforcer rule # Only Bypass GET Requests if ( http.getMethod() != "GET" )    break; # Only byPass requests with no Query String if ( http.getQueryString() )    break; # Array of file extensions to bypass $fileTypes = [ "css", "js", "png", "gif", "jpg" ]; # Pull out extension from path $extension = array.pop( string.split( http.getPath(), ".") ); # If the extension exists in our array, then set the whitelist flag if ( array.contains($fileTypes, $extension) ) {    connection.data.set("enforcer.whitelist", 1); }
View full article
Services Director 20.1 supports secure authentication for administrators, and is also made available as a Long-Term Support release for extended operations,
View full article
In this release, Pulse Secure Traffic Manager offers some additional capabilities to support secure authentication for administrators.
View full article
In order to support our new Certified Technical Expert training course for Pulse vADC, we have created a demonstration package which contains files to support the training course.
View full article
In this release, Pulse Secure Virtual Traffic Manager has more enhancements for closer integration with Pulse Connect Secure (PCS) and Pulse Policy Secure (PPS), including support for simpler session persistence of RADIUS.
View full article
Services Director certificates can be updated with a script, to ensure continued access on the secure authenticated communications channel.
View full article