Tag - web

Entries feed

Saturday, May 12 2018

HAProxy et compression Gzip

Pour accélérer l'accès à vos sites et services web propulsées par HAProxy, il est facile d'activer la compression automatique des contenus texte comme HTML, CSS, JS (s'ils ne le sont pas déjà par le serveur sous-jacent).

Rien de plus simple, il suffit d'ajouter les 2 lignes suivantes dans la section defaults de la configuration d'HAProxy :

compression algo gzip
compression type text/html text/plain text/xml text/css text/javascript application/javascript application/json text/json

et redémarrer HAProxy par une commande comme

/etc/init.d/haproxy restart

" Avec cette fonctionnalité activée, vous devez désormais voir dans les en-têtes des transmissions entre le serveur web et le navigateur que le contenu est compressé avec gzip (vérification facile avec le module "Développeur" de Firefox par exemple, onglet "Réseau") :

Content-Encoding: gzip

Sunday, May 6 2018

HAProxy in front of Unicorn

Unicorn, the "Rack HTTP server for fast clients", is really for "fast clients". Fast clients mean clients on the same local network (or even same machine) with very low latency and near-zero chance of packets loss. Slow clients are the usual web users: far away from the server. Unicorn has been designed to serve fast clients and if you try to use it to serve slow clients then the performance may be dismal. This is why the Unicorn developers recommend to use Nginx in front of Unicorn. To know more on the Unicorn philosophy, visit this page.

I recently tried to use HAProxy in front of Unicorn and was disappointed to see that:

  • the system was slow and unresponsive
  • a lot of 502 Gateway errors popped up for seemingly no reason (and this popped up unconsistently)

I came to the conclusion that the default configuration of HAProxy was not appropriate for Unicorn. After some web digging, I discovered the "http-buffer-request" option.

Here is what the HAProxy 1.8 documentation says about the "http-buffer-request" option :

It is sometimes desirable to wait for the body of an HTTP request before taking a decision. This is what is being done by "balance url_param" for example. The first use case is to buffer requests from slow clients before connecting to the server. Another use case consists in taking the routing decision based on the request body's contents. This option placed in a frontend or backend forces the HTTP processing to wait until either the wholebody is received, or the request buffer is full, or the first chunk is complete in case of chunked encoding. It can have undesired side effects with some applications abusing HTTP by expecting unbuffered transmissions between the frontend and the backend, so this should definitely not be used by default.

It seems to be the best fit with Unicorn's philosophy! Let's activate the option in each backend corresponding to a Unicorn-run application:

backend unicorn-app
	option http-buffer-request
	server unicorn-app 1.2.3.4:3000

and restart HAProxy:

/etc/init.d/haproxy reload

Sunday, May 15 2016

Scripts Munin pour Jirafeau

Si vous utilisez Jirafeau sur votre serveur pour partager des documents, vous serez peut-être contents d'avoir un suivi rapide du nombre de fichiers partagés et du volume total qu'occupent les fichiers de Jirafeau sur votre serveur.

jirafeau_nb-day.png

Rien de plus simple avec Munin !

Créons un premier plugin pour mesurer le nombre de fichiers

Sur la machine porteuse du client (node) Munin, nous allons créer un nouveau script dans le fichier /etc/munin/plugins/jirafeau-nb :

#!/bin/sh
case $1 in
   config)
        cat <<'EOM'
graph_title Jirafeau, number of files
graph_vlabel Nb of files
nbfiles.label Nb of files
EOM
        exit 0;;
esac

printf "nbfiles.value "
echo $(($(find /path/to/jirafeau/var-abcdefghiklmnop/files -type f |wc -l)*1/2))

Il faudra rendre le script exécutable par

chmod a+x /etc/munin/plugins/jirafeau-nb

puis le tester avec munin par

munin-run jirafeau-nb

et enfin redémarrer le client Munin

service munin-node restart

Créons un second plugin pour mesurer l'espace total occupé par les fichiers

On pratique comme au-dessus pour le script dont le contenu est :

#!/bin/sh
case $1 in
   config)
        cat <<'EOM'
graph_title Jirafeau, size of files in Kb
graph_vlabel Size of files in Kb
sizefiles.label Size
EOM
        exit 0;;
esac

printf "sizefiles.value "
du -hsk /path/to/jirafeau/var-abcdefghiklmnop/files |cut -f1

Et hop le tour est joué !

Saturday, May 14 2016

RIP demo.ovh.eu, alternatives open source

Pour partager des fichiers temporairement avec des amis/collaborateurs/proches, j'utilisais fréquemment l'outil demo.ovh.eu. Cet outil avait pour avantage d'être "share & forget" ce qui est idéal pour un document partagé que l'on ne veut pas conserver soi-même.

