It often happens that when using screen all the settings in .bash_profile are not available under screen. It has happened to me on Solaris and recently on a Rocks Cluster. The solution is it edit your ~/.screenrc and put the following line in it.
shell -$SHELL
Now all new screens that you create shall use the shell specified in your $SHELL environment variable, in my case it is bash thus making available the changes in .bash_profile.
Atif's Blog
Wednesday, August 13, 2008
Wednesday, April 23, 2008
GroovyWS and SOAP-Lite
Was trying out GroovyWS when it struck me that won't it be great if I could have Grooovy exposing the WS while SOAP-Lite would be used as a client. Now I've been experimenting with SOAP-Lite on the server side as well, but it is not my favourite. So to kick start the whole thing I started with Greeting webservice. Groovy code for the WS went as follows
Once the server was running I got the perl client ready
Everything should have worked but as usual it is never this easy. On running the client I got the following out
hi null
and on the server the output was
null
so somehow the string "atif" never got transmitted. Setting the debug option on
use SOAP::Lite +trace => 'debug';
output all the SOAP communication on the console. It became obvious that SOAP-Lite was encoding the string
but somehow GroovyWS is not picking it up. Extracting the wsdl by browsing to http://localhost:6980/GreetService?wsdl indicated that the WS was expecting the string in variable named 'arg0'.
The implementation of the GreetClient.pl does not use the wsdl, so I made the changes to make it
executing the new client resulted in an error message
as SOAP-Lite has limited support for WSDL 1.1. To solve this problem I ended up doing some searching and stumbling upon Complex SOAP::Lite requests - my rules for SOAP::Sanity!. Using the guide I made a new client
ATLAST!!! I got the output I expected.
hi atif
---------- GreetService.groovy --------------------
public class GreetService {
String hi(String s) {
println s;
return "hi " + s;
}
String bye(String s) {
println s;
return "bye " + s;
}
}
---------------------------------------------------
---------- GreetWS.groovy -------------------------
import groovyx.net.ws.WSServer
def server = new WSServer()
server.setNode("GreetService", "http://localhost:6980/GreetService")
---------------------------------------------------
Once the server was running I got the perl client ready
---------- GreetClient.pl -------------------------
use SOAP::Lite;
my $serviceNs = 'http://DefaultNamespace';
my $serviceUrl = 'http://localhost:6980/GreetService';
my $soap = SOAP::Lite->uri($serviceNs)->proxy($serviceUrl);
print $soap->hi("atif")->result . "\n";
---------------------------------------------------
Everything should have worked but as usual it is never this easy. On running the client I got the following out
hi null
and on the server the output was
null
so somehow the string "atif" never got transmitted. Setting the debug option on
use SOAP::Lite +trace => 'debug';
output all the SOAP communication on the console. It became obvious that SOAP-Lite was encoding the string
<hi xmlns="http://DefaultNamespace"><c-gensym3 xsi:type="xsd:string">atif</c-gensym3></hi>
but somehow GroovyWS is not picking it up. Extracting the wsdl by browsing to http://localhost:6980/GreetService?wsdl indicated that the WS was expecting the string in variable named 'arg0'.
<xsd:complexType name="hi">
<xsd:sequence>
<xsd:element minOccurs="0" name="arg0" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
The implementation of the GreetClient.pl does not use the wsdl, so I made the changes to make it
---------- GreetClient2.pl -------------------------
use SOAP::Lite;
my $serviceUrl = 'http://localhost:6980/GreetService?wsdl';
my $soap = SOAP::Lite->service($serviceUrl);
print $soap->hi("atif") . "\n";
----------------------------------------------------
executing the new client resulted in an error message
<faultstring>"http://schemas.xmlsoap.org/wsdl/soap/", the namespace on the "Envelope" element, is not a valid SOAP version.</faultstring>
as SOAP-Lite has limited support for WSDL 1.1. To solve this problem I ended up doing some searching and stumbling upon Complex SOAP::Lite requests - my rules for SOAP::Sanity!. Using the guide I made a new client
---------- GreetClient3.pl -------------------------
use SOAP::Lite;
my $serviceNs = 'http://DefaultNamespace';
my $serviceUrl = 'http://localhost:6980/GreetService';
my $soap = SOAP::Lite->uri($serviceNs)->proxy($serviceUrl);
my $data = SOAP::Data->name("arg0" => 'atif');
print $soap->hi($data)->result ."\n";
----------------------------------------------------
ATLAST!!! I got the output I expected.
hi atif
Friday, April 11, 2008
Port Knocking with iptables
After I implemented the port knocking using the code from Zeroflux (see my earlier post on Port Knocking) I discovered that actually I could have done the same with iptables. However, I was too busy with work to go and change the to iptables based solution. Today I wanted to do port knocking for another machine and so decided to investigate the iptables based solution. Turns out it is just 2 lines of iptables. Thanks Daniel de Graff for the link.
Monday, March 24, 2008
deleting UCSC temp files
Following command put as a cron job deletes all files not accessed in last 24 hours
% /usr/bin/find /var/www/trash -atime +1 | /usr/bin/xargs rm
This prevents the disk from filling up with unwanted old files. This works on Linux!
% /usr/bin/find /var/www/trash -atime +1 | /usr/bin/xargs rm
This prevents the disk from filling up with unwanted old files. This works on Linux!
Friday, December 28, 2007
Port Knocking
Just tried out port knocking on using the code from Zeroflux. I was using CentOS 4.5. Downloaded the SRPM from the site and build and install it.
% rpmbuild --rebuild knock-0.5-4.src.rpm
% cd /usr/src/redhat/RPMS/i386/
% rpm -i knock-0.5-4.i386.rpm
I tried a single port knock and it didn't work for me. It turns out that the state machine implemented expects at least 2 packets to match while in this example I want a knock on port 7000 to allow me to open the protected_port. I had to configure /etc/knockd.conf to have 7000 twice in my configuration.
now if I telnet to the port 7000 then after 2 syn packets the start_command executes adding the iptables rule leaving me with a 30 second window to do a connect to the service running on the Protected_port. After 30 seconds stop_command shall execute closing the window. I needed to have the following rule in my iptables
/sbin/iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
to allow established connection to continue even though the stop_command removed the rule from iptables.
% rpmbuild --rebuild knock-0.5-4.src.rpm
% cd /usr/src/redhat/RPMS/i386/
% rpm -i knock-0.5-4.i386.rpm
I tried a single port knock and it didn't work for me. It turns out that the state machine implemented expects at least 2 packets to match while in this example I want a knock on port 7000 to allow me to open the protected_port. I had to configure /etc/knockd.conf to have 7000 twice in my configuration.
[ProtectSvc]
sequence = 7000,7000
seq_timeout = 15
tcpflags = syn
start_command = /sbin/iptables -A INPUT -i eth0 -s %IP% -p tcp
-m tcp --dport-m state --state NEW -j ACCEPT
cmd_timeout = 30
stop_command = /sbin/iptables -D INPUT -i eth0 -s %IP% -p tcp
-m tcp --dport-m state --state NEW -j ACCEPT
now if I telnet to the port 7000 then after 2 syn packets the start_command executes adding the iptables rule leaving me with a 30 second window to do a connect to the service running on the Protected_port. After 30 seconds stop_command shall execute closing the window. I needed to have the following rule in my iptables
/sbin/iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
to allow established connection to continue even though the stop_command removed the rule from iptables.
Saturday, December 1, 2007
Integrating CAS with Apache
To protect content with CAS without modifying the underlying application, inspired by Apache::AuthCAS/Apache2::AuthCAS, I decided to write a short script to achieve the same; of course it has less capabilities since I don't need proxy related stuff.
This script has been tested on CentOS release 4.5 (Final). Also it is broken in two stages. First stage is integrated with Apache and is responsible for checking for a valid session. If no valid session is found it redirects the user to the second stage that checks whether a valid CAS ticket is provided and if so whether the user has the permission to the resource. If everything checks out to be ok it sets a cookie and redirects the user to the originally requested resource. Only downside to this strategy is that if the original request was a POST then by the time all these redirections have taken place, the originally posted values are lost. Underlying protected resources need to be able to handle such scenario gracefully (alternatively all original POSTS could be converted to GET; however, I decided to not do that for now). Example:
Suppose original request is
GET /cgi-bin/xyz.cgi?abc=def HTTP/1.1
First stage will check whether the browser provides a cookie containing the valid session id in it. If it does, let the URL be served. Otherwise the uri is changed to
GET /cgi-bin/login.cgi?url=/cgi-bin/xyz.cgi?abc=def HTTP/1.1
login.cgi now checks that there is no ticket in the request and redirects the browser to CAS server
https://cas.mycom.com/cas/login?service=http://myserver.mycom.com/cgi-bin/login.cgi?url=/cgi-bin/xyz.cgi?abc=def
Upon successful login CAS server redirects the browser back with a ticket added to the URL
http://myserver.mycom.com/cgi-bin/login.cgi?url=/cgi-bin/xyz.cgi?abc=def&ticket=XXX
this time the first stage sees the following request
GET /cgi-bin/login.cgi?url=/cgi-bin/xyz.cgi?abc=def&ticket=XXX HTTP/1.1
with no valid session id provided by the browser but detects that the request is for login.cgi; therefore, instead of modifying the uri it just lets the request go through. login.cgi now extracts the ticket provided and validates it with CAS for authenticity and receives a user ID. It checks whether the user id has access to the requested resource, if no then display an error message, else set a session in the cookie and let redirect the browser to the original requested url
http://myserver.mycom.com/cgi-bin/xyz.cgi?abc=def
Again this is seen by apache as
GET /cgi-bin/xyz.cgi?abc=def HTTP/1.1
but this time it receives a session id that it can successfully validate and therefore lets the request go through. The following code achieves the above mentioned.
Apache::Login.pm
login.cgi
This script has been tested on CentOS release 4.5 (Final). Also it is broken in two stages. First stage is integrated with Apache and is responsible for checking for a valid session. If no valid session is found it redirects the user to the second stage that checks whether a valid CAS ticket is provided and if so whether the user has the permission to the resource. If everything checks out to be ok it sets a cookie and redirects the user to the originally requested resource. Only downside to this strategy is that if the original request was a POST then by the time all these redirections have taken place, the originally posted values are lost. Underlying protected resources need to be able to handle such scenario gracefully (alternatively all original POSTS could be converted to GET; however, I decided to not do that for now). Example:
Suppose original request is
GET /cgi-bin/xyz.cgi?abc=def HTTP/1.1
First stage will check whether the browser provides a cookie containing the valid session id in it. If it does, let the URL be served. Otherwise the uri is changed to
GET /cgi-bin/login.cgi?url=/cgi-bin/xyz.cgi?abc=def HTTP/1.1
login.cgi now checks that there is no ticket in the request and redirects the browser to CAS server
https://cas.mycom.com/cas/login?service=http://myserver.mycom.com/cgi-bin/login.cgi?url=/cgi-bin/xyz.cgi?abc=def
Upon successful login CAS server redirects the browser back with a ticket added to the URL
http://myserver.mycom.com/cgi-bin/login.cgi?url=/cgi-bin/xyz.cgi?abc=def&ticket=XXX
this time the first stage sees the following request
GET /cgi-bin/login.cgi?url=/cgi-bin/xyz.cgi?abc=def&ticket=XXX HTTP/1.1
with no valid session id provided by the browser but detects that the request is for login.cgi; therefore, instead of modifying the uri it just lets the request go through. login.cgi now extracts the ticket provided and validates it with CAS for authenticity and receives a user ID. It checks whether the user id has access to the requested resource, if no then display an error message, else set a session in the cookie and let redirect the browser to the original requested url
http://myserver.mycom.com/cgi-bin/xyz.cgi?abc=def
Again this is seen by apache as
GET /cgi-bin/xyz.cgi?abc=def HTTP/1.1
but this time it receives a session id that it can successfully validate and therefore lets the request go through. The following code achieves the above mentioned.
Apache::Login.pm
package Apache::Login;
use strict;
use warnings;
use Apache::RequestRec ();
use Apache::RequestUtil;
use APR::Table;
use Apache::Log;
use Apache::URI;
use Apache::Const qw(DECLINED HTTP_MOVED_TEMPORARILY M_GET);
use Digest::MD5 qw(md5_base64);
use AuthCAS;
use DBI;
my $LOGIN = "/cgi-bin/login.cgi";
my $DATABASE = "db";
my $HOST = "db_hostname";
my $USERNAME = "db_username";
my $PASSWORD = "db_passowrd";
my $COOKIE_NAME = "cookie_name";
sub handler {
my $r = shift;
my $url = $r->unparsed_uri();
my $server = $r->get_server_name();
my $port = $r->get_server_port();
# authenticate the 1st internal request allow the rest to go through
if(!$r->is_initial_req) {
return DECLINED;
}
my $c = $r->headers_in->{Cookie};
if($c) {
# cookies can be of the type
# XXX=xxx; CGISESSID=xxx; YYY=xxx ...
my @cookies = split(/;/,$c);
for(my $i=0; $i<=$#cookies; $i++) {
my ($cookie_name, $cookie_value) = $cookies[$i] =~ m/($COOKIE_NAME=)(.*)/;
if($cookie_value) {
# check whether the session id is valid
my $dbh = DBI->connect("DBI:mysql:database=$DATABASE;host=$HOST",
$USERNAME, $PASSWORD, {RaiseError => 1})
or die $DBI::errstr;
my $sth = $dbh->prepare("SELECT user_id FROM session WHERE session_id = ?");
$sth->execute($cookie_value) or die $sth->errstr;
my $ref = $sth->fetchrow_hashref();
$sth->finish();
$dbh->disconnect();
if(defined $ref) {
return DECLINED;
}
}
}
}
# cookie not found
# if requesting login script then allow to go through
if($url =~ m/$LOGIN/) {
return DECLINED;
} else {
#redirect all other requests
# redirecting the request to the login script
$r->uri($LOGIN);
$r->args("url=$url");
return DECLINED;
}
}
1;
login.cgi
#!/usr/bin/perl -w
use strict;
use warnings;
use AuthCAS;
use CGI;
use CGI::Carp qw( fatalsToBrowser );
use File::Spec::Functions qw(splitpath);
use DBI;
use Digest::MD5 qw(md5_base64);
use Env;
my $DATABASE = "db";
my $DBHOST = "db_hostname";
my $USERNAME = "db_username";
my $PASSWORD = "db_password";
my $CAS_URL = "https://cas.mycom.com/cas/";
my $CA_FILE = "/some/location/cacert.pem";
my $COOKIE_NAME = "cookie_name";
my $q = new CGI();
my $query = $ENV{'QUERY_STRING'} || "";
my $server = $ENV{'SERVER_NAME'} || "";
my ($volume, $directories, $file) = splitpath($0);
my $cas = new AuthCAS(casUrl => $CAS_URL, CAFile => $CA_FILE);
my $ticket = $q->param('ticket');
if(!$ticket) {
my ($url) = ($query =~ m/^url=(.*)/);
my $login_url = $cas->getServerLoginURL("http://$server/cgi-bin/$file?url=$url");
print $q->redirect($login_url);
} else {
my ($url, $rest) = ($query =~ m/^url=(.*)&ticket=(.*)/);
my $user = $cas->validateST("http://$server/cgi-bin/$file?url=$url", $ticket)
or die AuthCAS::get_errors();
my $dbh = DBI->connect("DBI:mysql:database=$DATABASE;host=$DBHOST", $USERNAME, $PASSWORD, {RaiseError => 1, AutoCommit => 1}) or die $
DBI::errstr;
my $sth = $dbh->prepare("SELECT id FROM user WHERE username = ?");
$sth->execute($user) or die $sth->errstr;
my $ref = $sth->fetchrow_hashref();
if( defined($ref) ) {
my $id = $ref->{'id'};
my $session_id = md5_base64(time, $ticket);
$sth = $dbh->prepare("SELECT user_id, session_id, count FROM session WHERE session_id = ?");
$sth->execute($id);
my $exists = $sth->fetchrow_hashref();
if($exists) {
my $count = $exists->{'count'} + 1;
$sth = $dbh->prepare("UPDATE session SET session_id = ?, count = ? WHERE user_id = ?");
$sth->execute($session_id, $count, $id) or die $sth->errstr;
} else {
$sth = $dbh->prepare("INSERT INTO session (user_id, session_id, count) VALUES (?, ?, 1)");
$sth->execute($id, $session_id) or die $sth->errstr;
}
$sth->finish();
$dbh->disconnect();
my $cookie = $q->cookie($COOKIE_NAME => $session_id);
my $redirect_url = "http://$server$url";
print $q->header(-cookie=>$cookie);
print <<END;
<html>
<header>
<meta http-equiv="refresh" content="1; URL=$redirect_url">
</header>
</html>
END
} else {
$sth->finish();
$dbh->disconnect();
print <<END;
Content-type: text/html
<html>
<header>
<title> Access Denied </title>
</header>
<body>
<h1> Access Denied </h1>
You don't have access rights to this resource
</body>
</html>
END
}
}
Friday, September 28, 2007
Refreshing the database connection pool with Apache DBCP
After deploying CAS one problem that cropped up was that after 2 hours Oracle would close the database connection while Apache's DBCP would hold on the connection in its pool unaware what the database has done. Then next time a database query CAS would through the following exception
java.sql.SQLException: ORA-03135: connection lost contact
Our dba suggested we use OCI instead of thin client and he configured ORACLE configuration on the system. However it didn't work. It seems this scenario is not part of the failover, probably because DB responds with a RESET instead of a timeout, so at the connectivity is not lost with the server. I tried looking for a RECONNECT setting for Oracle JDBC but couldn't find it; however, DBCP provides testing of the connection in the pool before using it. So I configured the testing as follows
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>oracle.jdbc.driver.OracleDriver</value>
</property>
<property name="url">
<value>jdbc:oracle:oci:@<DB></value>
</property>
<property name="username">
<value>USERNAME</value>
</property>
<property name="password">
<value>PASSWORD</value>
</property>
<property name="validationQuery" value="select <something> from <table>" />
<property name="testOnBorrow" value="true"/>
</bean>
validationQuery is a quick query that is used to test the connection whenever the connection is borrowed from the pool. If the connection fails then it is removed from the pool and DBCP creates a new connection if it can't find any valid connection. This has been working so far without error. Only overhead is the execution of validationQuery. I would actually prefer to have a RECONNECT option as it would probably be with lower overhead. And for that the search continues.
java.sql.SQLException: ORA-03135: connection lost contact
Our dba suggested we use OCI instead of thin client and he configured ORACLE configuration on the system. However it didn't work. It seems this scenario is not part of the failover, probably because DB responds with a RESET instead of a timeout, so at the connectivity is not lost with the server. I tried looking for a RECONNECT setting for Oracle JDBC but couldn't find it; however, DBCP provides testing of the connection in the pool before using it. So I configured the testing as follows
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>oracle.jdbc.driver.OracleDriver</value>
</property>
<property name="url">
<value>jdbc:oracle:oci:@<DB></value>
</property>
<property name="username">
<value>USERNAME</value>
</property>
<property name="password">
<value>PASSWORD</value>
</property>
<property name="validationQuery" value="select <something> from <table>" />
<property name="testOnBorrow" value="true"/>
</bean>
validationQuery is a quick query that is used to test the connection whenever the connection is borrowed from the pool. If the connection fails then it is removed from the pool and DBCP creates a new connection if it can't find any valid connection. This has been working so far without error. Only overhead is the execution of validationQuery. I would actually prefer to have a RECONNECT option as it would probably be with lower overhead. And for that the search continues.
Subscribe to:
Posts (Atom)