Git : package binaire Mac OS X
- Mercredi 15 avril 2009
- Par Boris HUISGEN
- Ecrire
Un package binaire de Git pour Mac OS X est disponible à cet adresse :
http://code.google.com/p/git-osx-installer/downloads/list?can=3
Archives auteur
Un package binaire de Git pour Mac OS X est disponible à cet adresse :
http://code.google.com/p/git-osx-installer/downloads/list?can=3
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 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.
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.
Les statistiques des interfaces réseaux par SNMP (net-mgmt/net-snmp) ne sont pas directement fonctionnels sous FreeBSD 64 bits (aucun problème pour les OS 32 bits) :
# snmpwalk -v 2c -c public 127.0.0.1 .1.3.6.1.2.1.31.1.1.1 IF-MIB::ifName.1 = STRING: re0 IF-MIB::ifName.2 = STRING: lo0
Seuls les noms d’interfaces sont renvoyés. Les compteurs systèmes étant codés sur 64 bits et n’étant pas supportés par défaut par SNMP, il faut recompiler le port avec l’option WITH_MFD_REWRITES pour y remédier:
# make deinstall && make clean # make -DWITH_MFD_REWRITES # make install # /usr/local/etc/rc.d/snmpd restart
SNMP peut alors reporter l’ensemble des statistiques :
# snmpwalk -v 2c -c public 127.0.0.1 .1.3.6.1.2.1.31.1.1.1 IF-MIB::ifName.1 = STRING: re0 IF-MIB::ifName.2 = STRING: lo0 IF-MIB::ifInMulticastPkts.1 = Counter32: 368234 IF-MIB::ifInMulticastPkts.2 = Counter32: 0 IF-MIB::ifInBroadcastPkts.1 = Counter32: 0 IF-MIB::ifInBroadcastPkts.2 = Counter32: 0 IF-MIB::ifOutMulticastPkts.1 = Counter32: 0 IF-MIB::ifOutMulticastPkts.2 = Counter32: 0 IF-MIB::ifOutBroadcastPkts.1 = Counter32: 0 IF-MIB::ifOutBroadcastPkts.2 = Counter32: 0 IF-MIB::ifHCInOctets.1 = Counter64: 86439204 IF-MIB::ifHCInOctets.2 = Counter64: 1355164 IF-MIB::ifHCInUcastPkts.1 = Counter64: 549735 IF-MIB::ifHCInUcastPkts.2 = Counter64: 11430 IF-MIB::ifHCInMulticastPkts.1 = Counter64: 368234 IF-MIB::ifHCInMulticastPkts.2 = Counter64: 0 IF-MIB::ifHCInBroadcastPkts.1 = Counter64: 0 IF-MIB::ifHCInBroadcastPkts.2 = Counter64: 0 IF-MIB::ifHCOutOctets.1 = Counter64: 41458007 IF-MIB::ifHCOutOctets.2 = Counter64: 1355164 IF-MIB::ifHCOutUcastPkts.1 = Counter64: 121314 IF-MIB::ifHCOutUcastPkts.2 = Counter64: 11430 IF-MIB::ifHCOutMulticastPkts.1 = Counter64: 0 IF-MIB::ifHCOutMulticastPkts.2 = Counter64: 0 IF-MIB::ifHCOutBroadcastPkts.1 = Counter64: 0 IF-MIB::ifHCOutBroadcastPkts.2 = Counter64: 0 IF-MIB::ifHighSpeed.1 = Gauge32: 1000 IF-MIB::ifHighSpeed.2 = Gauge32: 0 IF-MIB::ifPromiscuousMode.1 = INTEGER: false(2) IF-MIB::ifPromiscuousMode.2 = INTEGER: false(2) IF-MIB::ifConnectorPresent.1 = INTEGER: true(1) IF-MIB::ifConnectorPresent.2 = INTEGER: true(1) IF-MIB::ifAlias.1 = STRING: IF-MIB::ifAlias.2 = STRING: IF-MIB::ifCounterDiscontinuityTime.1 = Timeticks: (0) 0:00:00.00 IF-MIB::ifCounterDiscontinuityTime.2 = Timeticks: (0) 0:00:00.00
Le monitoring réseau par SNMP est alors possible.
Une petite liste de DNS publics facilement mémorisables qui peut dépanner :
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.
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.
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 :
La redirection depuis l’ancien host sera grillée d’ici quelques jours…
La technique pour éviter les attaques SSH par brute force avec PacketFilter consiste à ajouter les IP fautives dans une table et d’exclure les IP qui y sont contenues pour les connexions SSH ou de façon globale (petit vilain).
Procédure chef :
Exemple d’un parefeu PF rudimentaire, fait main (70% main droite), autorisant le trafic ICMP et les connexions SSH :
#
# pf.conf
#
# macros
ext_if = "re0" # ou mi0, fa0, sol0 çà dépend
myip = "XX.XX.XX.XX" # comme c'est trop secure
# tables
table <bruteforce> persist {}
# options
set block-policy drop
set skip on lo0 # non ce n'est pas la0
set limit { states 20000, frags 5000, src-nodes 2000 }
# normalization
scrub in all fragment reassemble
scrub all reassemble tcp
scrub in all random-id
#
# rules
#
block all
block quick from <bruteforce>
antispoof quick for lo0
pass out inet proto tcp all flags S/SA keep state
pass out inet proto udp all keep state
pass in on $ext_if inet proto icmp from any to any keep state
pass out on $ext_if inet proto icmp from any to any keep state
pass in on $ext_if inet proto tcp from any to any port 22 flags S/SA keep state (source-track rule, max-src-nodes 8, max-src-conn 8, max-src-conn-rate 10/60, overload <bruteforce> flush global)
Dans le cas où un client se connecte 10 fois par minute, il est banni et bloqué pour toute nouvelle connexion. Il ne reste plus que de lancer la commande permettant de supprimer les adresses IP enregistrées, ici pour le jour précédent :
# /usr/local/sbin/expiretable -t 24h bruteforce