Outils du site


Gestion des entêtes avec relayd

Une gestion fine des entêtes (headers) peut vous intéresser. Cela peut notamment servir pour indiquer aux navigateurs de garder en cache plus longtemps les fichiers téléchargés et alléger la charge du serveur, ou encore régler des questions de sécurité.

httpd n'est pas capable de gérer les entêtes (ou headers). Heureusement, tout est prévu : nous allons utiliser relayd et le placer avant httpd.

Inutile d'installer quoi que ce soit, relayd est déjà présent dans OpenBSD. Elle est pas belle la vie ? ^^

Pour tester les entêtes de votre site, vous voudrez peut-être visiter celui-ci : https://securityheaders.com/

Configuration de relayd

La configuration de relayd est écrite dans le fichier /etc/relayd.conf que nous allons éditer.

À l'intérieur, et à titre d'exemple, nous allons mettre les lignes suivantes :

http protocol "http" {
  match request header remove "Proxy"
  match response header set "X-Xss-Protection" value "1; mode=block"

  return error
  pass
}

relay "www" {
  listen on 127.0.0.1 port 8080
  protocol "http"
  forward to destination
}

Voici ce que ces lignes signifient :

  • http protocol “http” { : On ouvre la configuration pour tout ce qui concerne le protocole http, qu'on appelle justement “http”.
  • match request header remove “Proxy” : On retire un entête qui peut poser des soucis de sécurité. Cela se produit à l'arrivée de la requête et permet de ne pas envoyer de mauvais entêtes au serveur httpd.
  • match response header set “X-Xss-Protection” value “1;… ” : On protège le site d'attaques XSS.
  • return error : On renvoie une erreur s'il y a eu le moindre souci.
  • pass : S'il n'y a pas eu de problèmes, on laisse passer.
  • relay “www” { : On va définir ici la redirection vers le serveur http.
  • listen on 127.0.0.1 on 8080 : On écoute sur le port 8080 uniquement en local.
  • protocol “http” : On utilise la configuration énoncée plus haut.
  • forward to destination : On renvoie vers le serveur qui doit initialement recevoir cette requête, c'est à dire httpd.

Justement, afin que relayd intercepte ce qu'httpd devait recevoir directement, nous devons modifier un petit peu les règles du parefeu. Vous devrez éditer le fichier /etc/pf.conf pour compléter ainsi les règles concernant le serveur web :

anchor "relayd/*"
pass in on egress proto tcp to port www divert-to 127.0.0.1 port 8080 modulate state
pass in on egress proto tcp to port https divert-to 127.0.0.1 port 8443 modulate state

Si on résume, les choses se passent désormais ainsi :

  1. Un visiteur demande à voir votre site web, il se présente sur le port 80 ou 443.
  2. pf le renvoie à relayd écoutant sur le port 8080 ou 8443 qui se permet au passage de modifier quelques entêtes.
  3. relayd redirige le tout à httpd qui retourne la page web demandée comme il le faisait d'habitude.

Après avoir réalisé vos modifications, n'oubliez pas d'activer relayd et de redémarrer les services, ainsi que recharger le parefeu :

# pfctl -f /etc/pf.conf
# rcctl enable relayd
# rcctl start relayd

Vous pouvez consulter un exemple de configuration à la fin du document.

Notez cependant que ces modifications sont valables sur l'ensemble de vos domaines et sites, vous ne pouvez pas gérer les entêtes au cas par cas. Si vous avez besoin de davantage de fonctionnalités, tournez vous plutôt vers un proxy comme nginx par exemple.

Pour aller plus loin sur relayd, vous pouvez lire la page du wiki OBSD4* correspondante.

TLS ou https

Si votre site propose un accès chiffré avec une adresse https://..., (c'est bien ! ;-)), la configuration de relayd peut-être déroutante.

Ci-dessous, voici un exemple de configuration de relayd correspondante. Notez les mentions de tls :

http protocol "https" {
  match request header remove "Proxy"
  match response header set "X-Xss-Protection" value "1; mode=block"

  return error
  pass
  
  tls keypair chezmoi.tld
  tls keypair ici.tld
}

relay "tlsforward" {
  listen on 127.0.0.1 port 8443 tls
  protocol "https"
  forward with tls to destination
}

Prêtez attention aux lignes tls keypair. Elles permettent de définir les certificats et clés à utiliser pour le chiffrement TLS. Dans l'exemple ci-dessus, on précise deux certificats possibles pour deux domaines différents. Vous pouvez ajouter une ligne pour chaque domaine géré par votre serveur.

Toutefois, il est indispensable que ces certificats soient placés comme on l'a présenté dans la partie sur acme-client. Autrement dit, vos certificats et clés seront les fichiers suivants :

/etc/ssl/private/chezmoi.tld.key
/etc/ssl/chezmoi.tld.crt

De plus, le fichier /etc/ssl/chezmoi.tld.crt doit être le certificat “fullchain”. Vous devrez donc certainement faire une copie :

# cp /etc/ssl/chezmoi.tld-fullchain.pem /etc/ssl/chezmoi.tld.crt

Inutile de préciser quoi que ce soit en plus dans la configuration de relayd ou httpd, tout fonctionne normalement comme prévu avec l'utilisation de vos certificats ;)

Renouvellement des certificats

Si vous renouvelez vos certificats avec acme-client, pensez à recharger relayd pour qu'il tienne compte des changements. Par exemple :

/usr/sbin/acme-client -v chezmoi.tld && /usr/sbin/rcctl reload relayd

Gestion des entêtes

Conserver l'IP du visiteur pour les logs

Si vous souhaitez analyser les logs pour par exemple générer des statistiques, vous remarquerez qu'en l'état, httpd détecte que la source des connexions est 127.0.0.1 au lieu de l'IP du visiteur. C'est normal, puisqu'il reçoit la requête depuis relayd.

Afin de conserver l'IP du visiteur, ajoutez ces lignes dans /etc/relayd.conf juste après http protocol… :

# Pour garder l'IP source
match request header append "X-Forwarded-For" \
    value "$REMOTE_ADDR"
match request header append "X-Forwarded-By" \
    value "$SERVER_ADDR:$SERVER_PORT"

Indiquez ensuite dans la configuration de httpd le nouveau style des logs :

log style forwarded

Httpoxy

Si votre site n'est accessible qu'en http (pas de chiffrement), alors je vous conseille vivement de vous prémunir contre une faille connue sous le doux nom de httpoxy. Cela peut permettre (en gros hein…) à un pirate de paraître venir de votre serveur lorsqu'il réalise une attaque.

Ou alors je mets tout en https, c'est plus simple non ?

Oui ça serait plus simple et mieux pour la sécurité de vos visiteurs.

Voici à quoi ressemblera le fichier relayd.conf minimal pour s'en protéger :

http protocol "http" {
    match request header remove "Proxy"
    return error
    pass
}

relay "www" {
    listen on 127.0.0.1 port 8080
    protocol "http"
    forward to destination
}

Autres entêtes de sécurité

En plus du précédent, comme proposé dans l'exemple à la fin, vous pouvez ajouter d'autres règles pour améliorer la sécurité de votre serveur en modifiant ces entêtes :

match request header remove "Proxy"
match response header set "X-Xss-Protection" value "1; mode=block"
match response header set "Frame-Options" value "SAMEORIGIN"
match response header set "X-Frame-Options" value "SAMEORIGIN"
match response header set "X-Robots-Tag" value "index,nofollow"
match response header set "X-Permitted-Cross-Domain-Policies" value "none"
match response header set "X-Download-Options" value "noopen"
match response header set "X-Content-Type-Options" value "nosniff"

Si vous n'hébergez qu'un seul nom de domaine, vous devriez ajouter ceci :

match response header set "Access-Control-Allow-Origin" value "chezmoi.tld"

Plus d'informations :

Optimisation du cache et de la bande passante

Que vous ayez une bande passante performante ou non, je vous conseille vivement d'optimiser le nombre de requêtes qu'un visiteur réalisera lorsqu'il visitera votre site. Pour cela, vous pouvez indiquer à son navigateur de garder les ressources (images, feuilles de style…) en cache pendant un temps assez long (21 jours dans l'exemple : 21 jours = 1814400 secondes).

Voici les éléments à ajouter dans la section “protocol” juste avant le mot clé “pass” :

match request path "/*.css" tag "CACHE"
match request path "/*.js" tag "CACHE"
match request path "/*.atom" tag "CACHE"
match request path "/*.rss" tag "CACHE"
match request path "/*.xml" tag "CACHE"
match request path "/*.jpg" tag "CACHE"
match request path "/*.png" tag "CACHE"
match request path "/*.svg" tag "CACHE"
match request path "/*.gif" tag "CACHE"
match request path "/*.ico" tag "CACHE"

match response tagged "CACHE" header set "Cache-Control" value "max-age=1814400"

L'idée est très simple, à chaque fois qu'un visiteur demande un fichier se terminant par l'une des extensions “.css, .js, .atom, … , .ico”, on colle sur la requête une étiquette “CACHE”. À la fin, lorsque relayd détecte une requête avec cette étiquette, il ajoute un entête “Cache-Control” avec une valeur assez grande pour que le navigateur ne tente pas de télécharger à nouveau ce fichier de sitôt.