cancel
Showing results for 
Search instead for 
Did you mean: 

Virtual Hosting FTP services

FTP is an example of a 'server-first' protocol. The back-end server sends a greeting message before the client sends its first request. This means that the traffic manager must establish the connection to the back-end node before it can inspect the client's request.

 

Fortunately, it's possible to implement a full protocol proxy in Stingray's TrafficScript language. This article (dating from 2005) explains how.

 

FTP Virtual Hosting scenario

 

We're going to manage the following scenario:

 

A service provider is hosting FTP services for organizations - ferrari-f1.com, sauber-f1.com and minardi-f1.com. Each organization has their own cluster of FTP servers:

 

  • Ferrari have 3 Sun E15Ks in a pool named 'ferrari ftp'
  • Sauber have a couple of old, ex-Ferrari servers in Switzerland, in a pool named 'sauber ftp'
  • Minardi have a capable and cost-effective pair of pizza-box servers in a pool named 'minardi ftp'

 

The service provider hosts the FTP services through Stingray, and requires that users log in with their email address. If a user logs in as 'rbraun@ferrarif1.com', Stingray will connect the user to the 'ferrari ftp' pool and log in with username 'rbraun'.

 

This is made complicated because an FTP connection begins with a 'server hello' message as follows:

 

220 ftp.zeus.com FTP server (Version wu-2.6.1-0.6x.21) ready.

 

... before reading the data from the client.

 

Configuration

 

Create the virtual server (FTP, listening on port 21) and the three pools ('ferrari ftp' etc).  Configure the default pool for the virtual server to be the discard pool.

 

Configure the virtual server connection management settings, setting the FTP serverfirst_banner to:

 

220 F1 FTP server ready.

 

Add the following trafficscript request rule to the virtual server, setting it to run every time:

 

$req = string.trim( request.endswith( "\n" ) );  
  
if( !string.regexmatch( $req, "USER (.*)", "i" ) ) {  
   # if we're connected, forward the message; otherwise  
   # return a login prompt  
   if( connection.getNode() ) {   
      break;  
   } else {  
      request.sendresponse( "530 Please log in!!\r\n" );  
      break;  
   }  
}  
  
$loginname = $1;  
  
# The login name should look like 'user@host'  
if( ! string.regexmatch( $loginname, "(.*)@(.*)" ) ) {  
   request.sendresponse( "530 Incorrect user or password!!\r\n" );  
   break;  
}  
  
$user = $1;   
$domain = string.lowercase( $2 );  
  
request.set( "USER ".$user."\r\n" );  
  
# select the pool we want...   
if( $domain == "ferrarif1.com" ) {   
   pool.use( "ferrari ftp" );  
} else if( $domain == "sauberf1.com" ) {   
   pool.use( "sauber ftp" );  
} else if( $domain == "minardif1.com" ) {   
   pool.use( "minardi ftp" );  
} else {  
   request.sendresponse( "530 Incorrect user or password!!\r\n" );  
}  

 

And that's it! Stingray automatically slurps and discards the serverfirst banner message from the back-end ftp servers when it connects on the first request.

 

More...

 

Here's a more sophisticated example which reads the username and password from the client before attempting to connect. You could add your own authentication at this stage (for example, using http.request.get or auth.query to query an external server) before initiating the connect to the back-end ftp server:

 

TrafficScript request rule

 

$req = string.trim( request.endswith( "\n" ) );  
  
if( string.regexmatch( $req, "USER (.*)" ) ) {  
   connection.data.set( "user", $1 );  
   $msg = "331 Password required for ".$1."!!\r\n";  
   request.sendresponse( $msg );  
   break;  
}  
  
if( !string.regexmatch( $req, "PASS (.*)" ) ) {   
   # if we're connected, forward the message; otherwise  
   # return a login prompt  
   if( connection.getNode() ) {   
      break;  
   } else {  
      request.sendresponse( "530 Please log in!!\r\n" );  
      break;  
   }  
}   
  
$loginname = connection.data.get( "user" );  
$pass = $1;  
  
# The login name should look like 'user@host'  
if( ! string.regexmatch( $loginname, "(.*)@(.*)" ) ) {  
   request.sendresponse( "530 Incorrect user or password!!\r\n" );  
   break;  
}  
  
$user = $1;   
$domain = string.lowercase( $2 );  
  
# You could add your own authentication at this stage.  
# If the username and password is invalid, do the following:  
#  
# if( $badpassword ) {   
#    request.sendresponse( "530 Incorrect user or password!!\r\n" );  
#    break;  
# }   
  
# now, replay the correct request against a new   
# server instance  
  
connection.data.set( "state", "connecting" );  
request.set( "USER ".$user."\r\nPASS ".$pass."\r\n" );  
  
# select the pool we want...   
if( $domain == "ferrarif1.com" ) {   
   pool.use( "ferrari ftp" );  
} else if( $domain == "sauberf1.com" ) {   
   pool.use( "sauber ftp" );  
} else if( $domain == "minardif1.com" ) {   
   pool.use( "minardi ftp" );  
} else {  
   request.sendresponse( "530 Incorrect user or password!!\r\n" );  
} 

 

TrafficScript response rule

 

if( connection.data.get("state") == "connecting" ) {  
   # We've just connected, but Stingray doesn't slurp the serverfirst  
   # banner until after this rule has run.  
   # Slurp the first line (the serverfirst banner), the second line   
   # (the 331 need password) and then replace the serverfirst banner  
   $first = response.getLine();  
   $second = response.getLine( "\n", $1 );  
   $remainder = string.skip( response.get(), $1 );  
   response.set( $first.$remainder );  
   connection.data.set( "state", "" );  
} 

 

Remember that both rules must be set to 'run every time'.

Version history
Revision #:
1 of 1
Last update:
‎02-22-2013 08:41:AM
Updated by:
 
Labels (1)
Tags (2)