Wednesday November 18, 2009 I found that experimental Intrusion Detection module as explained in my previous blog doesn't work as expected if an external plugin's AuthTrans SAF is added in obj.conf request processing and if that SAF returns REQ_PROCEED. This may be a rare case.
My id.conf :
SecRuleEngine on SecRequestBodyAccess on SecRule REQUEST_BODY "junk"
case 1: I created a dummy plugin having AuthTrans function myauth1; which just returns REQ_NOACTION it works fine. (look at
<ws7-install-dir>/samples/nsapi/ for examples of how to create a plugin)
#ifdef XP_WIN32
#define NSAPI_PUBLIC __declspec(dllexport)
#else /* !XP_WIN32 */
#define NSAPI_PUBLIC
#endif /* !XP_WIN32 */
#include "nsapi.h"
extern "C"
NSAPI_PUBLIC int myauth1(pblock *pb, Session *sn, Request *rq)
{
return REQ_NOACTION;
}
Added in Magnus.conf
Init fn="load-modules" shlib="myauth.so" funcs="myauth1"
Error logs in that case show :
...
... func_exec reports: executing fn="match-browser" browser="*MSIE*" ssl-unclean-shutdown="true" Directive="AuthTrans" magnus-internal="1"
... func_exec reports: fn="match-browser" browser="*MSIE*" ssl-unclean-shutdown="true" Directive="AuthTrans" magnus-internal="1" returned -2 (REQ_NOACTION)
... func_exec reports: executing fn="myauth1" Directive="AuthTrans"
... func_exec reports: fn="myauth1" Directive="AuthTrans" returned -2 (REQ_NOACTION)
... func_exec reports: executing fn="magnus-internal/secrule-filters-insert"
... func_exec reports: fn="magnus-internal/secrule-filters-insert" returned -2 (REQ_NOACTION)
... func_exec reports: executing fn="ntrans-j2ee" name="j2ee" Directive="NameTrans"
...
case 2: When I change this AuthTrans SAF to return REQ_PROCEED, it doesn't work as expected:
#ifdef XP_WIN32
#define NSAPI_PUBLIC __declspec(dllexport)
#else /* !XP_WIN32 */
#define NSAPI_PUBLIC
#endif /* !XP_WIN32 */
#include "nsapi.h"
extern "C"
NSAPI_PUBLIC int myauth2(pblock *pb, Session *sn, Request *rq)
{
return REQ_PROCEED;
}
Added in Magnus.conf
Init fn="load-modules" shlib="myauth.so" funcs="myauth2"
Error logs in that case shows :
... func_exec reports: executing fn="match-browser" browser="*MSIE*" ssl-unclean-shutdown="true" Directive="AuthTrans" magnus-internal="1" ... func_exec reports: fn="match-browser" browser="*MSIE*" ssl-unclean-shutdown="true" Directive="AuthTrans" magnus-internal="1" returned -2 (REQ_NOACTION) ... func_exec reports: executing fn="myauth2" Directive="AuthTrans" ... func_exec reports: fn="myauth2" Directive="AuthTrans" returned 0 (REQ_PROCEED) ... func_exec reports: executing fn="ntrans-j2ee" name="j2ee" Directive="NameTrans ...Note fn="magnus-internal/secrule-filters-insert" is not getting executed here.
You can add a workaround add this secrule-filters-insert SAF above your ExternalPluginAuthTransSAF function:
<Object name="default">This will work fine when ExternalPluginAuthTransSAF function returns REQ_PROCEED but when it returns REQ_NOATCION, these filters will be added twice. If thats ok you can add this.
AuthTrans fn="match-browser" browser="*MSIE*" ssl-unclean-shutdown="true"
AuthTrans fn="magnus-internal/secrule-filters-insert"
AuthTrans fn="ExternalPluginAuthTransSAF"
NameTrans fn="ntrans-j2ee" name="j2ee"
...
</Object>
<Object name="default">Posted by meena ( Nov 18 2009, 03:55:16 PM IST ) Permalink Comments [0]
AuthTrans fn="match-browser" browser="*MSIE*" ssl-unclean-shutdown="true"
AuthTrans fn="magnus-internal/secrule-filters-insert"
AuthTrans fn="ExternalPluginAuthTransSAF"
AuthTrans fn="myauth"
NameTrans fn="ntrans-j2ee" name="j2ee"
...
</Object>
About LDAP connections in Sun Web Server 7.0
When I have in my server.xml confiured an Sun LDAP server as given below :
<auth-db> <name>ldap</name> <url>ldap://localhost:1369/o%3DTestCentral</url> <property> <name>binddn</name> <value>cn=Directory Manager</value> </property> <property> <name>bindpw</name> <value>...</value><encoded/> </property> </auth-db>
When I have ACL set
acl "uri=/"; deny (all) user="anyone"; allow (all) user="all";
When I send a request via browser as user "alpha" and its correct password.
First it binds as the user specified in server.xml "binddn" property ( in this case "cn=Directory Manager")
Then sends a LDAP search query with filter "uid=alpha".
Third step it does is it binds as that user "alpha" with the password user specified. If bind is successful, access is allowed.
Here are the entries I get in LDAP server access logs:
[07/Oct/2009:13:16:16 +051800] conn=2 op=-1 msgId=-1 - fd=34 slot=34 LDAP connection from 127.0.0.1 to 127.0.0.1 [07/Oct/2009:13:16:16 +051800] conn=2 op=0 msgId=1 - BIND dn="cn=Directory Manager" method=128 version=3 [07/Oct/2009:13:16:16 +051800] conn=2 op=0 msgId=1 - RESULT err=0 tag=97 nentries=0 etime=0 dn="cn=directory manager" [07/Oct/2009:13:16:16 +051800] conn=2 op=1 msgId=2 - SRCH base="o=testcentral" scope=2 filter="(uid=alpha)" attrs="c" [07/Oct/2009:13:16:16 +051800] conn=2 op=1 msgId=2 - RESULT err=0 tag=101 nentries=1 etime=0 [07/Oct/2009:13:16:16 +051800] conn=2 op=2 msgId=3 - BIND dn="uid=alpha,o=TestCentral" method=128 version=3 [07/Oct/2009:13:16:16 +051800] conn=2 op=2 msgId=3 - RESULT err=0 tag=97 nentries=0 etime=0 dn="uid=alpha,o=testcentral"
To understand why we see these three entries, I started debugging Web Server from LASUserEval function and came at get_is_valid_password_ldap function. Note that line numbers in Open Web Server may differ but the concepts are the same :
t@16 (l@16) stopped in get_is_valid_password_ldap at line 455 in file "ldapacl.cpp"
467 if ((rv = ld->find_userdn(raw_user, basedn, &userdn)) == LDAPU_SUCCESS) {
(dbx) p filter
filter = "uid=alpha"
...
t@16 (l@16) stopped in LdapSession::find_userdn at line 1253 in file "LdapOps.cpp"
1253 retval = find(base, LDAP_SCOPE_SUBTREE, filter, attrs, 1 /* no attrs */, res);
(dbx) s
...
t@16 (l@16) stopped in LdapSession::find at line 1174 in file "LdapOps.cpp"
1174 res = search(base, scope, filter, (char **)attrs, attrsonly, 0);
(dbx) p base
base = 0x6ed6048 "o=TestCentral"
(dbx) p filter
filter = 0xfbfbc98c "uid=alpha"
...
(dbx) n
t@16 (l@16) stopped in LdapSession::search at line 289 in file "LdapSession.cpp"
289 rv = bindAsDefault();
(dbx) s
t@16 (l@16) stopped in LdapSession::bindAsDefault at line 235 in file "LdapSession.cpp"
235 int msg_id = ldap_simple_bind(session, ldapRealm->getBindName(), ldapRealm->getBindPwd());
(dbx) p msg_id
msg_id = 1
...
(dbx) n
t@16 (l@16) stopped in LdapSession::bindAsDefault at line 240 in file "LdapSession.cpp"
240 int ret = ldap_result( session, msg_id, 0, &time_out, &res);
(dbx) p ret
ret = 97
...
As we can see Web Server first tries to bindAsDefault ie. bind as cn=Directory Manager. by looking at LDAP Server logs, we confirm this.
[07/Oct/2009:14:52:03 +051800] conn=4 op=-1 msgId=-1 - fd=34 slot=34 LDAP connection from 127.0.0.1 to 127.0.0.1 [07/Oct/2009:14:52:03 +051800] conn=4 op=0 msgId=1 - BIND dn="cn=Directory Manager" method=128 version=3 [07/Oct/2009:14:52:03 +051800] conn=4 op=0 msgId=1 - RESULT err=0 tag=97 nentries=0 etime=0 dn="cn=directory manager"
When bind is successful, now it tries LDAP search on basedn="o=TestCentra" with filter "uid=alpha"
...
(dbx) n
t@16 (l@16) stopped in LdapSession::search at line 296 in file "LdapSession.cpp"
296 rv = lastoprv = ldap_search_ext_s(session, base, scope, filter,
297 attrs, attrsonly,
298 NULL, NULL, &time_out, LDAP_NO_LIMIT, &search_results);
(dbx) p base
base = 0x6ed6048 "o=TestCentral"
(dbx) p scope
scope = 2
(dbx) p filter
filter = 0xfbfbc98c "uid=alpha"
...
(dbx) n
t@16 (l@16) stopped in LdapSession::find at line 1178 in file "LdapOps.cpp"
1178 break;
(dbx) n
t@16 (l@16) stopped in LdapSession::find at line 1187 in file "LdapOps.cpp"
1187 numEntries = res->entries();
(dbx) n
t@16 (l@16) stopped in LdapSession::find at line 1189 in file "LdapOps.cpp"
1189 if (numEntries == 1) {
(dbx) p numEntries
numEntries = 1
(dbx) n
...
(dbx) n
t@16 (l@16) stopped in LdapSession::find_userdn at line 1278 in file "LdapOps.cpp"
1278 *dn = entry->DN();
(dbx) p *dn
*dn = 0x2d87b0 "uid=alpha,o=TestCentral"
(dbx)
...
Looking at LDAP server access logs it has created this "SRCH" entry :
[07/Oct/2009:14:55:51 +051800] conn=4 op=1 msgId=2 - SRCH base="o=testcentral" scope=2 filter="(uid=alpha)" attrs="c" [07/Oct/2009:14:55:51 +051800] conn=4 op=1 msgId=2 - RESULT err=0 tag=101 nentries=1 etime=0
So at the end of find_userdn function, we get our userdn "uid=alpha,o=TestCentral". Coming back to debugger
(dbx) p userdn userdn = 0x2d87b0 "uid=alpha,o=TestCentral" (dbx) t@16 (l@16) stopped in get_is_valid_password_ldap at line 531 in file "ldapacl.cpp" 531 rv = ld->userdn_password(userdn, raw_pw); (dbx) s ... (dbx) n t@16 (l@16) stopped in LdapSession::userdn_password at line 1052 in file "LdapOps.cpp" 1052 int msgid = ldap_simple_bind(session, userdn, password); (dbx) p userdn userdn = 0x2d87b0 "uid=alpha,o=TestCentral" ... (dbx) n t@16 (l@16) stopped in LdapSession::userdn_password at line 1058 in file "LdapOps.cpp" 1058 rc = ldap_result(session, msgid, 0, &zerotime, &res ); (dbx) n (dbx) p rc rc = 97 ...
As we can see Web Server tries to bind to LDAP server with userdn "uid=alpha,o=TestCentral" and we can confirm that by looking at LDAP server logs :
[07/Oct/2009:15:00:37 +051800] conn=4 op=2 msgId=3 - BIND dn="uid=alpha,o=TestCentral" method=128 version=3 [07/Oct/2009:15:00:37 +051800] conn=4 op=2 msgId=3 - RESULT err=0 tag=97 nentries=0 etime=0 dn="uid=alpha,o=testcentral"
Also refer Jyri's blog for troubleshooting
Posted by meena ( Oct 07 2009, 03:47:18 PM IST ) Permalink Comments [4]
Enabling Client Certificate Authentication in Sun Web Server 7.0 reverse proxy server and origin server
For this I have setup two Sun Java System Web Server 7.0 update 6 instances. One acting as a reverse proxy and the other origin server that serves the request. Enabled SSL and client authentication on both these instances. Sent SSL requests with client certificates from a test client.
I created self signed certificate in reverse proxy server.
$cd <ws-install-dir>/https-test/config$rm *.db$../../bin/certutil -N -d .$../../bin/certutil -S -d . -n Server-Cert-Reverse-Proxy-Server -s "CN=test.sun.com" -x -t "CT,CT,CT"$../../bin/certutil -L -d .Certificate Nickname Trust AttributesSSL,S/MIME,JAR/XPI Server-Cert-Reverse-Proxy-Server CTu,Cu,Cu
You can use Admin Server CLI to create a self signed certificate
wadm>create-selfsigned-cert --config=test.sun.com --server-name=test.sun.com --nickname=Server-Cert-Reverse-Proxy-Server
For convenience I copied these *.db to origin server instance config directory, and hence used the same server certificate in origin server. In real world use a new server certificate for origin server.
In obj.conf I have confiugured reverse proxy in such a way that all requests are redirected to origin server. In real world situation you can if you want redirect only certain requests depending on your requirements.
Run create-reverse-proxy CLI from Administration server
wadm>create-reverse-proxy --config test --vs test --uri-prefix=/ --server=https://test.sun.com:4444
*obj.conf gets modified as shown below :
<Object name="default">AuthTrans fn="match-browser" browser="*MSIE*" ssl-unclean-shutdown="true"NameTrans fn="map" from="/" name="reverse-proxy-/" to="/"...</Object><Object ppath="*">Service fn="proxy-retrieve" method="*"</Object><Object name="reverse-proxy-/">Route fn="set-origin-server" server="https://test.sun.com:4444"</Object>...
Enable "ssl" in http-listener, added server certificate nickname (if its different from "Server-Cert") and set client authentication as "required" :
You can use Admin Server CLI to do these steps
wadm> set-ssl-prop --config=test.sun.com --http-listener=http-listener-1
server-cert-nickname=Server-Cert-Reverse-Proxy-Server enabled=true client-auth=requiredwadm> deploy-config test.sun.com<http-listener>
<name>http-listener-1</name>
<port>3333</port>
<server-name>test.sun.com</server-name>
<default-virtual-server-name>test</default-virtual-server-name>
<ssl>
<server-cert-nickname>Server-Cert-Reverse-Proxy-Server</server-cert-nickname>
<client-auth>required</client-auth> </ssl>
</http-listener>
In server.xml I have also modified access log format so that we can see what is happening. This will slow down Web Server performance so do not do this in production environment.
<access-log><file>../logs/access</file><format>%Ses->client.ip% "%Req->reqpb.clf-request%" %Req->srvhdrs.clf-status% %Req->srvhdrs.content-length% %Ses->client.cipher% %Req->vars.auth-cert% </format></access-log>
Enable "ssl" in http-listener, added server certificate nickname (if its different from "Server-Cert") and set client authentication as "required" :
<http-listener><name>http-listener-1</name><port>4444</port><server-name>test.sun.com</server-name><ssl><server-cert-nickname>Server-Cert-Reverse-Proxy-Server</server-cert-nickname><client-auth>required</client-auth></ssl><default-virtual-server-name>test</default-virtual-server-name></http-listener>
In server.xml I have also modified access log format so that we can see what is happening. This will slow down Web Server performance so do not do this in production environment.
<access-log><file>../logs/access</file><format>%Ses->client.ip% "%Req->reqpb.clf-request%" %Req->srvhdrs.clf-status% %Req->srvhdrs.content-length% %Ses->client.cipher% %Req->vars.auth-cert% %Req->headers.proxy-auth-cert%</format></access-log>
Created a test.html file that should be served when the client requests for it :
$cat ../docs/test.htmlThis is test.html on origin server
I used "tstclnt" which is bundled with NSS-NSPR binaries in bin directory. Note that I used the server certificate of reverse proxy server as client certificate because I am too lazy to create more certificates. In real world use a proper client certificate.
Created a test request file :
$cat > sslreq.datGET /test.html HTTP/1.0^M^M
Sent request to the reverse proxy server
$tstclnt -h test.sun.com -p 3333 -n Server-Cert-Reverse-Proxy-Server -d https-test/config -c n -v -o -f < sslreq.dattstclnt: connecting to test.sun.com:3333 (address=xxx.xxx.xxx.xxx)...tstclnt: stdin read 27 byteststclnt: Writing 27 bytes to servertstclnt: SSL version 3.1 using 128-bit RC4 with 160-bit SHA1 MACtstclnt: Server Auth: 1024-bit RSA, Key Exchange: 1024-bit RSAsubject DN: CN=test.sun.comissuer DN: CN=test.sun.com...tstclnt: Read from server 350 bytesHTTP/1.1 200 OKServer: Sun-Java-System-Web-Server/7.0Date: Fri, 18 Sep 2009 10:59:13 GMTContent-type: text/htmlLast-modified: Thu, 17 Sep 2009 10:44:10 GMTContent-length: 35Etag: "23-4ab212fa"Accept-ranges: bytesVia: 1.1 https-testProxy-agent: Sun-Java-System-Web-Server/7.0Connection: closeThis is test.html on origin server...
tstclnt: exiting with return code 0
You can see the request is being served from origin server.
I have used in this test client cipher "n" which is SSL3 RSA WITH RC4 128 SHA because I know this cipher that is enabled in Sun Web Server 7.0 update 6. I can
see that at the time of server startup when run in
<log-level>finest</log-level>. You can use other ciphers as well.
$cat ../logs/access format=%Ses->client.ip% "%Req->reqpb.clf-request%" %Req->srvhdrs.clf-status% %Req->srvhdrs.content-length% %Ses->client.cipher% %Req->vars.auth-cert% xxx.xxx.xxx.xxx "GET /test.html HTTP/1.0" 200 35 RC4 MIIBujCCASOgAwIBAgIFAI566gYwDQYJKo...fBqhD710VkFmOScYjWBxZe1vhnTbu/NexX4NqLsZG9So=Note cipher is RC4.
$cat ../logs/accessformat=%Ses->client.ip% "%Req->reqpb.clf-request%" %Req->srvhdrs.clf-status% %Req->srvhdrs.content-length% %Ses->client.cipher% %Req->vars.auth-cert% %Req->headers.proxy-auth-cert%xxx.xxx.xxx.xxx "GET /test.html HTTP/1.1" 200 35 RC4 MIIBujCCASOgAwIBAgIFAI566gYwDQYJKo...fBqhD710VkFmOScYjWBxZe1vhnTbu/NexX4NqLsZG9So= MIIBujCCASOgAwIBAgIFAI566gYwDQYJKo...fBqhD710VkFmOScYjWBxZe1vhnTbu/NexX4NqLsZG9So=Note that as expected in origin-server, rq->headers pblock has the certificate in "proxy-auth-cert". Reverse proxy server sends the certificate to origin server in this header.
Posted by meena ( Sep 18 2009, 05:32:56 PM IST ) Permalink Comments [0]
- Sun Java System Web Server 7.0 Update 6 Administrator's Configuration File Reference - "forward-auth-cert"
- Configuring Reverse Proxy in Sun Java System Web Server 7.0
- Configuring reverse proxy in Sun Java System Web Server 7.0 when origin server is SSL enabled
- About trust flags of certificates in NSS database that can be modified by certutil
- http://forums.sun.com/thread.jspa?threadID=5397719
- http://forums.sun.com/thread.jspa?threadID=5373182
- http://forums.sun.com/thread.jspa?threadID=5359313
Building Open Web Server on OpenSolaris SPARC
I tried building Open Web Server on OpenSolaris SPARC.
Install package as given in http://wikis.sun.com/display/wsFOSS/Checkout+and+Build+Instructions
SUNWmozldap, SUNWxercesc, SUNWxalanc in http://src.opensolaris.org/source/xref/webstack/ were probably not built for 64 bit. I have only tested on OpenSolaris x86 32 bit. They do not work on SPARC yet.
Run this script it builds and puts Mozilla LDAP C SDK and puts it in /usr/local/include/mozldap/, /usr/local/lib/mozldap, /usr/local/lib/mozldap/64
#!/bin/sh #building 32 bit
cd
cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot co -P -rLDAPCSDK_6_0_5_RTM DirectorySDKSourceCcd mozilla/directory/c-sdk./configure --with-sasl --with-nspr-inc=/usr/include/mps --with-nspr-lib=/usr/lib/mps --with-nspr --with-nss-inc=/usr/include/mps --with-nss-lib=/usr/lib/mps --with-nssgmakecd ../../dist/sudo mkdir -p /usr/local/include/mozldap /usr/local/lib/mozldapsudo cp public/ldap/* /usr/local/include/mozldap/sudo cp lib/* /usr/local/lib/mozldap/cdmv mozilla mozilla32#building 64 bitcvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot co -P -rLDAPCSDK_6_0_5_RTM DirectorySDKSourceCcd mozilla/directory/c-sdk./configure --with-sasl --with-nspr-inc=/usr/include/mps --with-nspr-lib=/usr/lib/mps/64 --with-nspr --with-nss-inc=/usr/include/mps --with-nss-lib=/usr/lib/mps/64 --with-nss --enable-64bitgmakecd ../../dist/sudo mkdir -p /usr/local/lib/mozldap/64sudo cp lib/* /usr/local/lib/mozldap/64cdmv mozilla mozilla64
Build Xerces C and Xalan C 32 bit and 64 bit and put it in /usr/local/ area.
Build Xerces C 2.6 from http://xerces.apache.org/xerces-c/build-2.html
Build Xalan C 1.9 from http://xml.apache.org/xalan-c/build_instruct.html
hg clone ssh://anon@hg.opensolaris.org/hg/webstack/webserver
gmake BUILD_VARIANT=OPTIMIZEDIf you want 64 bit support also
gmake BUILD_VARIANT=OPTIMIZED BUILD64=1
gmake BUILD_VARIANT=OPTIMIZED installIf you want 64 bit also
gmake BUILD_VARIANT=OPTIMIZED install BUILD64=1
Go to work/B1/*/https-test/config directory. To run Open Web Server in 64 bit you need to add <platform>64</platfrom> in server.xml "server" element.
If you get error that looks like
ld: fatal: file /usr/lib/64/libsqlite3.so: version `SQLITE_3' does not exist:
required by file /usr/lib/mps/64/libsoftokn3.so
$mv /usr/lib/64/libsqlite3.so /usr/lib/64/libsqlite3.so.BACK
http://wikis.sun.com/display/wsFOSS/Checkout+and+Build+Instructions
http://forums.sun.com/thread.jspa?messageID=10810336#10810336
Posted by meena ( Sep 08 2009, 06:38:35 PM IST ) Permalink Comments [0]Jython Web Application on Sun Web Server 7.0
Download Jython from http://softlayer.dl.sourceforge.net/project/jython/jython/2.5.0/jython_installer-2.5.0.jar
Run Jython installer and install it in lets say in /opt/jython/jython2.5.0
$java -jar jython_installer-2.5.0.jar
Lets try deploying a simple demo web application that comes along with this. This demo application has a simple demo_app.py script.
cd /opt/jython/jython2.5.0/Demo/modjy_webapp/WEB-INFPosted by meena ( Aug 07 2009, 01:30:37 PM IST ) Permalink Comments [0]
cp /opt/jython/jython2.5.0/jython.jar lib/
In web.xml change python.home i.e.
<init-param>
<param-name>python.home</param-name>
<param-value>C:/jython2.5</param-value>
</init-param>
to
<init-param>
<param-name>python.home</param-name>
<param-value>/opt/jython/jython2.5.0</param-value>
</init-param>
cd /ws7-install-dir/https-instance-name/config
Add in server.xml in virtual server element, the location of this web application<web-app> <uri>/modjy_webapp</uri> <path>/opt/jython/jython2.5.0/modjy_webapp</path> </web-app>Start the web server instance, and access the web application via browser using URI http://host:port/modjy_webapyou should see a page starting with the contents:
Python CGIs in Sun Web Server 7.0
Add this test script in <ws7-install-dir>https-<instance-name>/docs/cgi-bin directory and call it hello.cgi
#!/usr/bin/env pythonprint("Content-Type: text/plain;charset=utf-8")print("\r\n")print("Hello World!")
Change obj.conf default object
<Object name="default">AuthTrans fn="match-browser" browser="*MSIE*" ssl-unclean-shutdown="true"NameTrans fn="ntrans-j2ee" name="j2ee"NameTrans fn="pfx2dir" from="/cgi-bin" dir="/<ws-install-dir>/https-<instance-name>/docs/cgi-bin" name="cgi"...<Object name="cgi"> ObjectType fn="force-type" type="magnus-internal/cgi" Service fn="send-cgi" </Object>
Send a request through the browser http://host:post/cgi-bin/hello.cgi
You can see Hello World! displayed.
References
http://docs.python.org/3.1/howto/webservers.html
This blog copyright 2009 by meena