Installation de django-helpdesk dans une jail

Dans l’association à laquelle je participe, nous avons un petit problème pour le traitement efficace des mails qui nous arrives. Il n’est pas rare que nous mettions plus d’une semaine à répondre à un mail. Ce n’est pas correcte vis-a-vis de nos correspondants.

L’idée d’utiliser un logiciel de helpdesk est venu à mon esprit. Ainsi nous consevrions la date d’arrivée des mails, la réponse serait affecté à membre de l’association qui serait en charge (en accord avec les autres membres si nécessaire) de répondre. En cas de non traitement au bout d’un temps défini, le mail serait transmis pour « escalade » c’est-à-dire qu’il devient urgent de répondre.

Nos besoins

Nous n’avons pas besoin d’une interface web pour que des utilisateurs fassent des tickets. Le seul point d’entrée doit être un mail envoyé à une adresse définie et connue de toutes et tous.

Nous n’avons pas besoin non plus que le correspondant reçoivent une notification impersonnelle du type « Votre ticket est bien arrivé chez nous. Un membre de l’équipe va prochainement prendre contact avec vous. Veuillez rappeler le numéro de ticket suivant 12345 pour toute correspondance ultérieure ».

Non, juste un mail est reçu à une adresse définie, cela crée un ticket qui est redistribué à l’ensemble de l’équipe et celui ou celle qui a du temps/les compétences/l’envie se l’approprie et le gère.

S’il s’agit d’un problème/demande qui exige plus de réflexion l’équipe au complet en discute et fait une réponse par l’intermédiaire d’un⋅e des membres.

Pour ce qui est des tickets qui concernent le code, les bugs, les demandes d’améliorations, nous utilisons le système interne de github qui nous convient parfaitement.

Les forces en présence

