Cheap VPS & Xen Server

Residential Proxy Network - Hourly & Monthly Packages

Serving CGI Scripts With Nginx On OpenSUSE 11.4


This tutorial shows how you can serve CGI scripts (Perl scripts) with nginx on OpenSUSE 11.4. While nginx itself does not serve CGI, there are several ways to work around this. I will outline two solutions: the first is to proxy requests for CGI scripts to Thttpd, a small web server that has CGI support, while the second solution uses a CGI wrapper to serve CGI scripts.

I do not issue any guarantee that this will work for you!

 

1 Preliminary Note

I’m using the website www.example.com here with the document root /srv/www/www.example.com/web/; the vhost configuration is located in the main nginx configuration file /etc/nginx/nginx.conf.

 

2 Using Thttpd

In this chapter I am going to describe how to configure nginx to proxy requests for CGI scripts (extensions .cgi or .pl) to Thttpd. I will configure Thttpd to run on port 8000.

First we install Thttpd. There is a Thttpd package for OpenSUSE, but the nginx ThttpdCGI page says that Thttpd should be patched – therefore we download the src.rpm package for OpenSUSE 11.4, patch it and build a new rpm package from it.

We must enable the openSUSE-11.4-Source repository first. Run

yast

and go to Software > Software Repositories:

2

Enable the openSUSE-11.4-Source repository and leave YaST:

3

We need to install the tools that are required to build a new rpm package:

zypper install patch automake glibc-devel gcc flex compat-readline4 db-devel wget gcc-c++ make vim libtool

Next we download the Thttpd src.rpm package for OpenSUSE 11.4:

cd /usr/src
zypper source-install thttpd

Now we download the patch to the /usr/src/packages/SOURCES/ directory and modify the /usr/src/packages/SPECS/thttpd.spec file accordingly:

cd /usr/src/packages/SOURCES
wget -O thttpd-2.25b-ipreal.patch http://www.danielclemente.com/amarok/ip_real.txt

Open /usr/src/packages/SOURCES/thttpd-2.25b-ipreal.patch

vi /usr/src/packages/SOURCES/thttpd-2.25b-ipreal.patch

… and modify the first two lines – in the original version they look as follows:

--- thttpd-2.25b/libhttpd.c     2003-12-25 20:06:05.000000000 +0100
+++ thttpd-2.25b-patched/libhttpd.c     2005-01-09 00:26:04.867255248 +0100
[...]

Remove the highlighted parts so that the file begins as follows:

--- libhttpd.c     2003-12-25 20:06:05.000000000 +0100
+++ libhttpd.c     2005-01-09 00:26:04.867255248 +0100
[...]

Next go to the /usr/src/packages/SPECS/ directory and edit thttpd.spec:

cd /usr/src/packages/SPECS/
vi thttpd.spec

Add the lines Patch13: thttpd-2.25b-ipreal.patch and %patch13:

[...]
Patch0:         %{name}-%{version}-configure.patch
Patch1:         %{name}-%{version}-dirs.patch
Patch2:         %{name}-%{version}-time_h.patch
Patch3:         %{name}-%{version}-newautoconf.patch
Patch4:         %{name}-%{version}-sec.patch
Patch5:         %{name}-%{version}-static.patch
Patch6:         %{name}-%{version}-pie.patch
Patch7:         %{name}-%{version}-syslogtocern.diff
Patch8:         %{name}-%{version}-overflow.diff
Patch9:         %{name}-%{version}-chown.diff
Patch10:        %{name}-%{version}-zerolen.patch
Patch11:        %{name}-%{version}-strcpy.patch
Patch12:        thttpd-2.25b-getline.patch
Patch13:        thttpd-2.25b-ipreal.patch
[...]
%prep
%setup -q -a 1
%patch0
%patch1
%patch2
%patch3
%patch4
%patch5
%patch6
%patch7
%patch8
%patch9
%patch10
%patch11
%patch12
%patch13
[...]

