Archives pour la catégorie ‘Hébergement’

Gentoo : PHP + mod_suphp = Internal Server Error

Encore une petite boulette de la team Gentoo pour mod_suphp en version 0.7.1  : le port installe un fichier de configuration non valide, ce qui provoque une inexécution de tous les scripts PHP ! C’est très fâcheux pour un module qui justement  sécurise l’exécution PHP…

Il faut donc corriger le bloc suivant dans /etc/suphp.conf :

[handlers]
;Handler for php-scripts
x-httpd-php=php:/usr/lib/php5/bin/php-cgi
x-httpd-php5=php:/usr/lib/php5/bin/php-cgi
x-httpd-phtml=php:/usr/lib/php5/bin/php-cgi

;Handler for CGI-scripts
x-suphp-cgi=execute:!self

par celui-ci :

[handlers]
;Handler for php-scripts
x-httpd-php="php:/usr/lib/php5/bin/php-cgi"
x-httpd-php5="php:/usr/lib/php5/bin/php-cgi"
x-httpd-phtml="php:/usr/lib/php5/bin/php-cgi"

;Handler for CGI-scripts
x-suphp-cgi="execute:!self"

Vous noterez donc les doubles quotes en plus.

Nginx : astuces pour un hébergement mutualisé

Deux astuces dans le cadre d’un hébergement mutualisé de plusieurs sites avec Nginx.

En premier lieu, il est possible d’inclure des fichiers de configuration par l’option include. Ceci permet alors de créer un répertoire vhosts dédié aux fichiers de configuration de chaque site. Dans le fichier de configuration principal, il faut ajouter par ex :

