Archives pour la catégorie ‘Hébergement’

Nginx : authentification utilisateur

C’est bien beau l’authentification utilisateur avec Nginx mais on a encore besoin de htpasswd ! Alors pour pas recompiler Apache et s’endormir pendant 15 minutes (voire beaucoup plus), on peut se rabattre sur cette ligne :

perl -le 'print crypt ("mot_de_passe", "grain_de_sel")'

Les entrées du fichier des mots de passe ont également le même format :

<user1>:<password>:<comment>
<user2>:<password>

Le commentaire n’est pas obligatoire. Pour le reste, cf la documentation du module.

Pour une authentification avancée du type LDAP ou MySQL, un module d’authentification PAM existe et est téléchargeable sur cette page.

Nginx : scripts CGI & Perl

Suite à la configuration du support PHP sous Nginx, il m’a été nécessaire de pouvoir exécuter les scripts CGI et Perl. Comme avec PHP, la configuration s’articule autour de FastCGI côté Nginx. Par contre côté interpréteur, spawn-fcgi ne peut pas être utilisé. Un interpréteur Perl doit donc être « préchargé » et en attente des requêtes clients par la socket dont il a au préalable créée. Socket au choix : UNIX ou TCP si l’exécution doit être locale ou déportée.

Voici le script de l’interpréteur perl-wrapper.pl :

#!/usr/bin/perl

use FCGI;
use Socket;
use POSIX qw(setsid);

require 'syscall.ph';

&daemonize;

END() { } BEGIN() { }
*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( "/tmp/fcgi-perl.sock", 10 );
        $request = FCGI::Request( \*STDIN, \*STDOUT, \*STDERR, \%req_params, $socket );
        if ($request) { request_loop()};
            FCGI::CloseSocket( $socket );
}

sub request_loop {
        while( $request->Accept() >= 0 ) {

           $stdin_passthrough ='';
           $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;
                }
            }

            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);
		my $pid = open(KID_TO_READ, "-|");
		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;
		}
		if ($pid > 0) {
			close(CHILD_RD);
			print PARENT_WR $stdin_passthrough;
			close(PARENT_WR);

			while(my $s = <KID_TO_READ>) { print $s; }
			close KID_TO_READ;
			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(STDIN);
			#fcntl(CHILD_RD, F_DUPFD, 0);
			syscall(&SYS_dup2, fileno(CHILD_RD), 0);
			#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";
            }
        }
}

Ensuite, le script de startup fcgi-perl.sh :

#!/bin/sh
#
# Boris HUISGEN <bhuisgen@hbis.fr>
#

PROVIDES=fcgi-perl

WRAPPER=/usr/local/etc/nginx/perl-wrapper.pl
SOCKET=/tmp/fcgi-perl.sock
SOCKET_USER=www
SOCKET_GROUP=www
SOCKET_PERMS=600

# script

cmd=$1

pcgi_start(){
 echo "Starting $PROVIDES..."
 perl -I /usr/local/lib/perl5/site_perl/5.8.9/mach/sys/ $WRAPPER &

 until [ -S $SOCKET ]
 do
 sleep 1
 done

 chown $SOCKET_USER:$SOCKET_GROUP $SOCKET
 chmod $SOCKET_PERMS $SOCKET
}

pcgi_stop(){
 echo "Stopping $PROVIDES..."
 pkill -f $WRAPPER
 rm $SOCKET
}

pcgi_restart(){
 pcgi_stop
 pcgi_start
}

pcgi_status(){
 [ -S $SOCKET ] && echo "$PROVIDES running" || echo "$PROVIDES NOT running"
}

pcgi_help(){
 echo "Usage: $0 {start|stop|restart|status}"
}

case ${cmd} in
[Ss][Tt][Aa][Rr][Tt]) pcgi_start;;
[Ss][Tt][Oo][Pp]) pcgi_stop;;
[Rr][Ee][Ss][Tt][Aa][Rr][Tt]) pcgi_restart;;
[Ss][Tt][Aa][Tt][Uu][Ss]) pcgi_status 0;;
*)      pcgi_help ;;
esac

Le support CGI/Perl au niveau de Nginx est calqué sur celui de PHP : un mapping d’extension + un nom de socket :

server {
   listen  80;
   server_name     cgi.site.fr;</pre>
location / {
      root /home/www/site.fr/cgi-bin;
      index index.cgi;
   }

   location ~ \.cgi$ {
      fastcgi_pass   unix:/tmp/fcgi-perl.sock;
      fastcgi_index  index.cgi;
      fastcgi_param  HTTP_ACCEPT_ENCODING gzip,deflate;
      fastcgi_param  SCRIPT_FILENAME  /home/www/site.fr/cgi-bin/$fastcgi_script_name;
      include        fastcgi_params;
   }
}

