VNC Port Monitoring
George Wolanin
A dilemma surfaced at my office
where users needed remote access to a server for capturing network traffic
using Ethereal. Corporate network security wouldn't allow X display
to be sent because of a security vulnerability with ports that use it, so
my responsibility as the systems administrator was to provide a solution
that provided the access. My solution was to use VNC (http://www.realvnc.com) on the
Ethereal servers.
VNC is the access of choice because of its ability to
work on multiple platforms and its ease of use and user setup. The VNC
servers (located in different states) have approximately 10 VNC server
sessions running on them to which the users can connect. With this many VNC
server sessions, I needed a way to monitor the connections for
troubleshooting purposes.
The Problem
After a few months with multiple VNC servers running
on a couple of servers and users capturing network data, I became aware of
some instability issues with the servers. Users were complaining that their access would disappear and then come back with all of their test results gone. To keep it short, the users who
couldn't access their assigned VNC port
were rebooting the server to get it back (at the time all trusted users had
root access), because I had started the VNC server process with the -DisconnectClients=off option:
#vncserver -DisconnectClients=off
This option prevents a user from
"stealing" the session when someone else is connected. Since
the root password was known to a couple of users, the initial solution was
to reboot the server. However, rebooting the server interrupted other users
who were capturing network traffic and caused a systems administration
nightmare! This is the action that provoked me to create a way to monitor
the ports and provide that availability to the users so they would know
which port they could use.
The Solution
The solution uses a Perl script that checks the VNC
server port status and updates a MySQL database. I used Perl because of its
availability on multiple platforms, such as RHEL and Solaris OS. The Web
script is based on a very simple PHP script that accesses the MySQL
database to make the status visible on the Web. Finally, there is an
addition to the crontab that will need to be added to each server that
contains the Perl script, and its responsibility is to talk to the database
server updating its VNC port status.
Scripts
The Perl script is the heart of the monitoring tool;
it does two things: it checks current server VNC port status, and updates
the MySQL database. Since VNC utilizes the ports 5901-59xx for *nix-based
systems, the netstat command comes in very handy to find the current connections to
those ports in getting their connection status for VNC. Another option
would be to use the lsof command within the script. The script is installed on each
server that will have the multiple VNC ports active as shown in Figure 1.
Setup
To start multiple VNC sessions
at system boot, add the following script to the
end of the /etc/init.d/rc.local file on Linux. Note that this script will
create vncserver sessions for the same UID, and you must have already
executed the VNC server command at the prompt as the same UID to create the
VNC password file:
Append to /etc/init.d/rc.d/rc.local
##
## NOTE THAT THIS SCRIPT IS NOT A REPLACEMENT FOR rc.local
## but add the following to the end of rc.local
##
n=0;
while [ "$n" -lt x ] ## x being the number of VNC server
## sessions you want
do
su UID -c "/usr/bin/vncserver -DisconnectClients=off" &
n=$(expr $n + 1)
sleep 5
done
Create a database in MySQL then import vnc.sql to
create the table definitions:
vnc.sql
CREATE TABLE `servers` (
`id` int(5) NOT NULL auto_increment,
`date_time_checked` timestamp NOT NULL default CURRENT_TIMESTAMP,
`server` varchar(50) NOT NULL default '',
`server_ip` varchar(16) default NULL,
`client` varchar(16) default NULL,
`vncport` int(1) NOT NULL default '0',
`status` varchar(35) NOT NULL default '',
PRIMARY KEY (`id`)
)
I usually place my cron scripts in /usr/local/scripts:
vnccheck.pl
#!/usr/bin/perl
#
use Shell qw( netstat );
use DBI;
use Socket;
use Sys::Hostname;
## Enter the server's IP address that this script
## Will be located on
$local_ipaddr = "";
$hostname = hostname();
##
## the netstat -at option looks at all TCP connections
## below we will search for TCP ports 5900 - 5919
##
@lis = netstat('-at | grep 59[0-1][0-9] | grep LISTEN');
@est = netstat('-at | grep 59[0-1][0-9] | grep ESTABLISHED');
foreach $i ( @lis ) {
@tmp = split( /:/, $i );
push @pAssign, substr( $tmp[1], 0, 4 )
}
foreach $k ( @pAssign ) {
$found = 0;
foreach $m ( @est ) {
@tmp = split( /:/, $m );
@ip = split( / +/, $tmp[1] );
if ( $k == $ip[0] ) {
### print "VNC port $k is taken by $ip[1]\n";
$found = 1;
push @final, "$k\t$ip[1]";
last;
}
}
if ( $found == 0 ) {
### print "VNC port $k is AVAILABLE\n";
push @final, "$k\tAVAILABLE";
}
}
print "------------------QUERY RESULTS-------------------------\n";
### ACCESS DB AND STORE THE QUERY RESULTS ###
my $dsn = 'dbi:mysql:vnc_ethereal:$dbIP:3306';
my $user = 'vnc';
my $pass = 'password';
my $dbh = DBI->connect( $dsn, $user, $pass )
or die "Cannot connect to the DB: $DBI::errstr\n";
### CHECK TO SEE IF SERVER IS ENTERED INTO THE DATABASE
### IF NOT, ADD IT
### IF IT IS, CHECK TO SEE IF THE CURRENTLY HELD PORTS ARE IN THE DB
### IF NOT, INSERT THEM
### IF THEY ARE, UPDATE THEM
$sE = 0;
$sth = $dbh->prepare( "SELECT server FROM servers WHERE \
server_ip = '$local_ipaddr'" );
$sth->execute();
if ( $sth->rows == 0 ) {
print "SERVER: $local_ipaddr does not exist in the database\n";
} else {
$sE = 1;
}
###
### write VNC info to DB
###
### If serverIP exists, delete ALL records containg it, and
### rewrite them (this should be reworked to be more efficient
print "value of sE: $sE\n";
if( $sE == 1 ) {
$sth = $dbh->prepare( "DELETE FROM servers \
WHERE server_ip = '$local_ipadddr'" );
$sth->execute();
}
foreach $a ( @final ) {
@tmp = split( /\t/, $a );
###
### tmp[0] = VNC PORT
### tmp[1] = IP | {connected ip}
###
my $wth = $dbh->prepare( "INSERT INTO servers ( server, \
server_ip, vncport, status ) VALUES \
( '$hostname','$local_ipaddr','$tmp[0]','$tmp[1]' ) " );
$wth->execute();
}
PHP Web Script
The PHP Web script is a simple script that accesses
the database that has the data for each VNC server. The following is my PHP
script to pull the data from the database:
vnc.php
<html>
<head>
<title>VNC Port Status</title>
<meta http-equiv=" Content-Type" content=" text/html; charset=iso-8859-1">
<style type="text/css">
<!--
.vncstyle {
font-family: "Lucida Sans";
font-size: 12px;
}
-->
</style>
</head>
<body>
<p align="center"> </p>
<p align="
center"
>The content of this page gets updated every 15 minutes</p>
<p align="center">Click REFRESH/RELOAD on your browser to see the \
latest info</p>
<p align="center">(i.e. HR:00, HR:15, HR:30, HR:45)</p>
<p align="left">
<?php
$dbServer="localhost";
$dbName="your_db_name";
$dbUser="your_db_user";
$dbPass="your_db_pass";
$dbConn = mysql_connect( $dbServer, $dbUser, $dbPass );
if ( !$dbConn ) {
die( 'Could not connect to the database: <b>' . mysql_error() );
}
$db_selected = mysql_select_db( 'vnc_ethereal' );
if( !$db_selected ) {
die( 'Could not connect to the database : <b>' . mysql_error() );
}
$result = mysql_query( "SELECT * FROM servers ORDER BY server,vncport ASC" );
if( !$result ) {
die( 'Could not query the vnc_server database : <b>' . mysql_error() );
}
print "<table class=\"vncstyle\" align=\"center\" border=\"1\">\n";
$previous = "";
print "<tr>\n";
print "\t<td align=\"center\" valign=\"top\"><b>VNC \
server</b></td>\n";
print "\t<td align=\"center\" \
valign=\"top\"><b>Date/Time Last Checked</b></td>\n";
print "\t<td align=\"center\" \
valign=\"top\"><b>VNC Port</b></td>\n";
print "\t<td align=\"center\" \
valign=\"top\"><b>Status of Port</b></td>\n";
print "</tr>";
while( $row = mysql_fetch_assoc( $result ) ) {
$current = $row['server'];
if( $current != $previous ) {
print "<tr>\n\t<td colspan=\"4\" align=\"left\" \
valign=\"top\"><b>";
print $row['server'] . " -- " . $row['server_ip'];
print "</b></td>\n";
print "</tr>";
} else {
if( $row['vncport'] != "5801" || $row['vncport'] \
!= "5901" ) {
$linkme = "<a href=\"http://" . \
$row['server_ip'] . ":" . str_replace( '59', \
'58', $row['vncport'] ) . "\" \
target=\"_new\">AVAILABLE</a>";
// $linkme = "AVAILABLE";
print "\t<td> </td>\n";
print "\t<td>" . $row['date_time_checked'] . "</td>\n";
print "\t<td align=\"center\">" . \
$row['vncport'] . "</td>\n";
print "\t<td align=\"center\">" . \
( ( $row['status'] == "AVAILABLE" ) ? \
$linkme : $row['status'] ) . "</td>\n</tr>";
}
}
$previous = $row['server'];
}
print "</table>\n";
mysql_free_result( $result );
?>
</p>
</body>
</html>
Figure 2 shows a screenshot of what the VNC monitor
will look like with the attached PHP script. As you can see there are four
columns. These are defined in Table 1.
Next, install the following entry into root's crontab:
su -
crontab -e
0,15,30,45 * * * * /usr/bin/perl /usr/local/scripts/vnccheck.pl
Conclusion
This solution has provided all the current users with
an easy way to see the status of a port. I do not receive many stability
issue calls on these servers anymore. Occasionally, there are a few
problems with a particular Linux server. When this user disconnects from a
separate network that is within our normal backbone network, the VNC server
doesn't disconnect the connection and continues to show it as being
used by that user. Other than this single issue, there haven't been
any drastic problems for users like I had experienced before implementing
this solution. When users are happy, I can go home for the day.
I hope you will have some use for these scripts or
even enhancements. Feel free to email me with ideas or questions, and I
will be more than happy to answer them for you.
George Wolanin has been a system/network and security
administrator in AT&T labs for eight years. His expertise is with
Solaris and Linux. He holds a BSEET from DeVRY Institute of Technology
(DeVRY University) and Master's Degree in Information Systems
Management with a specialization in security from Keller Graduate School of
Management.
|