Tags

arm64 blacklistd bluepill bluetooth cu dns dovecot envsubst esp8266 filter freebsd ipfw lets_encrypt opensmtpd python shell ssl stm32 template tls unbound yubikey

Powered by

blOg
maRkdown
awK
shEll

09/10/2022

[ stm32 bluepill ]

202210090800 stm32 bluepill

FreeBSD et bluepill

A force de suivre les vidéos de Sieur Rancune j'ai fini par craquer pour une poignée de bluepill. Parce qu'il est conseillé sur tous les interweb d'utiliser arm-none-eabi-gcc pour programmer ces charmantes bestioles j'ai décidé d'utiliser clang/llvm livré de base.

Si allumer une led constitue le 'Hello World!' de l'électronique, j'ai découvert le niveau -1 en lisant cette série de billets: placer une valeur particulière dans un registre.

Les sources

Le fichier core.S requiert 3 modifications mineures:

$ fetch -o - https://raw.githubusercontent.com/WRansohoff/STM32F0_minimal/master/core.S | sed -e 's/cortex-m0/cortex-m3/' -e 's/reset_handler/Reset_Handler/g' -e 's/_estack/__end_stack/' > core.S
$ clang --target=arm-none-eabi -mcpu=cortex-m3 -mfloat-abi=soft -c core.S -o core.o

Le binaire

Pour que mon binaire final soit adapté à ma carte, je dois récupérer un 'linker script':

$ fetch https://github.com/STM32-base/STM32-base/archive/refs/heads/master.zip
$ tar xf master.zip STM32-base-master/linker
$ clang --target=arm-none-eabi -mcpu=cortex-m3 -mfloat-abi=soft -nostdlib -L STM32-base-master/linker -T STM32-base-master/linker/STM32F1xx/STM32F103xB.ld core.o -o main.elf

C'est pour se conformer à ce script qu'il faut modifier le fichier core.S.

Les soucis commencent

Avant d'envoyer le fichier sur la carte, il faut le convertir:

$ objcopy -O binary main.elf main.bin
$ du -Ah main.bin 
384M  main.bin

Là je crois qu'on dépasse les bornes des limites: ça fait beaucoup de méga pour placer une valeur dans un registre. Si je regarde mon fichier elf:

$ readelf -l main.elf | grep 0x
Entry point 0x8000009
  LOAD           0x010000 0x08000000 0x08000000 0x0001c 0x0001c R E 0x10000
  LOAD           0x020000 0x20000000 0x20000000 0x00600 0x00600 RW  0x10000
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0
  ARM_EXIDX      0x01001c 0x0800001c 0x0800001c 0x00000 0x00000 R   0x4

Je peux me passer de GNU_STACK avec l'option -z nognustack:

$ clang --target=arm-none-eabi -mcpu=cortex-m3 -mfloat-abi=soft -z nognustack -nostdlib -L STM32-base-master/linker -T STM32-base-master/linker/STM32F1xx/STM32F103xB.ld core.o -o main.elf
$ readelf -l main.elf | grep 0x
Entry point 0x8000009
  LOAD           0x010000 0x08000000 0x08000000 0x0001c 0x0001c R E 0x10000
  LOAD           0x020000 0x20000000 0x20000000 0x00600 0x00600 RW  0x10000
  ARM_EXIDX      0x01001c 0x0800001c 0x0800001c 0x00000 0x00000 R   0x4

Mais cela ne corrige pas le problème. J'ai fini par comprendre qu'une section n'avait pas le bon type:

$ readelf -S main.elf.OK | grep ._user_heap_stack
[ 6] ._user_heap_stack NOBITS          20000000 020000 000600 00  WA  0   0  1
$ readelf -S main.elf.KO | grep ._user_heap_stack
[ 7] ._user_heap_stack PROGBITS        20000000 020000 000600 00  WA  0   0  1

La correction la plus simple que j'ai trouvé est de passer cette section en 'NOLOAD':

