How to use CGI in Nginx

Source: Internet
Author: User

How to use CGI in Nginx

Origin
When I found out what CGI really is, google SimpleCGI saw this article and read it a few times. I think it's good. There are several topics that interest me: CGI, FastCGI, and Nginx.

Body
For the most part, lack of CGI support in Nginx is not an issue and actually has an important side-benefit: because Nginx cannot directly execute external programs (CGI ), a malicious person can't trick your system into uploading and executing an arbitrary script.


In general, the lack of CGI support in Nginx is not a problem, but it actually brings some benefits: Nginx does not directly execute external programs (CGI ), attackers cannot upload and execute arbitrary scripts to attack your system.

There are still ways, of course (uploading a PHP script into a directory where you 've got a directive to execute PHP FastCGI scripts for example ), but it does in fact make it a bit more difficult (or conversely, easier to secure ).


Of course, there are still some methods to attack the system, such as uploading PHP scripts to a directory, but it is obviously more difficult to execute the PHP FastCGI script when you direct to the directory.

Still, sometimes you have a one-off simple CGI program you need to support and here's a recipe for exposing your CGI to Nginx as a FastCGI instead:


Sometimes, you need to support a one-time CGI script. Here is a method to convert a CGI program to FastCGI and then pass it to Nginx:

Let's say this is our CGI program, written in Perl:

The following describes the CGI program written in Perl:


#! /Usr/bin/perl

Print "Content-type: text/html \ n ";
Print "Make sure that the web server can read the file and it is marked as executable.


Make sure that the web server can read and treat it as an executable file.

Next, we need a Perl wrapper that will run as a FastCGI and run this for us:

Next, you need a Perl package that runs FastCGI to run this program:

This one is still pretty uugly, but forwards stdio around correctly (not necessarily efficiently or prettily), and also reads and spits back out STDERR into fcgi STDERR. I am not entirely confident there is no race condition in the event that an app STDERRS out without any output data, but it works for my purposes. this is for the most part, not my own work; 99.9% of this came from the varous improvements on the spawner script worked on in the mailing list and wiki here .. I just added some more pipe handling to enable communicating through STDERR, and a select loop to properly recieve both data streams. bryon@spideroak.com


The package below is quite simple, but it is very accurate in stdio (it does not need to be efficient or perfect). You can also read and output standard STDERR as fcgi's STDERR. I'm not sure if there are any competition conditions in the STDERRS where the Code does not have any output data in the application. This package meets my goal very well. 99.9% of the package's content comes from the mail list and wiki. I (original author) only added some mps queues to ensure communication through STDERR and a selection loop that accepts the appropriate data stream.


#! Perl
Use FCGI;
Use Socket;
Use FCGI: ProcManager;
Sub shutdown {FCGI: CloseSocket ($ socket); exit ;}
Sub restart {FCGI: CloseSocket ($ socket); & main ;}
Use sigtrap 'handler', \ & shutdown, 'normal-signal ';
Use sigtrap 'handler', \ & restart, 'hup ';
Require 'syscall. ph ';
Use POSIX qw (setsid );
 
END (){}
BEGIN (){}
{
No warnings;
* CORE: GLOBAL: exit = sub {die "fakeexit \ nrc =". shift (). "\ n ";};
};
 
Eval q {exit };
If ($ @){
Exit unless $ @ = ~ /^ Fakeexit /;
}
& Main;
 
Sub daemonize (){
Chdir '/' or die "Can't chdir to/: $! ";
Defined (my $ pid = fork) or die "Can't fork: $! ";
Exit if $ pid;
Setsid () or die "Can't start a new session: $! ";
Umask 0;
}
 
Sub main {
$ Proc_manager = FCGI: ProcManager-> new ({n_processes => 5 });
$ Socket = FCGI: OpenSocket ("/var/run/nginx/cgiwrap-dispatch.sock", 10)
; # Use UNIX sockets-user running this script must have w access to the 'nginx' folder !!
$ Request =
FCGI: Request (\ * STDIN, \ * STDOUT, \ * STDERR, \ % req_params, $ socket,
& FCGI: FAIL_ACCEPT_ON_INTR );
$ Proc_manager-> pm_manage ();
If ($ request) {request_loop ()}
FCGI: CloseSocket ($ socket );
}
 
Sub request_loop {
While ($ request-> Accept ()> = 0 ){
$ Proc_manager-> pm_pre_dispatch ();
 
# Processing any STDIN input from WebServer (for CGI-POST actions)
$ Stdin_passthrough = '';
{No warnings; $ req_len = 0 + $ req_params {'content _ length '};};
If ($ req_params {'request _ method'} eq 'post') & ($ req_len! = 0 )){
My $ bytes_read = 0;
While ($ bytes_read <$ req_len ){
My $ data = '';
My $ bytes = read (STDIN, $ data, ($ req_len-$ bytes_read ));
Last if ($ bytes = 0 |! Defined ($ bytes ));
$ Stdin_passthrough. = $ data;
$ Bytes_read + = $ bytes;
}
}
 
# Running the cgi app
If (
(-X $ req_params {SCRIPT_FILENAME}) & # can I execute this?
(-S $ req_params {SCRIPT_FILENAME}) & # Is this file empty?
(-R $ req_params {SCRIPT_FILENAME}) # can I read this file?
){
Pipe (CHILD_RD, PARENT_WR );
Pipe (PARENT_ERR, CHILD_ERR );
My $ pid = open (CHILD_O, "-| ");
Unless (defined ($ pid )){
Print ("Content-type: text/plain \ r \ n ");
Print "Error: CGI app returned no output-Executing $ req_params {SCRIPT_FILENAME} failed! \ N ";
Next;
}
$ Oldfh = select (PARENT_ERR );
$ | = 1;
Select (CHILD_O );
$ | = 1;
Select ($ oldfh );
If ($ pid> 0 ){
Close (CHILD_RD );
Close (CHILD_ERR );
Print PARENT_WR $ stdin_passthrough;
Close (PARENT_WR );
$ Rin = $ rout = $ ein = $ eout = '';
Vec ($ rin, fileno (CHILD_O), 1) = 1;
Vec ($ rin, fileno (PARENT_ERR), 1) = 1;
$ Ein = $ rin;
$ Nfound = 0;
 
While ($ nfound = select ($ rout = $ rin, undef, $ ein = $ eout, 10 )){
Die "$! "Unless $ nfound! =-1;
$ R1 = vec ($ rout, fileno (PARENT_ERR), 1) = 1;
$ R2 = vec ($ rout, fileno (CHILD_O), 1) = 1;
$ E1 = vec ($ eout, fileno (PARENT_ERR), 1) = 1;
$ E2 = vec ($ eout, fileno (CHILD_O), 1) = 1;
 
If ($ r1 ){
While ($ bytes = read (PARENT_ERR, $ errbytes, 4096 )){
Print STDERR $ errbytes;
}
If ($ !) {
$ Err = $ !;
Die $ !;
Vec ($ rin, fileno (PARENT_ERR), 1) = 0
Unless ($ err = EINTR or $ err = EAGAIN );
}
}
If ($ r2 ){
While ($ bytes = read (CHILD_O, $ s, 4096 )){
Print $ s;
}
If (! Defined ($ bytes )){
$ Err = $ !;
Die $ !;
Vec ($ rin, fileno (CHILD_O), 1) = 0
Unless ($ err = EINTR or $ err = EAGAIN );
}
}
Last if ($ e1 | $ e2 );
}
Close CHILD_RD;
Close PARENT_ERR;
Waitpid ($ pid, 0 );
} Else {
Foreach $ key (keys % req_params ){
$ ENV {$ key} = $ req_params {$ key };
}
 
# Cd to the script's local directory
If ($ req_params {SCRIPT_FILENAME} = ~ /^ (. *) \/[^ \/] + $ /){
Chdir $1;
}
Close (PARENT_WR );
# Close (PARENT_ERR );
Close (STDIN );
Close (STDERR );
 
# Fcntl (CHILD_RD, F_DUPFD, 0 );
Syscall (& SYS_dup2, fileno (CHILD_RD), 0 );
Syscall (& SYS_dup2, fileno (CHILD_ERR), 2 );
 
# Open (STDIN, "<& CHILD_RD ");
Exec ($ req_params {SCRIPT_FILENAME });
Die ("exec failed ");
}
} Else {
Print ("Content-type: text/plain \ r \ n ");
Print "Error: No such CGI app-$ req_params {SCRIPT_FILENAME} may not exist or is not executable by this process. \ n ";
}
}
}
Save the above script as/usr/local/bin/cgiwrap-fcgi.pl.
Save the above file as/usr/local/bin/cgiwrap-fcgi.pl.

