cancel
Showing results for 
Search instead for 
Did you mean: 

Pulse Secure vADC

Sort by:
Installation   Unzip the download ( Stingray Traffic Manager Cacti Templates.zip ) Via the Cacti UI, “Import Templates” and import the Data, Host, and Graph templates.  * Included graph templates are not required for functionality. Copy the files for the Cacti folder in the zip file to their corresponding directory inn your cacti install. Stingray Global Values script query - /cacti/site/scripts/stingray_globals.pl Stingray Virtual Server Table snmp query - cacti/resource/snmp_queries/stingray_vservers. Assign the host template to Traffic Manager(s) and create new graphs.   * Due to the method used by Cacti for creating graphs and the related RRD files, it is my recommendation NOT to create all graphs via the Device Page.   If you create all the graphs via the “*Create Graphs for this Host” link on the device page, Cacti will create an individual data source (RRD file and SNMP query for each graph) resulting in a significant amount of wasted Cacti and Device resources. Test yourself with the Stingray SNMP graph.   My recommendation is to create a single initial graph for each Data Query or Data Input method (i.e. one for Virtual Servers and one for Global values) and add any additional graphs via the Cacti’s Graph Management using the existing Data Source Drop downs.   Data Queries   Stingray Global Values script query - /cacti/site/scripts/stingray_globals.pl * Perl script to query the STM for most of the sys.globals values Stingray Virtual Server Table snmp query - cacti/resource/snmp_queries/stingray_vservers.xml * Cacti XML snmp query for the Virtual Servers Table MIB   Graph Templates   Stingray_-_global_-_cpu.xml Stingray_-_global_-_dns_lookups.xml Stingray_-_global_-_dns_traffic.xml Stingray_-_global_-_memory.xml Stingray_-_global_-_snmp.xml Stingray_-_global_-_ssl_-_client_cert.xml Stingray_-_global_-_ssl_-_decryption_cipher.xml Stingray_-_global_-_ssl_-_handshakes.xml Stingray_-_global_-_ssl_-_session_id.xml Stingray_-_global_-_ssl_-_throughput.xml Stingray_-_global_-_swap_memory.xml Stingray_-_global_-_system_-_misc.xml Stingray_-_global_-_traffic_-_misc.xml Stingray_-_global_-_traffic_-_tcp.xml Stingray_-_global_-_traffic_-_throughput.xml Stingray_-_global_-_traffic_script_data_usage.xml Stingray_-_virtual_server_-_total_timeouts.xml Stingray_-_virtual_server_-_connections.xml Stingray_-_virtual_server_-_timeouts.xml Stingray_-_virtual_server_-_traffic.xml     Sample Graphs (click image for full size)           Compatibility   This template has been tested with STM 9.4 and Cacti 0.8.8.a   Known Issues   Cacti will create unnecessary queries and data files if the “*Create Graphs for this Host” link on the device page is used. See install notes for work around.   Conclusion   Cacti is sufficient with providing SNMP based RRD graphs, but is limited in Information available, Analytics, Correlation, Scale, Stability and Support.   This is not just a shameless plug; Brocade offers a MUCH more robust set of monitoring and performance tools.
View full article
The libDNS.rts library provides a means to interrogate and modify DNS traffic from a TrafficScript rule, and to respond directly to DNS request when desired.
View full article
When deploying applications using content management systems, application owners are typically limited to the functionality of the CMS application in use or third party add-on's available. Unfortunately, these components alone may not deliver the application requirements.  Leaving the application owner to dedicate resources to develop a solution that usually ends up taking longer than it should, or not working at all. This article addresses some hypothetical production use cases, where the application does not provide the administrators an easy method to add a timer to the website.   This solution builds upon the previous articles (Embedded Google Maps - Augmenting Web Applications with Traffic Manager and Embedded Twitter Timeline - Augmenting Web Applications with Traffic Manager). "Using" a solution from Owen Garrett (See Instrument web content with Traffic Manager),This example will use a simple CSS overlay to display the added information.   Basic Rule   As a starting point to understand the minimum requirements, and to customize for your own use. I.E. Most people want to use "text-align:center". Values may need to be added to the $style or $html for your application, see examples.   1 2 3 4 5 6 7 8 9 10 11 if (!string.startsWith(http.getResponseHeader( "Content-Type" ), "text/html" ) ) break;       $timer =  ( "366" - ( sys. gmtime . format ( "%j" ) ) );       $html =  '<div class="Countdown">' . $timer . ' DAYS UNTIL THE END OF THE YEAR</div>' ;       $style = '<style type="text/css">.Countdown{z-index:100;background:white}</style>' ;       $body = http.getResponseBody();  $body = string.regexsub( $body , "(<body[^>]*>)" , $style . "$1\n" . $html . "\n" , "i" );  http.setResponseBody( $body );   Example 1 - Simple Day Countdown Timer   This example covers a common use case popular with retailers, a countdown for the holiday shopping season. This example also adds font formatting and additional text with a link.   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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #Only process text/html content  if ( !string.startsWith (http.getResponseHeader ( "Content-Type" ), "text/html" )) break;       #Countdown target  #Julian day of the year "001" to "366"  $targetday = "359" ;  $bgcolor = "#D71920" ;  $labelday = "DAYS" ;  $title = "UNTIL CHRISTMAS" ;  $titlecolor = "white" ;  $link = "/dept.jump?id=dept20020200034" ;  $linkcolor = "yellow" ;  $linktext = "VISIT YOUR ONE-STOP GIFT SHOP" ;       #Calculate days between today and targetday  $timer = ( $targetday - ( sys. gmtime . format ( "%j" ) ) );       #Remove the S from "DAYS" if only 1 day left  if ( $timer == 1 ){     $labelday = string.drop( $label , 1 );  };       $html = '  <div class= "TrafficScriptCountdown" >     <h3>       <font color= "'.$titlecolor.'" >         '.$timer.' '.$labelday.' '.$title.'        </font>       <a href= "'.$link.'" >         <font color= "'.$linkcolor.'" >           '.$linktext.'          </font>       </a>     </h3>  </div>  ';       $style = '  <style type= "text/css" >  .TrafficScriptCountdown {     position:relative;     top:0;     width:100%;     text-align:center;     background: '.$bgcolor.' ;     opacity:100%;     z- index :1000;     padding:0  }  </style>  ';       $body = http.getResponseBody();       $body = string.regexsub( $body , "(<body[^>]*>)" , $style . "$1\n" . $html . "\n" , "i" );       http.setResponseBody( $body );?    Example 1 in Action     Example 2 - Ticking countdown timer with second detail   This example covers how to dynamically display the time down to seconds. Opposed to sending data to the client every second, I chose to use a client side java script found @ HTML Countdown to Date v3 (Javascript Timer)  | ricocheting.com   Example 2 Response Rule   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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 if (!string.startsWith(http.getResponseHeader( "Content-Type" ), "text/html" ) ) break;       #Countdown target  $year = "2014" ;  $month = "11" ;  $day = "3" ;  $hr = "8" ;  $min = "0" ;  $sec = "0" ;  #number of hours offset from UTC  $utc = "-8" ;       $labeldays = "DAYS" ;  $labelhrs = "HRS" ;  $labelmins = "MINS" ;  $labelsecs = "SECS" ;  $separator = ", " ;       $timer = '<script type= "text/javascript" >  var CDown=function(){this.state=0,this.counts=[],this.interval=null};CDown. prototype =\  {init:function(){this.state=1;var t=this;this.interval=window.setInterval(function()\  {t.tick()},1e3)},add:function(t,s){tzOffset= '.$utc.' ,dx=t.toGMTString(),dx=dx. substr \  (0,dx. length -3),tzCurrent=t.getTimezoneOffset()/60*-2,t.setTime(Date.parse(dx)),\  t.setHours(t.getHours()+tzCurrent-tzOffset),this.counts. push ({d:t,id:s}),this.tick(),\  0==this.state&&this.init()},expire:function(t){ for (var s in t)this.display\  (this.counts[t[s]], "Now!" ),this.counts. splice (t[s],1)}, format :function(t){var s= "" ;\  return 0!=t.d&&(s+=t.d+ " " +(1==t.d? "'.string.drop( $labeldays, 1 ).'" :" '.$labeldays.' \  ")+" '.$separator.' "),0!=t.h&&(s+=t.h+" "+(1==t.h?" '.string.drop( $labelhrs, 1 ).' ":\  "'.$labelhrs.'" )+ "'.$separator.'" ),s+=t.m+ " " +(1==t.m?"\  '.string.drop( $labelmins, 1 ).' ":" '.$labelmins.' ")+" '.$separator.' ",s+=t.s+" "\  +(1==t.s? "'.string.drop( $labelsecs, 1 ).'" : "'.$labelsecs.'" )+ "'.$separator.'" \  ,s. substr (0,s. length -2)},math:function(t){var i=w=d=h=m=s=ms=0; return ms=( "" +\  (t %1e3 +1e3)). substr (1,3),t=Math.floor(t/1e3),i=Math.floor(t/31536e3),w=Math.floor\  (t/604800),d=Math.floor(t/86400),t%=86400,h=Math.floor(t/3600),t%=3600,m=Math.floor\  (t/60),t%=60,s=Math.floor(t),{y:i,w:w,d:d,h:h,m:m,s:s,ms:ms}},tick:function()\  {var t=(new Date).getTime(),s=[],i=0,n=0; if (this.counts) for (var e=0,\  o=this.counts. length ;o>e;++e)i=this.counts[e],n=i.d.getTime()-t,0>n?s. push (e):\  this.display(i,this. format (this.math(n)));s. length >0&&this.expire(s),\  0==this.counts. length &&window.clearTimeout(this.interval)},display:function(t,s)\  {document.getElementById(t.id).innerHTML=s}},window.onload=function()\  {var t=new CDown;t.add(new Date\  ( '.$year.' , '.--$month.' , '.$day.' , '.$hr.' , '.$min.' , '.$sec.' ), "countbox1" )};  </script><span id= "countbox1" ></span>';       $html =  '<div class= "TrafficScriptCountdown" ><center><h3><font color= "white" >\  COUNTDOWN TO RIVERBED FORCE '.$timer.' </font>\  <a href= "https://secure3.aetherquest.com/riverbedforce2014/" ><font color= "yellow" >\  REGISTER NOW</a></h3></font></center></div>';       $style = '<style type= "text/css" >.TrafficScriptCountdown{position:relative;top:0;\  width:100%;background: #E9681D;opacity:100%;z-index:1000;padding:0}</style>';       http.setResponseBody( string.regexsub( http.getResponseBody(),  "(<body[^>]*>)" , $style . "$1\n" . $html . "\n" , "i" ) );    Example 2 in action     Notes   Example 1 results in faster page load time than Example 2. Example 1 can be easily extended to enable Traffic Script to set $timer to include detail down to the second as in example 2. Be aware of any trailing space(s) after the " \ " line breaks when copy and paste is used to import the rule. Incorrect spacing can stop the JS and the HTML from functioning. You may have to adjust the elements for your web application. (i.e. z-index, the regex sub match, div class, etc.).   This is a great example of using Traffic Manager to deliver a solution in minutes that could otherwise could take hours.
View full article
Stingray Traffic Manager has a host of great, capable features to improve the performance and reliability of your web servers.  However, is there sometimes a case for putting the web content on Stingray directly, and using it as a webserver?   The role of a webserver in a modern application has shrunk over the years.  Now it's often just a front-end for a variety of application servers, performing authentication and serving simple static web content... not unlike the role of Stingray.  Often, its position in the application changes when Stingray is added:     Now, if you could move the static content on to Stingray Traffic Manager, wouldn't that help to simplify your application architecture still further? This article presents three such ways:   A simple webserver in TrafficScript   TrafficScript can send back web pages without difficulty, using the http.sendResponse() function.  It can load web content directly from Stingray's Resource Directory (the Extra Files catalog).   Here's a simple TrafficScript webserver that intercepts all requests for content under '/static' and attempts to satisfy them with files from the Resource directory:   # We will serve static web pages for all content under this directory $static = "/static/"; $page = http.getPath(); if( !string.startsWith( $page, $static )) break; # Look for the file in the resource directory $file = string.skip( $page, string.length( $static )); if( resource.exists( $file )) { # Page found! http.sendResponse( 200, "text/html", resource.get( $file ), "" ); } else { # Page not found, send an error back http.sendResponse( 404, "text/html", "Not found", "" ); }   Add this file (as a request rule) to your virtual server and upload some files to Catalog > Extra Files.  You can then browse them from Stingray, using URLs beginning /static.   This is a very basic example.  For example, it does not support mime types (it assumes everything is text/html) - you can check out the Sending custom error pages article for a more sophisticated TrafficScript example that shows you how to host an entire web page (images, css and all) that you can use as an error page if your webservers are down.   However, the example also does not support directory indices... in fact, because the docroot is the extra files catalog, there's no (easy) way to manage a hierarchy of web content.  Wouldn't it be better if you could serve your web content directly from a directory in disk?   A more sophisticated Web Server in Java   The article Serving Web Content from Stingray using Java presents a more sophisticated web server written in Java.  It runs as a Java Extension and can access files in a nominated docroot outside of the Stingray configuration.  It supports mime types and directory indices.   Another webserver - in Python   Stingray can also run application code in Python, by way of the PyRunner.jar: Running Python code in Stingray Traffic Manager implementation that runs Python code on Stingray's local JVM.  This article Serving Web Content from Stingray using Python presents an alternative webserver written in Python.   Optimizing for Performance   Perhaps the most widely used feature in Stingray Traffic Manager, after basic load balancing, health monitoring and the like, i s Content Caching .  Content Caching is a very easy and effective way to reduce the overhead of generating web content, whether the content is read from disk or generated by an application.  The content generated by our webserver implementations is fairly 'static' (does not change) so it's ripe for caching to reduce the load on our webservers.   There's one complication - you can't just turn content caching on and expect it to work in this situation.  That's because content caching hooks into two stages in the transaction lifecycle in Stingray:   Once all response rules have completed, Stingray will inspect the final response and decide whether it can be cached for future reuse Once all request rules have completed, Stingray will examine the current request and determine if there's a suitable response in the cache   In our webserver implementations, the content was generated during the request processing step and written back to the client using http.sendResponse (or equivalent).  We never run any response rules (so the content cannot be cached) and we never get to the end of the request rules (so we would not check the cache anyway).   The elegant solution is to create a virtual server in Stingray specifically to run the WebServe extension.  The primary virtual server can forward traffic to your application servers, or to the internal 'webserver' virtual server as appropriate.  It can then cache the responses (and respond directly to future requests) without any difficulty:   The primary Virtual Server decides whether to direct traffic to the back-end servers, or to the internal web server using an appropriate TrafficScript rule:   Create a new HTTP virtual server - this will be used to run the Webserver Extension. Configure the virtual server to listen on localhost only, as it need not be reachable from anywhere else.  You can chose an unused high port, such as 8080 perhaps.   Add the appropriate TrafficScript rule to this virtual server to make it run the WebServer Extension.   Create a pool called 'Internal Web Server' that will direct traffic to this new virtual server. The pool should contain the node localhost:[port].   Extend the TrafficScript rule on the original virtual server to:   # Use the internal web server for static content if( string.startsWith( http.getPath(), "/static" )) { pool.use( "Internal Web Server" ); }   Enable the web cache on the original virtual server.   Now, all traffic goes to the original virtual server as before. Static pages are directed to the internal web server, and the content from that will be cached. With this configuration, Stingray should be able to serve web pages as quickly as needed.   Don't forget that all the other Stingray features like content compression, logging, rate-shaping, SSL encryption and so on can all be used with the new internal web server. You can even use response rules to alter the static pages as they are sent out.   Now, time to throw your web servers away?  
View full article
With the evolution of social media as a tool for marketing and current events, we commonly see the Twitter feed updated long before the website. It’s not surprising for people to rely on these outlets for information.   Fortunately Twitter provides a suite of widgets and scripting tools to integrate Twitter information for your application. The tools available can be implemented with little code changes and support many applications. Unfortunately the same reason a website is not as fresh as social media is because of the code changes required. The code could be owned by different people in the organization or you may have limited access to the code due to security or CMS environment. Traffic Manager provides the ability to insert the required code into your site with no changes to the application.      Twitter Overview "Embeddable timelines make it easy to syndicate any public Twitter timeline to your website with one line of code. Create an embedded timeline from your widgets settings page on twitter.com, or choose “Embed this…” from the options menu on profile, search and collection pages.   Just like timelines on twitter.com, embeddable timelines are interactive and enable your visitors to reply, Retweet, and favorite Tweets directly from your pages. Users can expand Tweets to see Cards inline, as well as Retweet and favorite counts. An integrated Tweet box encourages users to respond or start new conversations, and the option to auto-expand media brings photos front and center.   These new timeline tools are built specifically for the web, mobile web, and touch devices. They load fast, scale with your traffic, and update in real-time." -twitter.com   Thank you Faisal Memon for the original article Using TrafficScript to add a Twitter feed to your web site   As happens more often than than not, platform access changes. This time twitter is our prime example. When loading Twitter js, http://widgets.twimg.com/j/2/widget.js you can see the following notice:   The Twitter API v1.0 is deprecated, and this widget has ceased functioning.","You can replace it with a new, upgraded widget from <https://twitter.com/settings/widgets/new/"+H+">","For more information on alternative Twitter tools, see <https://dev.twitter.com/docs/twitter-for-websites>   To save you some time, Twitter really means deprecated and the information link is broken. For more information on alternative Twitter tools the Twitter for Websites | Home. For information related to the information in this article, please see Embedded Timelines | Home   One of the biggest changes in the current twitter platform is the requirement for a "data-widget-id". The data-widget-id is unique, and is used by the twitter platform to provide information to generate the data. Before getting started with the Traffic Manager and Web application you will have to create a new widget using your twitter account https://twitter.com/settings/widgets/new/. Once you create your widget, will see the "Copy and paste the code into the HTML of your site." section on the twitter website. Along with other information, this code contains your "data-widget-id". See Create widget image.   Create widget (click to zoom)   This example uses a Traffic Script response rule to rewrite the HTTP body from the application. Specifically I know the body for my application includes a html comment   <!--SIDEBAR-->.    This rule will insert the required client side code into the HTTP body and send the updated body in to complete the request.  The $inserttag variable can be just about anything in the body itself  i.e. the "MORE LIKE THIS" text on the side of this page. Simply change the code below to:     $inserttag = "MORE LIKE THIS";   Some of the values used in the example (i.e. width, data-theme, data-link-color, data-tweet-limit) are not required. They have been included to demonstrate customization. When you create/save the widget on the twitter website, the configuration options (See the Create widget image above) are associated with the "data-widget-id". For example "data-theme", if you saved the widget with light and you want the light theme, it can be excluded. Alternatively if you saved the widget with light, you can use "data-theme=dark" and over ride the value saved with the widget.  In the example time line picture the data-link-color value is used to over ride the value provided with the saved "data-widget-id".   Example Response Rule, *line spaced for splash readability and use of variables for easy customization. 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 # Only modify text/html pages    if ( !string.startsWith( http.getResponseHeader( "Content-Type" ), "text/html" )) break;         $inserttag = "<!--SIDEBAR-->" ;       # create a widget ID @ https://twitter.com/settings/widgets/new  #This is the id used by riverbed.com   $ttimelinedataid = "261517019072040960" ;  $ttimelinewidth = "520" ; # max could be limited by ID config.  $ttimelineheight = "420" ;  $ttimelinelinkcolor = "#0080ff" ; #0 for default or ID config, #0080ff & #0099cc are nice  $ttimelinetheme = "dark" ; #"light" or "dark"  $ttimelinelimit = "0" ; #0 = unlimited with scroll. >=1 will ignore height.  #See https://dev.twitter.com/web/embedded-timelines#customization for other options.       $ttimelinehtml = "<a class=\"twitter-timeline\" " .                   "width=\"" . $ttimelinewidth . "" .                     "\" height=\"" . $ttimelineheight . "" .                     "\" data-theme=\"" . $ttimelinetheme . "" .                   "\" data-link-color=\"" . $ttimelinelinkcolor . "" .                   "\" data-tweet-limit=\"" . $ttimelinelimit . "" .                   "\" data-widget-id=\"" . $ttimelinedataid . "" .                    "\"></a><script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)" .                     "[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id))" .                     "{js=d.createElement(s);js.id=id;js.src=p+" .                   "\"://platform.twitter.com/widgets.js\";fjs.parentNode.insertBefore(js," .                   "fjs);}}(document,\"script\",\"twitter-wjs\");" .                     "</script><br>" . $inserttag . "" ;         $body = http.getResponseBody();    $body = string.replace( $body , $inserttag , $ttimelinehtml );  http.setResponseBody( $body );    A short version of the rule above, still with line breaks for readability.   1 2 3 4 5 6 7 8 9 if ( !string.startsWith( http.getResponseHeader( "Content-Type" ), "text/html" )) break;         http.setResponseBody(string.replace( http.getResponseBody(), "<!--SIDEBAR-->" ,   "<a class=\"twitter-timeline\" width=\"520\" height=\"420\" data-theme=\"dark\" " .  "data-link-color=\"#0080ff\" data-tweet-limit=\"0\" data-widget-id=\"261517019072040960\">" .  "</a><script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test" .  "(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;" .  "js.src=p+\"://platform.twitter.com/widgets.js\";fjs.parentNode.insertBefore(js,fjs);}}" .  "(document,\"script\",\"twitter-wjs\");</script><br><!--SIDEBAR-->" ));    Result from either rule:  
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
If you are unfortunate enough to suffer a total failure of all of your webservers, all is not lost. Stingray Traffic Manager can host custom error pages for you, and this article shows you how! If all of the servers in a pool have failed, you have several options:   Each pool can be configured to have a 'Failure Pool'. This is used when all of the nodes in the primary pool have completely failed. You may configure the traffic manager to send an HTTP Redirect message, directing your visitors to an alternate website. However, you may reach the point where you've got nowhere to send your users. All your servers are down, so failure pools are not an option, and you can't redirect a visitor to a different site for the same reason.   In this case, you can use a third option:   You may configure a custom error page which is returned with every request. Custom error pages   Use the error_file setting in the Virtual Server configuration to specify the response if the back-end servers are not functioning. The error file should be placed in your Extra Files catalog (in the 'miscellaneous files' class:     <html> <head> <title>Sorry</title> <link rel="stylesheet" href="main.css" type="text/css" media="screen" > </head> <body> <img src="logo.gif"> <h1>Our apologies</h1> We're sorry. All of our operators are busy. Please try again later. </body> </html>   This HTML error page will now be returned whenever an HTTP request is received, and all of your servers are down.   Embedding images and other resources   Note that the HTML page has embedded images and stylesheets. Where are these files hosted? With the current configuration, the error page will be returned for every request.   You can use a little TrafficScript to detect requests for files referenced by the error page, and serve content directly from the conf/extra/ directory.   First, we'll modify the error page slightly to may it easier to recognize requests for files used by the error page:   <link rel="stylesheet" href="https://community.brocade.com/.extra/main.css" type="text/css" media="screen">   and   <img src="/.extra/logo.gif">   Then, upload the main.css and logo.gif files, and any others you use, to the Extra Files catalog.   Finally, the following TrafficScript request rule can detect requests for those files and will make the traffic manager serve the response directly:   # Only process requests that begin '/.extra/' $url = http.getPath(); if( ! string.regexmatch( $url, "^/\\.extra/(.*)$" ) ) { break; } else { $file = $1; } # If the file does not exist, stop if( ! resource.exists( $file ) ) break; # Work out the MIME type of the file from its extension $mimes = [ "html" => "text/html", "jpg" => "image/jpeg", "jpeg" => "image/jpeg", "png" => "image/png", "gif" => "image/gif", "js" => "application/x-javascript", "css" => "text/css" ]; if( string.regexmatch( $file, ".*\\.([^.]+)$" ) ) { $mime = $mimes[ $1 ]; } if( ! $mime ) $mime = "text/plain"; # Serve the file from the conf/extra directory $contents = resource.get( $file ); http.sendResponse( "200 OK", $mime, $contents, "" );   Copy and paste this TrafficScript into the Rules Catalog, and assign it as a request rule to the virtual server. Images (and css or js files) that are placed in the Extra Files catalog can be refered to using /.extra/imagename.png . You will also be able to test your error page by browsing to /.extra/errorpage.html (assuming the file is called errorpage.html in the extra directory).
View full article
You may be familiar with the security concept of a 'honeypot' - a sandboxed, sacrificial computer system that sits safely away from the primary systems.  Any attempts to access that computer are a strong indicator that an attacker is at work, probing for weak points in a network.   A recent Slashdot article raised an interesting idea... 'honeywords' are fake accounts in a password database that don't correspond to real users.  Any attempts to log in with one of these accounts is a strong indicator that the password database has been stolen.   In a similar vein, attempts to log in with common, predictable admin accounts are a strong indicator that an attacker is scanning your system and looking for weaknesses.  This article describes how you can detect these attacks with ease, and then considers different methods you could use to block the attacker.   Detecting Attack Attempts   Attackers look for common account names and passwords (see [1], [2] and [3])   Traffic Manager is in an ideal position to detect attack attempts.  It can inspect the username and password in each login attempt, and flag an alert if a user appears to be scanning for familiar usernames.   Step 1: Determine how the login process functions   Credentials are usually presented to the server as HTTP form parameters, typically in an HTTP POST to an SSL-protected endpoint: Web Inspection tools such as the Chrome Developer tools (illustrated above) help you understand how the authentication credentials are presented to the login service.   You can use the TrafficScript function http.getFormParam() to look up the submitted HTTP form parameters - this function extracts parameters from both the query string (GET and POST requests) and HTTP request body (POST requests), handles any unusual body transfer encoding, and %-decodes the values:   $userid = http.getFormParam( "Email" ); $pass = http.getFormParam( "Password" );   Step 2: Does this constitute an attack?   You'll need to make a judgement as to what constitutes an attack attempt against your service.  A single attempt to log-in with 'admin:admin' is probably sufficient to block a user, but multiple attempts in a short period of time certainly indicate a concerted attack.   An easy way to count user/password combinations is to use a rate shaping class to count events.  Stingray's rate classes are usually used to implement queues (delaying requests that exceed the per-second or per-minute queue), but you can also use the rate.use.noQueue() function to determine if an event has exceeded the rate limit or not, without queuing it.   Let's construct a policy that detects if a particular source IP address is trying to log in to one of our false 'admin' accounts too frequently:   $path = http.getPath(); if( $path != "/cgi-bin/login.cgi" ) break; $ip = request.getRemoteIP(); $user = http.getFormParam( "user" ); if( string.regexmatch( $user, "^(admin|root|phpadmin|test|guest|user|administrator|mysql|adm|oracle)$" ) ) { if( rate.use.noQueue( "5 per minute", $ip ) == 0 ) { # User has exceeded the limits .... } }   An aside: If you would like to maintain a large list of honeyword names (making sure that none of them correspond to real accounts), then you may find it easier to store them in an external table using libTable.rts: Interrogating tables of data in TrafficScript.       Responding to Attack Attempts   If you determine that a particular IP address is generating attack attempts and you want to block it, there are a number of ways that you can do so.  They vary in complexity, accuracy and the ability to 'time out' the period that an IP address is blocked out for:   Method Sophistication Store data locally in the global data segment Straightforward to code, timeouts possible, not cluster-aware Store data in the resource directory Straightforward to code, timeouts possible, is cluster-aware Update configuration in service protection policy Straightforward to code, difficult to avoid race conditions, not possible to timeout the configuration, is cluster aware Provision iptables rules from an event Complex to code accurately but very effective, not possible to timeout, is cluster aware   Updating the configuration in a service protection policy could be achieved by calling the REST API from TrafficScript - perform a GET on the configuration ( /api/tm/1.0/config/active/protection/ name ), update the banned array, and PUT the configuration back again.  However, there is no natural way to remove (timeout) a block on an IP address after a period of inactivity.   Provisioning iptables rules would be possible with a specific event handler that responded to the TrafficScript function event.emit( "block", $ip ), but once again, there's no easy way to time a block rule out.   Storing data locally in the resource directory is a good approach, and is described in detail in the article Slowing down busy users - driving the REST API from TrafficScript.  The basic premise is that you can use the REST API to 'touch' a file (named after an IP address) in the resource directory, and you block a user if their IP address corresponds to a file in the resource directory that is not too old.  However, if the user does not return, you will build up a large number of files in the resource directory that should be manually pruned.   Storing data in the global data segment (How is memory managed in TrafficScript?) is perhaps the best solution.  The following code sample illustrates the basic premise:     $prefix = "blocked-ip-address:"; # Record that an IP address is blocked data.set( $prefix.$ip, 1 ); # Check if an IP address is blocked if( data.get( $prefix.$ip ) ) { connection.discard();#sthash.YB8cEYo7.dpuf } # Delete all records data.reset( $prefix );   You could implement timeouts in a simple fashion, for example, by calling data.reset() on the first transaction after the top of every hour:   $hour = sys.time.hour(); $last = data.get( $prefix."hour" ); if( $last != $hour ) { data.reset( $prefix ); data.set( $prefix."hour", $hour ); }   An aside: There is a very slight risk of a race condition here (if two cores run the rule simultaneously) but the effects are not significant.   This approach gives a simple and effective solution to the problem of detecting logins to fake admin accounts, and then blocking the IP address for up to an hour.   What if I want to block IP addresses for longer?   One weakness of the approach above is that if an IP address is added to the block table at 59 minutes past the hour, it will be removed a minute later.  This may not be a serious fault; if the user is continuing to try to force admin accounts, the rule will detect this and block the IP address shortly after.   An alternative solution is to store two tables - one for odd-numbered hours, and one for even-numbered hours:   When you add an IP address, place it in the odd or even table according to the current hour When you test for the presence of an IP address, check both tables When the hour rolls over and you switch to the even-numbered table (for example), delete all of the entries (using data.reset ) before proceeding - they will be between one and two hours old   $prefix = "blocked-ip-address:"; # Check if an IP address is blocked if( data.get( $prefix."0:".$ip ) || data.get( $prefix."1:".$ip ) ) { connection.discard(); } # Add an IP address (this is an infrequent operation we hope!) $hour = sys.time.hour(); $pp = ( $hour % 2 ) . ":"; # pp is either 0: or 1: $last = data.get( $prefix.$pp."hour" ); if( $last != $hour ) { data.reset( $prefix.$pp ); data.set( $prefix.$pp."hour", $hour ); } data.set( $prefix.$pp.$ip, 1 );   This extension to the rule could further be extended to any number of tables, and to any time interval, though this is almost certainly overkill for this solution.   Read More   Interested in knowing what usernames are most commonly used?  Check out the article Being Lazy with Java Extensions and the 'CountThis' extension Other security and denial-of-service -related articles - check out the Security section of the Top Stingray Examples and Use Cases article
View full article
Update: See also this new article including a simple template rule: A Simple Template Rule SteelCentral Web Analyzer - BrowserMetrix   Riverbed SteelCentral Web Analyzer is a great tool for monitoring end-user experience (EUE) of web applications, even when they are hosted in the cloud. And because it is delivered as true Software-as-a-Service, you can monitor application performance form anywhere, and drill down to analyse individual transactions by URL, location or browser type, and highlight requests which t ook too long to respond.   In order to track statistics, your web application needs to send statistics on each transaction to Web Analyzer (formerly BrowserMetrix) using a small piece of JavaScript, and it is very easy to inject the extra JavaScript code without needing to change the application itself. This Solution Guide (attached) shows you how to use TrafficScript to inject the JavaScript snippet into your web applications, by inspecting all web pages and inserting into the right place in each document:   No modification needed to your application Easy to select which pages you want to instrument Use with all applications in your data center, or hosted in the cloud Even works with compressed HTML pages (eg, gzip encoded) Create dynamic JavaScript code to track session-level information Use Riverbed FlyScript to automate the integration between Web Analyzer and Traffic Manager   How does it work? SteelApp Traffic Manager sits in front of the web applications on the right, and inspects each web page before it is sent to the client. Stingray checks to see if the page has been selected for analysis by Web Analyzer, and then constructs the JavaScript fragment and injects into the web page at the right place in the HTML document.   When the web page arrives at the client browser, the JavaScript snippet is executed.  It builds a transaction profile with timing information and submits the information to the Web Analyzer SaaS platform managed by Riverbed.  You can then analyze the results, in near-realtime, using the Web Analyzer web portal.   Thanks also to Faisal Memon for his help creating the Solution Guide.   Read more In addition to the attached deployment guide showing how to create complex rules for JavaScript Injection, you may be also be interested in this new article showing how to use a simple template rule wit Traffic Manager and SteelCentral Web Analyzer: A Simple Template Rule for SteelCentral Web Analyzer - BrowserMetrix   For similar solutions, check out the Content Modification examples in the Top vADC Examples and Use Cases article.   Updated 15th July 2014 by Paul Wallace. Article formerly titled "Using Stingray with OPNET AppResponse Xpert BrowserMetrix" Thanks also to Mike Iem for his help updating this article. 29th July 2014 by Paul Wallace. Added note about the new article including the simple template rule          
View full article
"The 'contact us' feature on many websites is often insecure, and makes it easy to launch denial of service (DoS) attacks on corporate mail servers," according to UK-based security consultancy SecureTest, as reported in The Register.   This article describes how such an attack might be launched, and how it can be easily mitigated against by using a traffic manager like Stingray.   Mounting an Attack   Many websites contain a "Contact Us" web-based form that generates an internal email. An attacker can use a benchmarking program like ApacheBench to easily submit a large number of requests to the form, bombarding the target organization's mail servers with large volumes of traffic.   Step 1. Identify the target web form and deduce the POST request     An appropriate POST request for the http://www.site.com/cgi-bin/mail.aspx page would contain the form parameters and desired values as an application/x-www-form-urlencoded file (ignore line breaks):   email_subject=Site+Feedback&mailto=target%40noname.org& email_name=John+Doe&email_from=target%40noname.org&email_country=US& email_comments=Ha%2C+Ha%2C+Ha%21%0D%0ADon%27t+try+this+at+home   Step 2. Mount the attack   The following example uses ApacheBench to submit the POST request data in postfile.txt. ApacheBench creates 10 users who send 10,000 requests to the target system.   # ab -p postfile.txt -c 10 -n 10000 -T application/x-www-form-urlencoded http://www.site.com/cgi-bin/mail.aspx   The attack is worsened because the web server typically resides inside the trusted DMZ and is not subject to the filtering that untrusted external clients must face. Additionally, this direct attack bypasses any security or validation that is built into the web form.   Ken Munro of SecureTest described the results of the firm's penetration testing work with clients. "By explicit agreement we conduct a 'contact us' DoS, and in every case we've tried so far, the client's mail server stops responding during the test window."   Defending against the Attack   There is a variety of ways to defend against this form of attack, but one of the easiest ways would be to rate-limit requests to the web-based form.   In Stingray, you can create a 'Rate Shaping Class'; we'll create one named 'mail limit' that restricts traffic to 2 requests per minute:     Using TrafficScript, we rate-limit traffic to the mail.aspx page to 2 requests per minute in total:   if( http.getPath() == "/cgi-bin/mail.aspx" ) { rate.use( "mail limit" ); }   In this case, one attacker could dominate the form and prevent other legitimate users from using it. So, we could instead limit each individual user (identified by source IP address) to 2 requests per minute:   if( http.getPath() == "/cgi-bin/mail.aspx" ) { rate.use( "mail limit", request.getRemoteIP() ); }   In the case of a distributed denial of service attack, we can rate limit on other criteria. For example, we could extract the 'name' field from the submitted data and rate-shape on that basis:   if( http.getPath() == "/cgi-bin/mail.aspx" ) { $name = http.getFormParam( "name" ); rate.use( "mail limit", $name ); }   Stingray gives you a very quick, simple and non-disruptive method to limit accesses to a vulnerable or resource-heavy web-based form like this. This solution illustrates one of the many ways that Stingray's traffic inspection and control can be used to help secure your public facing services.
View full article
This is a reference guide for the SteelApp Traffic Manager Puppet Module that discusses how to get started and how to do a few common SteelApp deployments using Puppet.
View full article
Content protection is a key concern for many online services, and watermarking downloaded documents with a unique ID is one way to discourage and track unauthorized sharing. This article describes how to use Stingray to uniquely watermark every PDF document served from a web site.     In this example, Stingray will run a Java Extension to process all outgoing PDF documents from the web sites it is managing. The Java Extension can watermark each download with a custom message, including details such as the IP address, time of day and authentication credentials (if available) of the client:     The extension then encrypts the PDF document to make it difficult to remove the watermark.   Quick Start   Upload the attached PdfWatermark.jar file to your Java Extensions Catalog in Stingray Traffic Manager:   Create the following 'PDFWatermark' rule and apply it as a response rule to your virtual server:   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 if ( http.getresponseheader( "Content-Type" ) != "application/pdf" ) break;       java.run( "PdfWatermark" ,         "x" , 10,         "y" , 20,         "textAlpha" , 30,         "textSize" , 40,         "textColor" , "0xff7f00" ,         "drawText" , "Downloaded by " .request.getRemoteIP(),         "textSize" , 26,         "drawText" , sys. gmtime . format ( "%a, %d %b %Y %T GMT" ),        "textSize" , 14,        "drawText" , http.getHostHeader() . http.getPath(),         "x" , 40,         "y" , 25,         "textAlpha" , 70,         "textColor" , "0xcccccc" ,         "textSize" , 16,         "textAngle" , 0,         "drawText" , "Copyright " .sys. time .year(),         "drawText" , "For restricted distribution"   );    Download a PDF document from your website, managed by the virtual server configured above.  Verify that the PDF document has been watermarked with the URL, client IP address, and time of download.   Troubleshooting   The Java Extension applies the watermark to PDF documents, and then encrypts them to make the watermark difficult to remove.   The Java Extension will not be able to apply a watermark to PDF documents that are already encrypted, or which are served with a mime type that does not begin ‘application/pdf’.   Customizing the extension   The behaviour of the extension is controlled by the parameters passed into the Java extension by the ‘ java.run() ’ function.   The following example applies a simple watermark:   1 2 3 4 5 6 7 8 9 10 11 if ( http.getresponseheader( "Content-Type" ) != "application/pdf" ) break;       $msg1 = http.getHostHeader() . http.getPath();  $msg2 = "Downloaded by " .http.getRemoteIP();  $msg3 = sys. gmtime . format ( "%a, %d %b %Y %T GMT" );       java.run( "PdfWatermark" ,      "drawText" , $msg1 ,      "drawText" , $msg2 ,      "drawText" , $msg3 ,  );    Advanced use of the Java Extension   This Java Extension takes a list of commands to control how and where it applies the watermark text:   Command Notes Default x As a percentage between 0 and 100; places the cursor horizontally on the page. 30 y As a percentage between 0 and 100; places the cursor vertically on the page. 30 textAngle In degrees, sets the angle of the text. 0 is horizontal (left to right); 90 is vertical (upwards). The special value "auto" sets the text angle from bottom-left to top-right in accordance with the aspect ratio of the page. “auto” textAlign Value is "L" (left), "R" (right), or "C" (center); controls the alignment of the text relative to the cursor placement. “L” textAlpha As a percentage, sets the alpha of the text when drawn with drawText."0" is completely transparent, "100" is solid (opaque). 75 textColor The color of the text when it is drawn with drawText, as hex value in a string. “0xAAAAAA” textSize In points, sets the size of the text when it is drawn with drawText. 20 drawText Draw the value (string) using the current cursor placement and text attributes; automatically moves the cursor down one line so that multiple lines of text can be rendered with successive calls to drawText.     Dependencies and Licenses   For convenience, the .jar extension contains the iText 5.4.0 library from iText software corp (http://www.itextpdf.com) and the bcprov-148 and bcmail-148 libraries from The Legion of the Bouncy Castle (http://www.bouncycastle.org), in addition to the PdfWatermark.class file.  The jar file was packaged using JarSplice (http://ninjacave.com/jarsplice).   Building the extension from source   If you'd like to build the Java Extension from source, here's the code:   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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 import java.awt.Color;  import java.io.IOException;  import java.io.InputStream;  import java.io.OutputStream;  import java.util.ArrayList;  import java.util.Enumeration;  import java.util.Hashtable;       import javax.servlet.ServletConfig;  import javax.servlet.ServletException;  import javax.servlet.http.HttpServlet;  import javax.servlet.http.HttpServletRequest;  import javax.servlet.http.HttpServletResponse;       import com.itextpdf.text.BaseColor;  import com.itextpdf.text.pdf.BaseFont;  import com.itextpdf.text.pdf.PdfContentByte;  import com.itextpdf.text.pdf.PdfGState;  import com.itextpdf.text.pdf.PdfReader;  import com.itextpdf.text.pdf.PdfStamper;  import com.itextpdf.text.pdf.PdfWriter;  import com.zeus.ZXTMServlet.ZXTMHttpServletResponse;       public class PdfWatermark extends HttpServlet {       private static final long serialVersionUID = 1L;            Hashtable<String, String> defaults = new Hashtable<String, String>();            public void init(ServletConfig config) throws ServletException {          super.init(config);               // Initialize defaults.  These are 'commands' that are run before any commands          // passed in to the extension through the args list          defaults.put( "x" , "30" );          defaults.put( "y" , "30" );          defaults.put( "textAngle" , "auto" );          defaults.put( "textAlign" , "L" );          defaults.put( "textAlpha" , "75" );          defaults.put( "textSize" , "20" );          defaults.put( "textColor" , "0xAAAAAA" );               // Read any values defined in the ZXTM configuration for this class          // to override the defaults          Enumeration<String> e = defaults. keys ();          while (e.hasMoreElements()) {             String k = e.nextElement();             String v = config.getInitParameter(k);             if (v != null)                defaults.put(k, v);          }       }            public void doGet(HttpServletRequest req, HttpServletResponse res)             throws ServletException, IOException {          try {             ZXTMHttpServletResponse zres = (ZXTMHttpServletResponse) res;                  String ct = zres.getHeader( "Content-Type" );             if (ct == null || !ct.startsWith( "application/pdf" ))                return ;                  // process args             String[] args = (String[]) req.getAttribute( "args" );             if (args == null)                throw new Exception( "Missing argument list" );             if (args. length % 2 != 0)                throw new Exception(                      "Malformed argument list (expected even number of args)" );                  ArrayList<String[]> actions = new ArrayList<String[]>();                  Enumeration<String> e = defaults. keys ();             while (e.hasMoreElements()) {                String k = e.nextElement();                actions.add(new String[] { k, defaults.get(k) });             }             for ( int i = 0; i < args. length ; i += 2) {                actions.add(new String[] { args[i], args[i + 1] });             }                  InputStream is = zres.getInputStream();             OutputStream os = zres.getOutputStream();                  PdfReader reader = new PdfReader(is);                  int n = reader.getNumberOfPages();                  PdfStamper stamp = new PdfStamper(reader, os);             stamp.setEncryption(                   PdfWriter.STANDARD_ENCRYPTION_128 | PdfWriter.DO_NOT_ENCRYPT_METADATA,                   null, null,                   PdfWriter.ALLOW_PRINTING | PdfWriter.ALLOW_COPY                      | PdfWriter.ALLOW_FILL_IN | PdfWriter.ALLOW_SCREENREADERS                      | PdfWriter.ALLOW_DEGRADED_PRINTING);                  for ( int i = 1; i <= n; i++) {                PdfContentByte pageContent = stamp.getOverContent(i);                com.itextpdf.text.Rectangle pageSize = reader                      .getPageSizeWithRotation(i);                     watermarkPage(pageContent, actions, pageSize.getWidth(),                      pageSize.getHeight());             }                  stamp. close ();               } catch (Exception e) {             log (req.getRequestURI() + ": " + e.toString());             e.printStackTrace();          }       }            public void doPost(HttpServletRequest req, HttpServletResponse res)             throws ServletException, IOException {          doGet(req, res);       }            private void watermarkPage(PdfContentByte pageContent,             ArrayList<String[]> actions, float width, float height)             throws Exception {          float x = 0;          float y = 0;          double textAngle = 0;          int textAlign = PdfContentByte.ALIGN_CENTER;          int fontSize = 14;                    pageContent.beginText();               for ( int i = 0; i < actions.size(); i++) {             String action = actions.get(i)[0];             String value = actions.get(i)[1];                  if (action.equals( "x" )) {                x = Float.parseFloat(value) / 100 * width;                continue ;             }                  if (action.equals( "y" )) {                y = Float.parseFloat(value) / 100 * height;                continue ;             }                  if (action.equals( "textColor" )) {                Color c = Color.decode( value );                pageContent.setColorFill(                   new BaseColor( c.getRed(), c.getGreen(), c.getBlue() ) );                     continue ;             }                  if (action.equals( "textAlpha" )) {                PdfGState gs1 = new PdfGState();                gs1.setFillOpacity(Float.parseFloat(value) / 100f);                pageContent.setGState(gs1);                continue ;             }                  if (action.equals( "textAngle" )) {                if (value.equals( "auto" )) {                   textAngle = (float) Math. atan2 (height, width);                } else {                   textAngle = Math.toRadians( Double.parseDouble(value) );                }                continue ;             }                  if (action.equals( "textAlign" )) {                if (value.equals( "L" ))                   textAlign = PdfContentByte.ALIGN_LEFT;                else if (value.equals( "R" ))                   textAlign = PdfContentByte.ALIGN_RIGHT;                else                    textAlign = PdfContentByte.ALIGN_CENTER;                continue ;             }                  if (action.equals( "textSize" )) {                fontSize = Integer.parseInt(value);                pageContent.setFontAndSize(BaseFont                      .createFont(BaseFont.HELVETICA, BaseFont.WINANSI,                            BaseFont.EMBEDDED), fontSize);                continue ;             }                  // x,y is top left/center/right of text, so that when we move the             // cursor at the end of a line, we can cater for subsequent fontSize             // changes             if (action.equals( "drawText" )) {                pageContent.showTextAligned(textAlign, value,                      (float) (x + fontSize * Math. sin (textAngle)),                      (float) (y - fontSize * Math. cos (textAngle)),                      (float) Math.toDegrees(textAngle));                     x += fontSize * 1.2 * Math. sin (textAngle);                y -= fontSize * 1.2 * Math. cos (textAngle);                continue ;             }                  throw new Exception( "Unknown command '" + action + "'" );          }               pageContent.endText();       }  }   Compile against the Stingray servlet libraries (see Writing Java Extensions - an introduction), and the most recent versions of the iText library (http://www.itextpdf.com ) and the bcprov and bcmail libraries ( http://www.bouncycastle.org ):   $ javac -cp servlet.jar:zxtm-servlet.jar:bcprov-jdk15on-148.jar:\ bcmail-jdk15on-148.jar:itextpdf-5.4.0.jar PdfWatermark.java   You can then upload the generated PdfWatermark.class file and the three iText/bcmail/bcprov jar files to the Stingray Java Catalog.   Creating a Fat Jar   Alternatively, you can package the class files and their jar dependencies as a single Fat Jar (http://ninjacave.com/jarsplice):   1. Package the PdfWatermark.class file as a jar file   $ jar cvf PdfWatermark.jar PdfWatermark.class   2. Run JarSplice   $ java -jar ~/Downloads/jarsplice-0.40.jar   4. Set the main class: 5. Hit 'CREATE FAT JAR' to generate your single fat jar: You can upload the resulting Jar file to the Stingray Java catalog, and Stingray will identify the PdfWatermark.class within.
View full article
TrafficScript rules often need to refer to tables of data - redirect mappings, user lists, IP black lists and the like.   For small tables that are not updated frequently, you can place these inline in the TrafficScript rule:   $redirect = [ "/widgets" => "/sales/widgets", "/login" => "/cgi-bin/login.cgi" ]; $path = http.getPath(); if( $redirect[ $path ] ) http.redirect( $redirect[ $path ] ); This approach becomes difficult to manage if the table becomes large, or you want to update it without having to edit the TrafficScript rule.  In this case, you can store the table externally (in a resource file) and reference it from the rule:     The following examples will consider a file that follows a standard space-separated 'key value' pattern, and we'll look at alternative TrafficScript approaches to efficiently handle the data and look up key-value pairs:   # cat /opt/zeus/zxtm/conf/extra/redirects.txt /widgets /sales/widgets /login /cgi-bin/login.cgi /support http://support.site.com We'll propose several 'ResourceTable' TrafficScript library implementations that express a lookup() function that can be used in the following fashion:   # ResourceTable provides a lookup( filename, key ) function import ResourceTable as table; $path = http.getPath(); $redirect = table.lookup( "redirects.txt", $path ); We'll then look at the performance of each to see which is the best.   For a summary of the solutions in this article, jump straight to libTable.rts: Interrogating tables of data in TrafficScript.   Implementation 1: Search the file on each lookup   ResourceTable1 sub lookup( $filename, $key ) { $contents = resource.get( $filename ); if( string.regexmatch( $contents, '\n'.$key.'\s+([^\n]+)' ) ) return $1; if( string.regexmatch( $contents, '^'.$key.'\s+([^\n]+)' ) ) return $1; return ""; } This simple implementation searches the file on each and every lookup, using a regular expression to locate the key and also the text on the remainder of the line.  It pins the key to the start of the line so that it does not mistakenly match lines where $key is a substring (suffix) of the key.   The implementation is simple and effective, but we would reasonably expect that it would become less and less efficient, the larger the resource file became.   Implementation 2: Store the table in a TrafficScript hash table for easy lookup   The following code builds a TrafficScript hash table from the contents of the resource file: $contents = resource.get( $filename ); $h = [ ]; foreach( $l in string.split( $contents, "\n" ) ) { if( ! string.regexmatch( $l, '(.*?)\s+(.*)' ) ) continue; $key = string.trim( $1 ); $value = string.trim( $2 ); $h[$key] = $value; } You can then quickly look up values in the hash table using $h[ $key ].   However, we don't want to have to create the hash table every time we call the lookup function; we would like to create it once and then cache it somewhere.  We can use the global data table to store persistent data, and we can verify that the data is still current by checking that the MD5 of the resource file has not changed:   ResourceTable2a sub update( $filename ) { # Store the md5 of the resource file we have cached. No need to update if the file has not changed $md5 = resource.getMD5( $filename ); if( $md5 == data.get( "resourcetable:".$filename.":md5" ) ) return; # Do the update $contents = resource.get( $filename ); $h = [ ]; foreach( $l in string.split( $contents, "\n" ) ) { if( ! string.regexmatch( $l, "(.*?)\\s+(.*)" ) ) continue; $key = string.trim( $1 ); $value = string.trim( $2 ); $h[$key] = $value; } data.set( "resourcetable:".$filename.':data', $h ); data.set( "resourcetable:".$filename.':md5', $md5 ); } sub lookup( $filename, $key ) { # Check to see if the file has been updated, and update our table if necessary update( $filename ); $h = data.get( "resourcetable:".$filename.':data' ); return $h[$key]; } Version 2a: we store the MD5 of the file in the global key 'resourcetable:filename:md5', and the hash table in the global key 'resourcetable:filename:data'.   This implementation has one significant fault.  If two trafficscript rules are running concurrently, they may both try to update the keys in the global data table and a race condition may result in inconsistent data.  This situation is not possible on a single-core system with one zeus.zxtm process because rules are run serially and only pre-empted if they invoke a blocking operation, but it's entirely possible on a multi-core system, and TrafficScript does not implement mutexes or locks to help protect against this.   The simplest solution is to give each core its own, private copy of the data.  Because system memory should be scaled with the number of cores, the additional overhead of these copies is generally acceptable:   ResourceTable2b: sub update( $filename ) { $pid = sys.getPid(); $md5 = resource.getMD5( $filename ); if( $md5 == data.get( "resourcetable:".$pid.$filename.":md5" ) ) return; $contents = resource.get( $filename ); $h = [ ]; foreach( $l in string.split( $contents, "\n" ) ) { if( ! string.regexmatch( $l, "(.*?)\\s+(.*)" ) ) continue; $key = string.trim( $1 ); $value = string.trim( $2 ); $h[$key] = $value; } data.set( "resourcetable:".$pid.$filename.':data', $h ); data.set( "resourcetable:".$pid.$filename.':md5', $md5 ); } sub lookup( $filename, $key ) { update( $filename ); $pid = sys.getPid(); $h = data.get( "resourcetable:".$pid.$filename.':data' ); return $h[$key]; }   Version 2b: by including the pid in the name of the key, we avoid multi-core race conditions at the expense of multiple copies of the date   Implementation 3: Store the key/value data directly in the global hash table   data.set and data.get address a global key/value table.  We could use that directly, rather than constructing a TrafficScript hash:   sub update( $filename ) { $pid = sys.getPid(); $md5 = resource.getMD5( $filename ); if( $md5 == data.get( "resourcetable".$pid.$filename.":md5" ) ) return; data.reset( "resourcetable".$pid.$filename.":" ); data.set( "resourcetable".$pid.$filename.":md5", $md5 ); $contents = resource.get( $filename ); foreach( $l in string.split( $contents, "\n" ) ) { if( ! string.regexmatch( $l, "(.*?)\\s+(.*)" ) ) continue; $key = string.trim( $1 ); $value = string.trim( $2 ); data.set( "resourcetable".$pid.$filename."::".$key, $value ); } } sub lookup( $filename, $key ) { update( $filename ); $pid = sys.getPid(); return data.get( "resourcetable".$pid.$filename."::".$key ); }   Version 3: key/value pairs are stored in the global data table.  Keys begin with the string "resourcetable:pid:filename:", so it's easy to delete all of the key/value pairs using data.reset() before rebuilding the dataset   How do these implementations compare?   We tested the number of lookups-per-second that each implementation could achieve (using a single-core virtual machine running on a laptop Core2 processor) to investigate performance for different dataset sizes:   Resource file size (entries) 10 100 1,000 10,000 Implementation 1: simple search 300,000 100,000 17,500 1,000 Implementation 2: trafficscript hash, cached in global data table 27,000 2,000 250 10 Implementation 3: key/value pairs in the global data table 200,000 200,000 200,000 200,000   ResourceTable lookups per second (single core, lightweight processor)   The test just exercised the rate of lookups in resource files of various sizes; the cost of building the cached datastructures (implementations 2 and 3) and one-off costs and not included in the tests.   Interpreting the results   The degradation of performance in implementation 1 as the file size increases was to be expected.   The constant performance of implementation 3 was as expected, as hash tables should generally give O(1) lookup speed, not affected by the number of entries.   The abysmal performance of implementation 2 is surprising, until you note that on every lookup we retrieve the entire hash table from the global data table:   $h = data.get( "resourcetable:".$pid.$filename.':data' ); return $h[$key];     The global data table is a key/value store; all keys and values are serialized as strings.  The data.get() operation will read the serialized version of the hash table and reconstruct the entire table (up to 10,000 entries) before the O(1) lookup operation.   What is most surprising perhaps is the speed at which you can search and extract data from a string using regular expressions (implementation 1).  For small and medium datasets (up to approx 50 entries), this is the simplest and fastest method, and it's only worth considering the more complex data.get key/value implementation for large datasets.   Read more   Check out the article How is memory managed in TrafficScript? for more detail on the ways that TrafficScript handles data and memory    
View full article
There are many reasons why you may want to serve web content directly from Stingray Traffic Manager - simplification, performance, ease of administration and, perhaps most importantly, to host a 'Sorry Page' if your entire web infrastructure has failed and Stingray is all that is left.   The article Using Stingray Traffic Manager as a Webserver describes the rationale in more detail and presents a simple TrafficScript-based webserver.  However, we can do a lot more with a more complete programming language - mime types, index pages, more control over the location of the document root are all simple to implement with Python.   Get started with PyRunner.jar   Start with the procedure describe in the article PyRunner.jar: Running Python code in Stingray Traffic Manager.  The PyRunner extension lets you run Python code in Stingray, using the local JVM and the Jython implementation.   Note that this example does not work reliably with versions of Jython prior to 2.7 beta1 - I hit problems when a library attempted to import the Jython errno module (possibly related to https://github.com/int3/doppio/issues/177).   webserver.py   Once you've installed PyRunner (using an appropriate version of Jython), upload the following Python script to your Extra Files catalog.  Make sure to call the script 'webserver.py', and edit the location of the docroot to an appropriate value:   from javax.servlet.http import HttpServlet from urllib import url2pathname from os import listdir from os.path import normpath,isdir,isfile,getmtime,getsize import mimetypes import datetime docroot = '/tmp' def dirlist( uri, path, response ): files = '' for f in listdir( path ): if (f[0] == '.') and (f[1] != '.'): continue # hidden files if isdir( path+'/'+f ): size = '&lt;DIR&gt;' f += '/' # Add trailing slash for directories else: size = '{:,d} bytes'.format( getsize( path+'/'+f )) mtime = datetime.datetime.fromtimestamp( getmtime( path+'/'+f )) files += '<a href="{f}">{f:<30}</a> {t:14} {s:>17}\n'.format( f=f, t=mtime, s=size ) html = ''''' <html><head><title>{uri}</title></head><body> <h1>Directory listing for {uri}</h1> <pre>{files}<a href="../">Up</a></pre> </body></html>''' .format(uri=uri, files=files ) response.setContentType( 'text/html' ) toClient = response.getWriter() toClient.write( html ) class webserver(HttpServlet): def doGet(self, request, response): uri = request.getRequestURI() print "Processing "+uri path = normpath( docroot + url2pathname( uri ) ) # Never access files outside the docroot if path.startswith( docroot ) == False: path = docroot uri = '/' if isdir( path ): if uri.endswith( '/' ) == False: response.sendRedirect( uri+'/' ) if isfile( path + '/index.html' ): response.sendRedirect( uri + 'index.html' ) else: dirlist( uri, path, response ) return try: c = open( path, 'r' ).read() except Exception, e: response.sendError( 400, "Could not read "+path+": "+str( e ) ) return mtype = mimetypes.guess_type( path )[0] if mtype == None: mtype = 'application/octet-stream' response.setContentType( mtype ) toClient = response.getWriter() toClient.write( c )   If you want to edit the rule and try some changes, they you'll find the publish.py script in Deploying Python code to Stingray Traffic Manager useful, and you should follow the Stingray Event log ( tail -f /opt/zeus/zxtm/log/errors ) to catch any problems.   Performance Optimization   The article Using Stingray Traffic Manager as a Webserver describes how to front the web server virtual server with a second virtual server that caches responses and responds directly to web requests.   It also presents an alternative webserver implementation written in Java - take your pick!
View full article
When Stingray load-balances a connection to an iPlanet/SunONE/Sun Java System Web Server server or application, the connection appears to originate from the Stingray machine. This can be a problem if the server wishes to perform access control based on the client's IP address, or if it wants to log the true source address of the request, and is well documented in the article IP Transparency: Preserving the Client IP address in Stingray Traffic Manager.   Stingray has an IP Transparency feature that preserves the client's IP address, but this requires a Stingray Kernel Modules for Linux Software (pre-installed on Stingray Virtual Appliances and available separately for Stingray software) and is currently only available under Linux. As an alternative, the mod_remoteip module is a good solution for Apache; this article presents a similar module for iPlanet and related webservers.   How it works   Stingray automatically inserts a special X-Cluster-Client-Ip header into each request, which identifies the true source address of the request. The iPlanet/Sun NSAPI module inspects this header and corrects the calculation of the source address. This change is transparent to the web server, and to any applications running on or behind the web server.   Obtaining the Module   Compile the module from source:   https://gist.github.com/5546803   To determine the appropriate compilation steps for an NSAPI module for your instance of iPlanet, you can first build the NSAPI examples in your SunONE installation:   $ cd plugins/nsapi/examples/ $ make cc -DNET_SSL -DSOLARIS -D_REENTRANT -DMCC_HTTPD -DXP_UNIX -DSPAPI20 \ -I../../include -I../../include/base -I../../include/frame -c addlog.c ld -G addlog.o -o example.so   You can build the iprewrite.so module using similar options. Set NSHOME to the installation location for iPlanet:   $ export NSHOME=/opt/iplanet $ cc -DNET_SSL -DSOLARIS -D_REENTRANT -DMCC_HTTPD -DXP_UNIX -DSPAPI20 \ -I$NSHOME/plugins/include -I$NSHOME/plugins/include/base \ -I$NSHOME/plugins/include/frame -c iprewrite.c $ ld -G iprewrite.o -o iprewrite.so $ cp iprewrite.so $NSHOME/plugins   Configuring the Module   To configure the module, you will need to edit the magnus.conf and obj.conf files for the virtual server you are using. If the virtual server is named 'test', you'll find these files in the https-test/config directory.   magnus.conf   Add the following lines to the end of the magnus.conf file. Ensure that the shlib option identifies the full path to the iprewrite.so module, and that you set TrustedIPs to either '*', or the list of Stingray back-end IP addresses:   Init fn="load-modules" funcs="iprewrite-init,iprewrite-all,iprewrite-func" \ shlib="/usr/local/iplanet/plugins/iprewrite.so" Init fn="iprewrite-init" TrustedIPs="10.100.1.68 10.100.1.69"   The TrustedIPs option specifies the back-end addresses of the Stingray machines. The iprewrite.so module will only trust the 'X-Cluster-Client-Ip' header in connections which originate from these IP addresses. This means that remote users cannot spoof their source addresses by inserting a false header and accessing the iPlanet/Sun servers directly.   obj.conf   Locate the 'default' object in your obj.conf file and add the following line at the start of the directives inside that object:   <Object name=default> AuthTrans fn="iprewrite-all" ...   Restart your iPlanet/Sun servers, and monitor your servers' error logs (https-name/log/errors).   The Result   iPlanet/Sun, and applications running on the server will see the correct source IP address for each request. The access log module will log the correct address when you use %a or %h in your log format string.   If you have misconfigured the TrustedIPs value, you will see messages like:   Ignoring X-Cluster-Client-Ip '204.17.28.130' from non-Load Balancer machine '10.100.1.31'   Add the IP address to the trusted IP list and restart.   Alternate Configuration   The 'iprewrite-all' SAF function changes the ip address for the entire duration of the connection. This may be too invasive for some environments, and its possible that a later SAF function may modify the IP address again. You can use the 'iprewrite-func' SAF function to change the ip address for a single NSAPI function. For example, BEA's NSAPI WebLogic connector ('wl_proxy') is normally configured as follows:   <Object name="weblogic" ppath="/weblogic/"> Service fn=wl_proxy WebLogicHost=localhost    WebLogicPort=7001 PathTrim="/weblogic" </Object>   You can change the IP address just for that function call, using the iprewrite-func SAF function as follows:   <Object name="weblogic" ppath="/weblogic/"> Service fn=iprewrite-func func=wl_proxy WebLogicHost=localhost    WebLogicPort=7001 PathTrim="/weblogic" </Object>
View full article
This Document provides step by step instructions on how to set up Brocade Virtual Traffic Manager for Microsoft Lync 2013.
View full article
Stingray's Feature Brief: Stingray Content Caching capability allows the traffic manager to operate as a high-performance proxy cache, with full control using TrafficScript. When Stingray is deployed in a cluster, each Stingray device manages its own local cache.  When an item expires from one Stingray's cache, it retrieves that item from the origin server the next time that it is used. Each Stingray manages its own cache independently In the majority of situations, this deployment pattern is entirely appropriate.  In some specialized situations, it is preferable to have 100% consistency between the caches in the traffic managers and to absolutely minimize the load on the origin servers.  This article describes the recommended deployment configuration, meeting the following technical goals: Fault Tolerance - Failure: the pair of Stingray Traffic Managers operate in a fully-fault tolerant fashion.  Cache consistency between peers ensures that in the event of planned or unintentional failure of one of the peers in the cluster, the remaining peer has a complete and up-to-date cache of content and the load on the origin servers does not increase. Fault Tolerance - Recovery: when the failed Stingray traffic manager recovers, it is automatically reintroduced into the cluster.  Its cache is automatically repopulated over a period of time (corresponding to the cache refresh period) without increasing the load on the origin servers. Load Minimization: the configuration minimizes the load on the origin servers.  In particular, one peer member will never request content from the origin server when the other peer has content that is still current. Stingray’s cache flood protection is used in this configuration to manage the effects of multiple simultaneous requests for the same content that is no longer fresh (defined by a cache refresh time).  If multiple requests arrive at the same instant, a maximum of one request per second will be forwarded to the origin servers. Operational Benefits The configuration delivers the following operational benefits: Resilience: The service is fully resilient in the event of planned or unplanned failure of one traffic manager, or one or more origin servers. Optimal end user performance: the load on the origin servers is minimized in accordance with the fully-user-configurable cache policy, meaning that the origin servers are most unlikely to be overloaded and end user performance will not suffer. ... letting you deliver reliable services with predictable performance whilst minimizing infrastructure costs and eliminating overspend to accommodate occasional spikes of traffic or visitor numbers. Solution Architecture Stingray Traffic Managers are deployed in a fault-tolerant pair, and external traffic directed to a public-facing IP address (“Traffic IP address”).  Both traffic managers are active, and the one that owns the traffic IP handles incoming traffic: Failover Scenarios During normal operation, the primary traffic manager receives traffic and responds directly from its local cache.  If a cache miss occurs (i.e when priming the cache, or when content expires or needs refreshing), the traffic manager first checks with its secondary peer and retrieves the secondary’s cached version if that is still valid (not expired or needing refreshed).  It caches the copy retrieved from the secondary according to the local cache policy. If the secondary does not have valid content, then the resource will be retrieved from the origin servers (load balancing as appropriate) and cached by both traffic managers. If the secondary traffic manager fails, the primary will continue to respond directly from its local cache whenever possible.  If a cache miss occurs or a refresh is needed, content is retrieved from the origin servers and cached in the primary traffic manager’s cache. When the secondary traffic manager recovers, it may have an out-of-date cache (in the event of a network failure), or the cache may be completely empty (in the event of a software restart).  This cache is fully repopulated with the working set of documents within the configured refresh time.  There is no risk of the traffic manager serving stale content, and the load on the origin server is not increased during the repopulation. If the primary traffic manager fails, the secondary will take over with a fully primed cache and respond to all traffic.  If a cache miss occurs or a refresh is needed, the secondary will retrieve the content from the origin servers and cache it locally. When the primary traffic manager recovers, its cache may be out-of-date or completely empty (as above).  The primary takes back responsibility for user traffic and will update its cache rapidly by retrieving fresh content from the secondary traffic manager on every cache miss or refresh.  There is no risk of the primary traffic manager returning stale content, and the load on the origin servers is not increased during the repopulation. Stingray Configuration The configuration instructions are based on the following context: Two traffic managers, named ‘stingray-1’ and ‘stingray-2’ are configured in a fault tolerant cluster. A single-hosted traffic IP group with one IP address (‘traffic IP’) is configured to receive incoming traffic, and ‘stingray-2’ is designated a passive member of that group.  External DNS is configured to resolve to the traffic IP address.  This ensures that in normal operation, stingray-1 will be the active traffic manager with respect to handling user traffic. Content is stored on three webservers (webserver1, webserver2, webserver3) and the intent is that requests are load-balanced across these. The desired cache policy is to cache HTTP content for a long period of time (99915 seconds), but refresh content with one origin request every 15 seconds. Configure the pools Configure two pools as follows:   Name: Origin Servers, pool members: webserver1:80, webserver2:80, webserver3:80   Name: Cache Servers, pool members: stingray-2:80, webserver1:80, webserver2:80, webserver3:80 Configure both pools to use an appropriate load balancing algorithm.  Additionally, configure priority lists for the Cache Servers pool as follows: Cache Servers pool: priority list configuration This configuration ensures that when the Cache Servers pool is selected, all traffic is sent to stingray-2 if it is available, or is load-balanced across the three origin webservers if not. Note: if any changes are made to the origin servers (nodes are added or removed), both the Origin Servers and Cache Servers pools must be updated. Configure the Virtual Server Create an HTTP virtual server named Cache Service, and set the default pool to Origin Servers.  Note: This virtual server should generally listen on all IP addresses.  If it’s necessary to restrict the IP addresses it listens on, it should listen on the public traffic IP and on the IP address that is resolved by ‘stingray-2’ (the first node in the Cache Servers pool). Configure caching in the virtual server to cache content for the desired period (e.g. 99915 seconds), and to refresh it (with a maximum of one request per second) once it has been cached for 15 seconds, as follows: Caching settings for the Cache Service virtual server Add a request rule named Synchronize Cache to the virtual server to select the Cache Servers pool (overriding the default Origin Servers pool). This rule selects the Cache Servers pool when traffic is received on the primary traffic manager (‘stingray-1’). Note: you will need to update this rule if the hostname for the primary traffic manager is not stingray-1. This completes the configuration for the Cache Service virtual server and related pools and rule: Testing the caching service Testing this configuration must be done with due care because the presence of multiple caches can make debugging dificult.  The following techniques are useful: Sample Traffic Test with a small sample set of content to verify that the cache policies function as desired.  For example, the following command will repeatedly request a single cacheable resource: bash$ while sleep 1 ; do wget http://host/cacheablecontent.gif ; done Activity Monitor Use the activity monitor to chart connections made to the origin servers: Note that the activity monitor charts generally merge data from all traffic managers in a cluster; for clarity, this chart plots traffic individually per traffic manager.  Observe that despite the front-end load of 1 request per second (not plotted), only one request every 15 seconds is sent to the origin server to refresh the cache.  All requests originate from stingray-2. If Stingray-2 is suspended or shut down, the activity monitor report run from stingray-1 will verify that there is no increase in origin server traffic; likewise if stingray-1 fails and the activity monitor chart is run from stingray-2. Connections report The connections report will assist in identifying where traffic is routed. In the following report, requests for the same 866-byte resource were issued once a second, and received by stingray-1 and responded from the local cache (‘ To ’ is ‘ none ’). At 10:47:25, a cache refresh event occurred.  Stingray-1 forwarded the request to stingray-2 (192.168.35.11).  Stingray-2’s cache also required a refresh (because the caches are synchronized) so stingray-2 requested the content from one of the origin servers (192.168.207.103). Conclusion The synchronization solution effectively meets the goals of reliability, performance and minimization of load on the origin servers.  It may be extended from an active-active pair of Stingray traffic managers to a larger cluster if required, which will increase the level of redundancy in the system, but at the expense of a small (probably insignificant) increase in latency as caches must be synchronized across a larger set of devices. Priming the cache It is generally not a good idea to pre-prime a cache because the act of priming the cache puts a large one-time load on the origin servers.  In the majority of situations, it is better to use this synchronization solution and allow the caches to fill on demand, in response to end user traffic. If it is necessary to pre-prime the cache, this can be done using synthetic transactions to submit requests through the stingray cluster.  For example, ‘wget –r’ was used with success during advanced testing of this solution. 
View full article
This Document provides step by step instructions on how to set up Brocade Virtual Traffic Manager for Microsoft Lync 2010.
View full article
Content and Intellectual Property protection is a serious issue for any web site providing paid-for content. The article How to stop 'login abuse', using TrafficScript article describes how Stingray can be used to detect when a username and password is reused from different locations; this article showcases the power of Stingray's Java Extensions to apply a dynamically-generated, visible watermark to every image served up by your website.   The article covers how to use the Eclipse IDE or the command line to build the extension, how to apply the extension to your traffic, some optimization tricks to maximise performance, and how to debug and patch the code on-the-fly.   For more information on Java Extensions, you may like to read the article Feature Brief: Java Extensions in Stingray Traffic Manager.     Prerequisites   Before you begin, make sure that you have:   A working copy of Stingray, with the correct Java Runtime Environment (Sun JRE 1.5+) installed on the server (or just deploy the Stingray virtual appliance); A copy of the Java Platform JDK (to compile from the command line), and optionally an IDE such as Eclipse.   Configure the Stingray to load-balance traffic to a suitable website; you can use a public website like www.riverbed.com (remember to add a rule to set the host header if necessary). Check you can receive the website content through Stingray.   Step 1: Create your Java Extension   If you want to skip this section, just grab the ImageWatermark.class file attached to this article and proceed with that.   Go to the Stingray Admin interface, and locate the Catalog->Java Extensions page. On that page, locate the links to the Java Servlet API and ZXTM Java Extensions API files, and save these two Jar files in a convenient, long-term location:   Download the Java Servlet API and Stingray Java Extensions API files   If you are using the command line   Save the .jar files in the current directory, and create a new file named ImageWatermark.java .   If you are using Eclipse:   In Eclipse, create a new project:   Project Type: Java Project; Project Name: WaterMark; use the default options; Java Settings: Select the 'Libraries' tar and add the two external Jar files that you stored in the previous step:   Once you've created the project, go to the Package Explorer. Right-click on your project and create a new Class named 'ImageWatermark' with the default options.   The Java Code   Paste the following code into the ImageWatermark.java file that is created:   import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // Additional imports import java.awt.*; import java.awt.color.*; import java.awt.geom.*; import java.awt.image.*; import java.io.*; import javax.imageio.ImageIO; import com.zeus.ZXTMServlet.*; public class ImageWatermark extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException { try { ZXTMHttpServletResponse zres = (ZXTMHttpServletResponse) res; String ct = zres.getHeader( "Content-Type" ); if( ct == null || ! ct.startsWith( "image/" ) ) return; InputStream is = zres.getInputStream(); BufferedImage img = ImageIO.read( is ); Graphics2D g = (Graphics2D)img.getGraphics(); int width = img.getWidth(); int height = img.getHeight(); if( width < 200 || height < 30 ) return; String[] args = (String[])req.getAttribute( "args" ); String message = ( args != null ) ? args[0] : "Hello world!"; g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setComposite( AlphaComposite.getInstance( AlphaComposite.SRC_OVER, (float)0.5 )); Font myFont = new Font( "Sans", Font.PLAIN, 18 ); Rectangle2D bb = myFont.getStringBounds( message, g.getFontRenderContext() ); int x = 2; int y = (int)bb.getHeight(); g.setFont( myFont ); g.setColor( Color.darkGray ); g.drawString( message, x, y ); zres.setHeader( "Content-Type", "image/png" ); ImageIO.write( img, "PNG", zres.getOutputStream() ); } catch( Exception e ) { log( req.getRequestURI() + ": " + e.toString() ); } } public void doPost( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException { doGet( req, res ); } }   From the command line, you can compile this as follows:   $ javac -cp servlet.jar:zxtm-servlet.jar ImageWatermark.java   This will create an 'ImageWatermark.class' file in the current directory.   Using Eclipse, p aste this source in and hit ‘Ctrl-Shift-O’ to get the correct imports. Then save the file – this will automatically compile it; check that there were no errors in the compilation.   This should create the output file ImageWatermark.class in somewhere like HOMEDIR/workspace/ImageWatermark/bin .   Step 2: Load the extension into Stingray and watermark some images   Go to the Java Extensions catalog page and upload the Java Extension 'class' file for the WaterMark extension.   When you upload the class file, the Stingray Admin Server will automatically create a simple RuleBuilder rule that invokes the Java Extension.   Configure your virtual server to run the RuleBuilder rule on each response, then shift-reload the webpage that is delivered through Stingray to clear your cache and reload each image: Note the little "Hello world!" watermark on the top left of any images larger then 200x30 pixels.   Step 3: Optimize the way that the extension is called   The Java Extension is called by the RuleBuilder rule on every HTTP response. However, the extension only processes images; HTML, CSS and other document types are ignored.   Selectively running Java Extensions   Invoking a Java Extension carries some overhead, so it is prudent to ensure that Extensions are only invoked when they are needed. With a small change to the rule, you can ensure that this is the case.   First, convert the "ImageWatermark" RuleBuilder rule to TrafficScript by editing the rule and using the "Convert Rule" button. This will create a simple TrafficScript rule which calls the WaterMark extension:   java.run( "ImageWatermark" );   Edit the rule to add a condition that only runs the WaterMark extension when the object type is an image:   $contenttype = http.getResponseHeader( "Content-Type" ); if( string.startsWith( $contenttype, "image/" ) ) { java.run( "ImageWatermark" ); }   Passing parameters to a Java Extension   You can pass parameters from TrafficScript to a Java Extension. They are passed in as additional arguments to the ‘java.run()’ TrafficScript function:   $ip = request.getRemoteIP(); $time = sys.timeToString( sys.time() ); $message = "IP: ".$ip.", ".$time; $contenttype = http.getResponseHeader( "Content-Type" ); if( string.startsWith( $contenttype, "image/" ) ) { java.run( "ImageWatermark", $message ); }   The Java extension will read these arguments using the 'args' attribute, which returns a string array of the argument values:   String[] args = (String[])req.getAttribute( "args" ); String message = ( args != null ) ? args[0] : "Hello world!";   Use the Stingray Admin Interface to load in the new copy of the Java extension.   Now, when you shift-reload the web page (to clear the cache) the watermark text on the image will contain the message created in the TrafficScript rule, with the IP address of the remote user and the time when the image was downloaded.   Of course, you could also generate this message directly in the Java Extension, but quick code changes (such as modifying the text in the message) are easier when the code resides in TrafficScript rather than a compiled Java class.   Step 4: Live debugging and hot code patching   Finally, refer to the "Remote Debugging" section of the Java Development Guide (Product Documentation). This describes how to configure the arguments used to start the Java Virtual Machine (JVM) so that it can accept live debugging sessions from a remote debugger, and how to configure Eclipse to connect to the JVM.   You can edit and save the code in Eclipse. When you save the code, Eclipse compiles it and patches the code in the JVM on the fly. Try changing the point size of the font or the color and see the effects immediately:   Font myFont = new Font( "Serif", Font.BOLD, 12 );   and...   g.setColor( Color.red );   Live patching in Eclipse is a great way to debug, test and update code, but remember that it only patches the code in the live Java VM. When Stingray restarts, it will fall back to the version of the Java class that you originally uploaded, so once you’re finished, remember to upload the compiled class through the Admin interface so that it persists.   Read more Feature Brief: Java Extensions in Traffic Manager Writing Java Extensions - an introduction Watermarking PDF documents with Java
View full article
This Document provides step by step instructions on how to set up Brocade Virtual Traffic Manager for Microsoft SharePoint 2013.  
View full article