Durcir un hébergement mutualisé sous Apache avec nginx et suPHP

Auteur :  x0r Publié le   Nombre de commentaires : 0
Mots-clefs : sysadmin nginx apache reverseproxy suphp

Lorsqu'on souhaite fournir un service d'hébergement mutualisé – que ce soit pour des dizaines d'utilisateurs ou simplement pour fournir un espace de développement Web pour sa bien aimée sur son propre serveur – il incombe à l'administrateur système de mettre en place un système d'hébergement qui soit à la fois flexible et sécurisé.

Apache et nginx sont deux serveurs Web qui sont radicalement différents : le premier est souvent le serveur de choix pour faire de l'hébergement mutualisé, en particulier grâce au contrôle qu'on laisse à l'utilisateur avec les fichiers .htaccess, et l'autre est plus petit, plus léger, plus minimaliste, et fait généralement office de « reverse proxy » pour aider à « durcir » un Apache.

Pourquoi alors vouloir associer ces deux serveurs, s'ils sont tous les deux censés servir des pages Web ? Simplement parce que :

  • j'héberge déjà ce site sur nginx et que je n'ai pas envie de tout casser, même si c'est pour que quelqu'un d'autre puisse faire mumuse sur le même serveur ;
  • le mécanisme de « reverse proxy » permet de mettre en cache des résultats et de filtrer sans trop de difficulté les requêtes à destination du serveur Apache. On applique le principe de la défense en profondeur : pour trouer le serveur, il faudrait exploiter une faille aussi bien dans nginx que dans Apache. Bien entendu, cela ne protège pas forcément contre les failles des scripts PHP des utilisateurs.

Il y a également d'autres avantages, en particulier la compression, le chiffrement SSL ou la répartition de charge, mais dont la pertinence est discutable dans le cadre de l'auto-hébergement.

