This one is in french to help fellow french-spekears
Vous avez codé un de vos premiers projets Django et vous êtes un peu perdu.e.s sur comment le déployer en pré-prod/prod ? Ne paniquez pas, voici une méthode qui a le mérite de marcher (on ne sait pas si c’est LA bonne méthode, mais on a réussi avec elle, plusieurs fois !)
Prérequis
- vous êtes un utilisateur non root avec les privilèges
sudo
- votre projet contient un
requirements.txt
travaille avec un environnement virtuel - votre projet contient un
settings.py
et unlocalsettings.py
- votre projet est versionné par git
- pour plus de simplicité, on va supposer que votre projet utilise sqlite3 (qui est la base de données par défaut de Django) (et honnêtement, avec mysql/postgresql il suffit de créer la base de données et l’utilisateur, vous êtes grand.e.s on vous fait confiance)
- on suppose que python3 et python3-dev sont installés
- on suppose que nginx est installé
- on suppose que vous avez accès au fichier de zone DNS et que vous avez ajouté votre sous-domaine subdomaine.ndd.tld au fichier de zone.
Si vous venez de le faire, la réplication DNS peut prendre du temps. En attendant, pensez à modifier votre /etc/hosts
en y ajoutant une ligne de correspondance IP/sous-domaine :
xxx.xxx.xx.xx subdomaine.ndd.tld
Créer un nouvel utilisateur pour le projet
Pour être sûr.e.s que votre projet est bien enfermé là où il faut, nous vous proposons de créer un utilisateur particulier pour y mettre votre code.
Pour cela, tapez :
sudo adduser user-with-explicit-name --disabled-password
Puis, pour devenir, user-with-explicit-name :
sudo su - user-with-explicit-name
Puis, allez dans le home du nouvel utilisateur (cd
).
Récupérer le code
Pour cela, il suffit de taper l’invocation git idoine :
git clone https://gitlab.ndd.tld/Username/my-awesome-project.git
Préparer l’environnement virtuel
Tout d’abord, entrez dans le dossier contenant le projet :
cd ~/my-awesome-project
Puis, créez l’environnement virtuel, que nous appelerons de façon très originale myvenv
:
python3 -m venv myvenv
Et activez-le :
source myvenv/bin/activate
Enfin, installez vos dépendances (et gunicorn, si ce n’est pas dans vos dépendances de prod) :
pip install -r requirements.txt
pip install gunicorn
Modifications des sources Django
Modifications du localsettings.py
Il faut modifier le ALLOWED_HOSTS
de la façon suivante :
ALLOWED_HOSTS = ['subdomaine.ndd.tld', 'domain2']
Modifications du settings.py
On peut mettre gunicorn dans les INSTALLED_APPS
du setting.py
.
Ouvrir les ports
iptables
Si vous utilisez IPTABLES, il faut taper quelque chose comme cela :
sudo iptables -I INPUT -p tcp --dport 80 -j ACCEPT<br />sudo iptables -I INPUT -p tcp --dport 443 -j ACCEPT
ferm (un petit aparté sur les firewall)
Si vous avez pas mal de règles IPTABLES à gérer, pas envie de créer un script qui les mets en place à chaque démarrage… il y a le formidable outil ferm !
Il permet de gérer très simplement ses règles IPTABLES via des fichiers et de générer plusieurs règles IPTABLES en une seule ligne. Il s’agit d’une surcouche d’IPTABLES et il possède sa propre syntaxe.
Installation
sudo apt install ferm
Attention, à l’installation, la configuration de firewall par défaut sera installé, pensez à sauvegarder vos règles IPTABLES quelque part.
Configuration
Le fichier de configuration se trouve dans /etc/ferm
et se nomme ferm.conf
.
Comme vous pouvez le voir dans ce fichier, il y a la table FILTER découpée en 3 parties :
- chain INPUT (qui concerne… les règles d’INPUT) ;
- chain OUTPUT (qui concerne… les règles d’OUTPUT) ;
- chain FORWARD (qui concerne… oui, vous avez bien deviné, les règles de FORWARD).
La politique de base pour INPUT et FORWARD est de tout dropper et la politique de base pour OUTPUT est de tout accepter.
Dans la section INPUT, on peut voir que sont ensuite autorisées :
- les connexions déjà en cours (
mod state state (ESTABLISHED RELATED) ACCEPT
) - l’IPsec (
proto udp dport 500 ACCEPT
etproto (esp ah) ACCEPT
) - le SSH (
proto tcp dport ssh ACCEPT
).
Si vous ne savez pas ce qu’est l’IPsec cela veut dire que vous n’en utilisez pas, vous pouvez donc supprimer les lignes concernant l’IPsec (ça ne sert jamais d’ouvrir des ports non utilisés en input et en forward à part prendre des risques inutiles :P)
Ensuite, c’est simple ! Pour ajouter vos règles vous les ajoutez dans la section concernée !
Par exemple, nous voulons ouvrir le port http (80) et https (443) en entrée (Input) sur le serveur, vu que nous sommes en train de parler d’une application web.
Il faut aller dans la section chain INPUT du fichier ferm.conf et ajouter :
proto tcp port http ACCEPT;<br />proto tcp port https ACCEPT;
Puis recharger ferm et c’est parti :
service ferm reload
Bonnes pratiques ferm (tant qu’on y est)
Le mieux, c’est d’écrire sur une seule et même ligne les choses qui sont similaires, par exemple :
proto scp dport (http https) ACCEPT;
De plus, ce qui peut être bien, pour éviter d’avoir un fichier ferm.conf à rallonge et ranger un peu toutes ces règles, c’est de séparer en répertoires nos configurations de firewall, et ce par thème (VPN, SSH, web, FTP, …).
C’est là qu’intervient la directive @include
!
Une bonne pratique, c’est de laisser les règles de base (utile) de ferm dans ferm.conf à l’installation et ensuite d’ajouter la directive
@include "inpud.d"
à la fin de votre section chain INPUT.
Ensuite, créez un répertoire input.d
dans /etc/ferm
et ajoutez-y vos configurations.
Par exemple, pour vos configurations web, vous pouvez créez le fichier web.conf dans ce répertoire où vous ajouterez les règles que l’on a élaboré et puis, hop ! Un petit relaod du service et les règles sont mises en place.
Tout ce qui est possible avec IPTABLES est possible avec ferm. Il faut juste faire un peu de traduction niveau syntaxe, mais la doc est claire.
Tester la capacité de gunicorn à servir le projet
On suppose que vous avez appelé votre projet mysite.
/home/user-with-explicit-name/myvenv/bin/gunicorn --bind 0.0.0.0:8001 mysite.wsgi:application
Puis, connectez vous pour vérifier que tout se passe bien, c’est-à-dire visitez : http://domain_or_IP:8001
Service systemd
Écriture du fichier de configuration
Commencez par ouvrir (avec les bons privilèges)(et en tant que vous) le fichier systemd idoine :
sudo vim /etc/systemd/system/my-awesome-project.service
Puis, vous allez écrire les invocations suivantes :
[Unit]
Description="My awesome project"
After=syslog.target
After=network.target
[Service]
Type=simple
User=user-with-explicit-name
Environment=SECRET_KEY=secret
WorkingDirectory=/home/user-with-explicit-name/my-awesome-project/
ExecStart=/home/user-with-explicit-name/myvenv/bin/gunicorn/ --access-logfile - --workers 3 --bind
unix:/home/user-with-explicit-name/my-awesome-project/my-awesome-project.sock mysite.wsgi:application
TimeoutSec=300
[Install]
WantedBy=multi-user.target
- La partie [Unit] concerne les métadonnées et les dépendances. Ici, on lui dit de démarrer après syslog (pour les logs) et après que le réseau ait démarré (ce qui est assez raisonnable pour un site :p).
- La partie [Service] concerne le service. On y spécifie l’utilisateur, le groupe, les modifications de l’environnement, le dossier dans lequel on travaille, le temps de timeout, mais surtout l’invocation de gunicorn.
- La partie [Install] concerne ce qu’il se passe si le service est indiqué comme devant démarrer au démarrage du serveur. Ici, on lui dit de démarrer quand le système multi-utilisateur est chargé.
Démarrage du service
Vous pouvez vous lancer ! Il suffit d’invoquer cela :
sudo systemctl start my-awesome-project.service
sudo systemctl enable my-awesome-project.service
(Le enable sert à activer le service, c’est-à-dire à ce qu’il se lance systématiquement au démarrage.)
Vérification du statut du service
Vous pouvez vérifier l’état du service en utilisant la commande suivante :
sudo systemctl status my-awesome-project.service
Vous devriez avoir un truc qui ressemble à ça :
● my-awesome-project.service - "My awesome project"
Loaded: loaded (/etc/systemd/system/my-awesome-project.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2017-11-12 16:55:57 CET; 2 weeks 3 days ago
Main PID: 15156 (gunicorn)
Tasks: 2 (limit: 4915)
Memory: 23.4M
CPU: 3min 31.717s
CGroup: /system.slice/my-awesome-project.service
├─15156 /home/user-with-explicit-name/myvenv/bin/python3 /home/user-with-explicit-name/myvenv/bin/gunicorn -access-logfile - --workers 3 --bind unix:/home/user-with-explicit-name/my-awesome-project/my-awesome-project.sock mysite.wsgi:application
└─15159 /home/user-with-explicit-name/myvenv/bin/python3 /home/user-with-explicit-name/myvenv/bin/gunicorn -access-logfile - --workers 3 --bind unix:/home/user-with-explicit-name/my-awesome-project/my-awesome-project.sock mysite.wsgi:application
Installer certbot
Sous Debian 8 (Jessie)
Activer les backports
Ajouter cette ligne dans /etc/apt/source.list
:
deb http://ftp.debian.org/debian jessie-backports main
Puis, faites un update.
Installation proprement dite
sudo apt-get install certbot -t jessie-backports
Si vous êtes sous Debian 9 (stretch)
Certbot est packagé, tout ce qu’il vous faut faire :
sudo apt install certbot
Certificats avec certbot
On présente une méthode, il en existe plusieurs (go go go go) !
La configuration de certbot
Il va falloir configurer un peu certbot pour nginx :
sudo certbot certonly --authenticator standalone --pre-hook "service nginx stop" <br />--post-hook "service nginx restart
La génération proprement dite
Il suffit de taper la commande suivante :
certbot certonly --standalone --preferred-challenges http subdomain.ndd.tld
Normalement vous devriez avoir plein de lignes qui vous disent où sont situés les fichiers importants pour la suite.
Le renouvellement des certificats
Comme les certificats Let’s Encrypt sont valides 90 jours, il paraît judicieux d’automatiser leur renouvellement ! Heureusement tout cela est pris en charge par certbot, via un cron.
Vérifiez que tout se passe bien en tapant :
sudo certbot renew --dry-run
Si vous voyez des erreurs, corrigez-les (mais tout devrait bien se passer !).
Dans tous les cas, il faut éditer la configuration pour recharger le service une fois les certificats renouvelés. Pour cela, il faut éditer le fichier /etc/letsencrypt/renewal/subdomain.ndd.tld.conf
en ajoutant sur la dernière ligne :
renew_hook = systemctl reload my-awesome-project.service
De la conf nginx !
Maintenant que tout est prêt, il faut aller créer le fichier de configuration pour nginx
dans /etc/nginx/sites-available/my-awesome-project
!
Il faut y ajouter la configuration suivante :
server {
listen 80;
server_name subdomain.ndd.tld;
server_tokens off;
location / {
return 301 https://subdomain.ndd.tld$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name subdomain.ndd.tld;
server_tokens off;
access_log /var/log/nginx/my-awesome-project.access.log;
error_log /var/log/nginx/my-awesome-project.error.log warn;
ssl on;
ssl_certificate /etc/letsencrypt/live/subdomain.ndd.tld/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/subdomain.ndd.tld/privkey.pem;
client_max_body_size 20M;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/user-with-explicit-name/my-awesome-project;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/user-with-explicit-name/my-awesome-project/my-awesome-project.sock;
}
}
Vous remarquerez qu’on utilise les certificats générés à l’étape précédente !
Superviser tout ça avec Icinga !
Il faut que vous supervisiez votre socket, pour voir les éventuels problèmes que vous avez sur votre site, et ce de façon plus pratique que de consulter votre site !
Fichiers de configuration Icinga
On ne va pas faire un cours complet sur comment déployer icinga, ni le configurer, ni mettre en place son interface web (vi o on sent bien que vous en avez marre :p…), on va juste se contenter de vous donner le fichier de configuration idoine :
define service{
use generic-service
host_name your-host-name
service_description check_my_awesome_project
check_command check_url_https!https://subdomain.domain.tld
}
Ensuite, il faut ajouter dans le fichier /etc/nagios-plugins/config/http.cfg
les invocations suivantes :
define command{
command_name check_url_https
command_line /usr/lib/nagios/plugins/check_http --ssl -H '$HOSTADDRESS$' -u '$ARG1$'
}
Pfiou c’est fini.