配置Nginx 运行CGI(Perl-cgi)[服务器安全]
本文“配置Nginx 运行CGI(Perl-cgi)[服务器安全]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
笔者的nginx安装目录为/usr/local/nginx
虚拟机配置文件为/usr/local/nginx/conf/vhosts.conf
目前php fast-cgi已支持
以下操作均在su下完成
=====================
安装perl cgi模块
wget http://www.cpan.org/modules/by-module/FCGI/FCGI-0.67.tar.gz
tar -zxvf FCGI-0.67.tar.gz
cd FCGI-0.67
perl Makefile.PL
make && make install
安装FCGI-ProcManager:
wget http://search.cpan.org/CPAN/authors/id/G/GB/GBJK/FCGI-ProcManager-0.18.tar.gz
tar -xzxf FCGI-ProcManager-0.18.tar.gz
cd FCGI-ProcManager-0.18
perl Makefile.PL
make make install
===================
先在/bin目录下安排一个perl写的分发器,取名叫perl-fcgi
vi /bin/perl-cgi
#!/usr/bin/perl -w
use FCGI;
use Socket;
use FCGI::ProcManager;
sub shutdown { FCGI::CloseSocket($socket); exit; }
sub restart { FCGI::CloseSocket($socket); &main; }
use sigtrap 'handler', \&shutdown, 'normal-signals';
use sigtrap 'handler', \&restart, 'HUP';
require 'syscall.ph';
use POSIX qw(setsid);
#&daemonize; we don't daemonize when running under runsv
#this keeps the program alive or something after exec'ing perl scripts
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 {
$socket = FCGI::OpenSocket( "127.0.0.1:10081", 10 ); #use IP sockets
#$socket = FCGI::OpenSocket( "/var/run/nginx/perl_cgi-dispatch.sock", 10 );
#use UNIX sockets - user running this script must have w access to the 'nginx' folder!!
#foreach $item (keys %ENV) { delete $ENV{$item}; }
$proc_manager = FCGI::ProcManager->new( {n_processes => 5} );
#$socket = FCGI::OpenSocket( "/opt/nginx/fcgi/cgi.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\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\r\n");
print
"Error: No such CGI app - $req_params{SCRIPT_FILENAME} may not exist or is not executable by this process.\n";
}
}
}
给这个脚本履行权限
chmod +x /bin/perl-fcgi
然后试试看可否启动
/bin/perl-cgi &
若是成功则会呈现以下信息
FastCGI: server (pid 21315): initialized
FastCGI: manager (pid 17915): server (pid 21315) started
假如碰到错误Can’t locate FCGI.pm,那么履行下面的号令
perl -MCPAN -e 'install FCGI'
perl -MCPAN -e 'install FCGI::ProcManager'
cd /usr/include; h2ph *.h */*.h
第1、二条号令是给perl安装FCGI模块,第三条是注册perl能辨认的头文件,然后重新履行/bin/perl-fcgi, 假如正常的话,那么履行:
netstat -tunlp
列表中应当呈现
tcp 0 0 127.0.0.1:10081 0.0.0.0:* LISTEN 5640/perl
启用分发器
/bin/perl-fcgi > /dev/null 2>&1 &
将其写入rc.local
echo "/bin/perl-fcgi > /dev/null 2>&1 &" >> /etc/rc.local
上面的方法启动后perl-fcgi是以履行它的用户身份运行的,关于web程序来说这是很不利的.老外用perl写了一个脚本Noah Friedman可以用指定的用户来运行某个程序,源程序在这里,这里也贴出来便利查阅vi /sbin/runas
- #!/bin/sh
- exec ${PERL-perl} -Swx $0 ${1+"$@"}
- #!perl [perl will skip all lines in this file before this line]
- # with --- run program with special properties
- # Copyright (C) 1995, 2000, 2002 Noah S. Friedman
- # Author: Noah Friedman <friedman@splode.com>
- # Created: 1995-08-14
- # $Id: with,v 1.12 2004/02/16 22:51:49 friedman Exp $
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2, or (at your option)
- # any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, you can either send email to this
- # program's maintainer or write to: The Free Software Foundation,
- # Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.
- # Commentary:
- # TODO: create optional socket streams for stdin or stdout before invoking
- # subprocess.
- # Code:
- use Getopt::Long;
- use POSIX qw(setsid);
- use Symbol;
- use strict;
- (my $progname = $0) =~ s|.*/||;
- my $bgfn;
- my $bgopt = 0;
- my $opt_cwd;
- my $opt_egid;
- my $opt_euid;
- my $opt_gid;
- my $opt_groups;
- my @opt_include;
- my $opt_name;
- my $opt_pgrp;
- my $opt_priority;
- my $opt_root;
- my $opt_uid;
- my $opt_umask;
- my $opt_foreground = 0;
- sub err
- {
- my $fh = (ref ($_[0]) ? shift : *STDERR{IO});
- print $fh join (": ", $progname, @_), "\n";
- exit (1);
- }
- sub get_includes
- {
- unshift @INC, @_;
- push (@INC,
- "$ENV{HOME}/lib/perl",
- "$ENV{HOME}/lib/perl/include");
- eval { require "syscall.ph" } if defined $opt_groups;
- }
- sub numberp
- {
- defined $_[0] && $_[0] =~ m/^-?\d+$/o;
- }
- sub group2gid
- {
- my $g = shift;
- return $g if numberp ($g);
- my $gid = getgrnam ($g);
- return $gid if defined $gid && numberp ($gid);
- err ($g, "no such group");
- }
- sub user2uid
- {
- my $u = shift;
- return $u if numberp ($u);
- my $uid = getpwnam ($u);
- return $uid if defined $uid && numberp ($uid);
- err ($u, "no such user");
- }
- sub set_cwd
- {
- my $d = shift;
- chdir ($d) || err ("chdir", $d, $!);
- }
- sub set_egid
- {
- my $sgid = group2gid (shift);
- my $egid = $) + 0;
- $) = $sgid;
- err ($sgid, "cannot set egid", $!) if ($) == $egid && $egid != $sgid);
- }
- sub set_gid
- {
- my $sgid = group2gid (shift);
- my $rgid = $( + 0;
- my $egid = $) + 0;
- $( = $sgid;
- $) = $sgid;
- err ($sgid, "cannot set rgid", $!) if ($( == $rgid && $rgid != $sgid);
- err ($sgid, "cannot set egid", $!) if ($) == $egid && $egid != $sgid);
- }
- sub big_endian_p
- {
- my $x = 1;
- my @y = unpack ("c2", pack ("i", $x));
- return ($y[0] == 1) ? 0 : 1;
- }
- # This function is more complex than it ought to be because perl does not
- # export the setgroups function. It exports the getgroups function by
- # making $( and $) return multiple values in the form of a space-separated
- # string, but you cannot *set* the group list by assigning those variables.
- # There is no portable way to determine what size gid_t is, so we must guess.
- sub set_groups
- {
- my @glist = sort { $a <=> $b } map { group2gid ($_) } split (/[ ,]/, shift);
- my $expected = join (" ", $(+0, reverse @glist);
- my @p = (big_endian_p() ? ("n", "N", "i") : ("v", "V", "i"));
- for my $c (@p)
- {
- err ("setgroups", $!)
- if (syscall (&SYS_setgroups, @glist+0, pack ("$c*", @glist)) == -1);
- return if ("$(" eq $expected);
- }
- err ("setgroups", "Could not determine gid_t");
- }
- sub set_pgrp
- {
- setpgrp ($$, shift) || err ("setpgrp", $!);
- }
- sub set_priority
- {
- my $prio = shift () + 0;
- setpriority (0, 0, $prio) || err ("setpriority", $prio, $!);
- }
- sub set_root
- {
- my $d = shift;
- chroot ($d) || err ("chroot", $d, $!);
- chdir ("/");
- }
- sub set_euid
- {
- my $suid = user2uid (shift);
- my $euid = $>;
- $> = $suid;
- err ($suid, "cannot set euid", $!) if ($> == $euid && $euid != $suid);
- }
- sub set_uid
- {
- my $suid = user2uid (shift);
- my $ruid = $<;
- my $euid = $>;
- $< = $suid;
- $> = $suid;
- err ($suid, "cannot set ruid", $!) if ($< == $ruid && $ruid != $suid);
- err ($suid, "cannot set euid", $!) if ($> == $euid && $euid != $suid);
- }
- sub background
- {
- my $pid = fork;
- die "$@" if $pid < 0;
- if ($pid == 0)
- {
- # Backgrounded programs may expect to be able to read input from the
- # user if stdin is a tty, but we will no longer have any job control
- # management because of the double fork and exit. This can result in
- # a program either blocking on input (if still associated with a
- # controlling terminal) and stopping, or stealing input from a
- # foreground process (e.g. a shell). So redirect stdin to /dev/null.
- open (STDIN, "< /dev/null") if (-t STDIN);
- return *STDERR{IO};
- }
- exit (0) unless $opt_foreground;
- wait;
- exit ($?);
- }
- sub dosetsid
- {
- background ();
- setsid (); # dissociate from controlling terminal
- return *STDERR{IO};
- }
- sub daemon
- {
- # Don't allow any file descriptors, including stdin, stdout, or
- # stderr to be propagated to children.
- $^F = -1;
- dosetsid ();
- # Duped in case we've closed stderr but can't exec anything.
- my $saved_stderr = gensym;
- open ($saved_stderr, ">&STDERR");
- close (STDERR);
- close (STDOUT);
- close (STDIN);
- return $saved_stderr;
- }
- sub notty
- {
- # Don't allow any file descriptors other than stdin, stdout, or stderr to
- # be propagated to children.
- $^F = 2;
- dosetsid ();
- # Duped in case we've closed stderr but can't exec anything.
- my $saved_stderr = gensym;
- open ($saved_stderr, ">&STDERR");
- open (STDIN, "+</dev/null");
- open (STDERR, "+<&STDIN");
- open (STDOUT, "+<&STDIN");
- return $saved_stderr;
- }
- sub set_bg_option
- {
- my %bgfntbl =
- ( 1 => \&background,
- 2 => \&daemon,
- 4 => \?ty,
- 8 => \&dosetsid,
- );
- $bgopt = $_[0];
- $bgfn = $bgfntbl{$bgopt};
- }
- sub parse_options
- {
- Getopt::Long::config (qw(bundling autoabbrev require_order));
- my $succ = GetOptions
- ("h|help", sub { usage () },
- "c|cwd=s", \$opt_cwd,
- "d|display=s", \$ENV{DISPLAY},
- "H|home=s", \$ENV{HOME},
- "G|egid=s", \$opt_egid,
- "g|gid=s", \$opt_gid,
- "I|include=s@", \@opt_include,
- "l|groups=s", \$opt_groups,
- "m|umask=s", \$opt_umask,
- "n|name=s", \$opt_name,
- "P|priority=i", \$opt_priority,
- "p|pgrp=i", \$opt_pgrp,
- "r|root=s", \$opt_root,
- "U|euid=s", \$opt_euid,
- "u|uid=s", \$opt_uid,
- "f|fg|foreground", \$opt_foreground,
- "b|bg|background", sub { set_bg_option (1); $opt_foreground = 0 },
- "a|daemon|demon", sub { set_bg_option (2) },
- "N|no-tty|notty", sub { set_bg_option (4) },
- "s|setsid", sub { set_bg_option (8) },
- );
- usage () unless $succ;
- my $n = 0;
- do { $n++ if $bgopt & 1 } while ($bgopt >>= 1);
- err ("Can only specify one of --background, --daemon, --notty, or --setsid")
- if ($n > 1);
- }
- sub usage
- {
- print STDERR "$progname: @_\n\n" if @_;
- print STDERR "Usage: $progname {options} [command {args...}]\n Options are: -h, --help You're looking at it. -D, --debug Turn on interactive debugging in perl. -I, --include DIR Include DIR in \@INC path for perl. This option may be specified multiple times to append search paths to perl. -d, --display DISP Run with DISP as the X server display. -H, --home HOME Set \$HOME. -n, --name ARGV0 Set name of running program (argv[0]). -c, --cwd DIR Run with DIR as the current working directory. This directory is relative to the root directory as specified by \`--root', or \`/'. -r, --root ROOT Set root directory (via \`chroot' syscall) to ROOT. -G, --egid EGID Set \`effective' group ID. -g, --gid GID Set both \`real' and \`effective' group ID. -l, --groups GLIST Set group list to comma-separated GLIST. -U, --euid EUID Set \`effective' user ID. -u, --uid UID Set both \`real' and \`effective' user ID. -m, --umask UMASK Set umask. -P, --priority NICE Set scheduling priority to NICE (-20 to 20). -p, --pgrp PGRP Set process group. The following options cause the resulting process to be backgrounded automatically but differ in various ways: -b, --background Run process in background. This is the default with the --daemon, --no-tty, and --setsid options. -f, --foreground Do not put process into the background when using the --daemon, --no-tty, and --setsid options. In all other cases the default is to remain in the foreground. -a, --daemon Run process in \"daemon\" mode. This closes stdin, stdout, and stderr, dissociates the process from any controlling terminal, and backgrounds the process. -N, --no-tty Run process in background with no controlling terminal and with stdin, stdout, and stderr redirected to /dev/null. -s, --setsid Dissociate from controlling terminal. This automatically backgrounds the process but does not redirect any file descriptors.\n";
- exit (1);
- }
- sub main
- {
- parse_options ();
- usage () unless @ARGV;
- get_includes (@opt_include);
- umask (oct ($opt_umask)) if defined $opt_umask;
- set_gid ($opt_gid) if defined $opt_gid;
- set_egid ($opt_egid) if defined $opt_egid;
- set_groups ($opt_groups) if defined $opt_groups;
- set_root ($opt_root) if defined $opt_root;
- set_cwd ($opt_cwd) if defined $opt_cwd;
- set_priority ($opt_priority) if defined $opt_priority;
- set_uid ($opt_uid) if defined $opt_uid;
- set_euid ($opt_euid) if defined $opt_euid;
- my $stderr = $bgfn ? &$bgfn () : *STDERR{IO};
- my $runprog = $ARGV[0];
- if ($opt_name)
- {
- shift @ARGV;
- unshift @ARGV, $opt_name;
- }
- local $^W = 0; # avoid implicit warnings from exec
- exec ($runprog @ARGV) || err ($stderr, "exec", $runprog, $!);
- }
- main ();
- # local variables:
- # mode: perl
- # eval: (auto-fill-mode 1)
- # end:
- # with ends here
-
我的vsftpd、nginx、php-fpm都是以nobody运行,则perl-cgi也用nobody运行
chmod +x /sbin/runas
runas --daemon -g nobody -u nobody /bin/perl-fcgi
==================
配置nginx
vi /usr/local/nginx/conf/fcgi_perl.conf- fastcgi_pass 127.0.0.1:10081;
- fastcgi_index index.cgi;
- fastcgi_param SCRIPT_FILENAME $document_root$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;
- fastcgi_read_timeout 60;
下面是一个cgi虚拟机的配置示例
- server {
- listen 80;
- server_name urdomain.com;
- index index.cgi
- root /var/www;
- location ~ .*\.cgi? {
- include fcgi_perl.conf;
- }
- }
/usr/local/nginx/sbin/nginx -t
测试OK之后重新加载nginx配置文件
/usr/local/nginx/sbin/nginx -s reload
enjoy it.
本文地址: | 与您的QQ/BBS好友分享! |