| Level: Introductory Nathan Harrington, Programmer, IBM
21 Oct 2008 Learn how to use Argus and client/server Perl code to monitor your network connections and
shut down machines no longer in use.
Recent pushes for "green" technology focus mostly on talk, with little action for the
typical home- or small-office environment. Many users leave their systems online
continuously through laziness or ignorance, resulting in a significant source of power
consumption, as well as an additional vector for malware propagation. The tools and
code presented here allow you to find those inactive systems and securely start the
shutdown process. With a Linux® box monitoring your network connections using
Argus and some custom Perl code, any system that supports Perl can be set to be
remotely shut down when a centralized set of inactivity rules are met.
Hardware requirements
Although the control code for securely communicating and shutting down systems is
cross-platform, you'll need hardware capable of running Linux for the monitoring
computer, which we'll call the control node. Such a node could be a Linksys
NSLU2 or similar low-wattage device running Linux. The control node needs a method for
monitoring network connections throughout your entire network. Minimal processing
power, RAM, and storage are required, unless you intend to monitor hundreds of machines
on a high-bandwidth pipe. Virtually any PC manufactured after 2000 with an appropriate
network adapter and the capability to run Linux should provide enough capacity to
monitor a small office or home network. One network adapter for the control node will
suffice in low-traffic settings, but two is better on busy networks. See the
"Hardware placement" section for suggestions on where to place your selected system.
Software requirements
Snort, Tcpdump, or a number of network monitoring software packages will work in theory
for measuring usage. This article uses a combination of Argus and racluster (an Argus
client) due to its simplicity and light weight for tracking connection status. In
addition to the heavy lifting done by Argus, you'll need Perl and the associated
modules for network communications, random string generation, and encryption. These
modules are POE, String::MkPasswd, and Crypt::Blowfish, respectively. See Resources for details on finding and installing these modules.
Hardware placement
Selecting an appropriate location for a full-network monitoring connection point is a
complex topic with many variables dependent on specific network attributes. A certain
amount of familiarity with network topologies is assumed, as well as the ability to
identify the upstream connection point all network traffic flows through.
For example, consider a typical home network with four computers connected to a
wireless access point (WAP) or router with an outbound connection through a cable or
DSL modem. (I realize using the term "modem" here is inaccurate, but since the industry
latched onto the misnomer, I'll use it, too — albeit reluctantly.)
Placing the control node network connection between the WAP and the modem allows
monitoring of all connections to the Internet from any machine on network.
Alternatively, many home-office WAPs are also routers with wired Ethernet ports, so
connecting to the WAP's wired port directly may suffice.
Keep in mind that the correct placement of the control node (with the appropriate
network interface) and the associated configuration of the network adapter or adapters
on the control node may present a formidable challenge for the casual reader. See Resources for tutorials and research materials to help you get started in network monitoring.
Argus setup on the control node
Providing simple measurements of network connection attributes with minimal resource
consumption is ideal for this article. Instead of writing Snort rules or analyzing
Tcpdump output, use Argus and some command-line switches to provide a minimal list of
network connections. On the control node, install Argus and run the following command.
Listing 1. Argus command on control node
argus -w /tmp/argusOutput -i eth0
|
Transaction records recorded by Argus are appended to the /tmp/argusOutput file, and
the eth0 interface is assigned to sniff packets in promiscuous mode. Now that Argus is
set up to monitor the connections for each host on the network, use one of the many
Argus clients to print out information about those connections. Use the command racluster -L0 -M -u -r /tmp/argusOutput tcp to show the current
connections on the network, including information like total bytes transferred and
connection start times. Note that this command may produce a great deal of
information, so consider isolating the most active connections with the command racluster -L0 -M -u -r /tmp/argusOutput tcp -w - | rasort -m bytes |
head .
rasort combined with head will produce a list of the top 10 connections by the number of bytes transferred.
Note that the Argus and process_racluster.pl program (listed below) need to be run as a
user with appropriate privileges for accessing the network adapter in promiscuous mode.
In most cases, this is the root user.
Selecting machines and bandwidth usage
Each machine to be monitored for inactivity needs to have an identifier, a number of
bytes indicating inactivity, and a command to run when inactivity is detected. Listing
2 shows an example activityRules file.
Listing 2. Example activityRules file
192.168.1.30_#_10_#_xmessage "Elena's laptop inactive"
192.168.1.35_#_400_#_xmessage "Alexander's desktop inactive"
|
Note that these numbers are examples only and need to be tuned for the specific usage
of machines on the network. The first line will pop up a message on the control nodes'
display (if X Window System is active), letting the user know that the laptop is inactive and needs to be powered down.
process_racluster.pl for inactivity monitoring
Reading the activityRules file, processing racluster output and executing commands is performed by the
process_racluster.pl program. Listing 3 lists the first part of process_racluster.pl.
Listing 3. process_racluster.pl part 1
#!/usr/bin/perl -w
# process_racluster.pl - run commands if inactivity conditions are met
use strict;
my $sleepTime = 600; # seconds between network activity reads
my $argusFile = "/tmp/argusOutput";
my $raclusterFile = "/tmp/raclusterOutput";
my %rules = (); # store commands, thresholds for each ip
open( INFILE, "activityRules" ) or die "can't open rules file";
while(my $line = <INFILE> )
{
my( $ip, $threshold, $command ) = split "_#_", $line;
$rules{ $ip }{ threshold } = $threshold;
$rules{ $ip }{ command } = $command;
}#while in file
close( INFILE );
|
After variable declaration and reading the activityRules file, the next step is the
main logic loop.
Listing 4. process_racluster.pl part 2
while( 1 )
{
# check if Argus is running
my $cmd = qq{ps -aef | grep argus | wc -l };
die "argus no longer running" unless ( `$cmd` ne "1\n" );
# transform the argus data into human readable form
$cmd = qq{racluster -L0 -M -u -r $argusFile tcp > $raclusterFile};
$cmd = `$cmd`;
my %hosts = ();
open( INFILE, "$raclusterFile" ) or die "can't open argus output file";
while(my $line = <INFILE> )
{
# grab destination addr, port, total packets, bytes, connection status
$line = substr($line,68);
my( $ipAndPort, undef, $bytes ) = split " ", $line;
my @parts = split '\.', $ipAndPort;
# skip if there is no port associated (probably not tcp)
next unless( $#parts == 4 );
# get just the ip address
my $ip = substr($ipAndPort, 0, rindex($ipAndPort,".") );
$hosts{$ip} += $bytes;
}#while linein
close(INFILE);
|
At each pass of the main logic, a simple process check is run to make sure the Argus
network connection monitor is active. If so, the racluster command described above is run, and the IP address and
associated byte count for all connections to that address is recorded in the %hosts hash. Listing 5 shows the remainder of the main logic loop.
Listing 5. process_racluster.pl part 3
for my $key( keys %rules )
{
my $foundKey = 0;
$foundKey = 1 if( ! exists($hosts{$key}) );
if( exists($hosts{$key}) && ($hosts{$key} < $rules{$key}{threshold}) )
{
$foundKey = 1;
}#if key is not found
next unless ( $foundKey == 1 );
# run the command, remove the entry from processing
system( $rules{$key}{command} );
delete $rules{$key};
}#for each key in the rules
# delete the processed files, wait for next pass
$cmd = qq{ rm $argusFile $raclusterFile };
$cmd = `$cmd`;
sleep($sleepTime);
}#while 1 loop
|
Each defined IP in the rule dataset is checked for existence in the %hosts hash. If the IP is not found, or the byte threshold is less
than that specified in the rules file, the inactive command is run. After an inactive
state has been found, the entry in the rule dataset is removed to prevent further
processing of an already-inactive host. /tmp/argusOutput is deleted to reset the recorded connection status, and the temporary racluster output file is deleted to save space. After sleeping for 10 minutes, the process repeats.
To begin the inactivity processing, run the command perl
process_racluster.pl with the same privileges as the ID that ran Argus (usually
root). As the "laptop" and "desktop" systems move to inactive states, you'll see a
pop-up message on the control node showing you which person to give the Kermit guilt
trip ("It's not easy being green").
Remote shutdown of systems
Providing a method for remotely shutting down systems is a great way to open doors for
control and abuse. To prevent some of these simpler attacks, the following client and
server implement a simple challenge-response system. This approach helps ensure that
systems will only respond to the shutdown command if the correct encryption key is used.
Listen for shutdown requests: shutdownListener.pl
Each system that will respond to remote shutdown requests needs to run the
shutdownListener.pl program.
Listing 6. shutdownListener.pl
#!/usr/bin/perl -w
# shutdownListener.pl - manage challenge/response, shutdown if authenticated
use strict;
use POE qw(Component::Server::TCP Filter::Reference);
use Crypt::Blowfish;
use String::MkPasswd qw(mkpasswd);
my $key = "<enter your key of choice here>";
my $random = substr(mkpasswd,0,8);
my $cipher = new Crypt::Blowfish $key;
my $cipherText = "";
# heavily influenced by the POE cookbook:
POE::Component::Server::TCP->new
( Alias => "shutdownListener",
Port => 11211,
ClientFilter => "POE::Filter::Reference",
ClientInput => sub {
my ( $sender, $heap, $input ) = @_[ SESSION, HEAP, ARG0 ];
if( $cipherText eq "" )
{
# send the challenge text back to client
$heap->{client}->put( \$random );
$cipherText = $cipher->encrypt( $random );
}elsif( $cipherText eq $$input )
{
print "challenge response match, shutting down\n";
# uncomment this line to actually shutdown the system
# system(`shutdown -h now`); # linux
# system(`shutdown -s -t 01`); # windows
exit(0);
}else
{
print "challenge response does not match, exiting server";
exit(1);
}#if cipher text not generated
},
);
$poe_kernel->run();
exit 0;
|
The POE module provides one of the easiest ways to create bidirectional TCP
communications using Perl. When a shutdown request is received, the current random
string is sent back to the client. If the client's encrypted return string and the
locally encrypted string match, the shutdown command is run. Note that you should
replace the key with a unique value that matches the key defined in clientShutdown.pl
below. In addition to shutting down the system, the shutdownListener.pl will terminate
its execution if the challenge response is not met to deter repeated connection attempts.
Save the shutdownListener.pl code on each machine that should enable remote shutdowns.
shutdownListener.pl should be run as a user with appropriate privileges to shutdown the machine and access the port selected.
Send shutdown requests: clientShutdown.pl
Each machine to be shut down listens to the control node for shutdown connection
requests. These requests come from the clientShutdown.pl program.
Listing 7. clientShutdown.pl
#!/usr/bin/perl -w
# clientShutdown.pl - compute challenge request, send response
use strict;
use POE qw( Component::Client::TCP Filter::Reference);
use Crypt::Blowfish;
die "specify hostname " unless @ARGV == 1;
my $host = $ARGV[0];
my $key = "<enter your key of choice here>";
my $cipher = new Crypt::Blowfish $key;
my $port = 11211;
my $sendStr = "getChallenge";
# heavily influenced by the POE cookbook:
POE::Component::Client::TCP->new
( RemoteAddress => $host,
RemotePort => $port,
Filter => "POE::Filter::Reference",
Connected => sub {
$_[HEAP]->{server}->put( \$sendStr );
},
ConnectError => sub {
die "could not connect to $host:$port ...\n";
},
ServerInput => sub {
# get the server response
my ( $input ) = @_[ ARG0 ];
# encrypt the response, send back
my $cipherText = $cipher->encrypt( $$input );
$_[HEAP]->{server}->put( \$cipherText );
},
);
$poe_kernel->run();
exit 0;
|
Similar to shutdownListener.pl, the clientShutdown.pl program connects to the machine
to be shut down, retrieves the random text, encrypts it with the shared key, and returns
the encrypted text. Again, the encryption key needs to match between the client and
server. Modify the activityRules file by inserting the line shown in Listing 8.
Listing 8. Remote shutdown in activityRules
192.168.1.33_#_5555554_#_perl clientShutdown.pl 192.168.1.33
|
Now when process_racluster.pl is restarted and the total network usage for the
192.168.1.33 machine drops below approximately 5.5 MB over 10 minutes, the shutdown command will be sent.
Usage
To recap the usage instructions: Modify the activityRules file to run the appropriate
commands for each machine. Start shutdownListener.pl on any machine that should be
remotely shut down. Start Argus and the process_racluster.pl program on the control node.
Conclusion, further examples
With the tools and code in this article, you can set up a network monitor to track the
total usage of specific machines on your network. Use the programs presented herein to
enforce an automatic shutdown when systems become inactive on the network. Consider
modifying Argus to search for specific connection types or hosts. Shut down systems
when only 15-minute e-mail checks are active, or when a streaming audio connection is
left playing in mute for hours. Monitor the protocol used or the connection start
times to further enhance the detection of inactivity that triggers a shutdown.
Download Description | Name | Size | Download method |
---|
Sample code | os-automatic-shutdown-inactiveShutdown_0.1.zip | 3KB | HTTP |
---|
Resources Learn
Get products and technologies
Discuss
About the author | | | Nathan Harrington is a programmer at IBM currently working with Linux and resource-locating technologies. |
Rate this page
| |