dimanche 19 février 2017

Adjust number of Sidekiq processes for GitLab

By default, GitLab starts 25 processes of Sidekiq. If this certainly makes sense on systems with high load, this could be overkill for simple instances of GitLab with only a handful of users and active projects. To reduce memory usage, it is possible to reduce the number of concurrent processes for Sidekiq by adding the following line to the /path/to/gitlab/config/sidekiq_queues.yml file:

:concurrency: 2

Install NodeJS v7.x on Debian Jessie

It is rather easy to deploy NodeJS v7.0 on Debian Jessie. First, it is necessary to add the deb repository for nodejs in /etc/apt/sources.list.d/nodesource.list:

deb https://deb.nodesource.com/node_7.x jessie main
deb-src https://deb.nodesource.com/node_7.x jessie main

Then, let's add a package preference in /etc/apt/preferences.d/node:

Package: nodejs
Pin: release o=Node Source
Pin-Priority: 1200

or

Package: *
Pin: release o=Node Source
Pin-Priority: 1200

I am rather reluctant to use the second option (with the wildcard) as it would imply that the nodesource.com deb repository could replace any package. Without the wildcard, there is a risk to see some dependences not deployed as expected... at the time of writing, it seems that limiting the priority to "nodejs" is sufficient ti have version 7.5.0 installed.

Enjoy!

mercredi 15 février 2017

Installer john (theripper) avec support de multiples coeurs sous Debian Stretch

Par défaut sous Debian Stretch, john (theripper), ami de l'adminsys testeur de la solidité des mots de passe, fonctionne en utilisant un seul coeur/processeur.

Il est nécessaire de recompiler john pour faire usage de multiples processeurs. Pas de panique, cela se fait très aisément.

Il faut d'abord install gcc et make pour permettre la compilation :

apt install gcc make

Ensuite, on télécharge la dernière version de john sur le site d'Openwall :

wget http://www.openwall.com/john/j/john-1.8.0.tar.xz

On décompresse l'archive :

tar xvf john-1.8.0.tar.xz

(si tar ne reconnaît pas tar.xz, c'est qu'il faut installer le paquet xz-utils)

On rentre dans le dossier source de john

cd john-1.8.0/src/

et on modifie le fichier Makefile pour décommenter les lignes OMPFLAGS. Ainsi :

#OMPFLAGS = -fopenmp
#OMPFLAGS = -fopenmp -msse2

devient

OMPFLAGS = -fopenmp
OMPFLAGS = -fopenmp -msse2

On lance alors la compilation par la commande

make linux-x86-64-avx

(attention, la cible peut être différente selon votre processeur et votre système... make vous proposera tous les choix possible si vous l'appelez sans paramètre)

Une fois la compilation terminée, vous pouvez lancer john :

cd ../run/
./john --test

et modifier lors de l'exécution le nombre de coeurs à utiliser si vous le souhaitez :

OMP_NUM_THREADS=1 ./john --test

mardi 14 février 2017

Activate logging and find logs for {Rainloop in Nextcloud}

You can activate Rainloop logging by toggling the enable parameter to On (from Off) in the logs section in the file /path/to/nextcloud/data/rainloop-storage/_data_/_default_/configs/application.ini

[logs]
enable = On

Then logs are then available in /path/to/nextcloud/data/rainloop-storage/_data_/_default_/logs/.

lundi 13 février 2017

Installer Rstudio sur Debian Sid/Stretch

Au moment où j'écris ces lignes, Rstudio nécessite les librairies libgstreamer-plugins-base0.10-0 et libgstreamer0.10-0 mais ces deux paquets ne sont pas disponibles dans Debian Stretch. Qu'à cela ne tienne !

Ajoutons ceci dans /etc/apt/sources.list :

deb http://httpredir.debian.org/debian jessie main

et ceci dans /etc/apt/preferences.d/pinning-jessie-for-rstudio :

Package: *
Pin: release l=Debian-Security
Pin-Priority: 1000

Package: *
Pin: release a=testing
Pin-Priority: 995

Package: *
Pin: release a=unstable
Pin-Priority: 50

Package: *
Pin: release o=Debian,n=jessie
Pin-Priority: 10

Il suffit alors d'exécuter les commandes suivantes avant d'installer rstudio et le tour est joué :

apt update
apt install libgstreamer0.10-0 libgstreamer-plugins-base0.10-0

dimanche 12 février 2017

Installer Rainloop sur Nextcloud

Rainloop s'installe extrêmement facilement sur Nextcloud 11 pour transformer votre gestionnaire cloud favori en webmail fort pratique.

Activer l'application

La première étape consiste à activer l'application dans le magasin à application de Nextcloud. D'abord, cliquer sur le menu en haut à gauche puis sur le bouton "+ Apps" pour ouvrir le magasin à application. Screenshot_2017-02-12_16-24-48.png

Dans la catégorie "Social & communication", on trouve l'application Rainloop qu'il suffit alors d'activer : Screenshot_2017-02-12_16-23-33.png

Accéder à l'administration de Rainloop

Après activation, réfrénez cette envie irrépressible de cliquer sur l'icône "Email" apparue aux côtés de vos applications (ça ne servira à rien sans quelques paramétrages préalables) et rendez-vous dans la page d'administration de Nextcloud, section "Additionnal settings". Là, dans le paragraphe "RainLoop Webmail" fraîchement apparu, cliquez sur "Go to RainLoop Webmail admin panel". Alternativement, vous pouvez appeler cette URL http://cloud.url/index.php/apps/rainloop/app/?admin (en remplaçant http://cloud.url par une URL valide et conforme à votre cas).

Au premier usage, le nom d'utilisateur de l'administration est "admin" et le mot de passe "12345". Nul besoin de vous inviter à modifier cela !

Paramétrer Rainloop

Les paramétrages et optimisations de Rainloop abondent. Pour aller à l'essentiel, rendez-vous dans la section "Domains".

Le principe est simple : vous allez paramétrer (et de fait autoriser) les utilisateurs de Rainloop (dans Nextcloud) à se connecter à un IMAP/SMTP selon le domaine de leur adresse courriel. Par exemple, vos utilisateurs ont des adresses alice@mondomaine.fr et bob@mondomaine.fr. Nous allons donc ajouter le domaine "mondomaine.fr" et paramétrer l'IMAP et le SMTP pour ce domaine :

Screenshot_2017-02-12_16-37-32.png

Désormais, sur leur compte Nextcloud, quand Alice et Bob ouvriront Rainloop, ils pourront saisir leur adresse (respectivement alice@mondomaine.fr et bob@mondomaine.fr) et Rainloop saura quels paramétrages IMAP/SMTP utiliser pour rapatrier le contenu de leur boîte courriel.

R et RStudio déployé avec Updatengine

Il est assez facile de déployer silencieusement R et RStudio avec Updatengine sur des machines propulsées par Windows.

Déploiement de R 3.3.2

Création du paquet de déploiement R 3.3.2 :

  • Nom: R 3.3.2
  • Description: Installation silencieuse de R
  • Comme commande: R-3.3.2-win.exe /VERYSILENT
  • Sélectionnez ensuite comme fichier l’exécutable de R R-3.3.2-win.exe téléchargé au tout début.
  • Paramétrez ensuite les deux options « ignorer les horaires de déploiement et « paquet public » selon vos préférences.

Et pour les conditions d'installation :

  • Nom: Installation R si < à 3.3.2
  • Condition: Logiciel non installé ou version inférieure à:
  • Nom du logiciel: R for Windows *
  • Version du logiciel: 3.3.2

Déploiement de RStudio

Création du paquet de déploiement RStudio 1.0.136 :

  • Nom: RStudio 1.0.136
  • Description: Installation silencieuse de RStudio
  • Comme commande: RStudio-1.0.136.exe /S
  • Sélectionnez ensuite comme fichier l’exécutable de RStudio RStudio-1.0.136.exe téléchargé au tout début.
  • Paramétrez ensuite les deux options « ignorer les horaires de déploiement et « paquet public » selon vos préférences.

Et pour les conditions d'installation :

  • Nom: Installation RStudio si < à 1.0.136
  • Condition: Logiciel non installé ou version inférieure à:
  • Nom du logiciel: RStudio
  • Version du logiciel: 1.0.136

samedi 12 novembre 2016

OpenSCAD pour faire de la 3D comme un programmeur, exemple de conception d'une boîte

OpenSCAD m'a réconcilié avec la conception 3D : plus la peine d'apprendre à utiliser Blender pour quelques pièces mécaniques, OpenSCAD permet de générer des pièces en 3D avec du code :-)