Il y a les poids lourd, tel Jira (que nous n’avons pas le moyen de nous offrir et qui me donne encore des cauchemars pour l’avoir utiliser dans le passer, redmine, projet libre qui ferait doublons avec le système de github et qui du coup plus orienté code.

Mais j’ai découvert django_helpdesk

Les avantages

C’est du python, avec django, deux technos qui sont maîtrisées dans l’association.

Les inconvénients

À ma première installation pour voir j’ai trouvé cela un poil ardu. C’est aussi pour cela que j’écris ce qui billet de blog, pour laisser des traces.

 Comme pour tous les billets concernant les jails, je suppose que vous disposez d’une jail état de marche avec accès réseau.

 Ce billet est divisé en deux parties, l’installation de django, et l’installation de django helpdesk.

 Nous installerons également gunicorn qui fait l’interface encore les requêtes entrante via nginx et django.

  Cette documentation est un mix entre cette page et la documentation d’installation de django-helpdesk.

Installation de python

Le langage utilisé par django et django_helpdesk, il est nécessaire d’installer python. Nous allons opter pour la version la plus récente, python 3.6.x.

 # pkg search python
 py36-python-tools-3.6.9_1      Supplementary tools for the Python language
 python27-2.7.16_1              Interpreted object-oriented programming language
 python35-3.5.7_2               Interpreted object-oriented programming language
 python36-3.6.9                 Interpreted object-oriented programming language

 # pkg install -y python36 py36-python-tools
 Updating poudriere repository catalogue...
 poudriere repository is up to date.
 All repositories are up to date.
 The following 1 package(s) will be affected (of 0 checked):

 New packages to be INSTALLED:
     py36-python-tools: 3.6.9_1

 Number of packages to be installed: 1

 12 KiB to be downloaded.
 [support] [1/1] Fetching py36-python-tools-3.6.9_1.txz: 100%   12 KiB  12.3kB/s    00:01
 Checking integrity... done (0 conflicting)
 [support] [1/1] Installing py36-python-tools-3.6.9_1...
 [support] [1/1] Extracting py36-python-tools-3.6.9_1: 100%

python36 était déjà installé dans ma jail semble t-il.

Nous aurons aussi besoin d’accéder au catalogue de greffon python en passant par l’outil pip que nous devons aussi installer.

 # pkg install -y py36-pip
 Updating poudriere repository catalogue...
 poudriere repository is up to date.
 All repositories are up to date.
 The following 2 package(s) will be affected (of 0 checked):

 New packages to be INSTALLED:
     py36-pip: 19.1.1
     py36-setuptools: 41.2.0

 Number of packages to be installed: 2

 The process will require 15 MiB more space.
 2 MiB to be downloaded.
 [support] [1/2] Fetching py36-pip-19.1.1.txz: 100%    2 MiB   1.9MB/s    00:01
 [support] [2/2] Fetching py36-setuptools-41.2.0.txz: 100%  498 KiB 510.2kB/s    00:01
 Checking integrity... done (0 conflicting)
 [support] [1/2] Installing py36-setuptools-41.2.0...
 [support] [1/2] Extracting py36-setuptools-41.2.0: 100%
 [support] [2/2] Installing py36-pip-19.1.1...
 [support] [2/2] Extracting py36-pip-19.1.1: 100%
 =====
 Message from py36-pip-19.1.1:

 --
 pip MUST ONLY be used:

  * With the --user flag, OR
  * To install or manage Python packages in virtual environments

 Failure to follow this warning can and will result in an inconsistent
 system-wide Python environment (LOCALBASE/lib/pythonX.Y/site-packages) and
 cause errors.

 Avoid using pip as root unless you know what you're doing.

Autres installations

Nous avons besoin aussi de sqlite3, la base de données utilisée par django et py36-sqlite3 pour accéder à cette base avec le langage python

 # pkg install -y sqlite3 py36-sqlite3

Installation de django

Django va tourner dans un environnement virtuel python avec un utilisateur qui n’est pas root. Il faut donc au préalable créer cet utilisateur.

Créer l’utilisateur support

Mon utilisateur s’appellera support afin d’être raccord avec le nom du service et le nom du serveur.

Utilisez la méthode que vous préférez pour cette création (pw ou useradd).

 # pw useradd -n support -c "Support user" -s /usr/local/bin/bash -m -w random

 Si vous utilisez la commande plus haut, penser à conserver le mot de passe généré aléatoirement.

 En tout état de cause, pensez à mettre bash comme shell pour cet utilisateur.1

Installation de django avec l’utilisateur support et pip

Notre première utilisation de pip sera de le mettre à jour&nbsp:

 # pip install --upgrade pip
et d’installer le pacquet virtualenv destiné à créer l’environnement virtuel dans lequel nous allons faire tourner django.

 # pip install virtualenv

Création de l’environnement virtuel

  Attention nous utilisons l’utilisateur support.

# su - support
[support@support ~]$ virtualenv --python=python3.6 venv
Already using interpreter /usr/local/bin/python3.6
Using base prefix '/usr/local'
New python executable in /usr/home/support/venv/bin/python3.6
Also creating executable in /usr/home/support/venv/bin/python
Installing setuptools, pip, wheel...
done.

Lancer notre environnement virtuel

  Cette commande sera à lancer à chaque fois que nous auront besoin de faire des changement dans django ou dans django-helpdesk.

[support@support ~]$ source venv/bin/activate

Notez le changement de prompt, qui nous indique que nous sommes dans un environnement virtuel et quel est son nom 

(venv) [support@support ~]$

Installer django et gunicorn

Enfin est venu le temps d’installer django et par la même occasion gunicorn :

1
2
(venv) [support@support ~]$ pip install django
(venv) [support@support ~]$ pip install gunicorn

Configuration de django

Une même instance de Django peut faire tourner plusieurs sous-projets, django lui même bien sûr, mais aussi des projets qui n’ont rien à voir entre eux.

Nous allons créer, dans django un projet support qui nous permettra de créer la base de données de django et ensuite de mettre en place django-helpdesk.

 (venv)[support@support ~]$ cd venv
 (venv)[support@support ~/venv]$ django-admin startproject support
 (venv)[support@support ~/venv]$ cd support
 (venv)[support@support ~/venv/support]$ pyton manage.py migrate
 Operations to perform:
   Apply all migrations: admin, auth, contenttypes, sessions
 Running migrations:
   Applying contenttypes.0001_initial... OK
   Applying auth.0001_initial... OK
   Applying admin.0001_initial... OK
   Applying admin.0002_logentry_remove_auto_add... OK
   Applying admin.0003_logentry_add_action_flag_choices... OK
   Applying contenttypes.0002_remove_content_type_name... OK
   Applying auth.0002_alter_permission_name_max_length... OK
   Applying auth.0003_alter_user_email_max_length... OK
   Applying auth.0004_alter_user_username_opts... OK
   Applying auth.0005_alter_user_last_login_null... OK
   Applying auth.0006_require_contenttypes_0002... OK
   Applying auth.0007_alter_validators_add_error_messages... OK
   Applying auth.0008_alter_user_username_max_length... OK
   Applying auth.0009_alter_user_last_name_max_length... OK
   Applying auth.0010_alter_group_name_max_length... OK
   Applying auth.0011_update_proxy_permissions... OK
   Applying sessions.0001_initial... OK

Installation de django-helpdek

Pré-requis

L’installation de django-helpdesk va compiler plusieurs choses et aura donc besoin d’un compilateur. Mais également de bibliothèques :

  Attention nous utilisons l’utilisateur root.

 # pkg install gcc py36-libxml2 libxml2 libxslt

Et bien sûr les dépendances qui vont avec.

Le retour de pip

  Attention nous utilisons l’utilisateur support

Avec notre utilisateur support, et dans l’environnement virtuel, nous allons installer django-helpdesk 2.

 (venv) [support@support ~/venv]$ pip install django-helpdesk

  Bien que le documentation officiel soit explicite, je vais continuer mon pas à pas, pour vous éviter les écueils dans lesquels je suis tombé.

Installation et configuration de django-helpdek dans django

Nous devons indiquer à django que django-helpdesk est l’une des applications qu’il est chargé de faire tourner.

Le fichier settings.py

Cela se fait dans le fichier /home/support/venv/support/support/settings.py. On ajoute les mêmes lignes que celles définies dans la documentation, certaines autres applications étant nécessaires 

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.sites',  # Required for determining domain url for use in emails
    'django.contrib.humanize',  # Required for elapsed time formatting
    'markdown_deux',  # Required for Knowledgebase item formatting
    'bootstrapform', # Required for nicer formatting of forms with the default templates
    'helpdesk',  # This is us!
]

