Configuration serveur web sous OpenBSD

Posted on December 3, 2016

J’ai récemment voulu héberger un mediawiki sur mes serveurs openbsd, voici un résumé des étapes.

Les outils utilisés :

  • nginx, car httpd ne permet pas encore (version 6.0) à faire du SNI
  • mariadb en tant que BDD
  • php56 fpm pour interpréter le PHP
  • doas pour exécuter des programmes sans avoir les droits root
  • cron pour renouveler les certificats

Installation des programmes qu’il faut :

pkg_add php56_fpm nginx mariadb-server

Premièrement, la configuration du serveur web nginx : pour l’instant sans la partie sécurisée.

# nginx, changer "domain" par votre nom de domaine

    server {
        listen       80;
        listen       [::]:80;
        server_name  domain www.domain;

        location /.well-known/acme-challenge {
                alias /htdocs/challenges/;
                location ~ /.well-known/acme-challenge/(.*) {
                        add_header Content-Type application/jose+json;
                }
        }
        location / {
                rewrite  ^ https://domain$request_uri? permanent;
        }
    }

Nginx est mis en chroot, donc les programmes n’ont pas accès à la résolution de noms si le fichier resolv.conf n’est pas copié dans le chroot.

mkdir /var/www/etc/
cp /etc/resolv.conf /var/www/etc/

Let’s encrypt : téléchargement d’un programme qui permet de faire signer notre certificat.

# changez partout "domain" par votre nom de domaine

cd /etc/ssl/
git clone https://github.com/diafygi/acme-tiny.git

# création d'une clé de compte
openssl genrsa 4096 > account.key

# générer une clé de domaine
openssl genrsa 4096 > domain.key

# pour un seul domaine
openssl req -new -sha256 -key domain.key -subj "/CN=domain" > domain.csr

# répertoire où se trouveront les fichiers de test
mkdir /var/www/htdocs/challenges

#run the script on your server
python3.5 acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir /var/www/htdocs/challenges/ > ./signed.crt

wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem
cat signed.crt intermediate.pem > chained-domain.pem

rcctl restart nginx

La partie sécurisée de la configuration de nginx, maintenant qu’on a notre certificat ça va bien se passer.

# nginx, changer "domain" par votre nom de domaine

    server {
        listen       443;
        listen       [::]:443;
        server_name  domain www.domain;
        root  /var/www/htdocs/domain/;

        index index.php index.html;

        ssl                  on;
        ssl_certificate      /etc/ssl/le/chained-domain.pem;
        ssl_certificate_key  /etc/ssl/le/domain.key;

        ssl_session_timeout  5m;
        ssl_session_cache    shared:SSL:1m;

        ssl_ciphers  HIGH:!aNULL:!MD5:!RC4;
        ssl_prefer_server_ciphers   on;

        location / {
                root  /var/www/htdocs/domain/;
        }
        location ~ \.php$ {
                try_files      $uri $uri/ =404;
                fastcgi_pass   unix:/run/php-fpm.sock;
                fastcgi_index  index.php;
                fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include        fastcgi_params;
        }

        location /.well-known/acme-challenge {
                alias /htdocs/challenges/;
                location ~ /.well-known/acme-challenge/(.*) {
                        add_header Content-Type application/jose+json;
                }
        }
    }

Configuration de php : on rajoute des extensions pour mediawiki et on ajoute des lignes dans le fichier /etc/php-5.6.ini.

# installation de bibliothèques pour PHP
pkg_add php-gd-5.6 php-intl php-xcache

# fichier /etc/php-5.6.ini
extension=curl.so
extension=gd.so
extension=intl.so
extension=mysql.so

# redémarrer PHP FPM
rcctl restart php56_fpm

# vérifier les extensions disponibles, voir si ça a été pris en compte
php-5.6 -m

Les modules installés se trouvent dans /usr/local/lib/php-5.6/modules/intl.so.

Ensuite, j’ai voulu installer un mariadb, pour ça j’ai simplement suivi mes propres conseils de la page dédiée à l’installation d’un mysql, cette fois en changeant mysql-server par mariadb-server.

Ensuite, quelques commandes pour mettre en place la BDD telle qu’on la voudrait :

# le % est là pour indiquer que cet utilisateur peut se connecter à partir de n'importe-quelle adresse IP
# mettre "localhost" si votre sgbd est sur la même machine que le serveur web
create database dbname;
create user username@"%";
set password for username@"%" = password('your-password');
grant all on username.* to dbname@"%";

Enfin, si votre sgbd est sur une autre machine, il faut qu’il accepte les connexions non locales. Pour ça on va dans le fichier /etc/my.cnf et on modifie la ligne bind-address par :

bind-address    = 0.0.0.0

Ensuite, pour mettre à jour les certificats, je me suis fait un script que j’ai mis dans /etc/ssl/le/renew-certs.sh :

#!/usr/bin/sh

DOMAIN=$1
LEDIR=/etc/ssl/le
ACMEDIR=/var/www/htdocs/challenges

python3.5 ./acme-tiny/acme_tiny.py --account-key $LEDIR/account.key --csr $LEDIR/$DOMAIN.csr --acme-dir $ACMEDIR > tmpsigned.crt || exit
ftp -o intermediate.pem https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem
cat tmpsigned.crt intermediate.pem > $LEDIR/chained-$DOMAIN.pem
rm tmpsigned.crt

Puis un script que j’ai placé dans /usr/local/bin/update-domain-certs pour lancer mes autres scripts.

# /usr/local/bin/update-domain-certs
#!/bin/sh

LEDIR=/etc/ssl/le

cd $LEDIR || exit 1
sh ./renew-certs.sh domain1 || exit 2
sh ./renew-certs.sh domain2 || exit 2
# ...

Pour lancer le script précédent, les droits de l’utilisateur www suffisent, donc on configure doas :

# /etc/doas.conf
permit nopass keepenv root as www cmd /usr/local/bin/update-domain-certs

Et pour que ça se fasse tous les mois (donc largement avant la limite des 90 jours pour l’expiration des certificats let’s encrypt), il suffit de faire appel à cron :

# fichier /etc/monthly.local
doas -u www /usr/local/bin/update-domain-certs && rcctl reload nginx

Je ne détaille pas l’installation de mediawiki, je pense qu’il n’y a pas grand chose à dire. Le site est à mettre dans le répertoire /var/www/htdocs/domain/.

Pour conclure je dirais que vivement que httpd gère SNI. La configuration devrait être encore plus légère et simple côté serveur web, et je serai un peu plus confiant puisque le développement est fait par des gens d’openbsd. fanboy detected.