Avec le petit exemple ci-dessous, vous aurez vite compris l'intérêt d'OpenSCAD pour certaines petites pièces à construire, et si comme moi vous n'avez pas jusqu'ici eu le courage de vous lancer dans Blender ou FreeCAD. Contrairement à Blender par exemple, chaque forme est définie dans un langage propre à OpenSCAD et la forme apparaît à la compilation. Ce n'est donc clairement pas adapté pour des représentations artistiques mais pour la fabrication de pièces mécaniques dont les cotes sont bien connues, cela permet très vite d'arriver au résultat escompté !

Installation d'OpenSCAD

Sous Debian, OpenSCAD s'installe très simplement car il est disponible dans les paquets de la distribution :

apt install openscad

Exemple de conception d'une boîte

Imaginons que l'on souhaite fabriquer une boîte avec une grille pour y ranger par exemple de petits montages électriques.

Commençons par la face principale : dessinons un rectangle de 82 mm par 40 mm :

square([82,40],center);

form1.png

Pour fabriquer les fentes, nous allons soustraire un rectangle de 32 mm par 1 mm :

difference() {
  square([82,40],center);
   square([1,32]);
}

form1b.png

Cela a fonctionné, mais il faudrait mieux placer notre fente : nous allons le faire à l'aide d'une translation :