Just running the program as above will bind it to a unix socket at/var/run/nginx/cgiwrap-dispatch.sock. be sure your nginx worker process user has read/write access to this file. the script does not fork itself, so you will need to background it somehow (with Bash add an ampersand "&" at the end of your command to execute it ).


Running the above program will bind it to a Unix socket located in/var/run/nginx/cgiwrap-dispatch.sock. The Nginx working process has the permission to read and write files. This script will not end itself (?), Run it as a background program (add & after executing the command in Bash &)

If this all works, then the next part is to setup Nginx:

If it works, start Nginx:


Http {
Root/var/www/htdocs;
Index index.html;
Location ~ ^/Cgi-bin/. * \. cgi $ {
Gzip off; # gzip makes scripts feel slower since they have to complete before getting gzipped
Fastcgi_pass unix:/var/run/nginx/cgiwrap-dispatch.sock;
Fastcgi_index index. cgi;
Fastcgi_param SCRIPT_FILENAME/var/www/cgi-bin $ fastcgi_script_name;
Fastcgi_param QUERY_STRING $ query_string;
Fastcgi_param REQUEST_METHOD $ request_method;
Fastcgi_param CONTENT_TYPE $ content_type;
Fastcgi_param CONTENT_LENGTH $ content_length;
Fastcgi_param GATEWAY_INTERFACE CGI/1.1;
Fastcgi_param SERVER_SOFTWARE nginx;
Fastcgi_param SCRIPT_NAME $ fastcgi_script_name;
Fastcgi_param REQUEST_URI $ request_uri;
Fastcgi_param DOCUMENT_URI $ document_uri;
Fastcgi_param DOCUMENT_ROOT $ document_root;
Fastcgi_param SERVER_PROTOCOL $ server_protocol;
Fastcgi_param REMOTE_ADDR $ remote_addr;
Fastcgi_param REMOTE_PORT $ remote_port;
Fastcgi_param SERVER_ADDR $ server_addr;
Fastcgi_param SERVER_PORT $ server_port;
Fastcgi_param SERVER_NAME $ server_name;
}
}
Restart Nginx and point your browser at your CGI program. The above sample config will execute any. cgi file in cgi-bin with the cgiwrap-fcgi.pl wrapper, tweak this to your heart's content.
Restart Nginx and direct the browser to the URL of the CGI program. The simple configuration above will use cgiwrap-fcgi.pl packaging to execute all. cgi suffixes located in cgi-bin, which may be dangerous to keep this in mind.