http {
[...]
# virtual hosts
 include /usr/local/etc/nginx/vhosts/*.conf;
}

Tous les fichiers .conf du répertoire vhosts seront alors inclus. Si un vhost doit être temporairement désactivé, il suffit de changer l’extension de son fichier.

La deuxième astuce touche l’utilisation du module FastCGI. Dans chaque vhost, il faut déclarer le support FastCGI pour PHP, Perl, etc… Il est possible de limiter cette déclaration à ces lignes :

location ~ \.php$ {
 fastcgi_pass   unix:/tmp/fcgi-php.sock;
 fastcgi_index  index.php;
 include        fastcgi_params;
 }

en rajoutant la ligne suivante dans le fichier fastcgi_params :

# Virtual hosts support
fastcgi_param SCRIPT_FILENAME   $document_root$fastcgi_script_name;

RoundCube : module de gestion des vacations

RoundCube est un webmail dernier cri avec glisser-déposer, carnet d’adresses LDAP, vérificateur d’orthographe… en somme tout ce qu’il faut mais en vraiment plus beau que les autres. Il manque encore des plugins d’extension mais çà ne devrait pas tarder avec l’API prévue à cet effet pour la version 0.3.

En attendant, j’ai fait un petit module permettant de gérer un répondeur  automatique de message. L’interface est des plus simples : un onglet « Répondeur » est ajouté dans les paramètres de l’utilisateur, une case à cocher pour activer le répondeur, plus les zones de saisie pour le message et le sujet. Le module est configuré avec les requêtes SQL pour le serveur SMTP Postfix & son interface d’administration PostfixAdmin (elle même pouvant gérer les vacations). Les requêtes sont évidemment modifiables depuis le fichier de configuration du module config/vacation.inc.php.

Module vacation pour RoundCube version 0.2.2 : roundcube-module_vacation-20090528

roundcube_vacation01roundcube_vacation02

Nginx : rewrite rules pour le CMS Joomla!

Pour utiliser les URL SEO de Joomla! sous Nginx, la configuration du virtual host est la suivante :


server {
   listen  80;
   server_name     site.fr;
   root            /opt/local/var/www/site.fr/www/html;
   index           index.php;

   location / {
      if ( !-e $request_filename ) {
         rewrite (/|\.php|\.html|\.htm|\.feed|\.pdf|\.raw|/[^.]*)$ /index.php last;
         break;
      }
  }

   location ~ \.php$ {
      fastcgi_pass   unix:/opt/local/var/run/nginx/fcgi-php.sock;
      fastcgi_index  index.php;
      include        fastcgi_params;
   }
}

Nginx : rewrite rules MediaWiki

Pour héberger un site MediaWiki avec Nginx, voici les rewrite rules à appliquer dans le virtual host :

server {
    listen  80;
    server_name     wiki.site.fr;
    root            /home/www/sites/site.fr/wiki/html;

    location / {
        index          index.php;
        rewrite        ^/$ /wiki/Main_Page permanent;
    }

    rewrite ^/wiki/([^?]*)(?:\?(.*))? /w/index.php?title=$1&$2;
    rewrite ^/wiki /w/index.php;
    location ~ \.php$ {
        fastcgi_pass   unix:/tmp/fcgi-php.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME /home/www/sites/site.fr/wiki/html$fastcgi_script_name;
        include        fastcgi_params;
    }
}

Serveur Web Nginx/PHP/MySQL sous MacOS X

Après différents essais d’utilisation Apache / MySQL / PHP sous MacOS X que ce soit avec les versions out-of-the box d’Apple ou les packages spécifiques qui ne fonctionnent pas bien/du tout comme ca, çà, et çà, la raison m’a amené à compiler/installer une suite Web complète : plus d’Apache mais du Nginx (et çà j’y tiens), un PHP 5 avec les extensions standards et en exécution CGI, et enfin un MySQL 5.

Alors quelle solution permet de faciliter tout çà ? La réponse se trouve dans MacPorts : un système de ports pour MacOS,  fortement inspiré des ports BSD.  Un port est un package permettant de télécharger/compiler/installer les sources d’un logiciel. Une unique commande permet de remplir ce rôle. Donc autant vous le dire, une fois qu’on l’a utilisée, on s’en passe plus. Il faut uniquement mettre à jour de temps à autre l’arborescence des ports pour récupérer et mettre à jour les dernières versions. Tous les programmes sont installés dans /opt/local ce qui fait que rien n’est touché au système. Bref, la complexité est de ce niveau à peu près.

Installation de MacPorts : http://www.macports.org/install.php. N’oubliez pas d’installer la dernière version des outils XCode et le serveur X11 pour pouvoir compiler certains ports.

A partir de maintenant, le terminal est notre ami. Voici les commandes courantes de MacPorts :

port search <string> : chercher un port par son nom
port info <port> : affiche les informations du port
port variants <port> : afficher les options de compilation du port
port install <port> : installe le port, les variants sont à fournir à la suite et préfixées par +
port uninstall <port : désinstalle le port
port deps <port> : les dépendances du port
port contents <port> : les fichiers installés par le port

Avant de débuter l’installation des ports, une mise à jour de l’arborescence des ports s’impose :

$ sudo port -v selfupdate

Installation de Nginx :

$ sudo port install nginx

Il faut copier les fichiers exemples et éditer le fichier de configuration :

$ cd /opt/local/etc/nginx
$ sudo cp mime.types.example mime.types
$ sudo cp nginx.conf.example nginx.conf
$ sudo cp fastcgi_params.example fastcgi_params
$ sudo nano nginx.conf

Vérifiez que le répertoire root de l’hôte prédéfini est valide.

Installation de MySQL :

$ sudo port install mysql5-server

Une fois installé, il faut installer les db de gestion de MySQL  :

$ sudo /opt/local/bin/mysql_install_db5 --user=mysql

Installation de PHP et son extension MySQL :

$ sudo port install php5 +fastcgi +pear
$ sudo port install php5-mysql

Le mode d’exécution CGI est nécessaire pour Nginx et son module FastCGI. Il faut également créer le fichier php.ini :

$ cd /opt/local/etc/php5
$ cp php.ini-production php.ini

Pour le support FastCGI/PHP, je vous invite à consulter mon article traitant de ce sujet (et la configuration de Nginx pour le support PHP). Il faudra installer le port  spawn-fcgi :

$ sudo port install spawn-fcgi

Concernant le script FCGI-PHP, j’utilise une socket UNIX dans /opt/local/var/run/nginx/.

Installation de PHPMyAdmin :

$ sudo port install phpmyadmin

Si besoin, copiez le répertoire /opt/local/www/phpmyadmin (ou faites un lien symbolique) vers votre répertoire root.

A présent que tout est installé, il convient de lancer les logiciels. Il existe deux méthodes : la méthode manuelle  par scripts start/stop et la méthode launchd. Si la seconde permet d’automatiser le lancement automatique, la première est préférable sur une station de développement, notamment en cas d’erreur dans le fichier de configuration Nginx.

Les scripts manuels start / stop sont les suivants :

$ more /opt/local/etc/nginx/start.sh
#!/bin/sh
/opt/local/etc/LaunchDaemons/org.macports.mysql5/mysql5.wrapper start
nginx
/opt/local/etc/nginx/fcgi-php.sh start
$ more /opt/local/etc/nginx/stop.sh
#!/bin/sh
/opt/local/etc/LaunchDaemons/org.macports.mysql5/mysql5.wrapper stop
kill `cat /opt/local/var/run/nginx/nginx.pid`
/opt/local/etc/nginx/fcgi-php.sh stop

Si vous préférez la méthode launchd :

$ sudo launchctl load -w /opt/local/etc/LaunchDaemons/org.macports.mysql5/org/macports.mysql5.plist
$ sudo launchctl start org.macports.mysql5
$ sudo launchctl load -w /opt/local/etc/LaunchDaemons/org.macports.nginx/org.macports.nginx.plist
$ sudo launchctl start org.macports.nginx

Le script FCGI-PHP est à lancer manuellement (cf script start/stop).

Une fois le serveur MySQL en exécution, n’oubliez pas de modifier le mot de passe root :

$ mysqladmin5 -u root password 'new-password'

Un test de navigation sur http://localhost doit valider l’installation de Nginx. Créez un fichier PHP avec une sortie phpinfo () pour valider l’interprétation PHP-CGI.

Votre Mac dispose à présent d’une suite Web complète digne de ce nom.

htscanner : patch correctif pour la version 0.9.0

htscanner est une extension PHP permettant de parser les fichiers .htaccess d’Apache, afin d’y récupérer les variables d’exécution fixées par php_flag et php_value. Cette extension est indispensable dans le cadre d’une exécution PHP par CGI car ni le serveur Web ni php-cgi ne peuvent les récupérer.

Ayant déjà soumis un patch pour fixer la récupération des valeurs entre simple/double quotes, en voici un nouveau pour la version 0.9.0 qui n’est toujours pas exempte de problèmes avec son parseur.

patch-htscanner-parser-20090401

- interprétation correcte des retours à la ligne (EOL) quelque soit la plateforme.
- restriction du parsing pour les php_flag, seules les valeurs on/off sont acceptées (cf documentation PHP).

Mise à jour du 2009-04-05 : j’ai intégré ce patch dans la version CVS ; vivement la release 0.9.1 !

suPHP : patch de désactivation du check "directory owner"

suPHP est un programme permettant de limiter l’exécution des scripts PHP à un UID spécifique afin de protéger le serveur d’hébergement de toute attaque de vulnérabilité. L’UID est modifié pour correspondre par exemple à celui du serveur Web, du propriétaire du fichier PHP ou encore un UID spécifique.

suPHP s’intercale entre Apache et PHP par le biais d’un handler spécifique, qui mappe l’extension d’un fichier au binaire suphp chargé de modifier les permissions UID puis d’appeler l’interpréteur CGI déclaré pour ledit fichier. Ce mécanisme permet d’exécuter des scripts PHP 4 / 5 / 6 depuis la même instance Apache, ce qui est fort utile dans le cas d’un serveur mutualisé.

Seul bémol : dans le cas d’un serveur Web de développement, il peut perturber les utilisateurs normaux qui ne peuvent modifier les droits d’accès des scripts PHP. Cela touche en particulier le répertoire parent car suPHP vérifie que son propriétaire correspond bien à l’UID configuré. A moins de pouvoir modifier ces droits, une erreur d’exécution est générée lors de la navigation :

Internal Server Error
Directory /var/www/site.fr/html/test is not owned by apache

Afin de contourner ce problème (plutôt marre d’entendre « je peux pas, j’ai pas les droits Apache »), j’ai donc fait un petit patch ajoutant l’option check_directory_owner à suPHP afin de désactiver ce test. L’option est à fixer dans le fichier de configuration :

; Security options
check_directory_owner=false # par défaut true
allow_file_group_writeable=false
allow_file_others_writeable=false
allow_directory_group_writeable=false
allow_directory_others_writeable=false

Le patch est à appliquer sur la version 0.7.1 de suPHP : patch-suphp-checkownerdirectory-20090328.

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.

Haut de page