Ainsi, dans ce modeste billet, je me propose de vous montrer les grandes lignes de l'installation d'Apache, PHP et nginx sur Gentoo (le tout étant bien entendu adaptable à n'importe quelle distribution Linux, voire même BSD, Mac OS ou Windows). Au menu :

  • installation de Apache et PHP avec les bons useflags ;
  • configuration de nginx comme proxy inversé pour uniquement les sous-domaines que l'on souhaite ;
  • configuration de suPHP afin de cloisonner les scripts PHP des utilisateurs.

Il y a bien entendu d'autres techniques, comme le lancement des serveurs Apache et nginx dans des chroot, dont je ne parlerai pas ici (ou seulement plus tard, dans un autre billet). Après tout, résumer la sécurisation d'un serveur Web de manière exhaustive dans un billet est une entreprise ambitieuse.

Installation de apache, mod_suphp et mod_rpaf

En tant que root :

# emerge -va apache mod_suphp mod_rpaf

Avant de valider, je vous recommande fortement de jeter un œil aux useflags à la compilation de apache ; en particulier, suexec est intéressant à activer.

Le module mod_suphp est un module qui va forcer l'exécution des scripts PHP sous l'utilisateur et le groupe propriétaires du fichier, plutôt que sous l'utilisateur apache. Cela permet de faire du cloisonnement, c'est-à-dire d'éviter que l'ensemble des utilisateurs subisse les conséquences d'une faille de sécurité dans un script d'une seule personne.

Le module mod_rpaf, quant à lui, permet de logguer la bonne adresse IP source lorsqu'Apache sera derrière le reverse proxy. Sans cela, tout le trafic Web semblera provenir de localhost, ce qui rendrait le traçabilité des actions d'un méchant pirate / hacker / cracker / script kiddie / ce que vous voulez sacrément plus délicate.

Mise en place du reverse proxy

nginx

Dans /etc/nginx/nginx.conf (ou dans un fichier à part inclus par nginx.conf si vous voulez faire les choses proprement), ajoutez une directive server supplémentaire (deux si vous voulez aussi faire du HTTPS). Le fichier de configuration devrait ressembler à celui-ci :

http {
    # trucs d'origine
    server {
        # ...
    }

    # Agir comme mandataire inversé pour tous les domaines listés dans
    # la directive server_name.
    server {
        server_name user1.example.com user2.example.com;
        listen 80;
        listen [::]:80;

        location / {
            proxy_pass http://localhost:81/;
            include /etc/nginx/proxy.conf;
        }
    }

    # Même chose, mais en HTTPS
    server {
        server_name user1.example.com user2.example.com;
        listen 443;
        listen [::]:443;

        ssl_certificate /etc/nginx/ssl/wildcard.example.com.crt;
        ssl_certificate_key /etc/nginx/ssl/wildcard.example.com.key;

        location / {
            proxy_pass http://localhost:81/;
            include /etc/nginx/proxy.conf;
        }
    }
}

Même sur un site en HTTPS, la directive proxy_pass pointe vers une URL en HTTP. En effet, derrière un reverse proxy, c'est nginx qui se charge du chiffrement. Idéalement, le certificat serveur présenté pour chaque vhost devrait être différent, mais comme je n'ai pour le moment qu'une seule personne qui profite de cette configuration, je n'ai pas eu l'occasion de tester si CAcert accepte de signer des certificats « wildcard ».

Le fichier /etc/nginx/proxy.conf, s'il n'existe pas déjà, contiendra des directives spécifiques aux cas où nginx agit comme reverse proxy. Son contenu est le suivant :

proxy_redirect      off;
proxy_set_header    Host        $host;
proxy_set_header    X-Real-IP   $remote_addr;
proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size    10m;
client_body_buffer_size 128k;
proxy_connect_timeout   90;
proxy_send_timeout  90;
proxy_read_timeout  90;
proxy_buffers       32 4k;

On remarquera les directives proxy_set_header, qui permettent de modifier ou d'ajouter des en-têtes HTTP à la volée, à destination d'Apache. L'en-tête Host est nécessaire pour qu'Apache puisse faire la différence entre ses propres vhosts ; l'en-tête X-Real-IP sert à logguer la bonne IP source, pour mod_rpaf.

Apache

Du côté d'Apache, il faut lui préciser d'écouter uniquement sur la boucle locale (pour que le serveur ne soit accessible qu'en passant par nginx), sur un port inutilisé (en l'occurrence, le port 81). Pour cela, dans /etc/apache2/vhosts.d/00_default_vhost.conf, remplacer le contenu par :

<IfDefine DEFAULT_VHOST>

Listen 127.0.0.1:81
Listen [::1]:81

NameVirtualHost *:81

<VirtualHost *:81>
    ServerName localhost
    Include /etc/apache2/vhosts.d/default_vhost.include

    <IfModule mpm_peruser_module>
        ServerEnvironment apache apache
    </IfModule>
</VirtualHost>
</IfDefine>

À présent, on peut définir des VirtualHosts à sa guise, comme avec un hébergement mutualisé classique.

Enfin, dans /etc/conf.d/apache2, n'oubliez pas d'ajouter -D PHP5, -D RPAF et -D SUPHP pour activer les modules du même nom, et de retirer -D SSL_DEFAULT_VHOST, car on n'a pas besoin d'écouter en SSL. Sous d'autres distributions comme Debian, regarder du côté des commandes a2enmod, a2dismod, a2ensite et a2dissite.

Mise en place de PHP et de suPHP

Installez PHP (emerge php), au moins avec les useflags apache2 et cgi. Choisissez les autres flags à votre guise, en fonction des fonctionnalités que vous souhaitez activer.

Dans chaque déclaration de VirtualHost Apache, il suffit d'ajouter la ligne :

suPHP_UserGroup user1 users

Évidemment, cette ligne est à adapter pour que cette ligne reflète l'utilisateur et le groupe principal du propriétaire du vhost.

Ensuite, dans /etc/suphp.conf, modifiez la variable docroot. Par exemple, si tous les utilisateurs ont leurs pages HTML dans ~/html :

;Path all scripts have to be in
docroot=/home/*/html

Tout script PHP exécuté en-dehors de ce répertoire sera bloqué par suPHP. Enfin, dans le même fichier, adapter la section [handlers] pour que les chemins soient corrects :

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

Finalisation

Démarrer Apache et redémarrer nginx pour que les nouvelles configurations soient prises en compte :

# /etc/init.d/apache2 start
# /etc/init.d/nginx restart

Il ne reste plus qu'à tester. En particulier, vérifier que suPHP fonctionne bien avec une petite page PHP :

<?php echo "PHP tourne sous l'utilisateur " . exec('/usr/bin/whoami') ?>

ou encore avec un bon vieux phpinfo().

Conclusion

Cette technique est une bonne démonstration des différences entre la configuration de nginx et Apache. Bien qu'Apache soit le serveur Web le plus répandu, en particulier pour ses fonctions d'hébergement mutualisé, ce serait intéressant de mesurer les performances d'un couple Apache-nginx ainsi configuré.

Une chose qui me met relativement mal à l'aise est le fait que suPHP semble lancer les scripts PHP des utilisateurs de la même manière qu'Apache exécute des scripts CGI ; cela signifie que chaque visite d'une URL implique le lancement d'un interpréteur PHP entier, le chargement de toutes ses bibliothèques, sans évidemment parler des include() et autres require() que font les scripts eux-mêmes. Tout ceci ajoute une énorme charge supplémentaire, ce qui limite donc beaucoup l'intérêt de cette solution dès qu'on a un peu plus de trafic. Certains suggéreraient plutôt d'utiliser nginx directement avec PHP-FPM : sur le forum de nginx ou le blog d'Interfacelab en particulier.

Et c'est pas pour dire mais... je ne peux pas parler de PHP sans mettre un lien vers phpsadness.com. Quoi, je trolle ?

minicurses 0.3.2

Auteur :  x0r Publié le   Nombre de commentaires : 0
Mots-clefs : programmation c minicurses minitel

Encore une release de minicurses corrigeant quelques bugs présents dans le script ./configure de la version 0.3.1, et permettant la compilation de la bibliothèque sur des systèmes Debian.

Téléchargements

ou, toujours, pour ceux qui souhaitent récupérer la toute dernière version de développement :

% hg clone https://bitbucket.org/xtab/minicurses

Un tuto et d'autres informations sont, comme d'habitude, disponibles dans les pages de man sur le wiki.

Changelog

Correctifs des bugs suivants :

  • Recherche du header pam.h de netpbm dans netpbm/pam.h ainsi que pam.h ;
  • Le script ./configure abandonne lorsque la libnetpbm n'est pas installée ;
  • Patchs de image.c afin de rendre minicurses compilable sur des systèmes Debian, où une version antérieure à juin 2004 semble être fournie.

Pour information

La version 0.4, permettant (enfin !) d'utiliser des fenêtres, est presque prête. Cependant, je n'ai pas encore eu le temps de le tester en profondeur, car j'ai prêté mon Minitel à l'association Arise. Merci à eux pour m'avoir informé de l'impossibilité de compiler sous Debian !

Pourquoi avons-nous toujours des pilotes d'imprimantes ?

Auteur :  x0r Publié le   Nombre de commentaires : 0
Mots-clefs : essai imprimantes drivers

Il y a quelques temps, je me suis acheté une nouvelle imprimante, afin de remplacer un vieux Canon MP600 dont l'alimentation avait visiblement claqué. Je ne me sens pas encore suffisamment expérimenté pour m'amuser à dessouder et souder des fusibles dans des circuits haute tension, donc cette machine a fini par prendre la porte. Dommage, je l'aimais bien.

J'étais presque surpris de constater à quel point son remplaçant était facile à configurer sous Linux. S'agissant d'une HP, il m'a suffi d'installer hplip, puis le reste se faisait en trois clics sur l'interface d'administration de CUPS. Mon imprimante Canon, à l'inverse, ne me permettait d'imprimer qu'après une longue et pénible installation des pilotes propriétaires cnijfilter. On peut au moins dire merci à Canon d'avoir fourni un pilote, mais celui-ci est assez difficile à mettre en œuvre, et exigeait des bibliothèques archaïques comme libpng 1.2. J'ai d'ailleurs rarement réussi à sortir une page de test du premier coup avec ces pilotes.

Force est de constater qu'en 2012, nous avons toujours recours à des pilotes d'imprimante qui ne fonctionnent généralement qu'avec un modèle au pire, et une seule marque d'imprimante au mieux. Alors qu'avec l'avancée de la technologie, l'installation d'une imprimante pourrait être aussi simple que celle d'une clé USB.

Pourquoi ?

Développer un pilote d'impression vise à simplifier l'électronique de l'imprimante. En effet, sous Linux, c'est CUPS qui, au moyen de filtres, fait la conversion d'un format "bureautique" (PostScript, PDF, image bitmap...) en un format plus simple pour l'imprimante (par exemple, des séries de blocs de 16 pixels de haut). Une fois dans le bon format, c'est une backend qui prend ensuite le relais, pour faire la communication proprement dite avec l'imprimante – que ce soit via le port parallèle, le réseau, par USB... Lorsqu'on installe un pilote d'imprimante, c'est souvent un filtre, et parfois une backend, qu'on ajoute au système. À cela s'ajoute aussi un PPD (PostScript Printer Definition), qui décrit au système ce que l'imprimante sait faire, les résolutions qu'elle supporte, si elle est capable de faire du recto-verso automatique, etc. Les formats manipulés sont généralement spécifiques à ces machines, ou à une certaine gamme, mais très souvent spécifique à une certaine marque.

Mais ça, c'était intéressant lorsque l'électronique embarquée était encore chère. Les imprimantes qui prenaient directement du PostScript, qui coûtaient les yeux de la tête et qui étaient donc surtout réservées aux grandes institutions ou entreprises, est révolue ; nous avons maintenant des systèmes d'exploitation intégrés dans à peu près tout ce qu'on a de relativement intelligent. Regardez maintenant les CPU que l'on a dans nos smartphones, et qui seraient fort capables de réaliser des opérations aussi complexes que de la conversion vectoriel-bitmap. Pourquoi ne pas déléguer ce genre d'opérations à l'imprimante en créant un pilote générique ?

Si les pilotes étaient payants, cela m'aurait semblé logique que les constructeurs se soient livrés à un "verrouillage" en forçant les utilisateurs à utiliser leur pilote, s'agissant de leur gagne-pain. Mais ce n'est pas le cas, car ces pilotes sont distribués gratuitement. Il n'y a donc pas non plus réellement d'intérêt commercial à utiliser des pilotes non génériques.

Un pilote moderne est un pilote générique

C'est pourquoi je pense que développer un pilote qui fonctionne avec toutes les marques d'imprimantes, et qui soit un standard ouvert comme le PDF, pourrait être très intéressant. Cela permettrait de faire du vrai plug 'n play, tout en réduisant les coûts de développement de nouveux modèles dans l'avenir. Un tel standard permettrait de simplement brancher une imprimante en USB sous n'importe quel OS, et de n'avoir quasiment rien d'autre à faire. Ça, c'est de la facilité d'utilisation. Ce serait aussi bien un argument pour un client potentiel, qu'un argument marketing.

D'ailleurs, qui n'a jamais rêvé de se débarrasser de tous les logiciels inutiles fournis de base avec son imprimante ? Certains sont peut-être pratiques, mais on ne devrait pas avoir besoin de les installer pour pouvoir imprimer. Je n'aime pas non plus les pop-ups qui apparaissent lorsque j'imprime et qui ne sont a priori pas nécessaires. Il faut le pilote, uniquement le pilote, et rien d'autre que le pilote. Bon, après, il s'agit d'un problème inhérent à la façon dont sont « packagés » les logiciels Windows, donc je m'écarte du sujet.

Conclusion

Enfin, pour une imprimante réseau qui fonctionne avec IPP (Internet Printing Protocol), il me semble que quasiment tout le travail est fait, et qu'il suffirait juste d'implémenter le "filtre" générique au sein du firmware de la machine. Les fonctions pour demander à l'imprimante ce qu'elle sait faire existent déjà. Mon imprimante est d'ailleurs capable d'imprimer des PDF envoyés à une adresse e-mail spécifique. Voir ça dans une imprimante qui m'a coûté autour de 150 euros montre bien que le glas des pilotes spécifiques devrait bientôt sonner. Nos imprimantes sont de plus en plus souvent connectées à un réseau local, plutôt qu'à un ordinateur via USB, ce qui leur donne réellement la possibilité de proposer des fonctions qui vont dans le sens de la facilité d'utilisation. Alors, à quand les imprimantes réellement intelligentes ?

minicurses 0.3.1

Auteur :  x0r Publié le   Nombre de commentaires : 0
Mots-clefs : programmation c minicurses minitel

Cette fois, il s'agit de quelques correctifs de bugs stupides que j'ai oubliés de corriger dans la version 0.3 de minicurses. Pour le détail, lisez le Changelog.

Téléchargements

ou, toujours, pour ceux qui souhaitent récupérer la toute dernière version de développement :

% hg clone https://bitbucket.org/xtab/minicurses

Un tuto et d'autres informations sont disponibles dans les pages de man sur le wiki.

Changelog

Correctifs des bugs suivants :

  • Oubli de faire l'édition de liens avec lib_move.o ;
  • Oubli du répertoire examples dans les tarballs (les exemples n'étaient disponibles qu'en clonant le dépôt Mercurial) ;
  • Corrections de petites erreurs dans la documentation.

Du « software-defined radio » avec un récepteur DVB-T

Auteur :  x0r Publié le   Nombre de commentaires : 0
Mots-clefs : rtlsdr radio radioamateur

Image : RTLSDR en action

Les radioamateurs parmi nous ont peut-être déjà entendu parler d'un petit adaptateur DVB-T qui serait "hackable", et qui peut ainsi être transformé en récepteur radio pour n'importe quelle fréquence dans la plage 50 – 2100 MHz environ.

Voilà qui est particulièrement intéressant, car il met à la portée de tous la possibilité d'explorer ce spectre comme bon nous semble, car j'ai récupéré mon récepteur pour la modique somme de 15 euros. Comparez ça à un scanner radio portatif qui coûte généralement dans les 200 euros... Évidemment, la bande des 3 – 30 MHz m'est inaccessible (ce qui est dommage, car les stations de nombres m'ont l'air fun à écouter), mais c'est déjà pas mal.

Il semblerait que la législation française est assez restrictive quant aux fréquences qu'on a le droit d'écouter avec ces trucs-là (le principal problème étant que les infos sont difficiles à trouver), donc faites gaffe et évitez de parler de ce que vous entendez lorsque vous vous baladez hors des fréquences radioamateur (et ne transmettez pas dessus, ce serait encore plus idiot :]). Je vous donne donc ces instructions en partant du principe que vous ne fassiez pas de bêtises. :)

Comment installer les logiciels nécessaires

Je montrerai la marche à suivre sur Gentoo encore une fois. Les utilisateurs d'autres distributions pourront suivre les instructions sur la page d'OSMO-SDR concernant rtl-sdr. Vous y trouverez également la liste des modèles de clés DVB-T qui fonctionnent.

Ajoutez les lignes suivantes à votre /etc/portage/package.keywords :

net-wireless/gnuradio
dev-lang/orc
dev-python/pyqwt
=net-wireless/rtl-sdr-9999 **
=net-wireless/gr-osmosdr-9999 **

puis dans /etc/portage/package.use :

net-wireless/gnuradio grc qt4 utils wavelet

Enfin, tapez la commande emerge gnuradio gr-osmosdr rtl-sdr, et une fois fini de compiler, vous voilà avec les softs nécessaires pour faire mumuse. Ou presque.

D'abord, lancez l'outil rtl_test qui vous permettra de tester la clé DVB-T. Si vous obtenez Reading samples in async mode..., c'est que votre SDR marche.

Enfin, pour avoir une interface un peu conviviale (et encore) pour utiliser cette SDR, vous pouvez par exemple installer multimode RX. Pour ce faire, faites un svn checkout https://www.cgran.org/svn/projects/multimode, puis lancez le multimode.rb dans le répertoire multimode/trunk. Et voilà !