Configurer son serveur web

Un petit article sans prétentions pour l’installation d’un serveur web maison.

Les accès système se feront par SSH, un protocole sûr et reconnu qui équipe nativement les serveurs sous Linux.

Le mieux est la connexion via SSH + clé d’identification (plus robuste qu’un simple mot de passe).

Générer des mots de passes fort

Pour générer un mot de passe fort (ajustez le 32 pour la longueur de la chaine)

openssl rand -base64 32

Mettre à jour le système

Pour mettre à jour le système d’exploitation ET les logiciels installés

apt update && apt -y upgrade && apt -y autoremove

Le must est de faire une mise à jour hebdomadaire du système (pour limiter les failles de sécurité). On créé donc un fichier update-system.sh

On va créer un dossier pour stocker nos scripts pour les mises à jour

mkdir /root/update/
nano /root/update/update-system.sh
#!/bin/bash
apt update && apt -y upgrade && apt -y autoremove

On rend le script exécutable

chmod +x /root/update/update-system.sh

On créé un lien pour une exécution hebdomadaire

ln -s /root/update/update-system.sh /etc/cron.weekly/update-system

Installation de Fail2Ban

Fail2Ban va nous permettre de bloquer durant un certain temps les personnes qui tentent de s’infiltrer dans le serveur.

Cela ne résous pas tous les problèmes, mais cela peut limiter les tentatives. Il faut penser à regarder les journaux système afin éventuellement d’être plus sévère sur le temps de banissement.

apt -y install fail2ban

Installation de duplicity

Pour le backup des sites

apt -y install duplicity

On créé le dossier qui va contenir les scripts

mkdir /root/backup
nano /root/backup/main.py
#!/usr/bin/env python3

import os
import sys

def backupSQL(site):
  # Supprimer les caractères interdits
  base = site
  base = (base).replace(".", "_")
  base = (base).replace("-", "_")

  print("SQL Base = " + base)

  # Contrôle que le répertoire de destination existe
  backPath = "/opt/sites/" + site + "/backup"

  try:
    os.makedirs(backPath)
  except:
    pass

  # La commande SQL de dump
  backCmd = "mysqldump -u root -pPa$$w0Rd" + base + " | gzip -9 -c > " + backPath + "/export.sql.gz"
  try:
    os.system(backCmd)
  except:
    pass

def backupFiles(site):

  with open("./.config", "w") as config:
    config.write('export SITE="' + site + '"' + "\n")
    config.write('export AWS_ACCESS_KEY_ID="xxxx"' + "\n")
    config.write('export AWS_SECRET_ACCESS_KEY="yyyy"' + "\n")
    config.write('export SCW_BUCKET="s3://bucket.cloud/' +                                      site + '"' + "\n")
    config.write('export SOURCE="' + '/opt/sites/' + site + '"' + "\n")
    config.write('export KEEP_BACKUP_TIME="1D"' + "\n")
    config.write('export FULL_BACKUP_TIME="1D"' + "\n")

  os.system("/root/backup/duplicity.sh")

for site in os.listdir('/opt/sites'):
  if os.path.isdir(os.path.join('/opt/sites/', site)):
    print("Backup %s" % site)
    backupSQL(site)
    backupFiles(site)
#    sys.exit()

Installation de MariaDB

On va maintenant installer le programme pour la gestion de base de données

apt -y install mariadb-server mariadb-client

Paramétrage

On va supprimer les utilisateurs qui ne servent à rien ET donner un mot de passe root en local uniquement.

mysql_secure_installation

On va modifier la façon dont notre utilsateur root peut se connecter

echo "UPDATE user SET plugin='' WHERE User='root'; FLUSH PRIVILEGES;" | mysql -u root -p mysql

Installation de Certbot

Pour faire des certificats SSL

apt -y install certbot

Installation de Apache + PHP

On installe Apache et PHP pour l’exécution des scripts.

apt -y install apache2 php libapache2-mod-php php-mysql

On installe également des extensions

apt -y install php-gd php-xml php-zip php-curl

Après chaque modification sur Apache, penser à redémarrer le service Apache :

systemctl restart apache2.service

Paramétrage PHP :

Attention à la version (ici 7.4), le chemin peut changer.
Pour savoir quelle version vous avez sur votre serveur

ls /etc/php/

sed -i 's/post_max_size = .*/post_max_size = 128M/' /etc/php/7.4/apache2/php.ini
sed -i 's/upload_max_filesize = .*/upload_max_filesize = 128M/' /etc/php/7.4/apache2/php.ini
sed -i 's/expose_php = .*/expose_php = Off/' /etc/php/7.4/apache2/php.ini

Paramétrage Apache

sed -i 's/ServerSignature .*/ServerSignature Off/' /etc/apache2/conf-enabled/security.conf
sed -i 's/ServerTokens .*/ServerTokens Prod/' /etc/apache2/conf-enabled/security.conf

On active quelques modules pour Apache

a2enmod ssl headers rewrite proxy proxy_http

