Les hôtes virtuels

Luc Didry, à partir des cours de Sébastien Jean. Licence CC-BY-SA
Bow ties are cool.
Le onzième docteur

Télécharger le PDF

L’hébergement virtuel est une technique permettant d’héberger plusieurs sites Web sur un seul serveur Web. Ces sites sont différenciés soit par des noms de domaines différents, soit par des adresses IP ou des ports différents.

C’est typiquement la technique utilisée par… quasiment tout le monde pour héberger des sites différents à partir d’un même serveur. Cette fonctionnalité est bien entendue supportée sous Apache, qui a été l’un des premiers serveurs Web à la mettre en œuvre.

Plusieurs techniques peuvent être utilisée pour réaliser ces hébergements virtuels :

  • Les hôtes virtuels par adresse IP : les requêtes sont alors triées en fonction de l’adresse IP qu’elles utilisent. Chaque hôte virtuel dispose de sa propre adresse IP et répond alors aux requêtes concernant son adresse.
  • Les hôtes virtuels par nom : cette technique repose sur l’évolution apportée par la norme HTTP 1.1, qui prévoit d’ajouter un champ « Host » dans les en-têtes HTTP. Ce champ est alors utilisé pour identifier l’hôte virtuel qui est concerné par une requête donnée.
  • Les hôtes virtuels dynamiques : qui ne possèdent pas de configuration propre, mais qui sont déterminés à partir de l’URL de la requête. Il est alors possible en utilisant quelques règles de décrire le comportement d’un ensemble d’hôtes virtuels.
  • Les hôtes virtuels par port : les requêtes sont alors triées en fonction du port qu’elles utilisent. Chaque hôte virtuel dispose de son propre port et répond alors aux requêtes concernant son adresse. Cette technique n’est quasiment jamais utilisée, car utiliser un port différent des ports standards (80 et 443) nécessite de le spécifier dans l’adresse du site.

1 Apache

1.1 Généralités

D’une manière générale, toutes les directives concernant un hôte virtuel particulier sont placées dans des conteneurs <VirtualHost>. Toute directive placée dans un tel conteneur ne s’applique donc qu’à l’hôte virtuel correspondant.

Il est également important de noter que les directives placées dans le contexte général sont héritées dans les hôtes virtuels, sauf si elles y sont surchargées. Il est conseillé de surcharger le moins de directives possibles dans le contexte d’hôte virtuel, en dehors des directives ServerName et DocumentRoot.

Avant de définir des hôtes virtuels, il est également important de définir des directives Listen, indiquant sur quelles adresses IP et sur quels ports Apache doit attendre des requêtes.

Listen 80

=> écoutera sur le port 80 de toutes les adresses IP du serveur.

Il est possible de définir plusieurs directives Listen, comme cela est indiqué dans les exemples ci-dessous :

Listen 192.168.1.100:80
Listen 192.168.1.101:1337

=> écoutera sur le port 80 de l’adresse 192.168.1.100 et sur le port 1337 de l’adresse 192.168.1.101.

1.2 Hôtes virtuels par adresse IP

Les hôtes virtuels par adresse IP doivent tous être associés à une adresse IP unique. En pratique cependant, les machines faisant office de serveurs Web ne peuvent avoir que quelques interfaces réseau physiques, ce qui implique donc l’utilisation d’alias d’interface réseau pour surmonter cette limitation.

La directive Listen sera positionnée comme suit afin d’écouter sur toutes les IPs :

Listen 80

Ancienne méthode : ces alias d’interface réseau peuvent être mis en place de la façon suivante :

ifconfig eth0:0 192.168.1.100
ifconfig eth0:1 192.168.1.101

Méthode moderne : la commande ip, contenue dans le paquet iproute2, est plus puissante et plus simple à utiliser qu’ifconfig, dont l’usage est aujourd’hui obsolète, comme le montre sa page de man en version anglaise1. Avec la commande ip, on ne parlera plus d’alias d’interface : une seule interface possèdera plusieurs adresses IP.

ip addr add 192.168.1.100 dev eth0
ip addr add 192.168.1.101 dev eth0