difference() {''''
  square([82,40],center);
    translate([3,4,0]) {
      square([1,32]);
    }
}

form1c.png

Nous pourrions maintenant répéter ces lignes de code autant de fois que nécessaire pour obtenir notre grille. Mais il y a mieux ; il est possible de paramétrer la création de chaque fente à l'aide d'une boucle :

difference() {
  square([82,40],center);
  for ( i = [1 : 26] ){
    translate([i*3,4,0]) {
      square([1,32]);
    }
  }
}

form2.png

Nous allons maintenant donner un peu d'épaisseur à cette première forme en l'extrudant : on ajoute cette instruction *avant* la forme 2D créée à l'instant :

 linear_extrude(height = 3, center = false)

form3.png

Ajoutons maintenant une première paroi latérale :

cube([82,1,15]);

form4.png

Puis une seconde paroi qui est identique à la première avec une translation :

translate([0,40,0]) {
    cube([82,1,15]);
}

form5.png

Rajoutons les dernières parois nécessaires :

cube([1,40,15]);
translate([81,0,0]) {
 cube([1,40,15]);
}

form6.png

Pour finir la boîte, nous voulons ajouter 2 petites surfaces qui en permettront la fixation. Comme la boîte sera imprimée en 3D, il est nécessaire que ces surfaces apparaissent progressivement. Nous allons donc les modéliser avec un prisme :

module prism(l, w, h){
  polyhedron(
     points=[[0,0,0], [l,0,0], [l,w,0], [0,w,0], [0,w,h], [l,w,h]],
     faces=[[0,1,2,3],[5,4,3,2],[0,4,5,1],[0,3,4],[5,2,1]] );
}

translate([6,30,15]) {
    rotate([0,180,90]) {
        prism(20,5,15);
    }
}

translate([76,10,15]) {
    rotate([0,180,270]) {
        prism(20,5,15);
    }
}

form7.png

Et voilà le tour est joué !

Pour aller plus loin

Dans l'exemple précédent, la longueur de la boîte (82 mm) a été écrite en dur et tous les autres objets ont été placés d'après cette dimension. Il eut bien sûr été possible de définir une variable (par exemple "length") en début de fichier et adapter le code !

Cela donne alors :

length=82;
linear_extrude(height = 3, center = false)
difference() {
  square([length,40],center);
  for ( i = [1 : (length / 3)] ){
    translate([i*3,4,0]) {
      square([1,32]);
    }
  }
}
cube([length,1,15]);
translate([0,40,0]) {
    cube([length,1,15]);
}

cube([1,40,15]);
translate([length-1,0,0]) {
 cube([1,40,15]);
}

module prism(l, w, h){
  polyhedron(
     points=[[0,0,0], [l,0,0], [l,w,0], [0,w,0], [0,w,h], [l,w,h]],
     faces=[[0,1,2,3],[5,4,3,2],[0,4,5,1],[0,3,4],[5,2,1]] );
}

translate([6,30,15]) {
    rotate([0,180,90]) {
        prism(20,5,15);
    }
}

translate([length-6,10,15]) {
    rotate([0,180,270]) {
        prism(20,5,15);
    }
}

Il n'y a alors plus qu'une seule variable à modifier pour fabriquer une boîte beaucoup plus longue !

form8.png

mardi 8 novembre 2016

Se connecter à l'ESP8266 avec ampy d'Adafruit pour plus de facilité

Dans les articles précédents (ici et ici), nous avons proposé de téléverser des fichiers sur l'ESP8266/NodeMCU à l'aide de l'utilitaire webcli.py fourni avec WebREPL. Si l'on accède physiquement à la puce (via le port série), il est possible d'utiliser l'utilitaire Ampy développé par Adafruit.

  1. Installons adafruit-ampy avec pip (pip3 si Python3)
pip3 install adafruit-ampy

Il se peut qu'il faille lancer cette commande en root ou avec sudo selon votre distribution.

  1. Flashons micropython sur le NodeMCU/ESP8266 sans surprise :
esptool.py --port /dev/ttyUSB0 --baud 460800 erase_flash
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=8m 0 /path/to/esp8266-20161017-v1.8.5.bin
  1. Après re-démarrage, connectons-nous à l'ESP8266 pour vérifier que tout fonctionne correctement :
picocom /dev/ttyUSB0 -b 115200
  1. Connectons-nous alors à l'aide d'ampy et affichons la liste des fichiers contenus sur la puce avec l'option "ls" :
ampy -p /dev/ttyUSB0 ls

Sur l'ESP8266 fraîchement flashé, on verra seulement le fichier "boot.py".

  1. Pour ajouter un fichier, il faut utiliser l'option "put"
ampy -p /dev/ttyUSB0 put /path/to/file

Les commandes disponibles avec ampy sont :

  get    Télécharger un fichier depuis la puce.
  ls     Lister le contenu de la puce.
  mkdir  Créer un dossier sur la puce.
  put    Téléverser un fichier sur la puce.
  reset  Provoquer un soft reset/reboot de la puce.
  rm     Supprimer un fichier de la puce.
  run    Exécuter un script sur la puce et afficher la sortie standard.

ampy from Adafruit to connect to ESP8266

In past articles, we mentioned WebREPL and webcli.py tool to connect and upload files to the ESP8266 board equipped with Python. I recently discovered a new tool that does the job even better: adafruit-ampy. It works well with NodeMCU-embarked ESP8266 chips.

  1. Download and install adafruit-ampy with pip (in the following I will assume you are equipped with Python 3)
pip3 install adafruit-ampy

Depending on your Linux distribution, you may have to run this with sudo or as root.

  1. Let's flash our ESP8266 with micropython with esptool.py
esptool.py --port /dev/ttyUSB0 --baud 460800 erase_flash
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=8m 0 /path/to/esp8266-20161017-v1.8.5.bin
  1. Let's reboot the board after a couple of seconds. We can connect to it with picocom for example to check that the command line is fully accessible and the micropython system works.
picocom /dev/ttyUSB0 -b 115200
  1. Now let's connect with ampy to list the files on the board:
ampy -p /dev/ttyUSB0 ls

On a freshly booted ESP8266/NodeMCU, I only see "boot.py".

  1. To add a file, just use the "put" command:
ampy -p /dev/ttyUSB0 put /path/to/file

For the reference, the commands available are:

  get    Retrieve a file from the board.
  ls     List contents of a directory on the board.
  mkdir  Create a directory on the board.
  put    Put a file on the board.
  reset  Perform soft reset/reboot of the board.
  rm     Remove a file from the board.
  run    Run a script and print its output.

dimanche 30 octobre 2016

Rotated backups for RethinkDB

Very simple bash script to rotate the backups (dumps) of RethinkDB:

#!/bin/bash
# DAILY BACKUPS
# Delete daily backups DAYS_TO_KEEP days old or more
# Config
BACKUP_DIR=/var/backups/rethinkdb/
DAYS_TO_KEEP=7
# Actions
find $BACKUP_DIR -maxdepth 1 -mtime +$DAYS_TO_KEEP -name "rethinkdb_dump_*" -exec rm -rf '{}' ';'
cd $BACKUP_DIR
rethinkdb dump

This assumes rethinkdb python utilities have been installed with:

pip install rethinkdb

jeudi 27 octobre 2016

Envoyer un courriel avec NodeMCU sur l'ESP8266

Nous avons précédemment parlé de l'ESP8266 en le propulsant à l'aide de Micropython. Il est églament possible de propulser un ESP8266 à l'aide de NodeMCU, un microframework en lua (attention, c'est trompeur, NodeMCU désigne à la fois un type de matériel et le logiciel qui le propulse).

9865736.png

Pour bâtir la version du firmware NodeMCU adapté à son usage, on peut utiliser ce site internet : https://nodemcu-build.com/

Une fois NodeMCU construit, on l'envoie à l'aide des commandes :

esptool.py --port /dev/ttyUSB0 erase_flash
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=8m 0 /path/to/nodemcu.bin

Voyons maintenant comment utiliser NodeMCU. Nous allons d'abord placer sur l'ESP8266 un fichier "init.lua" qui servira à l'initialisation de l'ESP8266 et notamment à sa connexion au WIfi local :

function startup()
    if file.open("init.lua") == nil then
      print("init.lua deleted")
    else
      print("Running")
      file.close("init.lua")
      print("Running main.lua")
      dofile("main.lua")
    end
end

--init.lua
wifi.sta.disconnect()
print("Set up wifi mode")
wifi.setmode(wifi.STATION) --on active la connexion à un point d'accès
wifi.sta.config("ssid","password",0) --SSID et mot de passe du point d'accès
wifi.sta.connect()
tmr.alarm(1, 1000, 1, function()
    if wifi.sta.getip()== nil then
        print("IP unavailable, Waiting...")
    else
        tmr.stop(1)
        print("Config done, IP is "..wifi.sta.getip()) --si réussite, alors on affiche les infos du réseau
        print("You have 5 seconds to abort Startup")
        print("Waiting...")
        tmr.alarm(0,5000,0,startup)
    end
 end)

Le code est assez facile à lire. Une fois les instructions de init.lua terminées, on lance le fichier main.lua. Il est alors possible de faire exécuter les actions de son choix à l'ESP8266.

Imaginons que l'on souhaite faire envoyer des courriels de notification par l'ESP8266 : on utiliserait alors le code suivant dans main.lua. Comme la librairie crypto est utilisée, il faut bien évidemment construire NodeMCU avec cette librairie.

require("crypto")

-- The email and password from the account you want to send emails from
local LOGIN = "login"
local MY_EMAIL = "name@domain.tld"
local EMAIL_PASSWORD = "password"

-- The SMTP server and port of your email provider.
-- If you don't know it google [my email provider] SMTP settings
local SMTP_SERVER = "smtp.domain.tld"
local SMTP_PORT = "465"

-- The account you want to send email to
local mail_to = "name@domain.tld"

-- These are global variables. Don't change their values
-- they will be changed in the functions below
local email_subject = ""
local email_body = ""
local count = 0

local smtp_socket = nil -- will be used as socket to email server

-- The display() function will be used to print the SMTP server's response
function display(sck,response)
     print(response)
end

-- The do_next() function is used to send the SMTP commands to the SMTP
server in the required sequence.
-- I was going to use socket callbacks but the code would not run
callbacks after the first 3.
function do_next()
            if(count == 0)then
                count = count+1
                local IP_ADDRESS = wifi.sta.getip()
                smtp_socket:send("HELO "..IP_ADDRESS.."\r\n")
            elseif(count==1) then
                count = count+1
                smtp_socket:send("AUTH LOGIN\r\n")
            elseif(count == 2) then
                count = count + 1
                smtp_socket:send(crypto.toBase64(LOGIN).."\r\n")
            elseif(count == 3) then
                count = count + 1
                smtp_socket:send(crypto.toBase64(EMAIL_PASSWORD).."\r\n")
            elseif(count==4) then
                count = count+1
               smtp_socket:send("MAIL FROM:<" .. MY_EMAIL .. ">\r\n")
            elseif(count==5) then
                count = count+1
               smtp_socket:send("RCPT TO:<" .. mail_to ..">\r\n")
            elseif(count==6) then
                count = count+1
               smtp_socket:send("DATA\r\n")
            elseif(count==7) then
                count = count+1
                local message = string.gsub(
                "From: "".. MY_EMAIL ..""<"..MY_EMAIL..">\r\n" ..
                "To: "".. mail_to .. ""<".. mail_to..">\r\n"..
                "Subject: ".. email_subject .. "\r\n\r\n"  ..
                email_body,"\r\n.\r\n","")

                smtp_socket:send(message.."\r\n.\r\n")
            elseif(count==8) then
               count = count+1
                 tmr.stop(0)
                 smtp_socket:send("QUIT\r\n")
            else
               smtp_socket:close()
            end
end

-- The connectted() function is executed when the SMTP socket is
connected to the SMTP server.
-- This function will create a timer to call the do_next function which
will send the SMTP commands
-- in sequence, one by one, every 5000 milliseconds.
-- You can change the time to be smaller if that works for you, I used
5000ms just because.
function connected(sck)
    tmr.alarm(0,5000,1,do_next)
end

-- @name send_email
-- @description Will initiated a socket connection to the SMTP server
and trigger the connected() function
-- @param subject The email's subject
-- @param body The email's body
function send_email(subject,body)
     count = 0
     email_subject = subject
     email_body = body
     smtp_socket = net.createConnection(net.TCP,1) -- the last paramater indicates the connection is secured by TLS/SSL
     smtp_socket:on("connection",connected)
     smtp_socket:on("receive",display)
     smtp_socket:connect(SMTP_PORT,SMTP_SERVER)
end

function query()
    subject = "Notification email"
    body = "This is the body of the email"
    send_email(subject,body.."\n.\n")
end

print("Ready to work. Sending email.")
query()

mardi 25 octobre 2016

Un couple d'enfer : ESP8266 et DS18B20, un petit objet connecté à quelques euros qui mesure la température et la transmet par Wifi

Nous avons parlé de l'ESP8266 propulsé par micropython dans le billet précent. Parmi la multitude d'application possible, nous allons voir aujourd'hui comment coupler l'ESP8266 à une sonde DS18B20 pour relever périodiquement la température d'une pièce et transférer la valeur en Wifi à une base InfluxDB.

Principe

Nous parlons ici de mesure de température dans un lieu de vie : une prise de température toutes les 10 minutes est suffisante. Nous allons dès lors mettre en place le fonctionnement suivant pour l'ESP8266 :

  1. allumage, mise en route du Wifi, connexion au Wifi local
  2. mesure à 10 reprises de la température
  3. suppression des 2 valeurs extrêmes supérieures et des 2 valeurs extrêmes inférieures
  4. moyenne des 6 valeurs restantes
  5. envoi de la valeur à une base de données InfluxDB au moyen d'une requête POST
  6. mise en sommeil de l'ESP8266 pour 10 minutes

A la sortie de la mise en sommeil le cycle recommence.

En outre, un minuteur logiciel provoquera un redémarrage de l'ESP8266 s'il est bloqué 2 minutes sans arriver au bout de son cycle.

Branchements

Rien de sorcier : on alimente la sonde 1-wire DS18B20 en 3.3V (comme l'ESP8266), on en relie les masses et on place le pin de données de la sonde 1-wire sur un GPIO adéquat de l'ESP8266. Il ne faudra pas oublier la résistance de 4.7 kOhms entre le pin Vin et le pin Data de la sonde ! Et pour permettre la sortie du sommeil, il faudra relier le pin RESET (RST) de l'ESP8266 avec le GPIO 16 (celui de l'alarme RTC).

Code sur l'ESP8266

Nous allons placer le code ci-dessous dans le fichier main.py qui sera ensuite envoyé sur l'ESP8266 à l'aide des outils évoqués dans le billet précent :

./webrepl_cli.py /path/to/main.py 192.168.4.1:/

Le code intégral, fortement commenté, est là :

import socket
import time
import machine #pour accéder aux GPIOs
import onewire, ds18x20 #pour lire la sonde 1-wire
import urequests  #pour envoyer la requête POST
from machine import Timer

#Faire flasher i fois la petite LED bleue de la puce ESP8266
def do_flashes(i):
    if i<1:
        i=1
    pin = machine.Pin(2, machine.Pin.OUT)
    for z in range(0,i):
        time.sleep_ms(250)
        pin.value(not pin.value())
        time.sleep_ms(250)
        pin.value(not pin.value())
    pin.value(1) #On s'assure que la LED finit dans l'état "éteint"

#Se connecter au Wifi local
def do_connect():
    import network
    sta_if = network.WLAN(network.STA_IF)
    if not sta_if.isconnected():
        print('connecting to network...')
        sta_if.active(True)
        sta_if.connect('SSID', 'password') #Remplacer le nom du SSID et le mot de passe de connexion
        while not sta_if.isconnected():
            pass
    print('network config:', sta_if.ifconfig())  #On imprime l'IP de la puce une fois connectée, pratique pour déboguer sur le port série
    do_flashes(4)

#Effectuer une requête POST sur une URL InfluxDB (ou autre)
def http_post(data):
    url = 'https://path.to.influx.db/write?db=dbname'
    data = ("temperature,tag1=x1,tag2=x2 value=%s" %
(str(data)))
    resp = urequests.post(url, data=data)

#Effectuer une mesure sur le GPIO 4
def do_ds18b20():
    dat = machine.Pin(4)
    ds = ds18x20.DS18X20(onewire.OneWire(dat)) #On crée l'objet OneWire
    roms = ds.scan()  #On scanne le bus pour trouver toutes les sondes
    print('found devices:', roms)

    #On mesure 10 fois la température
    if len(roms)>0:
        templist = []
        for i in range(10):
            ds.convert_temp()
            time.sleep_ms(1000) #On attend 1 seconde entre chaque prise de mesure pour laisser le temps à la sonde de se réinitialiser
            temp = ds.read_temp(roms[0])
            templist.append(temp)
        templist.remove(max(templist)) #On supprime les valeurs extrêmes
        templist.remove(max(templist))
        templist.remove(min(templist))
        templist.remove(min(templist))
        sum_of_temps = 0
        for temp in templist:
            sum_of_temps = sum_of_temps + temp
        final_temp = sum_of_temps/6
        print('Final temp: ', final_temp)
        return final_temp

#Redémarre l'ESP8266, "machine.reset" est équivalent à un redémarrage électrique
def force_reset():
    print("Machine inactive for too long, this is not normal, rebooting!")
    machine.reset()

#Regarde si l'ESP8266 se réveille d'un sommeil profond (et non d'un (re)démarrage électrique)
if machine.reset_cause() == machine.DEEPSLEEP_RESET:
    print('Woke from a deep sleep')

#On active le minuteur qui provoque le redémarrage dans 2 minutes
tim = Timer(-1)
tim.init(period=120000, mode=Timer.ONE_SHOT, callback=lambda
t:force_reset())

do_flashes(2) #On signale le démarrage du travail par 2 clignotements
do_connect() #Connexion au Wifi
time.sleep_ms(2000) #On attend 2 secondes
temp = do_ds18b20() #On mesure la température
http_post(temp) #On l'envoie sur InfluxDB

print('set RTC alarm to wake up later')
rtc = machine.RTC() 
rtc.irq(trigger=rtc.ALARM0, wake=machine.DEEPSLEEP) #On configure RTC.ALARM0 pour être capable de re-démarrer le périphérique
rtc.alarm(rtc.ALARM0, 600000) #  Après 600 secondes RTC.ALARM0 redémarrera la machine !
#Petit compte à rebours, pratique pour l'interrompre par Ctrl+C, quand l'on veut se connecter à l'ESP8266 pour en modifier le comportement
print('deep sleep in 20 seconds')
time.sleep_ms(15000)
print('deep sleep in 5 seconds')
time.sleep_ms(2000)
print('deep sleep in 3 seconds')
time.sleep_ms(1000)
print('deep sleep in 2 seconds')
time.sleep_ms(2000)
print('deep sleep in 1 seconds')
time.sleep_ms(1000)
print('deep sleep now! waking up in 600 seconds')
machine.deepsleep()

Et voilà une petite sonde qui prend la température toutes les 10 minutes.

Consommation ?

Un petit test rapide a montré que mon ESP8266 monté sur un kit de développement NodeMCU v1.0 (je pressens que la consommation serait moindre sans le kit de développement) :

  • demande ~100 mA lors des périodes de fonctionnement
  • demande <~10 mA lors de la phase de sommeil

C'est à creuser plus finement mais cela donne déjà quelques ordres de grandeur.

dimanche 23 octobre 2016

Micropython sur ESP8266, l'électronique toujours plus libre et puissante !

L'ESP8266 est un petite puce développée par Espressif qui a de multiples qualités :

  • elle dispose d'un petit coeur programmable
  • elle se connecte en Wifi ou/et se comporte comme un point d'accès Wifi
  • elle est fort peu onéreuse (quelques euros à peine)
  • elle a été largement adoptée par de nombreux bricoleurs de la blogosphère et on trouve une abondante littérature à son sujet

Initialement, elle a été beaucoup utilisée comme extension Wifi peu chère pour montages Arduino.

Mais ses qualités l'ont peu à peu rendu intéressante et utilisable seule : différents micro-systèmes ont été portés sur l'ESP8266 et l'ont peu à peu transformé en puce Wifi programmable. Ainsi, sur certains montages simples (internet des choses, domotique...) elle remplacera aisément un Arduino ou un Raspberry Pi et apportera en sus faible coût, faible consommation et encombrement réduit.

Nous allons voir dans ces lignes comment démarrer avec cette carte en l'équipement du micro-système micropython. Micropython a été porté récemment sur ESP8266 et le code en a été libéré suite à une campagne Kickstarter fructueuse.

Commençons par le matériel

La puce ESP8266 ressemble à ceci dans sa version 12 (qui est la plus performance notamment avec le plus grand nombre d'entrées-sorties) : esp8266-esp12.jpg

Pour un usage plus facile, on pourra la préférer pré-montée au sein d'un kit de développement qui en rendra :

  • l'alimentation facile (en USB 5V, la puce elle-même ne supporte que 3.3V)
  • le flashage aisé (toujours au travers d'un port USB fourni)
  • également le reset facilité à l'aide d'un bouton poussoir
  • enfin l'utilisation facile sur une breadboard pour prototypage

En inconvénient, la consommation électrique sera un peu plus forte et l'encombrement sera plus important. Mais passée la phase de prototypage, rien n'empêche d'utiliser la carte seule !

Parmi les kits de développement pratiques, on peut noter les planches NodeMCU (prendre la v1) qui sont très bien, sont libres (code source ici) et ressemblent à ceci : NodeMCU_DEVKIT_1.0.jpg

Attention toutefois à la correspondance des pins de la planche de développement et les GPIOs de l'ESP8266 : D1, D2, ... ne correspondent pas à GPIO1, GPIO2... Cf. le schéma ci-dessous : NODEMCU_DEVKIT_V1.0_PINMAP.png

Envoyons Micropython

Une fois en possession d'une puce ESP8266 et de son kit de développement, il suffit de s'y connecter à l'aide d'un câble USB et un nouveau périphérique /dev/ttyUSB0 doit devenir disponible pour le système.

On peut alors :

  • télécharger la dernière version du système micropython ici en prenant bien la version pour ESP8266
  • installer esptools.py sur sa machine par exemple avec la commande
pip install esptool
  • vider la mémoire actuelle de l'ESP8266 (pour éviter tout problème) avec la commande
esptool.py --port /dev/ttyUSB0 erase_flash
  • puis envoyer micropython (remplacer esp8266.bin par le fichier que vous avez obtenu sur le site de micropython)
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=8m 0 /path/to/esp8266.bin

Premier usage via le port série

Après redémarrage de la puce, on peut s'y connecter en série au travers de l'USB par exemple avec picocom (ou un autre logiciel de son choix) :

picocom /dev/ttyUSB0 -b 115200

et on obtient alors une console REPL Python qui permet d'exécuter toute commande de son choix dans l'environnement micropython.

Par exemple pour changer l'état d'un GPIO (attention à la numérotation, cf supra), on pourra appeler les commandes suivantes :

pin = machine.Pin(2, machine.Pin.OUT)
pin.value(1)
pin.value(0)

qui feront basculer en état HIGH/LOW le GPIO2 (D4 sur la planche de développement).

Usage au travers de WebREPL

Encore plus pratique, il est possible de se connecter à l'ESP8266 et à la console Python au travers du Wifi. Pour ce faire, l'ESP8266 se positionne comme un point d'accès Wifi auquel on peut se connecter : le mot de passe de connexion est "micropythoN".

Une fois connecté, on peut appeler l'ESP8266 à l'adresse 192.168.4.1 et se connecter à la console Python à l'aide de WebREPL, une page avec un peu de javascript qui aide à se connecter à la console REPL depuis un navigateur.

Il suffit de télécharger le contenu du dépôt WebREPL et lancer le fichier webrepl.html.

Envoyer un fichier sur l'ESP8266

Pour envoyer un fichier sur l'ESP8266, par exemple un fichier main.py qui sera exécuté à chaque lancement après initialisation, on pourra utiliser le commande outil webrepl_cli.py livré dans le dépôt WebREPL.

Connecté au point d'accès de l'ESP8266, la commande :

./webrepl_cli.py /path/to/main.py 192.168.4.1:/

se chargera de copier le fichier main.py à la racine de l'ESP8266.

Main.py pour tout contrôler

Dès lors, il est possible de placer le code de son choix dans le fichier main.py à la racine de l'ESP8266 pour l'asservir et lui faire exécuter le code de son choix.

On pourra par exemple :

  • désactiver le point d'accès
  • faire se connecter l'ESP8266 à un réseau WIfi local
  • faire agir l'ESP8266 sur le réseau (envoi de paquets, exécution de requêtes diverses...)
  • jouer avec les ports GPIOs

Les idées ne vous manqueront certainement pas pour peupler le fichier main.py. Voilà un bon point de départ dans la documentation de micropython sur ESP8266.

Python to look for hexadecimal patterns in a file

It is quite easy to look for hexadecimal patterns in a file in Python, for example match for the magic numbers for JPG or PNG:

import re

with open("~/file", "rb") as f:
  pattern =
b'(\xFF\xD8\xFF\xE0|\xFF\xD8\xFF\xE1|\xFF\xD8\xFF\xDB|\x89\x50\x4E\x47\x0D\x0A\x1A\x0A)'
  regex = re.compile(pattern)
  for match in regex.finditer(f.read()):
    print "%s" % (match.start())

This should print all the positions in the file where the given hexadecimal patterns were seen.

dd to split file

dd is one these magical GNU tools for which you can always find new usage! I recently discovered that dd could be used to split files:

dd if=~/input_file bs=1M skip=50 count=1000 iflag=skip_bytes,count_bytes of=~/output_file status=none

in details and with no surprise:

  • if for the input file
  • of for the output file
  • bs for the block size
  • skip to indicate the starting point in the input file
  • count to indicate the length of the file to copy
  • status=none is optional, it is used to make dd quiet
  • iflag is important as it gives important parameters to dd in this case:
    • skip_bytes forces dd to understand the skip parameter in bytes (and not blocks)
    • count_bytes forces dd to understand the count parameter in bytes (and not blocks)

Always useful!

Mesurer l'humidité avec une sonde DHT22 et la température avec une sonde DS18B20 sur Arduino, transférer l'information à un Raspberry Pi

L'objectif de ce billet est d'illustrer l'appariement et le travail synchronisé d'un Arduino et d'un Raspberry Pi. Concrètement, l'Arduino mesure l'humidité avec une sonde DHT22 et la température avec une sonde DS18B20 et l'objectif est de transférer cette information à un Raspberry Pi qui traite l'information et l'envoie sur Internet.

Certains se demanderont pourquoi ne pas tout faire directement avec le Raspberry Pi :

  1. on peut imaginer que tous les ports du Pi sont déjà utilisés
  2. ou que le montage se complique et qu'il n'est plus possible de tout gérer depuis le Pi mais qu'il est plus aisé de lui adjoindre une (ou des) planche(s) Arduino

Principe de communication entre Raspberry Pi et Arduino

Nous allons utiliser un lien série entre les 2 composants au travers d'une connexion USB qui relie les deux équipements. Concrètement, le Raspberry Pi enverra des ordres à l'Arduino sur le port série et écoutera attentivement la réponse qui viendra également sur le port série.

Code Arduino

Mesurer la température avec une sonde DS18B20

Pour faciliter les choses, on utilisera la bibliothèque DallasTemperature.h (disponible ici sur GitHub). Le code est développé ci-dessous et ne présente aucune difficulté particulière :

#include <OneWire.h> // Inclusion de la librairie OneWire
#include "DallasTemperature.h" //Librairie du capteur

// Initialize 1-wire
OneWire oneWire(10); //Bus 1-Wire sur la pin 10 de l'arduino
DallasTemperature sensors(&oneWire); //Utilistion du bus Onewire pour les capteurs
DeviceAddress sensorDeviceAddress; //Vérifie la compatibilité des capteurs

void doMeasures() {
  sensors.requestTemperatures(); //Demande la température aux capteurs
  Serial.print("temperature=");
  Serial.println(sensors.getTempCByIndex(0)); //Récupération de la température en degrés celsius du capteur n°0
}

// setup()
void setup() {
  sensors.begin(); //Activation des capteurs
  sensors.getAddress(sensorDeviceAddress, 0); //Demande l'adresse du capteur à l'index 0 du bus
  sensors.setResolution(sensorDeviceAddress, 11); //Résolutions possibles: 9,10,11,12
}

Pour mesurer la température, on appellera la fonction doMeasures().

Mesurer l'humidité avec une sonde DHT22

Là encore, le code n'est pas bien compliqué tant que l'on utilise la librairie DHT.h (par exemple celle d'Adafruit).

#include "DHT.h"

// Initialisation du capteur DHT
#define DHTPIN 12    // Pin sur lequel la sonde DHT 22 est attachée
#define DHTTYPE DHT22   // Type de sonde DHT22 : AM2302, AM2321
DHT dht(DHTPIN, DHTTYPE);

void doMeasures() {
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
  }

  // Compute heat index in Celsius (isFahreheit = false)
  float hic = dht.computeHeatIndex(t, h, false);

  Serial.print("humidity=");
  Serial.println(h);
  Serial.print("temperature="); //Oui, un DHT22 ça mesure aussi la température :-) 
  Serial.println(t);
  Serial.print("heat_index=");
  Serial.println(hic);
}

// setup()
void setup() {
  dht.begin();
}
Écoute sur le port série et actions

Là encore, pas de surprise. La boucle loop() est utilisée pour réagir quand des données sont présentes sur l'interface série. On détecte la nature de l'appel et on peut imaginer différents comportements selon le message reçu. Par exemple, un caractère pourra déclencher le relevé de l'humidité sur DHT22 tandis qu'un autre caractère reçu déclenchera la prise de température sur la sonde DS18B20.

//setup()
void setup() {
  Serial.begin(9600); // Initialisation du port série
}

// for incoming serial data
int incomingByte = 0;
String res = "";

// loop()
void loop() {
  // On écoute sur le port série
  if (Serial.available() > 0) {
          // On lit le contenu du message série et selon le caractère reçu on déclenche une action différente
          incomingByte = Serial.read(); 
          if (incomingByte == 103) { //g
            doAction1();
          }
          if (incomingByte == 104) { //h
            doAction2();
          }
  }
}
Il suffit alors de tout combiner !

Il n'est pas difficile de combiner ces trois morceaux de code !

#include "DHT.h"
#include <OneWire.h>
#include "DallasTemperature.h" 

// Initialize DHT sensor.
#define DHTPIN 12
#define DHTTYPE DHT22  
DHT dht(DHTPIN, DHTTYPE);

// Initialize 1-wire
OneWire oneWire(10);
DallasTemperature sensors(&oneWire); //Utilistion du bus Onewire pour les capteurs
DeviceAddress sensorDeviceAddress; //Vérifie la compatibilité des capteurs

// for incoming serial data
int incomingByte = 0;
String res = "";

void doMeasures() {
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
  }

  // Compute heat index in Celsius (isFahreheit = false)
  float hic = dht.computeHeatIndex(t, h, false);

  Serial.print("humidity=");
  Serial.println(h);
  Serial.print("temperature=");
  Serial.println(t);
  Serial.print("heat_index=");
  Serial.println(hic);

  sensors.requestTemperatures(); //Demande la température aux capteurs
  Serial.print("temperature=");
  Serial.println(sensors.getTempCByIndex(0)); //Récupération de la température en celsius du capteur n°0
}

// setup()
void setup() {
  Serial.begin(9600); // Initialisation du port série
  dht.begin();
  sensors.begin(); //Activation des capteurs
  sensors.getAddress(sensorDeviceAddress, 0); //Demande l'adresse du capteur à l'index 0 du bus
  sensors.setResolution(sensorDeviceAddress, 11); //Résolutions possibles: 9,10,11,12
}

// loop()
void loop() {
  // send data only when you receive data:
  if (Serial.available() > 0) {
          // read the incoming byte:
          incomingByte = Serial.read();
          if (incomingByte == 103) { //g
            doMeasures();
          }
  }
}

Code sur le Raspberry Pi

Nous allons utiliser un script Python qui se connectera via le port série à l'Arduino et :

  1. émettra le signal déclenchant l'action (dans notre cas le caractère "g")
  2. écoutera pendant 20 secondes l'éventuelle réponse
  3. transmettra la réponse à un serveur distant dans une requête GET
import serial
import os
import glob
import time
import requests

#Timeout de 20 secondes
tf = time.time()+20

#Initialisation du port série /dev/ttyUSB0 (cela peut varier selon la version de Raspbian et le type de planche Arduino, /dev/ttyUSB0 semble le défaut avec Raspbian Jessie et un Arduino Nano328)
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)

#On attend 2 secondes : si l'on écrit trop vite après l'ouverture du bus série, la communication risque d'échouer
time.sleep(2)

#On écrit le caractère "g" (ASCII 103) sur le port série
print("Now, we are sending the command to Arduino!")
ser.write("g".encode())

#Et on boucle désormais en lecture sur le port série
while time.time()<tf:
  line = ser.readline()
  if line:
    result = str(line).rstrip('\r\n')
    print(result)
    try:
      #Dans notre cas, le résultat brut de l'Arduino est passé à un serveur distant qui stocke l'information
      r = requests.post('https://monserveurdecollectedesdonnees', data=result, timeout=5.0)
      print(str(r.status_code)+" "+result)
    except:
      print("Failed, with payload: "+result)

#On ferme le canal de communication
ser.close()

Ce script peut alors être placé dans le crontab du système pour être exécuté périodiquement.

samedi 22 octobre 2016

Volumio 2, pour un Raspberry Pi musical

Bonne nouvelle, Volumio 2 est tout juste sorti et cette nouvelle version améliore encore ce splendide logiciel libre.

Qu'est-ce que c'est ?

Volumio est une version de Raspbian spécialement préparée pour transformer un Raspberry Pi (ou d'autres cartes comme Odroid, Beaglebone...) en serveur musical. Ainsi équipé de Volumio, un Raspberry Pi servira de la musique disponible localement (par exemple dans un disque dur attaché à Volumio) ou sur le réseau local (par exemple un NAS). La première version de Volumio était déjà super (on en parlait ici), la nouvelle édition embellit l'interface, facilite la gestion de systèmes avec plusieurs Volumio (pour des installations dans différentes pièces) tout en conservant les caractéristiques qui faisaient la force de la première itération.

À quoi ça ressemble ?

Voilà l'écran principal de Volumio : Screenshot_2016-10-21_09-13-20.png

Et voici l'affichage des différents Volumio indépendants mais connectés à un même réseau : il n'a jamais été aussi commode de passer de l'un à l'autre ! Screenshot_2016-10-21_09-13-31.png

Comment l'installer ?

C'est d'une grande simplicité. Tout a été déployé et a été fonctionnel sur mon Raspberry Pi équipé d'Hifiberry Amp+ en 15 minutes.

  1. D'abord, télécharger l'image correspondant à votre matériel sur le site de Volumio
  2. Ensuite, sortir le fichier .img de son archive
  3. Le transférer sur une carte micro-SD (/dev/sdb dans l'exemple ci-dessous, à adapter, attention à ne pas se tromper !) avec la commande dd :
dd if=volumio.img of=/dev/sdb bs=4M
  1. Insérer la carte dans le Raspberry Pi et le démarrer
  2. Et voilà ;-)

Après chargement, l'interface graphique est disponible via le navigateur internet que l'on fera pointer vers http://volumio ou vers l'adresse IP du Pi.

Enfin, il est toujours possible d'accéder en SSH au Raspbian sous-jacent avec le nom d'utilisateur "volumio" et le mot de passe "volumio".

L'essayer, c'est l'adopter !

Init script for Rails app served by Unicorn with RVM

Here is an init script for a Rails app served by Unicorn with RVM.

#!/bin/bash
### BEGIN INIT INFO
# Provides:          APP_NAME, with Unicorn serving
# Required-Start:    $all
# Required-Stop:     $network $local_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start APP_NAME unicorn at boot
# Description:       Enable APP_NAME at boot time.
### END INIT INFO

set -u
set -e

# Change these to match your app:
APP_NAME="APP_NAME"
APP_ROOT="/path/to/app"
PID="/path/to/app/tmp/pids/unicorn.pid"
ENV="production"
RVM="2.3.0@gemset"
USER="user"

UNICORN_OPTS="-D -E $ENV -c $APP_ROOT/config/unicorn.rb"

SET_PATH="cd $APP_ROOT; rvm use $RVM > /dev/null;"
CMD="$SET_PATH unicorn $UNICORN_OPTS"

old_pid="$PID.oldbin"

cd $APP_ROOT || exit 1

sig () {
	test -s "$PID" && kill -$1 `cat $PID`
}

oldsig () {
	test -s $old_pid && kill -$1 `cat $old_pid`
}

start () {
        echo "Starting $APP_NAME unicorn..."
        sig 0 && echo -e >&2 "\e[31mAlready running" && exit 0
        su - $USER -c "$CMD" > /dev/null
        echo -e "$APP_NAME has \e[32mstarted\e[0m, PID is `cat $PID`"
}

stop () {
        echo "Stopping $APP_NAME unicorn (signal QUIT)..."
        sig QUIT && echo -e "$APP_NAME has \e[32mstopped" && exit 0
        echo >&2 "Not running"
}

case ${1-help} in
start)
	start
	;;
stop)
	stop
	;;
force-stop)
	echo "Force stopping $APP_NAME unicorn (signal TERM)..."
	sig TERM && echo "$APP_NAME has stopped" &&exit 0
	echo -e >&2 "\e[31mNot running"
	;;
reload)
	echo "Reloading $APP_NAME unicorn (signal USR2)..."
	sig USR2 && echo -e "$APP_NAME has \e[32mreloaded" && exit 0
	echo -e >&2 "\e[31mCouldn't reload\e[0m, starting instead"
	start
	;;
status)
	sig 0 && echo -e "$APP_NAME \e[32mrunning\e[0m with PID `cat $PID`" && exit 0
	echo -e "$APP_NAME is \e[31mnot running!"
	;;
*)
 	echo >&2 "Usage: $0 <start|stop|reload|status|force-stop>"
 	exit 0
 	;;
esac

mercredi 19 octobre 2016

Let's Encrypt, comment l'usage d'un reverse proxy permet l'obtention de certificats sans se soucier nullement de la nature du service sous-jacent, cas pratique avec Pound et Apache

Quand on utilise un reverse proxy, il est possible de grandement simplifier l'usage de Let's Encrypt pour protéger tous ses accès avec HTTPS.

Méthodologie

Pour mémoire, afin de vérifier la bonne possession d'un domaine abc.tld, Let's Encrypt appelle l'adresse 'abc.tld/.well-known/acme-challenge/X' où X correspond à un nom de fichier unique que le client Let's Encrypt (par exemple certbot) aura créé lors du challenge ACME. Si le test réussit, c'est que l'utilisateur qui revendique le domaine en a pleinement la possession puisqu'il est capable de créer et rendre visible un fichier à sa racine ; le certificat est alors accordé et créé.

Dès lors : créons une règle sur le reverse proxy qui redirige toutes les requêtes de l'ACME challenge vers un seul emplacement dans lequel le client Let's Encrypt écrira !

En pratique avec Pound comme reverse proxy

Ajoutons un paragraphe en tête de la configuration de Pound :

        Service
                URL "/.well-known/acme-challenge/.*"
                BackEnd
                        Address 1.2.3.4
                        Port 8000
                End
        End

Désormais, toute requête de type ACME challenge est envoyé vers le serveur 1.2.3.4 sur le port 8000.

Imaginons que ce serveur soit un Apache. Paramétrons-le comme suit :

  • dans /etc/apache2/ports.conf, on ajoute une ligne Listen 8000
  • dans /etc/apache2/sites-available/letsencrypt.conf, on ajoute ceci :
<VirtualHost *:8000>
        DocumentRoot /var/www/letsencrypt
        <Directory /var/www/letsencrypt/>
                Options FollowSymLinks MultiViews
                AllowOverride All
		Require all granted
        </Directory>
</VirtualHost>
  • on active le site par a2ensite letsencrypt.conf
  • on redémarre Apache par service apache2 restart

Dès lors, tout appel à

certbot certonly --renew-by-default -a webroot --webroot-path /var/www/letsencrypt/ -d mondomaine.tld

devra aboutir à la génération du certificat souhaité, quelque soit le service sous-jacent !

En pratique avec un autre reverse proxy ?

La même stratégie est possible ! Seule la configuration du reverse proxy changera. On pourra également utiliser tout autre serveur web (même le micro-serveur web fourni dans python) pour servir les fichiers du ACME challenge !

- page 2 de 13 -