Malheureusement OVH a décidé d'éteindre demo.ovh.eu et d'inciter les gens à utiliser la plateforme Hubic (qui est très bien mais il est pénible de se connecter et de partager une photo que l'on ne veut pas conserver et qui peut être supprimée quelques jours après le partage).

J'ai donc cherché un outil équivalent Open Source et suis tombé sur Uguu qui répondait à mon cahier des charges mais qui était compliqué à déployer car il contenait tous ses paramètres "en dur" dans le code.

J'ai donc 'forké' Uguu et livre la communauté une version d'Uguu plus facile à déployer et paramétrer : https://github.com/pierre-alain-b/Uguu/

Screenshot_2016-05-14_12-16-35.png

Et d'ailleurs, le mainteneur de la version initiale a accepté le 'Pull request' et a fusionné toutes mes modifications dans l'arbre principal de développement, il est peut-être donc même préférable de se rendre ici : https://github.com/nokonoko/Uguu.

En espérant qu'il puisse rendre service à d'autres !

Et si Uguu ne vous convient pas, il y a aussi :

Merci à François B. pour ces suggestions !

Monday, February 1 2016

Letsencrypt : renouveler intelligemment malgré les limites de la bêta

Depuis Mai 2016, le service Let's Encrypt n'est plus en bêta et les limitations de la bêta évoquées ici ne sont plus mordantes !

Depuis Mai 2016, le client letsencrypt est devenu certbot (https://certbot.eff.org/) ! les commandes évoquées plus bas sont sans doute valables en remplaçant letsencrypt-auto par certbot ! Il faut aussi noter que le client certbot dispose désormais de fonctions de renouvellement plus élaborées qu'auparavant ! Cet article est conservé pour archive et inspiration.

Letsencrypt se présente comme le futur du chiffrement sur internet. Bien que déjà fonctionnel, le service offert est encore en phase de test (bêta) et par conséquent certaines contraintes s'appliquent aux usagers testeurs. Parmi ces contraintes, les 2 les plus bloquantes sont :

  • pas de système de renouvellement automatique pour le moment (c'est prévu, vivement !)
  • limite imposée de 5 certificats par domaine par semaine (i.e. qu'il faut attendre une semaine pour pouvoir créer des certificats pour un nouveau sous-domaine d'un domaine qui possède déjà 5 certificats de sous-domaine)

Pour Letsencrypt qui émet des certificats à la validité courte de 3 mois, le renouvellement doit être fait régulièrement et il correspond ni plus ni moins à l'édition d'un nouveau certificat.

La contrainte évoquée plus haut est bloquante pour le renouvellement : en effet il n'est pas possible de renouveler plus de 5 certificats par semaine pour un même domaine...

Principe du renouvellement à terme

A terme, un système de renouvellement automatique sera intégré à Letsencrypt et il suffira de lancer tous les 2 mois (par exemple via cron) la commande demandant le renouvellement de tous les certificats en approche de péremption. Vivement cela !

Une méthode fonctionnelle aujourd'hui

Aujourd'hui, à nous de créer l'automatisme qui va bien et qui permettra aussi de contourner la limite des 5 renouvellements par semaine (en réalisant un roulement). Nous allons détailler ici un script qui, appelé chaque semaine, :

  • détecte les certificats qui ont moins de 30 jours de validité (2592000 secondes)
  • demande le renouvellement de chaque certificat dans ce cas

Voici le code du script :

#!/bin/bash
declare -a list=(
"/etc/letsencrypt/live/domaine1.tld;certbot certonly --renew-by-default -a webroot --webroot-path /path/to/domaine1/website -d domaine1.tld -d www.domaine1.tld"
"/etc/letsencrypt/live/sous.domaine1.tld;certbot certonly --renew-by-default -a webroot --webroot-path /path/to/sous/domaine1/website -d sous.domaine1.tld -d www.sous.domaine1.tld"
"/etc/letsencrypt/live/domaine2.tld;certbot certonly --renew-by-default -a webroot --webroot-path /path/to/domaine2/website -d domaine2.tld -d www.domaine2.tld"
)

for line in "${list[@]}"
do
	IFS=";" read -ra stuff <<< $line
	folder=${stuff[0]}
	command=${stuff[1]}
        if openssl x509 -checkend 2592000 -noout -in $folder/fullchain.pem
        then
                echo "Nothing to do for $folder"
        else
                $command
                rm -f $folder/total.pem
                cat $folder/fullchain.pem $folder/privkey.pem > $folder/total.pem
                echo "Done for $folder"
        fi
done

Rapidement :

  • on commence par lister dans un tableau chaque domaine dont il faut s'occuper (en indiquant le chemin correspondant à ce domaine dans le dossier /etc/letsencrypt/live/) et, séparée par ";", la commande Letsencrypt utilisée pour le renouvellement. Il s'agit de la même commande que pour la création initiale du certificat sauf que l'on ajoute l'option "--renew-by-default"
  • on parcourt ensuite le tableau élément par élément :
    • en vérifiant l'âge du certificat (avec openssl)
    • en lançant la commande de renouvellement s'il est trop proche de la péremption

Si l'on utilise Letsencrypt avec Pound, on ajoutera 2 actions :

  • remplacer le fichier .pem utilisé par Pound par la nouvelle version obtenue
  • redémarrer Pound

Le script devient alors :

#!/bin/bash
declare -a list=(
"/etc/letsencrypt/live/domaine1.tld;certbot certonly --renew-by-default -a webroot --webroot-path /path/to/domaine1/website -d domaine1.tld -d www.domaine1.tld"
"/etc/letsencrypt/live/sous.domaine1.tld;certbot --renew-by-default -a webroot --webroot-path /path/to/sous/domaine1/website -d sous.domaine1.tld -d www.sous.domaine1.tld"
"/etc/letsencrypt/live/domaine2.tld;certbot certonly --renew-by-default -a webroot --webroot-path /path/to/domaine2/website -d domaine2.tld -d www.domaine2.tld"
)

for line in "${list[@]}"
do
	IFS=";" read -ra stuff <<< $line
	folder=${stuff[0]}
	command=${stuff[1]}
        if openssl x509 -checkend 2592000 -noout -in $folder/fullchain.pem
        then
                echo "Nothing to do for $folder"
        else
                $command
                rm -f $folder/total.pem
                cat $folder/fullchain.pem $folder/privkey.pem > $folder/total.pem
                echo "Done for $folder"
        fi
done
service pound restart

Archive

Dans une ancienne version de ce billet, on utilisait le morceau de script suivant se basant sur la date de dernière modification du fichier de certificat plutôt que d'utiliser openssl, mais c'est moins propre. Pour archivage, voici le test qui était alors utilisé (renouvellement des certificats plus vieux que 60 jours) :

	timesincelastchange=$(expr $(expr $(date +%s) - $(date +%s -r $folder/fullchain.pem )) / 86400)
	if [ $timesincelastchange -gt 60 ]
	then
		$command
		echo "Done for $folder"
	else
		echo "Nothing to do for $folder"
	fi

Monday, December 28 2015

Let's Encrypt et Gitlab

Pour permettre la génération d'un certificat avec Let's Encrypt pour Gitlab, il m'a été nécessaire d'effectuer les opérations suivantes :

  • Modifier la gestion des fichiers d'assets dans gitlab/config/environments/production.rb,
config.serve_static_assets = true
  • Redémarrer Gitlab par
service gitlab restart
  • Puis effectuer la génération du certificat avec le script Let's Encrypt en pointant le webroot vers le dossier public :
certbot certonly -a webroot --webroot-path /path/to/gitlab/public -d mongit.domaine.tld

Tuesday, March 10 2015

Se maintenir au top de la sécurité SSL/TLS en faisant le ménage sur sa suite de chiffrements (cipher suite)

Le monde de la sécurité informatique est en perpétuelle évolution et les chiffrements jugés solides à un instant T ne le sont plus forcément à T+1 ! En novembre dernier, je proposais un paramétrage qui permettait alors d'obtenir la note maximale sur Qualys SSLLabs https://www.ssllabs.com (ce qui n'est bien sûr pas une fin en soi mais qui est un indicateur facile du niveau de sécurité atteint... si l'on fait confiance aux gens de Qualys).

Cependant, les attaques sur l'algorithme de chiffrement RC4 se sont multipliées et celui-ci a été récemment déclaré comme faible et déconseillé donc pour l'établissement de connexions sécurisées. Diable, il faut donc mettre à jour la suite de chiffrements utilisés pour écarter ce chiffrement !

Heureusement, les gens de Mozilla (encore eux !) nous rendent la tâche aisée en publiant une série de 3 suites de chiffrement selon le niveau de sécurité et le niveau de rétro-compatibilité souhaités (avec de vieux navigateurs / de vieux OS) : https://wiki.mozilla.org/Security/Server_Side_TLS

En date du 10 mars, la suite de chiffrement conseillée pour le plus haut niveau de sécurité est la suivante :

Ciphersuite: ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK

Voilà qui devrait aider les administrateurs à optimiser la sécurité des connexions SSL/TLS proposées par leurs serveurs !

Rails 4.2, Thin et "Internal server error"

Un récent appel à une application développée avec Rails 4.2, et servie par Thin, m'a conduit à une erreur "500 Internal Server Error" dont je ne trouvais de trace dans aucun log de l'application, et aucun log pertinent pour le "load balancer" (pound) placé frontalement au-dessus de cette application.

Le plus étrange était de ne voir aucune trace dans les logs de Rails et notamment de ne voir aucune confirmation de la bonne réception de la requête par l'application Rails.

Après avoir exploré, ad nauseum, tous les potentiels blocages en amont de l'application Rails, j'ai fini par remplacer Thin par Webrick pour servir l'application : et là miracle, un message d'erreur plus explicite m'a été retourné en lieu et place de l'erreur 500 : "Unexpected error while processing request: Too many open files - Maximum file multiparts in content reached".

J'ai alors trouvé cette référence : http://stackoverflow.com/questions/27773368/rails-4-2-internal-server-error-with-maximum-file-multiparts-in-content-reached et celle-ci : https://github.com/rack/rack/pull/814

La première propose de contourner le problème en désactivant la limite sur le nombre d'éléments multipart en ajoutant cette ligne dans un fichier d'initialisation :

Rack::Utils.multipart_part_limit = 0

La seconde référence étudie la raison de ce comportement avec une mauvaise approche du comptage des balises dans le formulaire : "However, our form only had one file input. It had several hundred other form elements. Each was counted as a file.". Le commit en faute a été repéré et cela devrait être corrigé dans la prochaine version de Rails/Rake !

Tuesday, January 20 2015

Orange, le fournisseur d'accès à internet, grand complice des arnaques du web !

Oyez oyez, abonnés du fournisseur d'accès à internet Orange, ne vous laissez pas prendre dans le piège tendu par quelques vils commerciaux d'Orange...

Par défaut, les abonnés Livebox (fibre et ADSL) ont les options Contact+ et Internet+ activées. Concrètement, il s'agit d'options "minitelesques" : elles permettent à certains sites de vous facturer le temps de connexion à la minute, cette somme venant apparaître sur la facture internet ! Et bien sûr les sites en question n'affichent qu'en tout petit la mention "accès payant" (trop contents d'attirer le chalant !). Sans crier gare, vous voilà connectés à un site qui, minute après minute, vous soutire euro après euro... On se croirait revenu au temps du Minitel...

C'est à mes yeux un scandale qu'Orange se rende complice d'une telle pratique. Ce n'est pas un service "pratique" pour l'abonné mais bel et bien une manière d'attraper quelques euros auprès du public le plus crédule qui ne comprendra pas bien ce que cette somme fait sur sa facture Orange, et payera en secouant la tête la plupart du temps.

Heureusement, il est assez facile de désactiver les 2 options (Contact+ et Internet+) en ligne sur l'interface de gestion du compte Livebox d'Orange dans la section "Mes services" :

2015-01-20_09_38_58-Mes_services_Internet_-_Espace_Client_Orange_-__internet_.png

A faire d'urgence, et aider les gens autour de soi à faire ! On ne peut pas utiliser Internet et craindre, au détour de chaque page, une facture salée d'Orange !

Sunday, January 4 2015

Tomato, VLAN & WAN: this is poorly documented!

It is not obvious to change the VLAN for the WAN port on a router using the Tomato firmware. This might be a necessity when the PPPoE infrastructure of your internet provider requires such a setting.

This is the case in France with the Orange provider (not for DSL but for fiber access): the internet traffic should transit on the VLAN 835.

To do so with Tomato:

  1. choose the PPPoE connection for the WAN (do not forget username and password if needed)
  2. get to the Advanced settings, VLAN page and change the VID for the WAN to correspond to the required VLAN. With some routers (such as the Asus RT-N16), changing the VID does not work and you should in fact add an offset and change the VLAN number. As an example, for VLAN 835, you have to choose a VID offset of 832 and set the WAN to the VLAN 3. Then VID should automatically change to 835.
  3. save and reboot, the WAN connection should now work

You can see below the appropriate setting for a WAN over VLAN 835 as requested by French Orange provider.

vlan835-orange-tomato.png

Hope this helps!

Fibre FTTH Orange et routeur avec micrologiciel Tomato : l'astuce du VLAN

Si la Fibre Optique FTTH d'Orange offre une bonne qualité de service (du moins dans mon expérience), la LiveBox pro fournie est selon moi une catastrophe : instable, ne re-démarrant pas toute seule en cas de coupure de courant, possibilités de paramétrage faibles...

J'ai très vite décidé de la renvoyer à Orange et de la remplacer par un routeur Asus RT-N16 équipé du micrologiciel Tomato (by Shibby). J'ai donc conservé le convertisseur fibre/RJ45 mais ai ré-envoyé la Livebox elle-même à l'opérateur (ce qui m'a valu d'ailleurs la disparition des frais de location de modem sur ma facture).

Je n'étais cependant pas au bout de mes peines car l'infrastructure de la Fibre Orange nécessite un paramétrage particulier de VLAN pour fonctionner.

Comme expliqué ici, le vénérable VPI/VCI 8/35 de l'ADSL a été remplacé en Fibre chez Orange par un VLAN 835. Concrètement, le routeur doit s'enregistrer et communiquer sur ce VLAN 835 pour discuter avec les équipements réseaux d'Orange et obtenir une adresse IP puis faire transiter le trafic vers internet.

Il faut donc apprendre au micrologiciel Tomato que le port WAN du routeur (celui connecté filairement au convertisseur fibre/RJ45) doit communiquer exclusivement sur ce VLAN.

On choisit le PPPoE pour le WAN...

Dans l'onglet principal, on choisit une connexion PPPoE et on saisit le nom d'utilisateur 'fti/xxxxx' et le mot de passe pour la connexion.

... et on bascule le WAN dans le VLAN 835

Il faut alors se rendre dans les options avancées, catégorie VLAN.

  • Dans VID offset, on saisit : 832 (c'est le multiple de 16 le plus proche de 835).
  • Puis on sélectionne la ligne correspondant au VLAN du WAN et on on choisit le numéro de VLAN 3. Automatiquement, le VID (VLAN ID) doit devenir 835 (832 d'offset + 3).
  • Il ne faut pas oublier également de cocher la case 'Tagged' pour demander au retour de bien prendre en compte le trafic 'marqué' comme appartenant au VLAN 835.

vlan835-orange-tomato.png

Et voilà le tour est joué. On sauvegarde, le routeur inscrit ces paramètres dans sa nvram et redémarre. On voit que le WAN PPPoE parvient à se connecter à internet et le routeur distribue internet à tous les postes du réseau qui le référencent comme passerelle.

A noter : Sur ASUS RT-N16, il a été nécessaire d'utiliser l'Offset. Il semblerait que sur des modèles de routeur plus récents, il ne soit pas nécessaire de changer l'offset mais seulement de modifier manuellement le VID.

Thursday, November 7 2013

De l'acceptabilité d'un "cloud" non maîtrisé

Aujourd'hui, le mot de "cloud" est dans toutes les bouches dans tous les domaines : on proposera au photographe d'envoyer ses photos dans le "cloud", aux étudiants de travailler de manière collaborative grâce au "cloud", aux professionnels d'embrasser ces nouvelles technologies qui permettront des gains d'efficacité et même nos politiques saisissent la tendance et promettent de faire de la France un meneur dans le domaine...

Il y a pourtant d'irréductibles libristes qui prônent l'auto-hébergement et le rejet des plateformes privatrices et avancent pour cela de nombreux arguments (qui me semblent pertinents, vous vous en doutiez) et posent de nombreuses questions.

Revenons ici sur l'une de ces questions : quelle est l'acceptabilité d'une solution "cloud" hébergée par des acteurs non connus, dans des infrastructures informatiques distantes et non localisées ?

Un acteur raisonnable jugera "acceptable" toute technologie dont le bénéfice dépasse le risque et qui apporte un gain d'utilité (par ex. une meilleure efficacité, une meilleure sécurité). Le même acteur refuserait une technologie si le gain (par ex. gain de temps) risque de devenir négatif avec une forte probabilité. C'est finalement l'espérance du gain qui doit être prise en compte pour accepter ou non la nouvelle technologie.

Je prétends que l'espérance de gain est négative avec les solutions "cloud" sur lesquelles aucun contrôle n'est possible.

Pour représenter l'acceptabilité, traçons deux axes :

  • en abscisse, le degré de connaissance technique, compréhension d'une technologie
  • en ordonnée, le degré de délégation pour la mise en place de la technologie

Acceptabilité du risque

On peut découper cet espace en 4 zones :

  • En bas à gauche, nous avons les technologies connues, comprises et réalisées complètement sans délégation à un tiers. C'est la situation optimale car le risque est alors évalué et les mesures appropriées peuvent être prises pour le réduire.
  • En haut à gauche, ce sont les technologies que l'on maîtrise parfaitement et que l'on délègue toutefois à un tiers. La parfaite connaissance permet toutefois de s'assurer de la bonne réalisation par le tiers et cela reste encore acceptable.
  • En bas à droite, la technologie n'est pas du tout comprise mais elle est déployée localement. Le niveau de risque est important mais il est assumé localement. Un effort de formation permettra de revenir vers le quadrant "en bas à gauche". Ou alors il sera décidé de sous-traiter, et on arrive alors dans le dernier quadrant...
  • En haut à droite, la technologie n'est pas comprise et elle est déléguée à un tiers. Tout contrôle est alors illusoire et seule la confiance dans le tiers permet au système de tenir. C'est la situation la plus inacceptable puisque jugement et liberté disparaissent, le tiers devient tout-puissant dans le système.

A mes yeux, la sauvegarde classique ("à l'ancienne" avec par exemple un disque dur externe chez soi, ou alors en déployant un NAS local) appartient au quadrant le plus favorable (en bas à gauche). Les solutions de type "cloud" appartiennent de plus en plus au quadrant le plus défavorable "en haut à droite". En effet, pour simplifier la vie de l'utilisateur, on ne lui donne aucun détail technique et on lui propose des interfaces simplifiées, fonctionnelles seulement au travers de clients (certes bien léchés) non extensibles et pas parfaitement intégrables. Nous sommes alors dans la situation la plus inacceptable car la plus risquée et privatrice de liberté.

Acceptabilité du risque

Il y a toutefois des offres un peu plus responsables : Hubic d'OVH par exemple... A minima, l'utilisateur saura où ses données sont stockées (localisation géographique des datacenters), il connaît la technologie sous-jacente (OpenStack Swift) et une API (qui espérons-le va encore se développer) permet un certain niveau de contrôle. Ce n'est pas suffisant mais c'est un bon début !

Il est indispensable, me semble-t-il, d'éduquer les utilisateurs sur les risques d'un "cloud" non maîtrisé (pertes de données sans recours, aucune garantie autre que celle du marketing sur le soin prêté aux données, accès aux données par des tiers, erreurs inattendues...).

Stocker ses données dans des infrastructures dédiées et protégées est une très bonne démarche, mais elle ne doit absolument pas se faire en perdant de vue la compréhension des mécanismes en place, en renonçant à leur contrôle et en "offrant les clés de sa vie" à des tiers à qui l'on ne peut faire que moyennement confiance !

Sunday, October 27 2013

Fail2ban veille sur Dotclear, Wordpress et Piwik

Avec les bonnes règles, on peut demander à fail2ban de surveiller un certain nombre de services et de repérer toute tentative d'accès frauduleuse par force brute (i.e. essai de nombreuses combinaisons utilisateur/mot de passe possibles).

On crée d'abord un nouveau fichier de filtre dans /etc/fail2ban/filter.d/bruteforce.conf qui contient :

[Definition]
failregex = <insert here a regex to match in the log files> 
ignoreregex =

Il ne reste plus qu'à spécifier les règles (par expression régulière) nécessaires pour détecter les comportements coupables !

Pour protéger dotclear par exemple, on utilisera la règle suivante :

monblog.mondomain.tld:80 <HOST> - - .* "POST /admin/auth.php HTTP/1.1" 200

Pour Wordpress, on pourra utiliser celle-là :

monblog.mondomain.tld:80 <HOST> - - .* "POST /wp-login.php HTTP/1.1" 200

Et pour Piwik, celle-ci pourra faire l'affaire :

monpiwik.mondomain.tld:80 <HOST> - - .* "POST / HTTP/1.1" 200

Les dernières étapes consistent à ajouter une section dans le fichier /etc/fail2ban/jail.local pour utiliser ces nouveaux filtres et surveiller le fichier de log d'apache !

[apache-services-bruteforce]
enabled  = true
port     = http,https
filter   = bruteforce
logpath = /var/log/apache2/other_vhosts_access.log
maxretry = 8

Sunday, October 20 2013

fail2ban et IPv6

Fail2ban est un outil indispensable pour surveiller les tentatives d'accès frauduleuses à des serveurs. Quand un nombre déterminé d'actions offensantes ont été vues (dans les logs de vos serveurs SSH, FTP, HTTP...), alors fail2ban bannit l'IP mal intentionnée (grâce à iptables).

Malheureusment, la version 0.8.6 de fail2ban ne supporte que l'IPv4. Des travaux sont en cours à ce sujet et la version 0.9 (qui sera libérée bientôt) devrait contenir tout le nécessaire. En attendant, il existe un petit script que l'on peut déployer en suivant les instructions détaillées ici : http://www.crazyws.fr/dev/systeme/fail2ban-et-ipv6-sur-votre-serveur-debian-squeeze-MG970.html

Pour archive, je recopie aussi les grandes étapes à effectuer :

cd /root
wget http://thanatos.trollprod.org/sousites/fail2banv6/fail2ban-ipv6.tar.bz2
mkdir fail2ban-ipv6
tar xvjf fail2ban-ipv6.tar.bz2 -C fail2ban-ipv6

puis on sauvegarde les fichiers qui ont être patchés :

cd /usr/share/fail2ban/server
cp filter.py filter.py.withoutipv6
cp failregex.py failregex.py.withoutipv6

on teste le déploiement du patch :

cd /usr/share/fail2ban/server/
patch -p0 --dry-run < /root/fail2ban-ipv6/patchfilter.patch
patch -p0 --dry-run < /root/fail2ban-ipv6/regex.patch

puis on l'applique pour de vrai :

patch -p0 < /root/fail2ban-ipv6/patchfilter.patch
patch -p0 < /root/fail2ban-ipv6/regex.patch

et on rajoute les fichiers de configuration nécessaires :

cp /root/fail2ban-ipv6/ip64tables.sh /usr/bin/
chmod 755 /usr/bin/ip64tables.sh
cp /root/fail2ban-ipv6/iptables46-multiport.conf /etc/fail2ban/action.d/
chmod 644 /etc/fail2ban/action.d/iptables46-multiport.conf

On peut alors re-paramétrer les services à surveiller...

Pour SSH par exemple :

[ssh]
enabled = true
filter = sshd
action = iptables46-multiport[name=ssh, port=ssh, protocol=tcp]
logpath = /var/log/auth.log
maxretry = 6

ou pour Apache (en utilisant la spécificité du multi-port) :

[apache]
enabled = true
filter = http,https
action = iptables46-multiport[name=ssh, port="http,https", protocol=tcp]
logpath = /var/log/apache*/*error.log
maxretry = 6

Friday, October 18 2013

Votre service est-il en ligne ou hors ligne ? Le savoir sans délai avec Monit.

La grande frousse pour de nombreux administrateurs (enfin j'imagine), c'est la panne d'un système critique (généralement un samedi matin) qui passe inaperçue... Je vais vous présenter ici le petit utilitaire 'monit' qui, tout simplement, vous permettra de recevoir des notifications en cas de panne d'un de vos services. Et d'effectuer un ensemble de réactions ! C'est très certainement l'allié indispensable aux côtés de Munin que je présentais dernièrement !

Monit (à ne pas confondre avec M/Monit qui est son grand-frère non libre) est un ingénieux petit logiciel auquel on donne des missions de surveillance et des actions à effectuer lorsque certains comportements sont constatés. Je ne résiste pas à la tentation de recopier ici la bannière trouvée sur le site de monit : Monit, barking at daemons!

Un cas typique d'utilisation est le suivant : vous avez un processus P qui doit tourner sur votre machine mais que vous craignez voir s'arrêter... Vous pouvez demander à monit de vérifier toutes les X minutes que ce processus tourne toujours et le redémarrer si monit le trouve éteint. Dans ce cas, vous exécuterez monit sur le serveur qui propulse le processus à surveiller.

Autre scénario : votre serveur HTTP tourne sur l'adresse 2000:aaaa:bbbb:: et le port 80 et vous voulez vous assurer qu'il est toujours joignable. Monit peut se connecter à cette IP (ping) et dialoguer avec le service (voire même effectuer des requêtes HTTP complètes et évaluer la réponse) pour déterminer s'il est vivant. Et cela peut également fonctionner pour surveiller un FTP, un IMAP, un serveur OpenVPN... Dans ce cas, on hébergera monit sur le serveur local ou bien à distance si l'on souhaite également détecter une perte de connectivité (attention toutefois au cas de la coupure du serveur chargé de la surveillance !).

Et Monit permet bien plus encore : vérifier la présence d'un fichier, vérifier les droits sur un dossier, vérifier que la charge CPU ou la mémoire vive n'ont pas éteint des niveaux trop élevés et agir si nécessaire... Monit voit et agit !

L'installation de monit s'effectue (sous Debian) par un classique :

aptitude install monit

Pour le paramétrage, on parcourra d'abord /etc/monit/monitrc :

# Vérifier l'état des services toutes les 2 minutes
set daemon 120
# Raconter le quotidien dans un fichier
set logfile /var/log/monit.log
# Communiquer avec l'ami SMTP pour envoyer des courriels d'alerte
set mailserver monsmtp.domain.tld username "Moi" password "MonMDP" using tlsv1
# Choisir l'expéditeur de son choix quand Monit nous parle
set mail-format { from: monit@monserveur.domain.tld }
# Choisir à qui les alertes sont envoyées
set alert mon@courriel.fr
# Inclure les éléments de configuration compris dans /etc/monit/conf.d/
include /etc/monit/conf.d/*

On pourra s'occuper du ménage concernant le fichier de log (/var/log/monit.log) à l'aide de logrotate comme expliqué ici (section 'envie de rotation?').

Entrons alors dans le vif du paramétrage...

check host monsite.domain.tld with address a.b.c.d
 if failed icmp type echo count 3 with timeout 3 seconds then alert
 if failed port 80 protocol http with timeout 15 seconds then alert
 if failed port 1194 type udp with timeout 15 seconds then alert

La routine ci-dessous ne fait qu'envoyer des alertes : (i) si le serveur ne répond plus au ping, (ii) si un serveur web ne répond plus sur le port 80 et (iii) si un service ne répond pas en UDP sur le port 1194 (OpenVPN par exemple). Monit exécute chaque test les uns après les autres. Lorsqu'un test échoue au sein d'un groupe alors les tests suivants ne sont pas effectués. Monit envoie automatiquement un message lorsque le service est perdu et prévient également (c'est bien aimable de sa part) quand le service est rétabli.

Plus ardu :

check process apache with pidfile /usr/local/apache/logs/httpd.pid
  start program = "/etc/init.d/httpd start" with timeout 60 seconds
  stop program  = "/etc/init.d/httpd stop"
  if cpu > 60% for 2 cycles then alert
  if cpu > 80% for 5 cycles then restart
  if totalmem > 200.0 MB for 5 cycles then restart
  if children > 250 then restart
  if loadavg(5min) greater than 10 for 8 cycles then stop
  if failed host www.tildeslash.com port 80 protocol http
      and request "/somefile.html"
      then restart
  if failed port 443 type tcpssl protocol http
     with timeout 15 seconds
     then restart
  if 3 restarts within 5 cycles then timeout
  depends on apache_bin
  group server

Le script ci-dessous adopte le comportement suivant :

  • une alerte est émise sur la charge CPU est supérieure à 60% au cours de 2 vérifications successives
  • le serveur Apache est redémarré si la charge CPU est supérieure à 80% au cours de 5 vérifications successives
  • le serveur Apache est redémarré si la mémoire consommée est supérieure à 200 MB au cours de 5 vérifications successives
  • le serveur Apache est redémarré si plus de 250 processus fils sont détectés
  • le serveur Apache est stoppé (pas redémarré) si la charge est supérieure à 10 pendant 8 vérifications
  • le serveur Apache est redémarré si l'hôte www.tildeslash.com ou l'appel du fichier somefile.html échouent
  • le serveur Apache est redémarré si l'accès en HTTPS n'est pas disponible
  • et enfin si 3 redémarrages sont constatés en 5 cycles alors on passe à une alerte de niveau supérieur (timeout)

Je ne couvre ici qu'une faible part des possibles et je vous invite à explorer ! Un superbe outil à mettre dans toutes les mains !

Forcer la bascule vers HTTPS derrière un 'reverse proxy'

Pound (déjà évoqué à de nombreuses reprises dans ces lignes) est un 'reverse proxy' (et aussi un 'load balancer' si on souhaite utiliser ces fonctionnalités) très efficace... Il peut notamment gérer toutes les connexions en HTTPS vers vos serveurs web, les décrypter et ensuite les distribuer (bien sûr ce n'est à faire que sur un réseau local hermétique aux oreilles indiscrètes !) en HTTP à vos différents serveurs.

Selon les configurations, on acceptera les connexions en HTTP ou HTTPS ou seulement l'une ou l'autre. Parfois, on souhaite récupérer les utilisateurs qui se connectent en HTTP et les renvoyer automatiquement vers HTTPS.

Il faut alors demander au serveur web sous-jacent de réécrire les adresses, ce qui, sous Apache, se fait de la façon suivante :

        RewriteEngine on
        RewriteCond %{HTTP:X-Forwarded-Proto} !https
        RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R,L]

On notera que la réécriture ne s'applique que dans les cas où le drapeau 'X-Forwarded-Proto' n'est pas déjà HTTPS.

On peut bien sûr restreindre ce paramétrage à certains dossiers seulement (même si généraliser le HTTPS aujourd'hui serait une bonne pratique).

Pour restreindre cette ré-écrire (et donc ne forcer la connexion en HTTPS) que sur certains dossiers, on pourra inclure ces instructions dans un champ Directory :

        <Directory /var/www/appli/admin>
                RewriteEngine on
                RewriteCond %{HTTP:X-Forwarded-Proto} !https
                RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
        </Directory>

Ne laissez plus vos mots de passe transités en clair sur le net ! Forcez HTTPS a minima sur les zones sensibles !

Tuesday, September 10 2013

Partager un fichier, le faire disparaître après partage et savoir (approximativement) qui l'a téléchargé

J'utilise souvent demo.ovh.eu (le service de partage mis en place par OVH, qui, contrairement à beaucoup d'autres, n'oblige pas à visualiser des publicités) pour partager aisément des fichiers sur internet. Toutefois, je souhaiterais parfois que le fichier soit effacé après téléchargement et savoir quand celui-ci a été téléchargé. Certains services proposent sans doute cela et l'outil Jyraphe (dont le développement est stoppé ?) le permet. Je vous propose toutefois une solution maison toute simple à mettre en place et extrêmement simpliste à déployer sur serveur web + PHP !

Soit le fichier mon_archive.7z à proposer au téléchargement. Soit /var/www/ la racine du serveur web accessible sur le net. On place le fichier mon_archive.7z dans un dossier /var/www/hidden/ qu'un fichier .htaccess protège de toute ouverture. On place ensuite le code suivant dans un fichier /var/www/telechargement-ici_chaîne_unique_non_devinable.php :

<?php
    $yourfile = "hidden/mon_archive.7z";

    if (file_exists($yourfile)) {
        $file_name = basename($yourfile);

        header("Content-Type: application/zip");
        header("Content-Disposition: attachment; filename=$file_name");
        header("Content-Length: " . filesize($yourfile));

        readfile($yourfile);
        unlink($yourfile);
        if (file_exists($yourfile)) {
            mail('moi@courriel.fr', 'Fichier téléchargé mais pas supprimé !', 'Le fichier "'.$file_name.'" a bien été téléchargé par '.$_SERVER["REMOTE_ADDR"].' et n\'a pas été supprimé.');
        }
        else {
            mail('moi@courriel.fr', 'Fichier téléchargé et supprimé !', 'Le fichier "'.$file_name.'" a bien été téléchargé par '.$_SERVER["REMOTE_ADDR"].' et a été supprimé.');
        }
    }
    else {
        print "Unknown file...";
        mail('moi@courriel.fr', 'Tentative de téléchargement infructueuse.', 'Qqn ('.$_SERVER["REMOTE_ADDR"].') a tenté en vain de télécharger le fichier "'.$yourfile.'".');
    }
    exit;
?>

Le fichier php propose donc le fichier au téléchargement quand l'utilisateur appelle https://monserveur/telechargement-1g2j0a7j8.php puis l'efface du serveur et vous préviens (par courriel) que le téléchargement a eu lieu. Il enregistre également l'adresse IP de la personne qui a effectué le téléchargement (certes, c'est très approximatif comme identification). Le mot de passe est en fait la chaîne arbitrairement complexe et longue placée dans l'URL pour accéder au fichier .php permettant le téléchargement.

On pourrait améliorer encore la sécurité en récupérant le mot de passe (la chaîne arbitraire) dans un paramètre GET (?pass=1g2j0a7j8 par exemple) et bannir une IP qui ferait plus de 3 ou 4 tentatives d'accès infructueuses.

Le cahier des charges est respecté, ce n'est pas tout à fait automatique mais ça ne devrait pas être trop difficile à scripter si vous avez de nombreux fichiers de la sorte à mettre à disposition. Et surtout, votre archive (peut-être très confidentielle) ne transite pas sur un serveur tierce.

Thursday, September 5 2013

Accélérer matériellement Firefox pour des effets CSS3 plus fluides !

Je découvrais récemment les pouvoirs... non pas de la force... mais des fonctions de transformation et d'animation de CSS3 (c'est un peu comme la force penserez-vous peut-être). J'étais alors bluffé mais également chagriné car je constatais une certaine lenteur (des saccades) dans les animations.

Par exemple, l'introduction "à la Star Wars" mentionnée ici n'était pas tout à fait fluide. Je décidais alors d'investiguer...

Mon navigateur étant Mozilla Firefox et, à la recherche de pistes, j'entrai l'adresse about:support dans la barre d'adresse du navigateur et descendis jusqu'à la section 'Graphics'. Voici ce que j'y lus:

Firefox GPU acceleration does not work

La valeur "0/1 Basic" pour "GPU Accelerated Windows" ne me disait rien qui vaille... Je tentais alors de forcer l'accélération matérielle en me rendant dans "about:config" et en activant à "true" l'option "layers.acceleration.force-enabled".

Après un redémarrage du navigateur et un retour à "about:support", j'avais la satisfaction de lire :

Firefox GPU acceleration works

Et toutes les animations CSS3 étaient maintenant tout à fait fluides et ne souffraient plus d'aucune saccade.

Attention toutefois, forcer l'accélération matérielle n'est pas sans risque. J'observe effectivement depuis l'activation de cette option (et de manère aléatoire) un noircissement total de la fenêtre de Firefox, que je peux résoudre en jouant sur sa taille. Mystérieux, mais pour profiter de l'intro de Star Wars en CSS3 ET fluide, je suis prêt à ce sacrifice !

Mise à jour (08 Sep 2013) : on rapporte également ici (poste de P.Scoffoni) une fuite de mémoire qui aurait (conditionnel) pu être causée par l'activation de l'option, méfiance à donc !

Thursday, June 6 2013

"Stress test" pour une application Web - cas pratique sur Raspberry

Ceux qui auront lu mon précédent article ne seront pas étonnés si j'annonce que j'ai tout récemment déployé une application web (Zend Framework 1 + Doctrine 2 + PHP5 + MySQL) sur un (petit - euh... enfin normalement petit) Raspberry Pi (modèle B). Toutefois, je décidai d'étudier plus avant le système mis en place en le soumettant à un 'stress test' simulant un fort afflux d'utilisateurs effectuant diverses requêtes au sein de l'application.

Les applications de 'stress test' ne manquent pas et j'avais déjà entendu parler de JMeter et Tsung. Mais pour ce test, je décidai d'utiliser Gatling : logo-Gatling-StressTool.png

Gatling (open source, disponible sur http://gatling-tool.org/) est codé en Scala et s'exécute sans difficulté dans un environnement disposant d'une machine virtuelle Java. Pas d'installation nécessaire : on décompresse l'archive et les binaires de l'application sont disponibles dans /bin/.

Mise en place d'un scénario de stress : à la main...

Les scénarios de test de Scala sont à créer et placer dans /user-files/simulations dans des fichiers scala. La structure de base d'un fichier est la suivante :

import com.excilys.ebi.gatling.core.Predef._
import com.excilys.ebi.gatling.http.Predef._
import com.excilys.ebi.gatling.jdbc.Predef._
import Headers._
import akka.util.duration._
import bootstrap._
import assertions._

class SimulationWithFollowRedirect extends Simulation {
  val scn = scenario("My scenario")
            .exec(http("My Page")
              .get("http://mywebsite.com/page.html"))
  
  setUp(scn.users(1))
}

Il est donc possible en Scala de coder la liste des actions qu'effectueront les utilisateurs virtuels (des requêtes GET, des requêtes POST, le lancement de certaines routines PHP...). A la fin du fichier, la simulation est lancée par

  setUp(scn.users(x))

où x (entier naturel strictement positif) représentera le nombre d'utilisateurs qui seront simulés concurremment sur l'application.

Mise en place d'un scénario de stress : en automatique pour aller plus vite !

L'écriture du scénario "à la main" est très souple mais un petit peu complexe. Il existe donc un petit utilitaire (recorder) particulièrement pratique qui peut venir à notre rescousse. Il s'agit d'une application qui vient se positionner comme un proxy sur le navigateur web et qui enregistre toutes les opérations effectuées par le navigateur directement sous la forme d'un fichier Simulation.scala utilisable par Gatling.

Le 'recorder' se lance par :

sh bin/recorder.sh

Si les ports du proxy (HTTP/HTTPS, spécifiés en haut à gauche) conviennent, on clique sur 'Start' et désormais l'application enregistrera tout mouvement réalisé au travers du proxy.

Gatling-recorder.png

Une fois l'enregistrement terminé, on peut bien sûr modifier le scénario de simulation (suppression des pauses non nécessaires, suppression de certaines étapes...) en éditant le fichier MaSimulationDuJour.scala qui aura été créé dans /user-files/simulations. On peut notamment augmenter le nombre d'utilisateurs qui seront simulés :

  setUp(scn.users(100))

et on peut simuler une montée en charge progressive par la commande finale :

  setUp(scn.users(100).ramp(200)

qui signifie que 100 utilisateurs se connecteront progressivement pendant 200 secondes (soit 1 nouvel utilisateur toutes les 2 secondes). Bien sûr, si votre serveur répond bien et que la durée de simulation est courte alors il est possible que vous n'ayez jamais 100 utilisateurs connectés en même temps (car une fois que l'utilisateur simulé a terminé toutes les étapes alors il se déconnecte à moins que vous ne paramétriez des boucles). Le rapport d'analyse (cf. infra) vous permettra de bien visualiser ce phénomène.

Lancement du stress test

On lance donc gatling

sh bin/gatling.sh

qui propose de choisir la simulation à lancer. Et c'est parti !

L'état d'avancement du 'stress test' s'affiche dans la console. Une fois celui-ci terminé, un rapport est généré et est accessible dans le dossier /results/.

Voici un petit aperçu d'un rapport obtenu (sur un PC de bureau 4 coeurs +4 Go de RAM) : Gatling_-_stresstest1.png Gatling_-_stresstest2.png

Conclusion du stress test

Sur mon Raspberry Pi, je suis un peu déçu car dès 10 utilisateurs en concurrence les accès à la base de données et les requêtes pour les contenus non statiques mettent un temps long (les moyennes vont de 20 à 40 secondes selon les requêtes) à s'exécuter... Je m'attendais à ce que la Raspberry Pi ne tienne pas bien la charge mais je pensais la limite atteinte moins vite. C'est également le signe que le code de l'application n'est pas optimisé pour une exécution très légère et très économe en ressources ! Il va me falloir plonger dans le code pour optimer tout cela !

Très bonne journée à tous et bons 'stress tests' !

Saturday, May 11 2013

Un serveur courriel qui bloque quand mon ordinateur, ma tablette et mon téléphone se connectent...

Hébergeant mon propre serveur courriel, je butais depuis quelques temps sur un problème ennuyeux. De temps à autre, mon téléphone ou ma tablette Android refusaient la connexion au serveur courriel en avançant la raison d'un mot de passe incorrect. Longtemps, je crus à un bogue des clients Android jusqu'au moment où Thunderbird, par moment, se mit également à me refuser l'accès. Comment diable expliquer cette panne aléatoire et que quelques heures d'attente suffisaient généralement à résoudre ?

Je plongeais donc dans les logs du serveur Dovecot et fouillais. Voilà, je tenais le coupable : tous mes périphériques se connectaient depuis la même IP (partageant par exemple un même Wifi) et Dovecot refusait les pressants appels au nom de cette directive :

mail_max_userip_connections = 10

Un coup de nano (ndla un éditeur texte en ligne de commande pour ceux qui ne connaîtraient pas, l'équivalent de mon tournevis numérique) et je faisais disparaître (ou plutôt croître) la stricte limite ! Après un redémarrage de Dovecot, je n'avais plus aucun problème de connection au serveur IMAP ! Ouf, le standard faisait bien les choses et il n'y avait pas de bogue vicieux caché dans les divers clients ou le serveur !

- page 1 of 2