Sèkun Blog

Table des matières

1. Développement

1.1. Python / Django / Wagtail

1.1.1. Création d'un scatter plot de coordonnées avec matplotlib sur une carte openstreetmap   python

L'objectif va être de réprésenter des coordonnées gps sur une carte quelconque, ici on va utiliser une carte de la Belgique, mais le code s'adapte pour tout autre pays.

belgium_map.jpg

Figure 1 : Carte de la belgique avec des plusieurs coordonnées en bleu situées dessus

  1. Installation

    Les librairies utilisées sont principalement:

    • Pandas l'outil de manipulation de données en python.
    • Geopandas afin de représenter des données géo-spatiales, basé sur pandas.
    • Matplotlib comme outil de création de graphe dont la syntaxe est inspirée par Matlab (utilisé par pandas).
    • Contextily pour rajouter un fond en provenance de Openstreetmap.
    • Shapely pour convertir des données d'une projection à une autre.

    Le plus simple pour mettre en place l'environnement de travail est d'utiliser Anaconda.

    On peut alors installer les dépendances nécessaires, en créant un nouvel environnement virtuel via conda:

    conda create -n drawit matplotlib geopandas pandas shapely
    conda activate drawit
    pip install contextily==1.0rc2
    

    Les imports utilisés sont les suivants:

    import pandas as pd
    import matplotlib.pyplot as plt
    import geopandas as gpdd
    import json
    from random import choices
    from shapely.geometry import Point
    
  2. Données externes

    Pour la carte de la Belgique, on va utiliser un fichier GeoJSON ou shp (ESRI Shapefile), que Geopandas peut lire. Le fichier utilisé est le suivant: BEL_adm0.shp, et provient de ce repo github.

    Pour les coordonnées GPS à représenter, au lieu de les générer aléatoirement dans les bornes délimitant la Belgique, on peut plutôt utiliser la liste des coordonnées des villes du pays, obtenue via le ce repo github.

    belgium = gp.read_file("BEL_adm_shp/BEL_adm0.shp")
    with open("zipcode-belgium.json") as f:
        gps = json.load(f)
    

    On peut déjà afficher le rendu du fichier shp:

    belgium.plot(facecolor="none", edgecolor="gray")
    plt.axis("off")
    

    belgium_v1.jpg

    Figure 2 : Frontières de la Belgique

    Les données gps obtenues contiennent l'ensemble des villes, leur code postal ainsi que leurs coordonnées gps.

    [{'zip': '1000', 'city': 'Bruxelles', 'lng': 4.351697, 'lat': 50.8465573},
     {'zip': '1020', 'city': 'Laeken', 'lng': 4.3487134, 'lat': 50.883392},
     {'zip': '1030', 'city': 'Schaerbeek', 'lng': 4.3737121, 'lat': 50.8676041},
     ...
    
  3. Représentation des coordonnées sur la carte

    Nous allons d'abord prendre au hasard 50 données parmi ces dernières, que nous instancions via la classe DataFrame de pandas.

    data = pd.DataFrame(choices(gps, k=50))
    

    Il nous reste à représenter ces coordonnées sur la carte.

    # Création d'une seule figure de taille 10x10
    fig = plt.figure(figsize=(10,10))
    ax = fig.add_subplot(1,1,1)
    plt.axis("off") # masquer les axes
    belgium.plot(ax=ax, facecolor="none", edgecolor="gray", linewidth=0.4)
    data.plot(ax=ax, x="lng", y="lat", kind='scatter', alpha=0.7)
    plt.show()
    

    Le facecolor"none"= est la façon avec Matplotlib de rendre le fond de la carte transparent.

    belgium_v2.jpg

    Figure 3 : Frontières de la Belgique avec des coordonnées dessinées dessus

  4. Openstreetmap pour le fond de la carte

    Nous allons rajouter comme fond une carte en provenance de Openstreetmap. Pour ce faire, nous allons utiliser la librairie contextily, conseillée dans la doc de geopandas.

    Le problème est que les coordonnées GPS fournies, et celles utilisées dans les cartes Openstreetmap ne sont pas obtenues via la même projection.

    Pour Openstreetmap on a une projection «Spherical Mercator» (EPSG:3857), pour les coordonnées GPS: «World Geodetic System» (EPSG:4326). Nous allons devoir convertir les données de la variable belgium, ainsi que celle de gps de EPSG:4326 vers EPSG:3857. (Nous allons utiliser la librairie Shapely).

    belgium = belgium.to_crs(epsg=3857)
    geometry = data2["coords"].apply(Point)
    data = gpd.GeoDataFrame(data, crs={"init": 'epsg:4326'}, geometry=geometry).to_crs(epsg=3857)
    

    Reste à rajouter une carte comme fond ; nous avons plusieurs choix fournis par la librairie contextily. Celle-ci utilise en réalité une librairie basée sur leaflet. On choisit un modèle sur cette page, et le nom peut être récupéré via le fichier _providers.py de contextily généré automatiquement.

    On utilise simplement la méthode add_basemap de contextily.

    fig = plt.figure(figsize=(10,10))
    ax = fig.add_subplot(1,1,1)
    plt.axis("off")
    belgium.plot(ax=ax, facecolor="none", edgecolor="black", linewidth=1, alpha=0.8)
    data2.geometry.plot(ax=ax, alpha=0.5)
    ctx.add_basemap(ax, url=ctx.providers.Hydda.Base, alpha=0.8)
    
  5. Autres outils
    • Folium pour générer des cartes leaflet en python.

1.1.2. Ajouter une action sur un type de page uniquement avec wagtail   python wagtail

Wagtail permet de customiser à souhait son interface, il est possible de rajouter une action sur un type de page spécifique.

wagtail_page_action.png

On suppose qu'on dispose du modèle EventPage, héritant de Page:

# models.py
from wagtail.core.models import Page

class EventPage(Page):
    pass

Pour rajouter une action, celle-ci s'encode dans le fichier wagtail_hooks.py.

On crée une classe héritant de ActionMenuItem, et qui renverra une url.

# wagtail_hooks.py
from django.shortcuts import redirect
from wagtail.admin.action_menu import ActionMenuItem
class ShowEvent(ActionMenuItem):
    name = "show-event"
    label = _("Modifier le stage")
    icon_name = "edit"

    def get_url(self, request, context):
        return redirect("my_url_name")

Définir le classe ne suffit pas, il faut la rajouter à la liste des action_menu:

# wagtail_hooks.py
from wagtail.core import hooks
@hooks.register("register_page_action_menu_item")
def register_show_event_menu_item():
    return ShowEvent(order=10)

Le problème est que l'action apparaît sur toutes les pages, on va modifier ce comportement via le hooks construct_page_action_menu: on filtre la liste de menu_items, on n'affiche que ceux dont le nom n'est pas show-event, qui est le nom de notre classe ShowEvent définie plus haut.

# wagtail_hooks.py
@hooks.register("construct_page_action_menu")
from .models import EventPage
def show_event_only_on_event_page(menu_items, request, context):
    page = context.get("page")
    if not isinstance(page, EventPage):
        menu_items[:] = [item for item in menu_items if item.name != "show-event"]

1.1.3. Générer un fichier eml avec django   django

Il est possible de générer un fichier .eml en django: cela peut être utile si l'on souhaite créer des templates d'email à ouvrir un client mail afin de les envoyer manuellement.

On va utiliser la classe EmailMessage de from django.core.mail.

email_msg = EmailMessage(...)
email_msg.content_subtype = "html" # si on souhaite envoyer un email au format html

Pour ne pas avoir des problèmes de caractères (rencontré surtout avec Outlook), on doit modifier l'instance de Policy de l'e-mail. Par défaut j'ai remarqué que email.policy.Compat32 était utilisé (car une instance de email.mime.text.MIMEText est créée), on va le changer.

On récupère l'instance

msg = email_msg.message()
with open(filepath, "w") as f:
    gen = generator.Generator(f, policy=policy.SMTP)
    gen.flatten(msg)

Reste à renvoyer le fichier à l'utilisateur. Pour avoir un exemple concret, plaçons tout le code dans une requête de type fonction.

def gen_email(request):
    filename = "email.eml"
    email_msg = EmailMessage(
        subject="Le sujet...",
        body="Le corps de l'email...",
        from_email="from@example.com",
        to=["to@example.com"],
        headers={"X-Unsent": "1"},
    )
    email_msg.content_subtype = "html"

    _, path = mkstemp(filename)

    msg = email_msg.message()

    with open(path, "w") as f:
        gen = generator.Generator(f, policy=policy.SMTP)
        gen.flatten(msg)

    return FileResponse(open(path, "rb"), as_attachment=True, filename=filename)

1.1.4. Django: mixin ou décorateur pour la gestion des accès aux vues   django

Avec les vues sous forme de fonction en Django, on gérait les permissions à l'aide de décorateurs.

@login_required
def index(self):
  return render("index.html")

Depuis l'introduction des vues de type classe, la documentation de django précise que pour avoir le même comportement qu'avec les décorateurs, on peut utiliser des classes mixins (ayant peu d'effets de bord).

class IndexView(LoginRequiredMixin, TemplateView):
  template_name = "index.html"

Nous allons voir que le comportement est tout de même différent.

Considérons la vue TextView1 suivante:

class TestView1(LoginRequiredMixin, View):
    def dispatch(self, request, *args, **kwargs):
        if not request.user.has_perm(app.do_something):
            raise PermissionDenied
        return super().dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        return HttpResponse("Contenu view1")

Lors de l'appel à TestView1.as_view(), la méthode dispatch() est appelée. On vérifie que l'utilisateur a bien la permission app.do_something, mais rien ne garantit que l'utilisateur est bien connecté ! Cela se fera seulement après l'appel à super().dispatch(), qui va d'abord appeler la méthode dispatch() de la mixin LoginRequiredMixin.

On pourrait donc appeler d'abord la méthode dispatch() du parent, puis ensuite vérifier que l'utilisateur a bien les permissions nécessaires.

On considère la vue View2 suivante:

class View2(LoginRequiredMixin, View):
    def dispatch(self, request, *args, **kwargs):
        response =  super().dispatch(request, *args, **kwargs)
        if not request.user.has_perm('stage.gerer_stage'):
            raise PermissionDenied
        return response

    def get(self, request, *args, **kwargs):
        return HttpResponse("Contenu view2")

Ici il y a deux possibilité:

  • Soit on met l'attribut raiseexception à True, dans ce cas, lors de l'appel à super().dispatch(), une exception sera renvoyée.
  • Soit on met l'attribut à False, et la réponse renvoyée par super().dispatch() sera une url de redirection vers la page de login. Or, on devrait renvoyer directement vers cette url, mais on vérifie que l'utilisateur a bien la permission app.do_something, même s'il n'est pas connecté!

De plus, en imaginant que l'utilisateur est connecté, l'appel à la méthode super().dispatch() va exécuter un tas d'instructions qui, s'il s'avère que l'utilisateur n'a pas les permissions nécessaires, seront de toute façon futiles car un raise PermissionDenied sera de renvoyé.

La dernière méthode, pour se rapprocher des décorateurs utilisés avec les vues de types fonction (fbv), on peut utiliser @method_decorator sur la classe (qui va en réalité s'appliquer sur la méthode dispatch()).

@method_decorator(login_required, name='dispatch')
class Test3(View):
    def dispatch(self, request, *args, **kwargs):
        if not request.user.has_perm('stage.gerer_stage'):
            raise PermissionDenied
        return super().dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        return HttpResponse("Contenu view2")

Dans ce cas, la méthode dispatch() est changée, et son contenu tel qu'implémenté dans la vue Vue3 ne sera appelé que si l'utilisateur est connecté.

Un projet avec test est disponible sur framagit/sekun/cbv-mixin-test.

1.2. Emacs

1.2.1. Utiliser minted pour l'export en latex avec orgmode   orgmode

Le package minted permet un rendu plus beau (via l'outil pygments écrit en python) du code. De plus, je préfère utiliser xelatex au lieu de latex.

Il faut changer la manière dont on compile notre programme, pour ma part j'utilise latexmk. Le souci est que, lors des prévisualisation en latex, c'est le format .dvi qui est converti en png, or avec xelatex, il génère des pdf. Il faut donc modifier quelques variables:

(add-to-list 'org-latex-packages-alist '("" "minted"))
(setq org-latex-create-formula-image-program 'imagemagick)
(setq org-latex-listings 'minted)
(setq org-latex-pdf-process '("latexmk -shell-escape -f -pdf -%latex -interaction=nonstopmode -output-directory=%o %f"))
(setq org-preview-latex-process-alist
      (let*
          ((imagemagick (alist-get 'imagemagick org-preview-latex-process-alist))
           (imagemagick (plist-put imagemagick :programs '("xelatex" "convert")))
           (imagemagick (plist-put imagemagick :latex-compiler '("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f")))
           (imagemagick (plist-put imagemagick :image-converter '("/usr/bin/gm convert -density %D -trim -antialias %f -quality 100 %O"))))

        (cons (cons 'imagemagick imagemagick) (assq-delete-all 'imagemagick org-preview-latex-process-alist))))

J'utilise ici GraphicsMagick plutôt que ImageMagick (ligne /usr/bin/gm convert au lieu de /usr/bin/convert).

Au début de votre document, vous pourrez spécifier le compilateur:

#+latex_compiler: xelatex

1.3. Wordpress

1.3.1. Migrer un site wordpress avec wp-cli

Depuis la sortie de wp-cli, les opérations courantes sur un site web écrit en wordpress sont plus facilement automatisables. La mise à jour et la migration sont moins contraignantes.

Les opérations qui suivent demandent un accès ssh aux différents serveurs.

On souhaite migrer notre site de https://foo.com vers https://bar.com. On se rend sur le serveur foo.com, on suppose que notre site web wordpress est dans le dossier /path/wordpress. On va créer un backup de la BD et du projet (dans /backup)

ssh -p xxx username1@foo.com # où xxx est le numéro de port pour se connecter en ssh à foo.com
cd /path/wordpress
wp-cli db export /backup/wordpress.sql
tar czf /backup/wordpress.tar.gz .
exit

On copie les données de l'ancien site vers le nouveau (on peut se connecter à bar.com, et le copier directement via foo.com, mais parfois les hébergeurs bloquent certains ports, on va le faire le local).

Sur le serveur distant, on suppose qu'on a l'arborescence suivante: /path2/wordpress pour le dossier wordpress, et /backup2/ pour le dossier de backup.

mkdir -p /tmp/wordpress
cd /tmp/wordpress

# récupère le backup de bar.com en local
scp -P xxx username1@foo.com:/backup/wordpress.sql .
scp -P xxx username1@foo.com:/backup/wordpress.tar.gz .

# envoie le backup vers foo.com
scp -P yyy wordpress.sql username2@bar.com:/backup2/ # où yyy est le numéro de port pour se connecter en ssh à foo.com
scp -P yyy wordpress.tar.gz username2@bar.com:/backup2/

On se rend ensuite sur le serveur bar.com pour y migrer notre site.

ssh username2@bar.com
cd /path2/wordpress
tar xzf /path2/wordpress.tar.gz

On doit ensuite créer une base de donnée si elle n'existe pas, et modifier les informations d'accès dans le fichier wp-config.php, On peut l'ouvrir via nano ou un autre éditeur en console (nano wp-config.php).

On modifie les informations suivantes:

define('DB_NAME', 'Nom de la base de données');

/** MySQL database username */
define('DB_USER', 'Nom d`utilisateur de la BD');

/** MySQL database password */
define('DB_PASSWORD', 'Mot de passe d`accès à la BD');

/** MySQL hostname */
define('DB_HOST', 'Nom d`hôte de la BD (ou localhost si pas fourni)');

Reste à importer les différentes tables, et changer le nom de domaine, on est toujours dans foo.bar:/path2/wordpress.

wp-cli db import /backup2/wordpress.sql
wp-cli search-replace "https://foo.com" "https://bar.com"
wp-cli search-replace "https%3A%2F%2Ffoo.com" "https%3A%2F%2Fbar.com"
# on en profite aussi pour migrer les url qui ne sont pas en https://
wp-cli search-replace "http://foo.com" "https://bar.com"
wp-cli search-replace "http%3A%2F%2Ffoo.com" "https%3A%2F%2Fbar.com"

On vérifie également le fichier .htaccess, en remplaçant toute occurrence de foo.com par bar.com, et notre site devrait être disponible via https://bar.com.

1.3.2. Changer le préfix des tables avec wordpress

On va manipuler les bases de données d'un site wordpress (un ou plusieurs si vous partagez la même BD entre plusieurs sites). Avant de faire quoi que ce soit, faites un backup de votre base de données, et testez-le pour être sûr de pouvoir annuler vos opérations.

L'article se base sur la méthode 1 d'un poste disponible à l'adresse suivante: wpmyweb.com/change-wordpress-database-table-prefix.html

Il faut déterminer le préfix des tables utilisées, on l'obtient via la valeur de la variable $table_prefix dans wp-config.php.

On récupère d'abord toutes les tables concernées dans une variable $tables:

tables=$(mysql -u DBUSER DBNAME -e "show tables;" -B -p | grep "DBPREFIX")

DBUSER, DBNAME et DBPREFIX sont respectivement le nom d'utilisateur qui a accès à la base de données, le nom de la BD ainsi que le préfix des tables utilisé.

On renomme ensuite toutes les tables via l'instruction sql RENAME. Notons NEWPREFIX le nouveau préfix souhaité. Pour tester notre code avant de l'exécuter, on peut vérifier ce qu'il va réaliser:

while read table; 
    do newname="NEWPREFIX${table#DBPREFIX}"
    echo "rename table \`$table\` to \`$newname\`"
done <<< "$tables"

Une fois le code sql approuvé, on peut l'exécuter:

while read table; 
    do newname="NEWPREFIX${table#DBPREFIX}"
    echo "rename table \`$table\` to \`$newname\`" | mysql -u DBUSER DBNAME -p
done <<< "$tables"

Ce n'est pas tout, car wordpress hardcode le préfix dans des entrées de deux de ses tables: options et usermeta.

Vous remplacez NEWPREFIX par le nouveau préfix ci-après. Par exemple, si NEWPREFIX vaut wp2_, on a: NEWPREFIXoptions doit être remplacé par wp2_options. De même pour DBPREFIX qui équivaut à l'ancien préfix. Lancez mysql via mysql -u DBUSER DBNAME -p, et exécutez:

UPDATE `NEWPREFIXoptions` SET `option_name`=REPLACE(`option_name`,'DBPREFIX','NEWPREFIX') WHERE `option_name` LIKE 'DBPREFIX%';
UPDATE `NEWPREFIXusermeta` SET `meta_key`=REPLACE(`meta_key`,'DBPREFIX','NEWPREFIX') WHERE `meta_key` LIKE 'DBPREFIX%';

1.3.3. Supprimer toutes les tables non propres au site

Si on a une base de données par site wordpress, parfois, lors d'un exporte, il arrive qu'on ait récupérer des tables d'autres sites, avec d'autres préfix, on peut les supprimer.

On suppose ici que la BD de votre site wordpress ne stocke rien d'autres d'utiles, faites bien un backup de la BD avant toute opération.

Grâce à wp-cli, on peut réaliser ceci en une commande:

old_tables=$(echo "show tables" | wp-cli db cli  | tail -n +2 | grep -v "^$(wp-cli db prefix)")

if [[ -z $old_tables ]]; then
    echo "No table to clean..."
else
    while read db; do
        echo "Remove $db..."
        echo "drop table $db" | wp-cli db cli
    done <<< "$old_tables"
fi

1.4. Linux et Administration système

1.4.1. Installer Linux Mint LMDE4 avec une partition btrfs et deux sous-volumes @ et @home

Sur les éditions basées sur Ubuntu de Linux Mint, l'installateur permet d'avoir deux sous-volumes @ et @home dans une même partition btrfs, ce que ne permet pas l'installateur de la version LMDE, sauf en mode expert.

La première chose va être de créer les partitions nous-mêmes: ici j'utilise un simple système de partition DOS, avec un partition btrfs et une autre swap, que vous pouvez faire avec gparted (note : mettez bien 2Mo de libre en début du disque, pour le MBR).

linux:lmde4_garted.png

Une fois ceci fait, on va créer nos sous-volumes, ici la partition à monter est /dev/sda1. Les commandes sont les suivantes :

sudo mount /dev/sda1 /mnt
sudo btrfs subvolume create /mnt/@
sudo btrfs subvolume create /mnt/@home
sudo umount /mnt

Lors de l'installation, on va devoir monter nos dossiers dans /target, ce qu'on fait.

sudo mkdir /target
sudo mount -o subvol=@ /dev/sda1 /target
sudo mkdir /target/home
sudo mount -o subvol=@home /dev/sda1 /target/home

Ensuite, on va lancer l'installateur en mode expert via sudo live-installer-expert-mode, lors du partitionnement, on choisira le mode expert en-bas à gauche.

linux:lmde4_installer_expert.png

L'installation du GRUB se fera sur /dev/sda. Une fois la copie faite, un fenêtre apparaîtra vous laissant le temps de modifier les fichiers présents dans la partition. L'objectif est de créer le fichier /target/etc/fstab.

On a besoin de l'UUID de notre partition, ce qu'on peut obtenir via la commande blkid.

Voici ce qu'on peut obtenir:

$ blkid
/dev/loop0: TYPE="squashfs"
/dev/sda1: UUID="9a79ccef-7d32-4d40-aa9d-cb92b8212808" TYPE="btrfs" 
/dev/sda2: UUID="e716ac11-7962-4154-b679-bcf1459e84cf" TYPE="swap"

Ouvrez le fichier /target/etc/fstab comme suit:

UUID=9a79ccef-7d32-4d40-aa9d-cb92b8212808   /           btrfs       defaults,subvol=@       0   0
UUID=2fd518c1-6d5e-47f3-a155-92ec3cb9d3d4   /home       btrfs       defaults,subvol=@home   0   0
UUID=e716ac11-7962-4154-b679-bcf1459e84cf   none        swap        defaults                0   0

Vous pouvez ensuite appuyer sur suivant pour que l'installation se termine.

1.4.2. Intégrer le kernel sagemath à jupyterhub

Par défaut, lorsque l'on installe The Littlest JupyterHub, le kernel par défaut est un environnement Anaconda. Nous allons voir comment rajouter le kernel de Sagemath.

L'avantage d'avoir cet environnement est la facilité d'installer des outils à jours et cohérents, grâce à l'écosystème fourni pour Anaconda. Sagemath 9 enfin compatible avec la version 3 de python, le plus simple pour l'installer est d'utiliser anaconda lui-même.

Ouvrez une console via l'interface admin de votre jupyterhub, et écrivez les commandes suivantes:

sudo -E conda install mamba -c conda-forge
sudo -E mamba create -n sage sage -c conda-forge

Cela crée un environnement sage indépendant de celui par défaut (vous aurez donc deux kernels, un python3 utilisant l'environnement par défaut de conda, et l'autre sagemath).

Sagemath vient avec un kernel déjà configuré, reste à le rendre visible dans l'interface.

  1. On se rend là où sont situés les kernels

    cd /opt/tljh/user/share/jupyter/kernels
    

    Par défaut, seul python3 est présent.

  2. On copie le kernel fourni par sage dans le répertoire courant.

    sudo -E cp -R /opt/tljh/user/envs/sage/share/jupyter/kernels/sagemath .
    

    Si on regarde le fichier kernel.json s'y trouvant, on remarque qu'il exécute le programme /opt/tljh/user/envs/sage/bin/sage.

  3. Pour l'exécuter, il faut y ajouter des variables d'environnement requises par sage pour fonctionner, ce que l'on ajoute au fichier kernel.json via la clé env. On obtient le fichier suivant:

    {
        "argv": [
            "/opt/tljh/user/envs/sage/bin/sage",
            "--python",
            "-m",
            "sage.repl.ipython_kernel",
            "-f",
            "{connection_file}"
        ],
        "display_name": "SageMath 9.0",
        "language": "sage",
        "env": {
            "SAGE_ROOT": "/opt/tljh/user/envs/sage",
            "SAGE_LOCAL": "/opt/tljh/user/envs/sage"
        }
    }
    

    On peut vérifier que le kernel apparaît bien ensuite.

sysadmin:jhub_with_sage_kernel.png

1.4.3. Visualiser ses logs d'accès nginx avec goaccess

Lorsque l'on veut avoir des statistiques d'accès sur un site, on peut mettre en place google analytics, ou la solution hébergeable et opensource matomos (anciennement piwik).

On peut aussi se contenter des logs fournis par nginx, et utiliser le programme goaccess qui va lire notre fichier de logs généré par nginx afin d'en traiter les données.

La première chose à vérifier est que nginx stocke bien les logs d'accès. Dans /etc/nginx/nginx.conf, on peut vérifier la présence de la ligne suivante :

access_log /var/log/nginx/access.log;

Notons que l'on peut aussi spécifier un fichier de log différent par virtualhost. Nous allons faire en sorte de pouvoir accéder aux logs de notre serveur, via l'emplacement /log, et sécurisé par un mot de passe (on suppose que le serveur nginx est déjà lancé, et propulse un serveur web en http+ssl).

La commande suivante va analyser les logs d'accès générés par nginx, et en générer un rendu html dans /var/www/log/index.html.

# Concatène tous les fichiers access.log
$ /bin/zcat -f /var/log/nginx/access.log* \

# Ignore les accès à la page de logs
  | grep -v "GET /log/" \

# Génère les statistiques des logs dans un fichier html
  | /usr/bin/goaccess - -a --log-format=COMBINED -o /var/www/log/index.html &>/dev/null

En une seule ligne:

$ /bin/zcat -f /var/log/nginx/access.log* | grep -v "GET /log/" | /usr/bin/goaccess - -a --log-format=COMBINED -o /var/www/log/index.html &>/dev/null

Il suffit de rajouter cette commande au crontab, afin qu'elle soit exécutée assez souvent. Par exemple pour que les stats soient rafraîchies toutes les 15 minutes, après un crontab -e :

# m h  dom mon dow   command
*/15   *  *   *   *  /bin/zcat -f /var/log/nginx/access.log* | grep -v "GET /log/" | /usr/bin/goaccess - -a --log-format=COMBINED -o /var/www/log/index.html &>/dev/null

Reste à rendre accessible ce fichier. Générons d'abord des mots de passe d'accès (nécessite htpasswd, présent dans le paquet apache2-utils sous debian).

htpasswd -c "/etc/nginx/.htpasswd" USERNAME

Rentrez un mot de passe, et rajoutons le chemin /log à notre fichier nginx. Vous devriez avoir un fichier /etc/nginx/sites-enabled/WEBFILE. Rajoutez dedans :

location /log {
    root /var/www;
    index index.html;
    auth_basic "Restricted";
    auth_basic_user_file "/etc/nginx/.htpasswd";
}

Après un systemctl reload nginx, vous devriez pouvoir accéder à vos log depuis https://<servname>/log/.

1.4.4. Partager une connexion wifi via ethernet

Cet article se base principalement sur le post sharing-internet-connection-from-a-linux-machine-over-ethernet.

On souhaite obtenir une connexion internet sur un ordinateur ne disposant que d'une connexion filaire. On va relier ce dernier au port ethernet de notre portable, qui dispose d'un accès à internet via sa connexion wifi.

Le portable sera donc celui qui dispose d'une connexion internet via wifi, et le PC celui qui souhaite obtenir, en filaire, une connexion internet.

Je votre portable, on lance la commande ip a, on obtient plusieurs interfaces, par exemple:

  • enp0s31f6: ethernet
  • wlp0s20f3: wifi

Les commandes suivantes sont à exécuter sur le portable.

ip a add 192.168.122.10/24 dev enp0s31f6
iptables -t nat -A POSTROUTING -o wlp0s20f3 -j MASQUERADE
iptables -I FORWARD -o wlp0s20f3 -s 192.168.0.0/16 -j ACCEPT
iptables -I INPUT -s 192.168.0.0/16 -j ACCEPT

Ensuite, sur le PC, on lui attribue:

  • Une adresse ipv4 statique: 192.168.120.05
  • Un masque: 255.255.255.0
  • L'ip de notre potable comme passerelle: 192.168.120.10
  • Des serveurs dns: 1.1.1.1

1.5. Divers

1.5.1. Transférer ses sms de Firefox OS vers Android

Mozilla a cessé de maintenir son OS fin 2015. Hésitant entre le Librem 5 et un Fairphone 2, j'ai opté pour le second, afin d'accéder immédiatement à l'ensemble des applications android (le librem pourra également, plus tard, permettre de lancer ces applications). L'équipe en charge du fairphone propose en plus un OS alternatif, le Fairphoneopen, supprimant des applications propriétaires et srevices google.

Le problème était de transférer les sms de mon mobile sous Firefox OS, et de les récupérer sur android.

J'ai d'abord utilisé la webapp SMSBackupRestore, l'installation se fait facilement via le WebIDE de firefox MAJ+F8. Une fois lancé, un fichier backup-messages.xml est créé et déposé dans la carte SD.

Ensuite, l'objectif est d'importer les sms via l'application SMS Backup & Restore (Note : si l'application google play n'est pas installée, on peut se tourner vers Aurora Store).

Le fichier xml généré par SMSBackupRestore (FOS) n'est pas compatible avec celui pouvant être importé par SMS Backup & Restore (Android), dont la spécification est décrite sur leur site. J'ai donc créé un script python qui génère le fichier attendu par ce dernier : fos_copy_file_script.py

Created: 2022-08-14 dim 20:37

Validate