I 've been able to run Python, Perl, and C ++ cgi apps with this-apps that use GET or POST.

All cgi applications of C ++, Perl, and Python that can use GET or POST methods can run.

You may find that $ document_root does not point to your actual document root (hardcoded upon build), so you can replace the fastcgi param DOCUMENT_ROOT with "/the/real/path ". also replace the SCRIPT_FILENAME param if that is used by your CGI wrapper (the one above does)


As you can see, $ document_root does not point to the actual document root (based on the constructed hard code). Instead of the fastcgi parameter DOCUMENT_ROOT, replace the SCRIPT_FILENAME parameter with the actual value used by the CGI package.

Postscript
Although I have not written CGI code or configured and installed Nginx, I think these things are very interesting. After I finish learning Rails, I must study Nginx well, compared with Apahce.

For more Nginx tutorials, see the following:

Deployment of Nginx + MySQL + PHP in CentOS 6.2

Build a WEB server using Nginx

Build a Web server based on Linux6.3 + Nginx1.2 + PHP5 + MySQL5.5

Performance Tuning for Nginx in CentOS 6.3

Configure Nginx to load the ngx_pagespeed module in CentOS 6.3

Install and configure Nginx + Pcre + php-fpm in CentOS 6.4

Nginx installation and configuration instructions

Nginx log filtering using ngx_log_if does not record specific logs

Nginx details: click here
Nginx: click here

This article permanently updates the link address:

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.