Archive for Perl
Flex Socket Connections : Socket Policy File
Posted by: | CommentsStarting with certain versions in the 9.0’s of Flash player, socket communication in Flex began adding additional security measures. The one I am going to discuss in the post is the socket policy file. In short, the socket policy file is an XML file that is served by default from port 843 and contains information regarding which ports on _this_ server that Flash may connect to. Additionally it allows you to specify from which domains you wish to allow connections.
Loading the Policy File From Flex
The policy file can be explicitly requested by making the call:
Security.loadPolicyFile("host.withpolicyfile.com:843");
Or you can trust it will implicitly make the request when you attempt a socket connection. The policy is valid for a particular IP address over the life of the SWF. A policy request consists of the following line, nothing more:
<policy-file-request/>
And the correct response is the policy file, followed by a null byte. My example policy server file will not be so picky about it’s request, use it at your own risk. Adobe has one that actually checks to see if the request was formatted correctly before sending the response. Furthermore, rather than reading in an actual policy file, my example hard codes it into the policy server.
Policy File Format
Here is a sample policy file, it is provided by Adobe. You can make whatever changes you need to, as I did in mine:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd"> <!-- Policy file for xmlsocket://socks.example.com --> <cross-domain-policy> <!-- This is a master socket policy file --> <!-- No other socket policies on the host will be permitted --> <site-control permitted-cross-domain-policies="master-only"/> <!-- Instead of setting to-ports="*", administrator's can use ranges and commas --> <!-- This will allow access to ports 123, 456, 457 and 458 --> <!--allow-access-from domain="swf.example.com" to-ports="123,456-458" /--> <allow-access-from domain="*" to-ports="80" /> </cross-domain-policy>
Policy File Server
And here is the Perl code that runs the policy server. You can see it is just a basic socket server. Adobe’s version of this (which I based mine off) allows you to pass in the port as well as the path to the policy file. This is a stripped down version of that server, with most of the essentials hard coded.
use Socket;
my $NULLBYTE = pack('c', 0);
my $port = 843;
my $content ='<?xml version="1.0"?>'."\n" .
'<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">'."\n" .
'<cross-domain-policy>' . "\n" .
'<site-control permitted-cross-domain-policies="master-only"/>'."\n" .
'<allow-access-from domain="*" to-ports="80" />'."\n" .
'</cross-domain-policy>'."\n";
socket (LISTENSOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp'))
or die "socket() error: $!";
setsockopt(LISTENSOCK, SOL_SOCKET, SO_REUSEADDR, pack('l', 1))
or die "setsockopt() error: $!";
bind (LISTENSOCK, sockaddr_in($port, INADDR_ANY))
or die "bind() error: $!";
listen (LISTENSOCK, SOMAXCONN)
or die "listen() error: $!";
while ( my $clientAddr = accept(CONNSOCK, LISTENSOCK)) {
my ($clientPort, $clientIp)= sockaddr_in($clientAddr);
my $clientIpStr = inet_ntoa($clientIp);
# Consume the request
local $/ = $NULLBYTE;
my $request = <CONNSOCK>;
chomp $request;
# Send the policy file
print CONNSOCK $content;
print CONNSOCK $NULLBYTE;
close CONNSOCK;
}
}
Opening A Port
Remember to open port 843 (in Fedora Core) by adding the following line in /etc/sysconfig/iptables :
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 843 -j ACCEPT
Then reload the iptables:
/etc/init.d/iptables restart
Book Review: Learning Perl
Posted by: | CommentsPerl is one of those languages I probably don’t care if I ever master, but I have to deal with it from time to time both in web applications and in shell scripting, that I wanted to gain a better understanding of it. For that reason I passed up on getting the highly acclaimed “camel book” and got Learning Perl, 5th edition, by Randal L. Schwartz, Tom Phoenix, and brian d foy, which is a slim 328 pages. I also found the subtitle encouraging, “Making Easy Things Easy & Hard Things Possible”– my early experiences with Perl have not been pleasant ones.
I enjoyed the book more than I expected, and have found it equal to the tasks I need to perform with Perl. It reads much like any “beginning” programming book (without all of the ‘what is a computer?’ nonsense you’d find in a Deitel&Deitel beginner book). My depth of experience with PHP helped me to be a little more comfortable with the syntax, and allowed me to ponder some of the trickier concepts a little more deeply. Like the default variable $_… I’m still thinking about that.
The book has a good introduction to modules, and covers both using cpan to install, and installing from source. Both of which I’ve had to do recently. The chapter on Regular Expressions was especially helpful, and probably one of the best short-tutorials on regular expressions I’ve ever read. Someday I’m going to have to read a book about those, maybe I’ll remember it better, but until then brief explanations like these will be my regex life blood.
The book offers exercises at the end of each chapter, in fact the authors claim this book is the product of their curriculum taught over a number of years. I didn’t work through all of them, but I did a few and I found them helpful. They also include possible solutions to each of them. As a student of computer science, I appreciated their preface to each solution “Here’s one way to do it:”.
All things considered, I enjoyed my experience with this book. If your goal is to become a hard core Perl wizard, you might want to go with the camel book. If your intentions for Perl are more casual, then you probably want this book.
Apache mod_proxy_balancer Self Registration : Part 3
Posted by: | CommentsI’ll start off by going over the basic high level architecture for my self registration procedure:
There is a register.php script residing on the load balancer, accessible via HTTP.
There is a deregister.php script residing on the load balancer, accessible via HTTP.
There is a register_with_lb.pl script residing on the web server, in /usr/local/bin/.
There is a deregister_with_lb.pl script residing on the web server, in /usr/local/bin/.
There is a MySQL database that stores the current configuration state, on it are two stored procedures register_lb and deregister_lb.
register.php
No changes were made to register.php as described in this post , though I’m considering some alterations to increase its security.
deregister.php
The biggest difference between register.php and deregister.php (aside from their purpose) is where the insert/delete database code is called from and why. When register.php is called by the web server, it will have already inserted information about itself into the database, including its hash. I made the decision that I did not want the load balancer responsible for inserting servers into the database. It would merely check that the requesting server inserted itself, and then regenerate the balancer_members.conf.
In the case of deregister.php I decided I wanted the server making the call to still be in the database so the script could verify the identity before removing it and regeneration the balancer_members. And since the deregistration SQL is contained within a stored procedure, I needed to make some changes to the script (as compared to register.php) regarding the database.
Specifically, the standard mysql library cannot call stored procedures. So I had to convert it to using mysqli, which is a similar, though more OO approach. The portion of the code that regenerates the balancer_members.conf is similar enough that I won’t re-list it here, but I will show how to connect using mysqli, and how to call a stored procedure.
$mysqli = new mysqli($dbhost, $dbuser, $dbpass, $dbname);
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
}
$query = "SELECT count(*) as count FROM " . $dbtable . " WHERE ip='" . $_SERVER['REMOTE_ADDR'] . "';";
$result = $mysqli->query($query);
$row = $result->fetch_row();
echo $row[0];
if ($row[0] >= 1) {
$del_query = "call deregister_lb('" . $_SERVER['REMOTE_ADDR'] . "');";
$del_result = $mysqli->query($del_query);
//<code for regenerating the conf file removed here>
echo exec('echo "' . $file . '" > /etc/httpd/conf.d/balancer_members.conf');
echo exec("sudo /usr/local/bin/reload_httpd");
}
As you can see, I’m using the actual REMOTE_ADDR to determine the validity of the request.
(de)register_lb.sql Stored Procedure
Here is the code for the deregister_lb stored procedure:
DROP PROCEDURE IF EXISTS deregister_lb $$
CREATE PROCEDURE deregister_lb ( ip VARCHAR(100) )
BEGIN
DELETE FROM lb2_members
WHERE ip=_ip;
END $$
and also for the register_lb stored procedure:
DROP PROCEDURE IF EXISTS register_lb $$
CREATE PROCEDURE register_lb (
_hostname VARCHAR(100),
_ip VARCHAR(40),
_loadfactor INT,
_hash VARCHAR(100)
)
BEGIN
DECLARE already_exists INT DEFAULT 0;
SELECT count(*) INTO already_exists FROM lb2_members WHERE hash=_hash;
IF already_exists=1 THEN
UPDATE lb2_members
SET hostname=_hostname, ip=_ip, loadfactor=_loadfactor
WHERE hash=_hash;
ELSE
INSERT INTO lb2_members (ip, hostname, loadfactor, hash)
VALUES (_ip, _hostname, _loadfactor, _hash);
END IF;
END $$
Note that I’ve omitted the code that changes the delimiter to $$ instead of a semicolon.
register_with_lb.pl
This perl script uses perl DBI for accessing the database. I had to get that installed on my web server since it wasn’t already. Normally you can install perl packages using the cpan command. In which case you would issue the following commands to install DBI and a MySQL driver for it:
cpan DBI cpan DBD::mysql
If it’s the first time you’ve run cpan, you will need to go through some configuration. It’s pretty much self explanatory, and I just accepted all of the defaults. Everything installed correctly except for the MySQL driver, which I ended up having to install from source. If I had executed the command:
yum install mysql-devel.i386
first, then my cpan install of DBD::mysql might have worked, but I didn’t realize that until installing from source. In case you ever need to install a perl module from source, particularly the DBD::mysql driver, enter these commands (which I think is basically what cpan does):
yum install mysql-devel.i386 #(only requred in this particular instance) wget http://www.cpan.org/modules/by-module/DBD/DBD-mysql-4.011.tar.gz gzip -cd DBD-mysql-4.011.tar.gz | tar xf - cd DBD-mysql-4.011 #(or whatever version you downloaded) perl Makefile.PL make
Here is how you connect to the database and call a stored procedure:
my $dsn = "DBI:mysql:host=mysql.host;database=lb_register";
my $dbh = DBI->connect ($dsn, "lbuser", "lbpasswd")
or die "Cannot connect to MySQL server\n";
my $sql = "call register_lb('" . $localhost . "', '" . $localip . "', " . $loadfactor . ", '" . $hash . "')";
$dbh->do($sql);
$dbh->disconnect();
After that, register_with_lb.pl opens a socket to the load balancer and makes an HTTP request over the socket. There are probably easier ways to do this, I just happened to have the socket code lying around and was glad to be able to reuse it. Here’s the gist of it, in case you’re interested:
# Parse the URI.
my $url = URI->new("http://load.balancer.com/register/register.php?hash=" . $hash);
# Parse these in from the command line
$host = $url->host;
$port = $url->port;
$resource = $url->path;
$query = $url->query;
# Initialize the socket
$socket = IO::Socket::INET->new ( Proto => "tcp", PeerAddr => $host, PeerPort => $port,);
unless ($socket) { die "Error connecting to $host" }
$socket->autoflush(1);
# Format the request
my $request = "GET " . $resource . (($query)?"?" . $query : "") . " HTTP/1.1" . $EOL . "Host: " . $host . $EOL . "User-agent: register_script" . $EOR;
# Use send() to make the request, and output the response.
# Not necessary in this example, but informational.
if ( $socket->send($request) ) {
while ( <$socket> ) { print }
}
# Close the socket
close $socket;
The above code pretty much sums up deregister_from_lb.pl, since no database calls are made, a call is simply made to the deregister script. The line you would change is as follows:
my $url = URI->new("http://my.balancer.com/register/deregister.php");
Then make the files executable, and copy them to be used by the startup script described in the previous post:
chmod a+x register_with_lb.pl chmod a+x deregister_with_lb.pl cp register_with_lb.pl /usr/local/bin/ cp deregister_with_lb.pl /usr/local/bin
I don’t show it here, but right now my IP addresses are hard coded. There are a number of ways you can find out your actual IP address from within perl, I’m just not doing that right now.
Securing the register scripts
As an additional security measure, I’ve restricted access to the /register/ location on the load balancer to the IP address range I expect my web servers to be from, like this:
<Location /register> Order Deny,Allow Deny from all Allow from 10.0.0. </Location>
And now you have a web server that can register automatically (if you’ve gone through the previous two posts as well) with a mod_proxy_balancer load balancer.
Update
I did some searching around to find a way to determine your IP address from inside the perl script. This is a simple way if your server has a public IP address and reverse DNS set up correctly for that IP address:
use Socket; use Sys::Hostname; my $host = hostname(); my $addr = inet_ntoa(scalar(gethostbyname($host)) || 'localhost');
If your slave web servers are on a private network, the above command will return the loopback IP address (127.0.0.1) which isn’t useful for the load balancer (I wonder if it would start an infinite loop and crash the load balancer?). I found a function that prints out the IP address by parsing it out from the results of the ifconfig command.
It seemed a little long to just rip off and copy verbatim. So here’s a link to that code (which is what I’m using now) in case you’d like to use it. Perl script to get IP address.