On supprime les fichiers par défaut et l’on créé un fichier vide à la place.

rm /var/www/html/index.html
touch /var/www/html/index.php

On modifie le site par défaut de Apache

nano /etc/apache2/sites-enabled/000-default.conf
<VirtualHost *:80>
  DocumentRoot /var/www/html
  <Directory /var/www/html>
    DefaultType application/x-httpd-php
    DirectoryIndex index.php
    AddType application/x-httpd-php .php
    Require all granted
    Options +FollowSymLinks +MultiViews
  </Directory>
</VirtualHost>

Installation de Adminer

Pour la gestion des bases de données, beaucoup plus léger que phpMyAdmin

wget "http://www.adminer.org/latest-mysql-en.php" -O /var/www/html/adminer.php

On peut optimiser la mise à jour de Adminer

nano /root/update/update-adminer.sh
#!/bin/bash
wget "http://www.adminer.org/latest-mysql-en.php" -O /var/www/html/adminer.php

On rend le script exécutable

chmod +x /root/update/update-system.sh

On créé un lien pour une exécution hebdomadaire

ln -s /root/update/update-adminer.sh /etc/cron.weekly/update-adminer

Ajout des différents sites

On part sur une configuration chaque site web aura sa propre arborescence

  • « backup » pour le backup de la base de données
  • « conf » qui vont contenir les fichiers de configuration
  • « logs » qui vont contenir les logs
  • « public » pour les pages qui seront affichées sur internet

On imagine que le site à configurer est « mon-site.com »

mkdir -p /opt/sites/mon-site.com/backup /opt/sites/mon-site.com/conf /opt/sites/mon-site.com/logs /opt/sites/mon-site.com/public

Pensez à redonner les droits à l’utilisateur Apache sur ces répertoires

chown -R www-data:www-data /opt/sites/mon-site.com

On va donc rajouter un fichier de configuration pour Apache (prendre un fichier par site, ce qui limitera les problèmes). Attention au fait que le fichier doit finir par « .conf »

nano /etc/apache2/sites-enabled/zzz_mon-site.com.conf

Configuration sans certificat (http standard)

Define vHost mon-site.com
<VirtualHost *:80>
  ServerName ${vHost}
  ServerAlias *.${vHost}
  DocumentRoot /opt/sites/${vHost}/public
  <Directory /opt/sites/${vHost}/public>
    DefaultType application/x-httpd-php
    DirectoryIndex index.php
    AddType application/x-httpd-php .php
    Require all granted
    AllowOverride All
    Options +FollowSymLinks +MultiViews
  </Directory>
  ErrorLog "|/usr/bin/rotatelogs -l -f   /opt/sites/${vHost}/logs/apache-error.%Y.%m.%d.log 86400"
  CustomLog "|/usr/bin/rotatelogs -l -f /opt/sites/${vHost}/logs/apache-access.%Y.%m.%d.log 86400" common
  LogLevel warn
</VirtualHost>
UnDefine vHost

Configuration avec certificat (https)

Define vHost mon-site.com
<VirtualHost *:80
  ServerName ${vHost}
  ServerAlias *.${vHost}
  Redirect permanent / https://${vHost}/$1
</VirtualHost>

<VirtualHost *:443>
  ServerName ${vHost}
  ServerAlias *.${vHost}
  DocumentRoot /opt/sites/${vHost}/public
  <Directory /opt/sites/${vHost}/public>
    DefaultType application/x-httpd-php
    DirectoryIndex index.php
    AddType application/x-httpd-php .php
    Require all granted
    AllowOverride All
    Options +FollowSymLinks +MultiViews
  </Directory>
  ErrorLog "|/usr/bin/rotatelogs -l -f   /opt/sites/${vHost}/logs/apache-error.%Y.%m.%d.log 86400"
  CustomLog "|/usr/bin/rotatelogs -l -f /opt/sites/${vHost}/logs/apache-access.%Y.%m.%d.log 86400" common
  LogLevel warn
  SSLEngine on
  SSLCertificateFile      /etc/letsencrypt/live/${vHost}/cert.pem
  SSLCertificateKeyFile   /etc/letsencrypt/live/${vHost}/privkey.pem
  SSLCertificateChainFile /etc/letsencrypt/live/${vHost}/fullchain.pem
  SSLProtocol all -SSLv2 -SSLv3
  SSLHonorCipherOrder on
  SSLCompression off
  SSLOptions +StrictRequire
  SSLCipherSuite 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:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
  Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</VirtualHost>
UnDefine vHost

Ajouter un utilisateur pour la base de données. Privilégiez 1 utilisateur par base de données avec un mot de passe fort.

  • dbName est le nom de la base de données
  • dbUser est le nom de l’utilisateur
  • dbPassword est le mot de passe pour cet utilisateur
CREATE DATABASE dbName;
GRANT ALL ON dbName.* TO 'dbUser'@'localhost' IDENTIFIED BY 'dbPassword';