$ grep ._user_heap_stack STM32-base-master/linker/common.ld
._user_heap_stack (NOLOAD) : {
$ clang --target=arm-none-eabi -mcpu=cortex-m3 -mfloat-abi=soft -z nognustack -nostdlib -L STM32-base-master/linker -T STM32-base-master/linker/STM32F1xx/STM32F103xB.ld core.o -o main.elf
$ readelf -S main.elf | grep ._user_heap_stack
[ 8] ._user_heap_stack NOBITS          20000000 020000 000600 00  WA  0   0  1
$ objcopy -O binary main.elf main.bin
$ du -Ah main.bin
512B  main.bin

C'est déjà plus raisonnable.

Les soucis continuent

Il est temps de programmer la carte. J'utilise donc un adaptateur usb st link v2 et devel/openocd avec la configuration suivante:

$ cat openocd.cfg
source [find interface/stlink.cfg]
transport select hla_swd
source [ find target/stm32f1x.cfg]
reset_config none separate

Je connecte la bluepill à l'adaptateur, je branche l'adaptateur et paf:

$ openocd -f openocd.cfg -c "init"
Open On-Chip Debugger 0.11.0
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate

Info : clock speed 1000 kHz
Info : STLINK V2J17S4 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.137032
Warn : UNEXPECTED idcode: 0x2ba01477
Error: expected 1 of 1: 0x1ba01477

Pour qu'openocd daigne utiliser mon adaptateur je dois lui dire qu'il est "de confiance" (kofkof):

$ cat openocd.cfg
set CPUTAPID 0x2ba01477
source [find interface/stlink.cfg]
transport select hla_swd
source [ find target/stm32f1x.cfg]
reset_config none separate

$ openocd -f openocd.cfg -c "init"
Open On-Chip Debugger 0.11.0
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate

Info : clock speed 1000 kHz
Info : STLINK V2J17S4 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.138581
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f1x.cpu on 3333
Info : Listening on port 3333 for gdb connections
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections

Et quand y'en a plus y'en a encore

On va facilement trouver des exemples d'utilisation conjointe d'openocd et de gdb. Pas de bol pour moi, le debugger llvm n'est pas compatible, d'après https://stackoverflow.com/questions/36287351/how-to-setup-lldb-with-openocd-and-jtag-board:

"from what we were researching, it is not possible to debug remote (bare-metal!) targets with lldb without writing extra code."

Et autant dire que l'extra code en python, je passe.

Le bout du tunnel

Je vais donc utiliser l'interface "telnet" d'openocd:

$ echo 'reset halt' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> reset halt
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x1fffe61c msp: 0x20000180

$ echo 'stm32f1x mass_erase 0' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> stm32f1x mass_erase 0
device id = 0x20036410
flash size = 25616kbytes
stm32x mass erase complete

$ md5 main.bin 
MD5 (main.bin) = 09c6d874e631d40370b70a7bcc120f41

$ echo 'flash write_image erase main.bin 0x8000000' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> flash write_image erase main.bin 0x8000000
auto erase enabled
wrote 1024 bytes from file main.bin in 0.087373s (11.445 KiB/s)

$ echo 'reset run' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> reset run

$ echo 'halt' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> halt
target halted due to debug-request, current mode: Handler HardFault
xPSR: 0x00000003 pc: 0x012fff1e msp: 0xe59efff8

$ echo 'reg' | nc -N 127.0.0.1 4444
Open On-Chip Debugger
> reg
===== arm v7m registers
(0) r0 (/32): 0x008fec14
(1) r1 (/32): 0x00000000
(2) r2 (/32): 0x00000000
(3) r3 (/32): 0x00000000
(4) r4 (/32): 0x00000000
(5) r5 (/32): 0x00000000
(6) r6 (/32): 0x00000000
(7) r7 (/32): 0xdeadbeef
(8) r8 (/32): 0x00000000
(9) r9 (/32): 0x00000000
(10) r10 (/32): 0x00000000
(11) r11 (/32): 0x00000000
(12) r12 (/32): 0x00000000
(13) sp (/32): 0x20005000
(14) lr (/32): 0xffffffff
(15) pc (/32): 0x08000012
(16) xPSR (/32): 0x01000000
(17) msp (/32): 0x20005000
(18) psp (/32): 0x00000000
(20) primask (/1): 0x00
(21) basepri (/8): 0x00
(22) faultmask (/1): 0x00
(23) control (/3): 0x00
===== Cortex-M DWT registers

Pour être sûr de mon coup, j'essaie avec les valeurs suivantes:

A moi les leds qui clignottent !

Commentaires: https://github.com/bsdsx/blog_posts/issues/15


Lien vers ce billet

21/05/2022

[ yubikey ]

202205210800 yubikey

FreeBSD 13.1, OpenSSH et Yubikey

Cette nouvelle mouture de FreeBSD vient entre autre avec une mise à jour d'OpenSSH et ça cause FIDO/U2F. Il serait temps d'utiliser enfin cette Yubikey qui traine sur mon porte-clefs depuis des lustres:

$ dmesg | tail -n 6
ugen0.3: <Yubico YubiKey OTP+FIDO+CCID> at usbus0
ukbd1 on uhub0
ukbd1: <Yubico YubiKey OTP+FIDO+CCID, class 0/0, rev 2.00/5.12, addr 7> on usbus0
kbd3 at ukbd1
uhid1 on uhub0
uhid1: <Yubico YubiKey OTP+FIDO+CCID, class 0/0, rev 2.00/5.12, addr 7> on usbus0

On va s'éparger du temps et des cheveux avec ce petit paquet kivabien:

$ doas pkg install u2f-devd
$ doas service devd restart
$ doas pw groupmod u2f -m MonLogin
[ snip déco/reco pour que le nouveau groupe soit pris en compte ]
$ ssh-keygen -t ed25519-sk
Generating public/private ed25519-sk key pair.
You may need to touch your authenticator to authorize key generation.
Key enrollment failed: requested feature not supported

Pas de soucis, c'est juste que le firmware de la clef est trop vieux pour utiliser ed25519-sk. Avec ecdsa-sk:

$ ssh-keygen -t ecdsa-sk
Generating public/private ecdsa-sk key pair.
You may need to touch your authenticator to authorize key generation.
[ la clef clignotte et attend une pression ]
Enter file in which to save the key (/home/MonLogin/.ssh/id_ecdsa_sk): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/MonLogin/.ssh/id_ecdsa_sk
Your public key has been saved in /home/MonLogin/.ssh/id_ecdsa_sk.pub$ ssh-keygen -t ecdsa-sk
...

Reste à déployer cette clef:

$ cat .ssh/id_ecdsa_sk.pub | ssh MonServeur "cat >> .ssh/authorized_keys"
$ unsetenv SSH_AUTH_SOCK
$ ssh -i .ssh/id_ecdsa_sk MonServeur
Confirm user presence for key ECDSA-SK SHA256:gkWxSHUxZjN5Q6svr51Mi0N+vMc051YPwXo7L1JkaQw
[ la clef clignotte et attend une pression ]
User presence confirmed
Last login: Sat May 21 10:07:59 2022 from dd.bsdsx.fr
...

Parfois la clef ne semble pas répondre:

$ ssh -i .ssh/id_ecdsa_sk nuc date
Confirm user presence for key ECDSA-SK SHA256:gkWxSHUxZjN5Q6svr51Mi0N+vMc051YPwXo7L1JkaQw
User presence confirmed
sam. 21 mai 2022 10:17:09 CEST
$ ssh -i .ssh/id_ecdsa_sk nuc date
Confirm user presence for key ECDSA-SK SHA256:gkWxSHUxZjN5Q6svr51Mi0N+vMc051YPwXo7L1JkaQw
User presence confirmed
sam. 21 mai 2022 10:17:14 CEST
$ ssh -i .ssh/id_ecdsa_sk nuc date
Confirm user presence for key ECDSA-SK SHA256:gkWxSHUxZjN5Q6svr51Mi0N+vMc051YPwXo7L1JkaQw
User presence confirmed
sam. 21 mai 2022 10:17:20 CEST
$ ssh -i .ssh/id_ecdsa_sk nuc 
Confirm user presence for key ECDSA-SK SHA256:gkWxSHUxZjN5Q6svr51Mi0N+vMc051YPwXo7L1JkaQw
[ la clef ne clignotte pas ]

Pas grand chose à faire:

^C
$ ssh -i .ssh/id_ecdsa_sk nuc
Confirm user presence for key ECDSA-SK SHA256:gkWxSHUxZjN5Q6svr51Mi0N+vMc051YPwXo7L1JkaQw
User presence confirmed
Last login: Sat May 21 10:14:38 2022 from dd.bsdsx.fr

Lien utile: https://cryptsus.com/blog/how-to-configure-openssh-with-yubikey-security-keys-u2f-otp-authentication-ed25519-sk-ecdsa-sk-on-ubuntu-18.04.html (anglais)

Commentaires: https://github.com/bsdsx/blog_posts/issues/14


Lien vers ce billet

16/04/2022

[ esp8266 python ]

202204160800 esp8266 python

esp8266 et MicroPython

Il y a déjà plusieurs mois, au détour d'une commande de composants électronique, j'avais ajouté dans mon panier un choupi bouzin àpacher qui non seulement faisait wifi mais intégrait en plus un écran oled. Vite oublié pendant ma découverte de l'Arduino, revoila t'y pas que je tombe dessus en faisant un peu de tri dans mon bazar.

Le bouzin

C'est un heltec wifi kit 8. Il existe une autre version plus péchue du bouzin, le wifi kit 32 pour qui il faudra faire en gros

s/8266/32/gc

Installer de quoi causer avec le bouzin:

$ doas pkg install py38-esptool

Tester qu'on peut causer avec le bouzin:

$ esptool.py --port /dev/cuaU0 --chip ESP8266 read_mac
esptool.py v3.3
Serial port /dev/cuaU0
Connecting.........
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: dc:4f:22:19:18:88
Uploading stub...
Running stub...
Stub running...
MAC: dc:4f:22:19:18:88
Hard resetting via RTS pin 
$ esptool.py --port /dev/cuaU0 --chip ESP8266 flash_id
esptool.py v3.3
Serial port /dev/cuaU0
Connecting.........
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: dc:4f:22:19:18:88
Uploading stub...
Running stub...
Stub running...
Manufacturer: ef
Device: 4016
Detected flash size: 4MB
Hard resetting via RTS pin...

On a bien un bouzin avec 4 Mo de RAM (Detected flash size: 4MB), on va pouvoir jouer avec MicroPython

MicroPython

L'installation de MicroPython est largement décrite dans les liens précédents, mais histoire d'avoir une version FreeBSD:

On nettoie le bouzin:

Tout en pressant le bouton 'PRG' (on peut le relacher quand apparait 'Erasing flash (this may take a while)...')

$ esptool.py --port /dev/cuaU0 --chip ESP8266 erase_flash
esptool.py v3.3
Serial port /dev/cuaU0
Connecting......
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: dc:4f:22:19:18:88
Uploading stub...
Running stub...
Stub running...
Erasing flash (this may take a while)...
Chip erase completed successfully in 7.4s
Hard resetting via RTS pin...

Et on installe micropython fraîchement téléchargé depuis https://micropython.org/download/esp8266/ :

Tout en pressant le bouton 'PRG' (on peut le relacher quand apparait le pourcentage de progression)

$ esptool.py --port /dev/cuaU0 --chip ESP8266 write_flash --flash_mode dio --flash_size detect 0x0 /tmp/esp8266-20220117-v1.18.bin
esptool.py v3.3
Serial port /dev/cuaU0
Connecting......
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: dc:4f:22:19:18:88
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Flash will be erased from 0x00000000 to 0x0009bfff...
Flash params set to 0x0240
Compressed 635992 bytes to 418428...
Wrote 635992 bytes (418428 compressed) at 0x00000000 in 37.2 seconds (effective 136.9 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...

Et maintenant magie du bouzin:

Taper Enter pour afficher l'invite de l'interpréteur python (>>>), tilde + point pour quitter cu

$ cu -l /dev/cuaU0 -s 115200
can't open log file /var/log/aculog.
Connected

>>> import esp
>>> esp.check_fw()
size: 635976
md5: 40b5ad861d83667d6c8ea59fd38c7ff9
True

Il ne me serait jamais venu à l'esprit d'utiliser un language de si haut niveau avec un microcontrôleur (le C c'est bon, mangez-en) mais je dois avouer que la simplicité du truc m'a laissé sur le cul.

L'interpréteur python c'est bien pratique pour tester en direct mais pour survivre à un reset il faut télécharger le code sur le bouzin:

$ doas pkg install py38-adafruit-ampy
$ ampy --port /dev/cuaU0 ls
/boot.py

Reste à lire https://pythonforundergradengineers.com/upload-py-files-to-esp8266-running-micropython.html, envoyer son premier main.py et reset le bouzin.

Le traditionnel clignotement de led se verra remplacé ici par l'affichage d'un "Hello world" (il n'y a pas de led pilotable sur la carte). J'ai suivi les liens suivants:

Je commence par scanner le bus i2c:

>>> from machine import Pin, I2C
>>> i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)
>>> devices = i2c.scan()
>>> print('i2c devices found:', len(devices))
i2c devices found: 1
>>> for device in devices:
...     print("Decimal address: ", device, " | Hexa address: ", hex(device))
... [ appui sur backspace puis enter ]
Decimal address:  60  | Hexa address:  0x3c

Je récupère le module kivabien:

$ cd /tmp && fetch https://raw.githubusercontent.com/micropython/micropython/master/drivers/display/ssd1306.py && ampy --port /dev/cuaU0 put ssd1306.py && ampy --port /dev/cuaU0 ls
/boot.py
/ssd1306.py

Et c'est parti:

>>> from machine import Pin, I2C
>>> import ssd1306
>>> i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)
>>> oled = ssd1306.SSD1306_I2C(128, 32, i2c, 0x3c)
>>> oled.fill(0)
>>> oled.text("Hello World", 0, 0)
>>> oled.show()

Je ne dirais pas que le python est ma tasse de thé ou mon bol de chocolat ou mon mug de café mais il faut bien reconnaitre que:

Commentaires: https://github.com/bsdsx/blog_posts/issues/13


Lien vers ce billet

09/12/2021

[ envsubst template ]

202112090700 envsubst template

envsubst, le templating du shell

envsubst est une commande que j'ai découvert sur le tard et je le regrette. Le script suivant montre un exemple d'utilisation:

$ cat -n pouet.sh ; echo ; ./pouet.sh | cat -n
 1  #!/bin/sh
 2
 3  set -eu
 4
 5  set -a
 6  FOO=foo
 7  BAR=bar
 8  set +a
 9  BAZ=baz
10
11  echo 'FOO: ${FOO} BAR: $BAR BAZ: ${BAZ}' | envsubst
12  echo 'FOO: ${FOO} BAR: $BAR BAZ: ${BAZ}' | envsubst '$FOO $BAZ'
13  echo "FOO: ${FOO} BAR: $BAR BAZ: ${BAZ}"
 1  FOO: foo BAR: bar BAZ: 
 2  FOO: foo BAR: ${BAR} BAZ: 
 3  FOO: foo BAR: bar BAZ: baz

On observe:

Un moteur de blog

Quelques fichiers suffisent pour générer un site statique:

$ envsubst < top.htm > .fichier.html
$ envsubst < fichier.md | markdown2html >> .fichier.html
$ envsubst < bottom.htm >> .fichier.html
$ mv .fichier.html fichier.html

Le moteur de ce blog utilise une (légère) déclinaison du code ci-dessus.

Des fichiers de configuration

J'apprécie Xen et le fichier de configuration d'une "machine virtuelle" (aka domU) ressemble à

name     = "al30"
maxvcpus = 1
memory   = 1024
kernel   = "/xen/vmlinuz-virt"
ramdisk  = "/xen/initramfs-virt"
vif      = [ 'bridge=br40' ]

# run
extra = "root=/dev/xvda3 modules=ext4 console=hvc0"
disk  = [ 'format=raw, vdev=xvda, access=w, target=/xen/al30.img' ]
type  = "pvh"
# run

# install
#extra = "modules=modules=loop,squashfs console=hvc0"
#disk  = [ 'format=raw, vdev=xvdc, access=r, devtype=cdrom, target=/xen/alpine-virt-3.14.3-x86_64.iso', 'format=raw, vdev=xvda, access=w, target=/xen/al30.img' ]
#type  = "pv"
# install

Plutôt que de jouer avec des lignes commentées, je fais un template:

# [${GENERATED_ON}] ${GENERATED_PATH}> ${GENERATED_BY} @${GENERATED_AT}

maxvcpus = 1
memory   = 1024
kernel   = "/xen/vmlinuz-virt"
ramdisk  = "/xen/initramfs-virt"
vif      = [ 'bridge=br40' ]

name  = "${XEN_NAME}"
type  = "${XEN_TYPE}"
extra = "${XEN_EXTRA} console=hvc0"
disk  = [ ${XEN_DISK} ]

Un script enrobe le tout, génère les variables suivant le mode run/install et se termine par:

...
WORKDIR=$(mktemp -d)
envsubst < ${tmpl} > ${WORKDIR}/xl.cfg
cd ${WORKDIR}

${AS_ROOT} xl create xl.cfg $@

Je l'utilise comme suit:

# mode install avec la console xen (-- -c)
$ ./domU -i al30.cfg -- -c
# mode normal
$ ./domU al30.cfg

al30.cfg définit le template et le nom du domU.

La documentation

Commentaires: https://github.com/bsdsx/blog_posts/issues/12


Lien vers ce billet

20/11/2021

[ opensmtpd filter ]

202111200800 opensmtpd filter

Les filtres avec OpenSMTPD

Pour protéger les ressources de mon serveur de courriel j'utilise ces filtres:

filter check_rdns   phase connect match !rdns   disconnect "550 no rDNS please fix"
filter check_fcrdns phase connect match !fcrdns disconnect "550 no FCrDNS please fix"

qui sont les pendants DNS des célèbres "pas d'bras, pas d'chocolat" et "T'as des baskets ? Bah ! Tu rentres pôôô" (chercher "sketch Maxime 'le videur'" pour la référence). Pour efficaces qu'ils soient, ces filtres ne peuvent pas grand chose face au côté répétitif des pénibles:

$ grep --count 'smtp connected address=193.56.29.192 host=<unknown>' /var/log/mail.log
109

Sachant qu'une seule connexion de ce genre génère 3 lignes de log:

Nov  2 00:01:33 backup_mx smtpd[60503]: 88166f7f21c951f4 smtp connected address=193.56.29.192 host=<unknown>
Nov  2 00:01:33 backup_mx smtpd[60503]: 88166f7f21c951f4 smtp failed-command command="" result="550 no rDNS please fix"
Nov  2 00:01:33 backup_mx smtpd[60503]: 88166f7f21c951f4 smtp disconnected reason=quit

Il est temps de bouter ces mécréants hors du royaume.

Table et mur de feu

J'ai déjà évoqué blacklistd qui, à mon sens, est la bonne réponse au problème mais OpenSMTPD ne le supporte pas (encore ?). Reste que le principe est le bon: placer l'adresse ip du rabouin dans une table (ipfw, npf, pf), un pool (ipf) ou un set (iptables/ipset/nftables/truc/je m'y perds dans ces linuxeries) et bloquer tout ce qui provient des adresses de cette table. Voyons comment faire avec un filtre.

Filtre

Un filtre OpenSMTPD se déclare de la façon suivante:

$ grep ^filter /etc/mail/smtpd.conf
filter nom_du_filtre proc-exec "/chemin/vers/mon/filtre arguments..."

Sans plus de précision ce filtre sera exécuté (froidement) en tant qu'utilisateur _smtpd du groupe _smtpd. Si les droits de cet utilisateur ne sont pas suffisants, on peut en spécifier un autre:

filter nom_du_filtre proc-exec "/chemin/vers/mon/filtre arguments..." user root group wheel

Un filtre s'active depuis la directive listen:

$ grep ^listen /etc/mail/smtpd.conf
listen on lo1 port 2525 filter nom_du_filtre

Un filtre de doit rien faire d'autre que lire depuis son entrée standard et écrire vers sa sortie standard (aka UNIX way of life). je ne vais pas paraphraser cet ancien billet mais en gros un bête script shell fait parfaitement l'affaire:

#!/bin/sh
set -eu
while true; do
    if read line; then
    ....
    fi
done

Le script devra commencer par lire les lignes suivantes:

config|smtpd-version|6.8.0p2
config|smtp-session-timeout|300
config|subsystem|smtp-in
config|admd|nom.de.la.machine
config|ready

Cette dernière ligne devra provoquer une réponse sur la sortie standard:

register|filter|smtp-in|connect
register|ready

Le filtre s'étant enregistré sur la phase connect du subsystem smtp-in, les prochaines lignes à lire auront cette forme:

filter|0.6|1636278882.100084|smtp-in|connect|b5c1acf410d9dbac|bdf7b27e9c960f99|<unknown>|192.168.192.90
filter|0.6|1636280533.458048|smtp-in|connect|3954af28cda90f23|f4a93480796395c4|foo.example.com|172.30.63.10

que l'on décompose en:

Le filtre devra répondre comme suit:

filter-result|$6|$7|$resultat

$resultat devra obligatoirement prendre une des valeurs suivantes:

Dehors les romanos

 1	FWCMD="${1:-true}"
 2	TIMESTAMP=${2:-1} # 0 no timestamp, 1 timestamp (default), 2 timestamp without nano seconds
 3	
 4	while true; do
 5	    if read line; then
 6	        case $line in
 7	            'config|ready') break;;
 8	        esac
 9	    fi
10	done
11	echo 'register|filter|smtp-in|connect'
12	echo 'register|ready'
13	
14	IFS='|'
15	while true; do
16	    if read line; then
17	        set -- $line
18	        case $1 in
19	            filter);;
20	            *) continue;;
21	        esac
22	        [ $# -ne 9 ] && echo "filter-result|$6|$7|proceed" && continue
23	        [ $8 != '<unknown>' ] && echo "filter-result|$6|$7|proceed" && continue
24	        echo "filter-result|$6|$7|disconnect|550 go away"
25	        ts=$3
26	        case $TIMESTAMP in
27	            0) ts='';;
28	            2) ts=${3%%.*};;
29	        esac
30	        ${FWCMD} $9 $ts || continue
31	    fi
32	done