Pour détecter tout problème d’exécution, lancez le wrapper Perl en console. Vérifiez que le module Perl perl-sys-syscall (devel/p5-Sys-Syscall) est installé. Si le wrapper ne parvient pas à trouver le fichier syscall.ph, il faut le générer :

# cd /usr/include
# h2ph * sys/*

Le fichier va être ajouté dans un nouveau répertoire, pour ma part /usr/local/lib/perl5/site_perl/5.8.9/mach/sys/, que j’ajoute en option à Perl dans le script de startup. Ces modifications seront à effectuer en concordance avec votre installation Perl.

Nginx : FastCGI PHP par socket UNIX

L’exécution de scripts PHP avec le serveur Web Nginx s’effectue par le biais du module FastCGI. Ce module assure la liaison entre Nginx et un interpréteur PHP par socket UNIX ou TCP (ce dernier cas permet une exécution sur un serveur distinct). L’utilitaire spawn-fcgi fourni avec le serveur Lighttpd assure le lien entre FastCGI et les interpréteurs PHP ; il ouvre la socket et préloade un ou plusieurs interpréteurs php-cgi.

Dans le cas où votre serveur Nginx est en place avec son module FastCGI, il vous reste à créer le script de lancement des interpréteurs PHP. Voici le mien, fcgi-php.sh, qui s’appuie sur une socket UNIX, afin de traiter localement les scripts PHP :

#!/bin/sh
# Boris HUISGEN <bhuisgen@hbis.fr>
# - convert to use UNIX socket support
# - force socket permissions

PROVIDES=php-cgi

SPAWN_FCGI=/usr/local/bin/spawn-fcgi
PHP_CGI=/usr/local/bin/php-cgi
PHP_CGI_CHILDREN=4

SOCKET=/tmp/fcgi-php.sock
SOCKET_USER=www
SOCKET_GROUP=www
SOCKET_PERMS=600

# script

cmd=$1

pcgi_start() {
   echo "Starting $PROVIDES..."
   $SPAWN_FCGI -s $SOCKET -u $SOCKET_USER -g $SOCKET_GROUP -f $PHP_CGI -C $PHP_CGI_CHILDREN
   chown $SOCKET_USER:$SOCKET_GROUP $SOCKET
   chmod $SOCKET_PERMS $SOCKET
}

pcgi_stop() {
   echo "Stopping $PROVIDES..."
   killall $PROVIDES
   rm $SOCKET
}

pcgi_restart(){
   pcgi_stop
   pcgi_start
}

pcgi_status() {
   [ -S $SOCKET ] && echo "$PROVIDES running" || echo "$PROVIDES NOT running"
}

pcgi_help() {
   echo "Usage: $0 {start|stop|restart|status}"
}

case ${cmd} in
   [Ss][Tt][Aa][Rr][Tt]) pcgi_start;;
   [Ss][Tt][Oo][Pp]) pcgi_stop;;
   [Rr][Ee][Ss][Tt][Aa][Rr][Tt]) pcgi_restart;;
   [Ss][Tt][Aa][Tt][Uu][Ss]) pcgi_status 0;;
   *) pcgi_help ;;
esac

Attention, la socket doit posséder les permissions suffisantes pour que Nginx puisse y écrire.

Au niveau de Nginx, voici la configuration globale du module FastCGI, fichier fastcgi_params :

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  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  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

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;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

Finalement, la configuration de l’hôte virtuel :

server {
   listen  80;
   server_name     www.site.fr

   location / {
      root           /home/www/site.fr/html;
      index          index.php;
   }

   location ~ \.php$ {
      fastcgi_pass   unix:/tmp/fcgi-php.sock;
      fastcgi_index  index.php;
      fastcgi_param  SCRIPT_FILENAME  /home/www/site.fr/www/html$fastcgi_script_name;
      include        fastcgi_params;
   }
}

L’option location permet de mapper les fichiers d’extension .php au module FastCGI, plus précisément à la socket de spawn-fcgi. Dans le cas d’une connexion socket TCP, il faut modifier le script de démarrage fcgi-php.sh et l’option fastcgi_pass de l’hôte (tutorial)

Au final, en reproduisant cette configuration, un même serveur ou encore site peut exécuter en parallèle des scripts PHP 4 & 5, si chaque interpréteur possède une socket dédiée. D’autres langages sont d’ailleurs possibles, c’est pourquoi mon prochain billet s’attardera au support des scripts CGI / Perl.

Nouvel hosting

Ce blog est à présent hébergé sur un nouveau serveur dédié. J’en ai profité pour enregistrer mon nom de domaine hbis.fr.

La nouvelle URL d’accès du blog devient donc :

http://blog.hbis.fr

La redirection depuis l’ancien host sera grillée d’ici quelques jours…

Haut de page