Pour connaître quelques commandes utiles sur ifconfig et ip, consultez la page http://www.tty1.net/blog/2010/ifconfig-ip-comparison_en.html.

Il faut ensuite créer dans le DNS, ou dans le fichier /etc/hosts pour un travail en local, les entrées correspondantes avec les noms associés.

Exemple :

192.168.1.100 dalek.example.org
192.168.1.101 cybermen.example.org

La configuration de ce type d’hôte se fait par une directive conteneur <VirtualHost *adresse_IP*>, placée après les directives du contexte général.

La configuration d’un hôte virtuel se fait généralement dans un fichier différent de celui la configuration générale, qui comporte alors une directive Include repertoire_du_fichier/* pour prendre en compte l’hôte virtuel. Dans Debian, comme vu dans le TD 1, les fichiers des hôtes virtuels sont à placer dans le répertoire /etc/apache2/sites-available/, avec un lien symbolique de ces fichiers dans le dossier /etc/apache2/sites-enabled/.

Il est ainsi possible, pour continuer l’exemple précédent, de faire les définitions suivantes :

<VirtualHost 192.168.1.100>
  ServerName dalek.example.org
  DocumentRoot /var/www/vhost1
</VirtualHost>
<VirtualHost 192.168.1.101>
  ServerName cybermen.example.org
  DocumentRoot /var/www/vhost2
</VirtualHost>

Pour ce type d’hôte virtuel, le nom de domaine n’est alors d’aucune importance, étant donné qu’Apache se base uniquement sur l’adresse IP pour déterminer quel hôte virtuel est concerné par une requête.

Cependant, si les noms de domaine sont enregistrés dans le DNS, ou le fichier /etc/hosts pour un usage local, ces noms, après résolution DNS, peuvent être directement saisis dans un navigateur. Mais c’est bien l’adresse IP associée au nom de domaine qui est déterminante dans le choix de l’hôte virtuel. Bien entendu, pour que cette technique soit mise en place, il faut que le FAI fournisse plusieurs IPs (ce qui ne pose pas de souci pour l’IPv6).

Si un serveur Web est contacté par une requête comportant une adresse IP ne correspondant à aucun hôte virtuel, c’est le premier hôte (dans l’ordre de lecture des fichiers de configuration) qui répondra à la requête. Cela est risque d’erreur car, dans le cas de multiples fichiers d’hôtes virtuels, il est facile de changer par mégarde l’ordre de définition des hôtes en créant un nouvel hôte.

Pour changer ce comportement, il est alors possible de définir un hôte virtuel par défaut permettant de mieux traiter l’erreur rencontrée. Ces hôtes virtuels par défaut se définissent au moyen de directives <VirtualHost> associés à la valeur spéciale _default_ (<VirtualHost _default_>).

1.3 Hôtes virtuels par port

Tout comme on peut créer des hôtes virtuels en discriminant sur l’adresse IP utilisée, on peut utiliser le port utilisé pour créer différents Hôtes virtuels.

La directive Listen sera positionnée comme suit :

Listen 80
Listen 1337
<VirtualHost 192.168.1.100:80>
  ServerName dalek.example.org
  DocumentRoot /var/www/vhost1
</VirtualHost>
<VirtualHost 192.168.1.100:1337>
  ServerName cybermen.example.org
  DocumentRoot /var/www/vhost2
</VirtualHost>

Là encore, la directive ServerName ne sert à rien.

1.4 Hôtes virtuels par nom

Cette fonctionnalité repose sur les « nouvelles » fonctionnalités proposées par HTTP 1.1, et en particulier le support du champ « Host ».


Apache 2.2 uniquement

Pour activer le support d’hôtes virtuels par nom, il faut utiliser la directive NameVirtualHost en lui passant en paramètre l’adresse IP du serveur (adresse qui n’est pas déjà utilisée pour des hébergements virtuels par adresse IP).

NameVirtualHost 192.168.1.102

Il ne peut y avoir qu’une seule directive NameVirtualHost pour une adresse IP donnée, mais plusieurs hôtes virtuels peuvent être définis pour une adresse IP donnée. La directive NameVirtualHost est obsolète depuis la version 2.4 d’Apache et n’est donc plus nécessaire sur Apache 2.4.


L’hôte virtuel souhaité est déterminé par le champ « Host » de l’en-tête HTTP de la requête.

Si aucune correspondance n’est trouvée pour l’hôte demandé par le client, c’est le premier hôte virtuel (dans l’ordre de lecture des fichiers de configuration) défini pour cette adresse qui est utilisé. On appelle pour cette raison celui-ci hôte virtuel primaire.

Voici des exemples de définition :

<VirtualHost 192.168.1.102>
  ServerName slitheen.example.org
  DocumentRoot /var/www/vhost1
</VirtualHost>
<VirtualHost 192.168.1.102>
  ServerName sontarans.example.org
  DocumentRoot /var/www/vhost2
</VirtualHost>

Là encore, pour que les noms de domaines puissent être directement utilisés depuis un navigateur, il faut qu’ils figurent dans le DNS ou dans le fichier /etc/hosts de la machine pour des tests en local.

En vertu de la règle énoncée ci-dessus, le premier des hôtes virtuels définis est généralement conçu pour traiter les cas d’erreur2. Ces cas arriveront pour toute requête formulée auprès de 192.168.1.102 comportant un champ « Host » ne correspondant à aucun hôte virtuel défini.

1.5 Hôtes virtuels dynamiques

Pour les serveurs Web hébergeant des centaines ou des milliers de sites Web différents, les techniques présentées ci-dessus ne sont pas toujours suffisantes3.

Il est alors possible de définir des hôtes virtuels dynamiques, dont le support est assuré par le module mod_vhost_alias. Ces hôtes reposent alors sur la disponibilité de 4 directives :

  • Pour les hôtes dynamiques par nom : VirtualDocumentRoot, indiquant un DocumentRoot construit à partir de l’URL de la requête, et VirtualScriptAlias, indiquant comment construire le nom du répertoire contenant des scripts CGI (voir la directive ScriptAlias) à partir de l’URL de la requête.
  • Pour les hôtes dynamiques par adresse : VirtualDocumentRootIP, indiquant comment construire DocumentRoot à partir de l’adresse IP de la requête, et VirtualScriptAliasIP, indiquant comment construire le nom du répertoire contenant des scripts CGI (voir la directive ScriptAlias) à partir de l’adresse IP de la requête.

Avec ces directives, il n’est alors plus nécessaire de créer des directives <VirtualHost> pour chaque hôte virtuel à décrire. Chacune des 4 directives utilise un ensemble de spécificateurs permettant d’extraire des éléments de l’URL de la requête pour déterminer DocumentRoot et ScriptAlias (voir la documentation4).

Ainsi, par exemple, avec la configuration suivante :

UseCanonicalName Off
VirtualDocumentRoot /usr/local/apache/vhosts/%0

Une requête sur http://www.example.org/dir/file.html sera satisfaite avec le fichier /usr/local/apache/vhosts/www.example.org/dir/file.html.

Notez la directive UseCanonicalName paramétrée à Off : elle sert à indiquer à Apache qu’il va devoir construire ses URL auto-identifiante — c’est à dire les URL qui font référence au serveur lui-même — à partir des informations fournies par le client et est nécessaire pour utiliser des hôtes virtuels dynamiques.

L’inconvénient de cette technique est que tous les hôtes virtuels partageront la même configuration. Or, on ne configure pas de la même façon l’hôte virtuel pour un Wordpress, un Nextcloud… et encore moins pour un Gitlab !

1.6 Quelques conseils à propos des hôtes virtuels…

  • Toujours utiliser une adresse IP dans les directives <VirtualHost> et jamais de nom de machine. En effet, un problème de résolution DNS peut alors empêcher Apache de démarrer.
  • Toujours définir une directive ServerName dans tous les hôtes virtuels et donc ne pas se fier au DNS inverse pour connaître le nom de serveur d’un hôte virtuel.
  • Les hôtes virtuels par adresse et par nom sont indépendants. Il faut donc s’arranger dans leur définition pour qu’il n’y ait pas de conflit.
  • En production, toujours définir un hôte virtuel par défaut. Si on ne peut pas utiliser _default_, on s’assurera qu’un hôte virtuel est celui par défaut en nommant son fichier de configuration de façon à ce qu’il soit lu en premier par Apache. Le nom de ce fichier est généralement préfixé par 000- car les fichiers sont lus dans l’ordre alphabétique.

1.7 Nota Bene

On n’est pas obligé de préciser l’IP ou le port dans la directive <VirtualHost>. On peut par exemple écrire :

<VirtualHost *:*>
<VirtualHost *:80>
<VirtualHost 192.168.1.100>
<VirtualHost 192.168.1.100:*>

On peut spécifier plusieurs adresses IP ou plusieurs ports pour un seul hôte virtuel. Par exemple :

<VirtualHost 192.168.1.100:80 192.168.1.100:1337 192.168.1.101:80>
  …
</VirtualHost>

Dans le cas de l’utilisation d’IPv6, il faudra mettre l’adresse IP entre crochets quand on la spécifie :

Listen [2001:db8:a::42]:80
NameVirtualHost [2001:db8::a::42]:80
<VirtualHost [2001:db8:a::42]:80>

2 Nginx

2.1 Hôtes virtuels par adresse IP

Ici, point de directive Listen dans le contexte main (correspondant au contexte global), mais une ou plusieurs directives listen dans les contextes server, suivie d’une IP et/ou d’un port. On pourra omettre le port utilisé, auquel cas le port 80 sera utilisé. Une IPv6 sera encore une fois mise entre crochets.

listen 192.168.1.100;
listen 192.168.1.101:80;
listen [2001:db8:a::42]:80;

Si la directive listen n’est pas présente dans un contexte server, le port 80 sera utilisé par défaut, sur toutes les interfaces (équivalent à listen *:80;).

2.2 Hôtes virtuels par nom

On utilisera la directive server_name.

listen 80;
server_name example.org;

Pour définir un hôte virtuel par défaut, au cas où aucun hôte virtuel ne correspond à la requête, on utilisera l’argument default_server dans la directive listen, sinon c’est l’ordre d’apparition des configurations qui s’appliquera (comme pour Apache) :

listen 80 default_server;

2.3 Hôtes virtuels dynamiques

Il n’y a pas de module équivalent à celui d’Apache, mais on peut aisément se débrouiller avec une regex et une capture :

server {
    listen 80;
    server_name   ~^(www\.)?(?<domain>.+)$;
    root       /srv/httpd/$domain/public_html/;
}

Source : http://syshero.org/post/68729802960/nginx-dynamically-configured-mass-virtual-hosting

3 Exercices

Les exercices sont à rendre sur https://lstu.fr/asrall-web-tp3. Envoyez vos réponses dans un fichier appelé tp3_votre_nom.txt.

Vous pouvez envoyer un seul fichier pour un binôme, mais pensez bien à mettre vos deux noms dans le nom du fichier.

Pour chacun des sites mis en place, définissez un document HTML minimal permettant de voir quel est le site correspondant à chaque requête. Un simple echo hostname > index.html suffira.

3.1 Apache ET Nginx

Chaque question est donc à traiter deux fois.

  1. Définissez 2 hôtes virtuels par adresse. Pour cela, associez plusieurs adresses IP différentes à votre interface réseau. Si vous utilisez une machine virtuelle, vous pouvez ajouter plusieurs adresses IPs à celle-ci, et tester les différentes adresses IP depuis l'intérieur de la machine virtuelle avec curl ou un navigateur texte comme lynx ou w3m.
  2. Définissez 2 hôtes virtuels par nom. Utilisez la commande telnet pour mettre en évidence le mécanisme de sélection de l’hôte. Pour une utilisation directe dans votre navigateur, modifiez le fichier /etc/hosts.
  3. Mettez en place un ensemble d’hôtes virtuels dynamiques : en particulier, définissez plusieurs sites tels qu’ils soient accessibles via une url du type http://site.tld

  1.  http://linux.die.net/man/8/ifconfig

  2.  À votre avis, pourquoi le fichier par défaut 000-default.conf est-il nommé ainsi ?

  3.  Imagine-t-on un hébergement mutualisé où chaque hôte virtuel serait ajouté manuellement ?

  4.  http://httpd.apache.org/docs/2.4/mod/mod_vhost_alias.html


https://asrall.fr