ipfw car je le vaux bien

J'ai déjà parlé d'ipfw, de tables et de timestamp, ma commande sera la suivante:

/sbin/ipfw table t_pouilleux add

et le smtpd.conf devient:

filter nom_du_filtre proc-exec "/chemin/vers/mon/filtre '/sbin/ipfw table t_pouilleux add' 2" user root group wheel

jail ou chroot

Dans le cas où le filtre ne peut pas accéder directement à la commande du mur de feu, on peut imaginer un système à base de fifo:

...
fifo=/chemin/vers/fifo
...
echo "filter-result|$6|$7|disconnect|550 go away"
...
[ -p $fifo ] || continue
echo $9 $ts > $fifo || continue

et un autre script se chargera de la bonne commande:

$ cat fifo2fw.sh
#!/bin/sh

set -eu

echo $$ > $1

fifo=${2}

[ -p $fifo ] || mkfifo $fifo

while true; do
    if read ip timestamp < $fifo; then
        /sbin/ipfw table t_pouilleux add $ip $timestamp
    fi
done

Exemple d'une jail:

...
$fifo = "/chemin/complet/vers/fifo";
$pid  = "/var/run/fifo2fw.pid";
...
exec.prestart += "/chemin/vers/fifo2fw.sh $pid $fifo &";
exec.start += "/usr/local/sbin/smtpd -f /etc/mail/smtpd.conf";
exec.release += "pkill -9 -F $pid";
exec.release += "rm -f $fifo $pid";

La documentation

Commentaires: https://github.com/bsdsx/blog_posts/issues/11


Lien vers ce billet