Now we build our Thttpd rpm package as follows:

rpmbuild -ba thttpd.spec

Our Thttpd rpm package is created in /usr/src/packages/RPMS/x86_64 (/usr/src/packages/RPMS/i386 if you are on an i386 system), so we go there:

cd /usr/src/packages/RPMS/x86_64
ls -l

server1:/usr/src/packages/RPMS/x86_64 # ls -l
total 276
-rw-r–r– 1 root root 278693 Oct  5 17:20 thttpd-2.25b-181.1.x86_64.rpm
server1:/usr/src/packages/RPMS/x86_64 #

Install the Thttpd package as follows:

rpm -ivh thttpd-2.25b-181.1.x86_64.rpm

Then we make a backup of the original /etc/thttpd.conf file and create a new one as follows:

mv /etc/thttpd.conf /etc/thttpd.conf_orig
vi /etc/thttpd.conf

# BEWARE : No empty lines are allowed!
# This section overrides defaults
# This section _documents_ defaults in effect
# port=80
# nosymlink         # default = !chroot
# novhost
# nocgipat
# nothrottles
# host=0.0.0.0
# charset=iso-8859-1
host=127.0.0.1
port=8000
user=wwwrun
logfile=/var/log/thttpd.log
pidfile=/var/run/thttpd.pid
dir=/srv/www
cgipat=**.cgi|**.pl
nochroot

This will make Thttpd listen on port 8000 on 127.0.0.1; its document root is /srv/www.

Create the system startup links for Thttpd…

chkconfig -f –add thttpd

… and start it:

/etc/init.d/thttpd start

Next create /etc/nginx/proxy.conf:

vi /etc/nginx/proxy.conf

proxy_redirect          off;
proxy_set_header        Host            $host;
proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size    10m;
client_body_buffer_size 128k;
proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;

Now open your vhost configuration file…

vi /etc/nginx/nginx.conf

… and add a location /cgi-bin {} section to the server {} container:

server {
[...]
   location /cgi-bin {
      include proxy.conf;
      proxy_pass http://127.0.0.1:8000;
   }
[...]
}

Reload nginx:

/etc/init.d/nginx reload

Because Thttpd’s document root is /srv/www, location /cgi-bin translates to the directory /srv/www/cgi-bin (this is true for all your vhosts, which means each vhost must place its CGI scripts in /srv/www/cgi-bin; this is a drawback for shared hosting environments; the solution is to use a CGI wrapper as described in chapter 3 instead of Thttpd).

Create the directory…

mkdir /srv/www/cgi-bin

… and then place your CGI scripts in it and make them executable. For testing purposes I will create a small Hello World Perl script (instead of hello_world.cgi you can also use the extension .pl -> hello_world.pl):

vi /srv/www/cgi-bin/hello_world.cgi

#!/usr/bin/perl -w

     # Tell perl to send a html header.
     # So your browser gets the output
     # rather then <stdout>(command line
     # on the server.)
print "Content-type: text/html\n\n";

     # print your basic html tags.
     # and the content of them.
print "<html><head><title>Hello World!! </title></head>\n";
print "<body><h1>Hello world</h1></body></html>\n";

chmod 755 /srv/www/cgi-bin/hello_world.cgi

Open a browser and test the script:

http://www.example.com/cgi-bin/hello_world.cgi

If all goes well, you should get the following output:

1

3 Using Fcgiwrap

Fcgiwrap is a CGI wrapper that can be used for shared hosting environments because it allows each vhost to use its own cgi-bin directory.

As there’s no fcgiwrap package for OpenSUSE, we must build it ourselves. First we install some prerequisites:

zypper remove patterns-openSUSE-minimal_base

zypper install git patch automake glibc-devel gcc flex compat-readline4 db-devel wget gcc-c++ make vim libtool FastCGI-devel

Create the following symlinks:

ln -s /usr/include/fastcgi/fastcgi.h /usr/local/include/
ln -s /usr/include/fastcgi/fcgi_config.h /usr/local/include/
ln -s /usr/include/fastcgi/fcgi_stdio.h /usr/local/include/
ln -s /usr/include/fastcgi/fcgiapp.h /usr/local/include/
ln -s /usr/include/fastcgi/fcgimisc.h /usr/local/include/
ln -s /usr/include/fastcgi/fcgio.h /usr/local/include/
ln -s /usr/include/fastcgi/fcgios.h /usr/local/include/

Now we can build fcgiwrap as follows:

cd /usr/local/src/
git clone git://github.com/gnosek/fcgiwrap.git
cd fcgiwrap
autoreconf -i
./configure
make
make install

This installs fcgiwrap to /usr/local/sbin/fcgiwrap.

Next we install the spawn-fcgi package which allows us to run fcgiwrap as a daemon:

zypper install spawn-fcgi

We can now start fcgiwrap as follows:

spawn-fcgi -u nginx -g nginx -s /var/run/fcgiwrap.socket -S -M 0700 -F 1 -P /var/run/spawn-fcgi.pid — /usr/local/sbin/fcgiwrap

You should now find the fcgiwrap socket in/var/run/fcgiwrap.socket, owned by the user and group nginx.

If you don’t want to start fcgiwrap manually each time you boot your system, open /etc/init.d/boot.local

vi /etc/init.d/boot.local

… and add the spawn-fcgi command at the end of the file – this will automatically start fcgiwrap at the end of the boot process:

[...]
/usr/bin/spawn-fcgi -u nginx -g nginx -s /var/run/fcgiwrap.socket -S -M 0700 -F 1 -P /var/run/spawn-fcgi.pid -- /usr/local/sbin/fcgiwrap

Now open your vhost configuration file…

vi /etc/nginx/nginx.conf

… and add a location /cgi-bin {} section to the server {} container:

server {
[...]
   location /cgi-bin/ {
     # Disable gzip (it makes scripts feel slower since they have to complete
     # before getting gzipped)
     gzip off;
     # Set the root to /usr/lib (inside this location this means that we are
     # giving access to the files under /usr/lib/cgi-bin)
     root  /srv/www/www.example.com;
     # Fastcgi socket
     fastcgi_pass  unix:/var/run/fcgiwrap.socket;
     # Fastcgi parameters, include the standard ones
     include /etc/nginx/fastcgi_params;
     # Adjust non standard parameters (SCRIPT_FILENAME)
     fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
   }
[...]
}

Reload nginx:

/etc/init.d/nginx reload

Next we create our cgi-bin directory – /srv/www/www.example.com/cgi-bin because we defined root /srv/www/www.example.com; in the location /cgi-bin {} container:

mkdir /srv/www/www.example.com/cgi-bin

Now we place our CGI scripts in it and make them executable. For testing purposes I will create a small Hello World Perl script (instead of hello_world.cgi you can also use the extension .pl -> hello_world.pl):

vi /srv/www/www.example.com/cgi-bin/hello_world.cgi

#!/usr/bin/perl -w
     # Tell perl to send a html header.
     # So your browser gets the output
     # rather then <stdout>(command line
     # on the server.)
print "Content-type: text/html\n\n";
     # print your basic html tags.
     # and the content of them.
print "<html><head><title>Hello World!! </title></head>\n";
print "<body><h1>Hello world</h1></body></html>\n";

chmod 755 /srv/www/www.example.com/cgi-bin/hello_world.cgi

Open a browser and test the script:

http://www.example.com/cgi-bin/hello_world.cgi

If all goes well, you should get the following output:

1

  • Nginx: http://nginx.org/
  • Nginx Wiki: http://wiki.nginx.org/
  • Thttpd: http://acme.com/software/thttpd/
  • nginx ThttpdCGI: http://wiki.nginx.org/ThttpdCGI
  • nginx Fcgiwrap: http://wiki.nginx.org/Fcgiwrap

Comments

comments