Installation d’un blog WordPress sur un VPS Gandi

Date: 25/08/2010 | Catégories: Blog,Open-source,Web | Tags: ,,,,,,

Comme vous le savez, je me suis fait hacké mon site pendant les "grandes vacances". Cette petite contrariété m'a fait faire dans l'urgence une chose que j'avais planifier depuis un certain moment: la migration du Blog de Nicolargo sur un serveur privé virtuel de Gandi.

Nous allons donc voir dans ce billet comment installer, sécuriser et optimiser une blog WordPress sur une serveur privé virtuel !

Commencons par le commencement

Il faut d'abord choisir les performances initiales du serveur virtuel. J'ai utilisé le serveur que j'avais créé il y a quelques mois. C'est l'entrée de gamme, c'est à dire 1 part correspondant aux spécifications suivantes:

  • 1/64ème des capacités globales du serveur, soit environ 1 GHz
  • 1/64ème des 16 Go de mémoire du serveur, soit 256 Mo de RAM garantie avec un burst possible à 2 Go (+512 Mo de Swap)
  • 1/64ème des capacités réseau, soit 5 Mbits dédiés
  • 1/64ème du disque réservé au serveur, soit 5 Go de disque data (+3 Go pour le système)

Comme nous allons le voir plus tard cette configuration n'est pas adapté pour un blog avec un trafic relativement important (environ 1 00 000 pages vues par mois) . Je suis donc rapidement passé à la configuration suivante:

Nous disposons donc d'un accès SSH (root) sur le serveur,. Après une mise à jour du système d'ecploitation (Ubuntu Server 9.04) nous pouvons enfin commencer les choses sérieuses...

Installation du serveur Web Apache

On commence par installer le serveur:

sudo aptitude install apache2 php5 libapache2-mod-php5 php5-curl

Puis on active les modules qui vont nous permettre d'optimiser les performances de WordPress:

sudo a2enmod deflate

sudo a2enmod headers

sudo a2enmod rewrite

sudo a2enmod expires

Il est également possible d'adapter la configuration des processus Apache (nombre de process, nombre maximal de client par process..) en modifiant le fichier /etc/apache2/apache2.conf:

<IfModule mpm_prefork_module>

StartServers 5

MinSpareServers 5

MaxSpareServers 10

MaxClients 40

MaxRequestsPerChild 2000

</IfModule>

StartServers permet de configurer le nombre de processus Apache lancés au démarrage.

MinSpareServers et MaxSpareServers donnent respectivement le nombre minimum et maximum de processus Apache selon les besoins.

MaxClients permet de configurer le nombre de clients (au sens session TCP) pouvant se connecter simultanément à un processus Apache. Pour des sites dynamiques (comme WordPress) il est plutôt conseillé de ne pas avoir une valeur trop élevée afin d'éviter les swap mémoire sur disque.

Une solution proposée sur ce blog pour calculer la valeur MaxClients est la suivantes: Il faut d'abord regarder combien de mémoire prend un processus apache2 (par exemple avec la commande top), puis diviser la mémoire système disponible par cette valeur. Par exemple, pour un serveur disposant de 1 Go de RAM (1000 Mo) et dont chaque processus Apache occupe 25 Mo, on a le calcul suivant:

MaxClients = 1000 / 25 = 40