Pour permettre à plusieurs projets de coexister, notre fichier doit aussi contenir un SITE_ID que nous positionnons à 1, mais qui devra évoluer pour d’autres applications.

SITE_ID = 1

Le fichier urls.py

Le fichier urls.py permet de définir urls propres au projet et définies dans un troisième fichier auquel, en théorie nous n’avons pas à toucher. Nous devons simplement nous assurer que le fichier urls.py est accessible et contient :

 from django.urls import path

  path('support/', include('helpdesk.urls')),
3

Une nouvelle migration

Avec ces informations, nous allons lancer une nouvelle fois la commande manage.py migrate afin de créer, dans la base de données django, les tables qui sont utiles au projet django-helpdesk.

  Faire tourner deux fois cette migration aboutira à une erreur.

 (venv)[support@support ~/venv/support/support]$ cd ..
 (venv)[support@support ~/venv/support]$ python manage.py migrate helpdesk

Copie/création des fichiers statique

Certaines fichiers (comme les css de django sont statiques. Il est temps de les installer.

Il est nécessaire de définir leurs emplacements avant de les y copier. Dans le fichier support/settings.py nous allons ajouter les lignes suivantes 

 STATIC_ROOT = '/usr/home/support/venv/support/static/'
 STATIC_URL = '/static/'

 MEDIA_ROOT = '/usr/home/support/venv/support/media/'
 MEDIA_URL = '/media/'

Tant que nous sommes dans ce fichier nous allons ajouter l’adresse IP de notre jail dans le tableau ALLOWED_HOSTS[]. Si nous ne faisons rien seul 127.0.0.1 pourra accéder à notre application. Nous allons donc être complètement explicite en y mettant :

 ALLOWED_HOSTS=['127.0.0.1', '192.0.2.11','support.example.com']
4

 (venv)[support@support ~/venv/support]$ python manage.py collectstatic
 800 static files copied to '/usr/home/support/venv/support/static'.

Une grosse étape vient d’être franchie, django et djangof-helpdesk sont installés, vous pouvez respirer.

Configuration de gunicorn

Nous avons, en même temps que django, guinicorn qui fait la liaison entre nginx le serveur web et django le serveur d’application.

Nous allons devoir configurer et lancer gunicorn. Cela se fait avec un fichier unique, le fichier de démarrage de gunicorn.

Ce fichier doit être placé dans le répertoire ~/venv/bin. Je l’ai appelé guincorn_start.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 #!/usr/local/bin/bash

 NAME="support"                                               # Django Project Name
 DJANGODIR=/usr/home/support/venv/support                     # Django Project Directory
 SOCKFILE=/usr/home/support/venv/support/run/gunicorn.sock    # Gunicorn Sock File
 USER=support                                                 # Django Project Running under user support
 GROUP=support                                                # Django Project Running under group support
 NUM_WORKERS=3
 DJANGO_SETTINGS_MODULE=support.settings
 DJANGO_WSGI_MODULE=support.wsgi

 echo "Starting $NAME as `whoami`"

 # Activate the virtual environment
 cd $DJANGODIR
 source ../bin/activate
 export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
 export PYTHONPATH=$DJANGODIR:$PYTHONPATH

 # Create the run directory if it doesn't exist
 RUNDIR=$(dirname $SOCKFILE)
 test -d $RUNDIR || mkdir -p $RUNDIR

 # Start your Django Unicorn
 # Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon)
 exec ../bin/gunicorn ${DJANGO_WSGI_MODULE}:application \
 --name $NAME \
 --workers $NUM_WORKERS \
 --user=$USER --group=$GROUP \
 --bind=unix:$SOCKFILE \
 --log-level=debug \
 --log-file=-

Donnons les droits d’exécution à son propriétaire :

 (venv)[support@support ~/venv]$ chmod 755 bin/gunicorn_start

Installation et configuration de supervisord

Le démon supervisrd sera en charge de lancer et de vérifier que gunicorn/django tournent et les relancera si le besoin s’en fait sentir.

Installation de supervisord

  Cette fois-ci nous utilisons à nouveau l’utilisateur root.

 # pkg install py-supervisor

Il faut faire en sorte que supervisord soit lancé au démarrage de la jail et nous en profitons pour le démarrer tout de suite :

 # sysrc supervisord_enable="YES"
 # service supervisord start

Il est important de créer le fichier de configuration de supervisord qui se trouve dans /usr/local/etc.

À la fin du fichier, on ajoute les informations qui concernent notre configuration :

1
2
3
4
5
6
 [program:support]
 command = sh /usr/home/support/venv/bin/gunicorn_start
 user = support
 stdout_logfile = /usr/home/support/venv/logs/gunicorn_supervisor.log
 redirect_stderr = true
 environment=LANG=en_US.UTF-8,LC_ALL=en_US.UTF-8

Créons le répertoire de logs et le fichier idoine :

 # mkdir /usr/home/support/venv/logs
 # touch /usr/home/support/venv/logs/guincorn_supervisor.log

Maintenant que supervisord est configuré, nous allons le relancer et vérifier que gunicorn est bien lancé :

 # service supervisord restart
 # supervisorclt status
 support                          RUNNING   pid 55406, uptime 0:00:03

Installation et configuration de nginx

Nous avons besoin d’un (et même de deux) serveurs nginx. Le premier tourne sur l’hôte, reçoi les requêtes de l’extérieur et les transfères vers le nginx de la jail en se basant sur le nom de la machine.

        /----------\        +--------------+
        |          |        |              |
        | Internet |------->+     HOTE     |
        |          |        |              |
        \----------/        +------+-------+
                                   |
                                   v
                         +---------+----------+
                         |                    |
                         |       Jails        |
                         |  Guincorn + Django |
                         |                    |
                         +--------------------+

Inatallation et configuration de nginx sur l’hôte

L’installation de nginx sur la machine hôte ne pose pas de réelle problème, pas plus que la configuration.

Installation de nginx

Simple, avec pkg 

 # pkg install -y nginx

Configuration de nginx sur l’hôte

Le fichier de configuration est à placer dans le répertoire /usr/local/etc/nginx/sites-available5 :

Le fichier s’appelle chez moi support-jail.conf.

 server {
    listen 80;
    server_name support.example.com;
        access_log /var/log/nginx/support-access.log combined;
        error_log /var/log/nginx/support-error.log;

     location /.well-known/acme-challenge {
        root /usr/local/www/bidon;
     }
     location / {
       return 301 https://$server_name/$request_uri;
     }
}

server {
    listen 443 ssl http2;
    server_name support.example.com;

    access_log /var/log/nginx/support-access.log combined;
    error_log /var/log/nginx/support-error.log;

    ssl_certificate /usr/local/etc/letsencrypt/live/support.example.com/fullchain.pem;
    ssl_certificate_key /usr/local/etc/letsencrypt/live/support.example.com/privkey.pem;

    # enables recent versions of TLS, but not SSLv2 or 3 which are weak and now deprecated.
    ssl_protocols TLSv1.2 TLSv1.3;

    # disables all weak ciphers
    ssl_ciphers 'AES128+EECDH:AES128+EDH';

    ssl_prefer_server_ciphers on;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_pass http://192.0.2.11:80;  # no trailing slash
        proxy_redirect off;

        # Socket.IO Support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

}

  Si ce fichier vous parait abscons, je vous conseil de lire la documentation de nginx, ce n’est pas le propos de ce billet.

  Le secret de la transmission vers le nginx de la jail est dans le bloc location / avec les directives proxy_* et en particulier proxy_pass.

  N’oubliez pas de créer un certificat valide pour votre serveur à l’aides des outils pour let's encrypt tel que certbot ou dehydraded.

Pour activer ce fichier et le faire lire par nginx il faut faire un lien vers le répertoire /usr/local/etc/nginx/sites_enabled5. Assurez vous aussi que dans la configuration de nginx le contenu de ce répertoire est bien inclus.

Ensuite il convient de recharger la configuration de nginx :

 # ln -s support-jail.conf ../sites-available/.
 # service nginx reload

Configuration et installation de nginx sur la jail

Là encore, pas de grand secret pour cette installation et cette configuration :

 # pkg install nginx
 # sysrc nginx_enable="YES"
 # service nginx start

Le fichier de configuration, support.conf est à mettre dans le répertoire /usr/local/etc/nginx/sites-available5.

 upstream support_server {
 server unix:/usr/home/support/venv/support/run/gunicorn.sock fail_timeout=0;
 }

 server {

 listen   80;
 server_name support.example.com;

 client_max_body_size 4G;

 access_log /usr/home/support/venv/logs/nginx-access.log;
 error_log /usr/home/support/venv/logs/nginx-error.log;

 location /static/ {
 alias   /usr/home/support/venv/support/static/;
 }

 location /media/ {
 alias   /usr/home/support/venv/support/media/;
 }

 location / {
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header Host $http_host;
 proxy_redirect off;

 # Try to serve static files from nginx, no point in making an
 # *application* server like Unicorn/Rainbows! serve static files.
 if (!-f $request_filename) {
 proxy_pass http://support_server;
 break;
 }
 }

 # Error pages
 error_page 500 502 503 504 /500.html;
 location = /500.html {
 root /home/support/venv/support/static/;
 }
 }

Une fois ce fichier créé, il faut faire un lien symbolique vers le répertoire /usr/local/etc/nginx/sites-enabled5 et s’assurer que le contenu de ce dernier est bien lu dans la configuration de nginx :

 # ln -s support.conf ../sites-enabled/.

Et recharger nginx :

 # service nginx restart

On a fini \o/ (pour django)

Normalement, si tout c’est bien passé vous avez un django opérationnel. Vérifions que django est bien accessible. En pointant votre navigateur vers https://support.example.com/admin vous devriez tomber sur la page de connexion de django

la mire de connexion de
django

Mais impossible de nous connecter, nous n’avons pas le mot de passe. Il manque une étape, la création du super utilisateur qui aura le droit de se connecter.

Création du super utilisateur

Retour sur la jail, avec l’utilisateur support dans l’environnement virtuel :

(venv)[support@support ~/venv/support]$ ./manage.py createsuperuser
Username (leave blank to use 'support'): admin
Email address: admin@example.com
Password:
Password (again):
Superuser created successfully.
6

Le résultat d’une connexion devrait vous afficher quelque chose qui ressemble à cela :

l'écran principal de
gestion de django

Finition de django_helpdesk

Il nous reste quelques détails à régler pour finir l’installation de django-helpdesk pour en finir avec ce tutoriel (avant de s’attaquer à la documentation du django-helpdsk par lui même).

Création du répertoire des pièces attachées

Ce répertoire va contenir les pièces attachées aux tickets créés par les utilisateurs ou bien envoyer avec des réponses à ces tickets.

C’est l’utilisateur support, dans l’environnement virtuel qui va créer ce répertoire (gardez un shell root sous la main).

Le répertoire doit se situer dans le celui désigné par la variable MEDIA_ROOT.

 (venv)[support@support ~/venv]$ mkdir -p
 /usr/home/support/venv/support/media/attachments
 (venv)[support@support ~/venv]$ chown www:www support/media/attachments
 (venv)[support@support ~/venv]$ chmod 700 support/media/attachments

Permettre les tickets en markdown

S’il n’est pas déjà installé, il est temps de le faire, nous allons avoir besoin du module django-markdown-deux. Assurez-vous qu’il est inclus dans le fichier settings.py.

 (venv)[support@support ~/venv]$ pip install django-markdown-deux

URL de connexion

Nous allons définir dans settings.py l’URL de connexion par défaut :

 LOGIN_URL = '/support/login/'

Définition des modèles de mail

Il est important de mettre dans la base de données les chemins vers les modèles de mails qui seront envoyés :

 (venv)[support@support ~/venv]$ ./support/manage.py loaddata emailtemplate.json
 Installed 144 object(s) from 1 fixture(s)

Connexion

En pointant votre navigateur sur l’url de votre site/support, vous devriez pouvoir vous connecter.

C’est avec l’administration django que vous allez pouvoir régler votre sie (les queues de tickets, les préférences mail, les utilisateurs, etc).

Conclusion

Nous avons, au pris de quelques effort mis en place une instance django et le projet django-helpdesk.

Il faut maintenant se plonger dans la documentation de django-helpdesk pour l’apprivoiser.


  1. Pour installer bash utilisez la commande usuelle pkg install -y bash. [return]
  2. N’hésitez pas lire la documentation [return]
  3. Cette partie diffère de la documentation parce que nous utilisons une version plus récente de django. [return]
  4. Il est évident que je n’ai pas mis l’adresse IP de ma jail, mais une adresse réservé à la documentation (cf.RFC5737) et que vous devez mettre l’adresse réelle de votre jail. [return]
  5. Si ce répertoire n’existe pas, il est nécessaire de le créer [return]
  6. Pour créer des mots de passe aléatoire assez long j’utilise la commande suivante date | md5 | base64. Il est préférable d’utiliser un gestionnaire de mot de passe. [return]