Enfin MaxRequestsPerChild permet de définir le nombre maximal de requête qu'un processus peut prendre en charge avant d'être tué. Il est conseillé de mettre une valeur différente de 0 (le processus n'est alors jamais tué) pour éviter qu'une processus ne consomme trop de mémoire si un problème arrive.

On passe ensuite aux directives KeepAlive qui permette de configurer finement le temps de vie des sessions TCP. Pour un site dynamique comportant un nombre important d'images et de JS (comme mon blog), les valeurs suivants sont un bon point de départ:

# KeepAlive: Whether or not to allow persistent connections
KeepAlive On
# MaxKeepAliveRequests: The maximum number of requests to allow
MaxKeepAliveRequests 200
# KeepAliveTimeout: Number of seconds to wait for the next request
KeepAliveTimeout 5
# Timeout: The number of seconds before receives and sends time out.
Timeout 50

Enfin on relance le serveur pour que la configuration soit prise en compte:

sudo /etc/init.d/apache2 restart

Installation de MySQL

On commence par installer le serveur MySQL:

sudo aptitude install mysql-server

Puis on créé une base de donnée et un compte utilisateur associé qui va servir pour WordPress:

mysql -u adminusername -p

mysql> CREATE DATABASE databasename;

Query OK, 1 row affected (0.00 sec)

mysql> GRANT ALL PRIVILEGES ON databasename.* TO "wordpressusername"@"hostname"

-> IDENTIFIED BY "password";

Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;

Query OK, 0 rows affected (0.01 sec)

mysql> EXIT

On peut optimiser la base de donnée en éditant le fichier /etc/mysql/my.cnf:

query_cache_type = 1

query_cache_limit = 2M

query_cache_size = 32M

Ne pas oublier de relancer le serveur MySQL pour prendre en compte les modifications.

Installation de WordPress

Depuis le hack de mon blog, j'ai décidé de travailler directement avec la version SVN de WordPress afin de disposer au plus vite des patchs de sécurité. Une fois de le répertoire racine de votre site Web (/home/nicolargo/web/blog dans mon cas), il faut saisir la commande suivante:

svn co http://core.svn.wordpress.org/trunk/ .

On ajoute ensuite la ligne suivante dans le fichier .htaccess afin de rendre invisible les sous répertoires .svn:

RewriteRule ^(.*/)?\.svn/ - [F,L]

Puis on finalise l'installation en suivant la fameuse installation de WordPress en 5 minutes.

Il est ensuite conseillé de supprimer le fichier wp-admin/install.php (qui ne servira plus une fois l'installation faite):

rm wp-admin/install.php

Sécurisation de WordPress

Cette sécurisation passe par deux étapes. Une première est de protéger le blog à l'aide du fichier .htaccess. La seconde est d'installer des plugins de sécurité qui vont nous permettre de faire régulièrement des audits sur notre serveur.

Pour le fichier .htaccess, j'utilise la base suivante:

# MAIN

RewriteEngine On

ServerSignature Off

Options All -Indexes

Options +FollowSymLinks


# SVN protect

RewriteRule ^(.*/)?\.svn/ - [F,L]


# Secure .htaccess

<Files .htaccess>

Order Allow,Deny

Deny from all

</Files>


# Secure wp-config.php

<Files wp-config.php>

Order Deny,Allow

Deny from all

</Files>


# FILTER REQUEST

<IfModule mod_rewrite.c>

RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule . /index.php [L]

</IfModule>


# BEGIN Expire Header

<FilesMatch "\.(ico|jpg|jpeg|png|gif|js|css|swf)$">

ExpiresDefault "access plus 2 hours"

</FilesMatch>


On passe ensuite à l'installation des plugins de sécurité suivants:

Le premier (Secure WordPress) permet d'effectuer simplement et automatiquement une liste d'actions essentielle à la sécurité de votre site:

Le second (WP Security Scan) effectue régulièrement un scan de l'arborescence de votre site et remonte dans l'interface d'administration de WordPress les fichiers modifiés.

Optimisation de WordPress

Pour l'optimisation des performance du blog, je me base sur l'indispensable plugin W3 Total Cache. Bien qu'il ne se limite pas qu'a cela, il permet de mettre facilement en place un système de cache ou les pages sont directement gérées en mémoire RAM plutôt qu'en lecture sur le disque dur. J'ai choisi de coupler W3 Total Cache avec Memcached (un cache mémoire sous licence libre).

L'installation de Memcached est des plus simple:

sudo aptitude install libcache-memcached-perl memcached

Si votre serveur est protégé par un Firewall (ce que je conseille évidemment...), il ne faut pas oublier d'ajouter la règle suivante:

iptables -A OUTPUT -p tcp --destination 127.0.0.1 --dport 11211 -j ACCEPT

La configuration de W3 Total Cache se fait via l'onglet Performance / General settings:

Une dernière optimisation consiste à installer le plugin WP-DBManager et de suivre régulièrement (tout les mois environ) les procédures d'optimisation (onglet Database).

Test et validation

Pour tester les performances de votre serveur, vous pouvez utiliser la commande suivante:

ab -t 30 -c 5 http://votreblog.com/

L'information intéressante se trouve sur la ligne:

Requests per second:    159.46 [#/sec] (mean)

Il est également important de surveiller les paramètres suivant: charge CPU, mémoire RAM disponible, accès disque et réseau. Gandi propose pour cela une interface d'administration Web avec un système de sonde permettant de remonter des alertes par mail ou même d'augmenter dynamiquement le nombre de part lors d'un pic de trafic.

Personnellement, je surveille mon serveur via Nagios mais cela sera le sujet d'un prochain billet...

  • Dommage j’aurais bien nginx au lieu d’apache 😀

  • @artiflo: j’ai hésité mais j’ai une problématique à laquelle NGinx ne sait pas encore répondre (enfin je pense), pouvoir servir de serveur Webdav pour accèder à mon serveur SVN qui est également hébergé sur ce serveur…

  • anthony

    Afin d’éviter de devoir poser une rewrite pour « cacher » les répertoires svn, je te conseille d’utiliser la commande « svn export » qui te construira ton arborescence sans info svn 😉

  • Jérémie

    « un trafic relativement important (environ 1 00 000 pages vues par mois) »

    Un serveur avec un CPU à 1.2 GHz peut largement faire tourner un blog de cette ampleur. C’est le système inefficace de virtualisation et de disques dur en réseau qui fait chuter les performances. Les performances indiquées sont fictivent et ne reflètent pas les performances d’un vrai serveur dédié.
    Fait un test avec un simple « cp » sur un gros fichier, tu vas rire assez vite des performances 🙂

    Et avec plus d’une part, mon petit doigt me dit que tu payes plus chez qu’un serveur dédié à 17€TTC/mois avec un CPU à 1.2Ghz… (cf: kimsufi.com).

    Options All -Indexes
    Options +FollowSymLinks

    « Options All » active toutes les options sauf multiviews, la seconde ligne est donc redondante.

    « RewriteRule ^(.*/)?\.svn/ – [F,L] »

    Tu peux surement remplacer cette commande par quelque chose de plus standard/propre tel le blocage des .htaccess/.htpasswd fait par Apache :

    DENY FROM ALL

    « # Secure .htaccess »
    C’est dans la conf Apache par défaut. Inutile donc.

    «  »
    Le fichier n’affiche rien, personne ‘nest censé voir son contenu. Sauf si tu te plantes sur la conf apache et que le module PHP n’est pas appelé pour exécuter le contenu.

    « Deny from 75.126.85.215 »
    Ridicule de mettre ça dans un .htaccess, encore plus ridicule de le bloquer à la main. Install fail2ban et configure le pour lire les lgos d’apache et bloquer les comportements anormaux.
    Fail2ban bloquera l’IP au niveau du système (/etc/hosts.deny et/ou iptables), ce qui est maintenable et propre.

    « # QUERY STRING EXPLOITS »
    La protection du pauvre contre les scripts kiddies, c’est pas ça qui va sécuriser ton blog…

    « # CHARACTER STRINGS »
    Encore une protection bizarre venue d’un ancien temps.

    Bloquer les anciens exploits ne sert à rien puisqu’ils sont corrigés (du moins j’espère, sinon dans 2 jours ton site sera down, encore). Les protections à coup de « pattern matching » ça marche contre les scripts tous fait, donc ça va pas sécuriser grand chose en gros.
    Ca ressemble plus à un gros fichier htaccess trouvé sur le net.

    Pose toi surtout la question de la sécurité autour du blog :
    * des backups régulières (fichier + base de données) doivent être faites
    * le système entier et apache doivent être sécurisés

    On pourrait parler de …
    * l’exécution du PHP sous un utilisateur non privilégié (www-data est clairement privilégié).
    * protéger les tentatives d’accès via fail2ban
    * limiter l’élévation de privilège avec du chroot…

  • @Hérémie: c’est vrai pour les performances du serveur si il ne servait que pour héberger le blog, ce qui n’est pas le cas. Je l’utilise pour d’autres besoins (développement, validation) et les 3 parts ne sont pas de trop.

    ====

    Je trouve les performances en lecture/écriture sur le disque plutôt bonne:

    Test écriture:
    # time dd if=/dev/zero of=mytestfile.out bs=1000 count=1000000
    > 1000000000 bytes (1.0 GB) copied, 4.64558 s, 215 MB/s

    Test lecture:
    # time cp mytestfile.out /dev/null
    > real 0m28.052s (36 MB/s)

    ===

    Pour ce qui est de la sécurisation du blog je suis loin d’être un spécialiste. Je me suis basé sur certains articles de blog ayant pignon sur rue:
    http://thethemefoundry.com/blog/optimize-apache-wordpress/
    http://net.tutsplus.com/tutorials/wordpress/20-steps-to-a-flexible-and-secure-wordpress-installation/
    http://www.prelovac.com/vladimir/wordpress-optimization-guide

    Si tu as l’adresse d’un article complet (ou si tu veux en écrire un sur le blog :)), je suis preneur !

  • Jérémie

    Evite d’écrire des zéros … ça donne des résultats grotesques, comme une écriture plus rapide qu’une lecture !

    Fait un « cp » sur un fichier tiré de /dev/urandom, tu aurais des performances moindre. Sur mon serveur : 24Mo/s

    Cela dit, la vitesse de lecture est impressionnante. Mais quid des temps de latence ? Il faudra faire des tests plus poussés pour ça. Une copie pure cache les temps de latence, important quand on doit lire 50 petits fichiers, typiquement le code d’un blog en PHP 🙂

    Pour sécuriser un blog, y a pas tellement de solutions, il faut une applis avec une bonne sécurité et éviter les erreurs stupides de configuration. WordPress devrait etre sécurisé par défaut (bloquer le directory traversal par htaccess par exemple).

    Pour le serveur, la doc est éparpillée. J’ai beaucoup aimé les manuels « security » de Gentoo (court et efficace) et Debian (qui traine un peu en longueur sur certains points).
    Il faut se pencher sur les modules suphp ou fastcgi pour ne rien exécuter sous www-data.
    Et administrer correctement sa machine : pare feu, fail2ban, logcheck, …

    Le problème de wrodpress c’est que si une faille est trouvée dans un module, tous les sites peuvent être hackés via un script automatique.

    Moins moche que le htaccess, il y a une appli open source qui scanne les requetes http à la recherche de patterns.
    Je ne me souviens plus de son nom par contre …
    J’ai pas ce genre de solutions pour autant, ça ne fait que masquer le problème.

  • @Jérémie: effectivement les performances en écriture sont moins bonne avec des données aléatoires. J’obtient environ 5.5 Mo/s et tjrs 24 Mo/s pour la lecture.

    J’ai mis en place le pare feu iptables + fail2ban, par contre je ne connaissais pas logcheck…

  • Jérémie

    Les logiciels que j’utilise pour la sécurité :
    * logwatch : rapport de synthèse journalier, permet de vérifier l’espace disque restant, les connexions SSH, les mails envoyés
    * logcheck : connaitre en temps reel (rapport toutes les heures) des activités dans les logs, permet d’être au courant de tentatives d’attaque, de comportement étrange du système (attention, c’est verbeux, il faut parfois rajouter des exceptions pour ne plus être spammé)
    * cron-apt : télécharger les mises à jours et prévenir par mail de leur mise à disposition, un must-have pour rester à jour !
    * rkhunter : anti rootkit, scan journalier, prévient de fichiers suspect
    * chkrootkit : un autre anti rootkit
    * tiger : recherche de failles, il faut analyser les logs en détail, permet de fixer des problèmes de sécurité après l’installation
    * tmpreaper : vider le /tmp régulièrement
    * portsentry : detecter et bloquer les scans de port (très efficace quand on lui fait ecouter le port 22, après avoir changé SSH de port)

    A faire aussi :
    * passer les dossiers temporaires « world writable » en tmpfs : /tmp, /var/tmp
    * mettre /var dans une partition séparée
    * instaurer des quota disk stricts par utilisateur (quotatool)
    * se protéger des forks bombes : /etc/security/limits.conf
    * configurer les paramètres de sécurité réseau : /etc/sysctl.conf
    * empecher le reboot par syskey /etc/inittab
    * changer le port de SSH
    * utiliser rsyslog pour envoyer les lgos sur un serveur distant (sans accès de modification/suppression de logs de préférence)
    * et j’en oublie surement

    Et concernant les backups, une chose que je ne fais pas faute de moyen : fait des backups à sens unique. Seul l’envoie de fichier étant autorisé, le pirate ne peut pas supprimer les logs pour cacher ses méfaits, on aura donc quelques infos sur le vecteur d’attaque utilisé. Pour récupérer les sauvegardes, on peut imaginer qu’on utilise d’autres identifiants de connexion, que l’on ne laisse pas trainer sur le serveur.
    Je ne connais pas de service qui offre ce type de compétences (j’ai pas cherché non plus).

  • @Jérémie: très interessant tout ca. Je vais un peu creuser quand j’ai le temps !

    Merci

  • Je vais t’appeler Nico les bons tuyaux, entre l’article et les commentaires j’ai appris pas mal de bonnes choses que je vais pouvoir creuser sur mon temps libre 🙂

  • @Jérémie: je pense prochainement écrire un billet sur la sécurisation d’un serveur Ubuntu, serais tu intéressé pour le relire/commenter/améliorer avant publication ? Vu tes compétences je pense que cela aiderait pas mal la communauté… Tu peux me contacter par mail pour me fournir ton adresse perso (via la page Contact).

  • Jérémie

    Avec plaisir 😉

  • Pingback: tutos pour installation Lamp sur serveur dédié. | carnet linux (notes perso)()

  • Excellent article et commentaires. Je vais stocker les infos de @Jeremy dans un coin, ca me servira certainement!

    Pour l’offre de Gandi, je n’ai pas l’impression que c’est plus intéressant qu’une dedibox V3. Le rapport qualité/prix me parait plus favorable pour le service de online.net… Pour avoir un serveur pas mal il faut trop monter dans les parts chez gandi à mon goût.

    Petite note pour le look du site : agréable, clair et beau, bref top!

  • Pingback: Sécuriser son blog Wordpress #1()

  • Avant tout, je voudrais remercier Nicolas pour l’excellent travail qu’il fait sur ce blog, plein de ressources très utiles.

    J’ai décidé de m’attaquer à l’amélioration des performances et de la sécurité de mon blog, et je tente plein d’expériences inspirées par ce blog.

    Je gère moi-même une machine chez Amazon EC2, plus pour le fun et la possibilité d’apprendre un peu comment ça marche. En appliquant différentes méthodes trouvées ici, mon bench sur Apache est passé de 1,5 opérations à la seconde (dans la config par défaut), à environ 130 opérations/seconde. Je suis encore loin des 150 ou 200 que je vois chez certain, mais ça progresse ;). Alors merci à Nicolas et aux commentateurs.

    En revanche, je voulais signaler qu’en WordPress 3.0.4, l’exemple de fichier .htaccess que tu donnes rend l’ajax non fonctionnel sur la page des widgets. Je me suis arraché les cheveux depuis hier pour essayer de trouver la source du problème, mais j’en ai été incapable.

    De même, l’accès à la page updates du Dashboard ne fonctionne plus.

    Pour le moment, j’ai enlevé toute la partie à partir de Blacklist Candidates et ça remarche correctement. Mais je reste preneur de conseils pour améliorer la sécurité ;).

  • Pingback: 12 étapes pour optimiser les performances de son blog Wordpress()

  • Pingback: Installation automatique de NGinx, PHP-FPM, MemCached sous Debian()

  • Avec plaisir 😉

  • topwubcom

    système d’ecploitation-> exploitation 🙂