Introduction
Toujours dans l’optique de maîtriser ses communications et sa vie privée, le projet Nostr 1 a attiré mon attention. Voici un petit résumé du projet, extrait du web.
“Nostr (Notes and Other Stuff Transmitted by Relays) est un nouveau protocole simple, ouvert et résistant à la censure. Actuellement, les developpeur-euse-s sont concentrés sur la construction d’un réseau social décentralisé basé principalement sur des clés publiques et privées2. Les spécifications sont faites au niveau protocolaire ; avec des parties obligatoires, et optionnelles. On parle alors de NIPs (Nostr Implementation Possibilities) 3.
Pour atteindre cela, les clients vont se connecter à différents relais et vont y publier des messages appelés événements. Nostr utilise la notion de relai simple et client intelligent qui vont se charger de signer les événements à l’aide d’une paire de clés pour authentifier la personne 2.”
Les relais peuvent être utilisés pour récupérer les évènements (read) ou en publier (write). Un évènement peut autant être une note (similaire à un tweet ou un toot) qu’un message privé.
➡ Pourrait-on mettre en place un relai et communiquer de manière sécurisée en étant sûr que chaque élément soit connu et maîtrisé ? Mettre en place une infrastructure légère à installer semble intéressant, et diffère des écosystèmes complexes à maintenir tels que Matrix. Voyons plus en détails les étapes à mettre en oeuvre pour ajouter un relai public et s’y connecter.
Choix du relai
Il existe de nombreux logiciels relais4, développés en différents langages. Les relais n’implémentent pas tous toutes les mêmes NIPs, mais généralement les NIPs obligatoires sont disponibles.
Mon choix se portera sur le relai PyRelay 5 à jour du commit 79dca44, assez récent au moment de l’écriture et développé en Python.
Test du relai
Après avoir corrigé quelques détails dans le code6 — lié au manque de maintenance du logiciel et des fonctionnalités grandissantes des clients, le relai peut être lancé.
On construit une dernière fois l’image Docker :
$ docker build . -t pyrelay:0.0.1
FROM python:3.11.1-slim
WORKDIR /app/
RUN apt-get update
RUN apt-get install build-essential -y
RUN apt-get install pkg-config -y --no-install-recommends
COPY pyproject.toml requirements.txt /app/
RUN pip install -r requirements.txt
COPY pyrelay /app/pyrelay
ENV PYTHONPATH=/app
Et on lance le fichier docker-compose.yml
suivant avec la commande $ docker-compose up
.
version: "2"
services:
relay:
image: pyrelay:0.0.1
ports:
- "80:8001"
command:
- bash
- -c
- |
set -e
python pyrelay/relay/server.py
Le relai démarre et attend les connexions client:
$ docker-compose up
Recreating pyrelay_relay_1 ... done
Attaching to pyrelay_relay_1
relay_1 | alembic.runtime.migration - INFO - Context impl SQLiteImpl.
relay_1 | alembic.runtime.migration - INFO - Will assume non-transactional DDL.
relay_1 | alembic.runtime.migration - INFO - Running upgrade -> 9ef618136f49, Add kind
relay_1 | alembic.runtime.migration - INFO - Running upgrade 9ef618136f49 -> 9b2e8a3aee6b, Add tag extras
relay_1 | alembic.runtime.migration - INFO - Running upgrade 9b2e8a3aee6b -> 5c571f8007b7, Add deleted at
relay_1 | /app/pyrelay/relay/server.py:33: DeprecationWarning: There is no current event loop
relay_1 | event_loop = asyncio.get_event_loop()
relay_1 | websockets.server - INFO - server listening on [::]:8001
relay_1 | websockets.server - INFO - server listening on 0.0.0.0:8001
Côté client, on va tenter de se connecter avec l’application Nostur 7. On peut configurer les relais via les settings. Le relai à ajouter n’est autre que notre machine personnelle.
$ ip a
[...]
42: xxxxx: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
inet 192.168.1.78/24 brd 192.168.1.255 scope global dynamic noprefixroute antoine.localx
[...]
L’URI à ajouter dans Nostur sera donc ws://192.168.1.78
, grâce à l’exposition du port 80
du conteneur.
La connexion est non-chiffrée, et dans ce contexte cela n’est pas critique : on teste la faisabilité de l’écosystème. Pour la suite, chiffrer les connexions peut s’envisager avec un composant dédié tel qu’un proxy.
Une fois le relai renseigné, on observe les requêtes du client:
relay_1 | __main__ - INFO - New message from connection conn_uid=366a6ada-bd6b-4cd8-86cf-1120db1dedc5
relay_1 | __main__ - INFO - ['REQ', 'Notifications', {'#p': ['KEY'], 'since': 1704289599, 'kinds': [7, 9735, 1, 4, 6, 9802, 30023], 'limit': 500}]
relay_1 | pyrelay.relay.dispatcher - INFO - Got request=NostrRequest(subscription_id='Notifications', filters=[NostrFilter(ids=None, authors=None, kinds=[], since=1704719625, until=None, limit=500, generic_tags={'p': ['KEY']})]) conn_uid=366a6ada-bd6b-4cd8-86cf-1120db1dedc5
relay_1 | __main__ - INFO - New message from connection conn_uid=366a6ada-bd6b-4cd8-86cf-1120db1dedc5
relay_1 | __main__ - INFO - ['REQ', 'Notifications-CATCHUP', {'#p': ['KEY'], 'since': 1704289599, 'kinds': [7, 9735, 1, 4, 6, 9802, 30023], 'limit': 500}]
relay_1 | pyrelay.relay.dispatcher - INFO - Got request=NostrRequest(subscription_id='Notifications-CATCHUP', filters=[NostrFilt
On voit ici les requêtes du client. La clé KEY
demande les informations de type kinds == [7, 9735, 1, 4, 6, 9802, 30023]
depuis la date du 03 janvier 2024. Voici le tableau des kinds
Nostr. Notons qu’il n’y a rien à récupérer, le relai n’a pas pu stocker quelconque information au préalable.
➡ La faisabilité est validée, on peut passer au déploiement !
Architecture cible
Les ports utilisés sont les ports standards 80
et 443
. Pour la gestion du cycle de vie du conteneur, on utilise le mode swarm
8 de Docker Engine.
Pour la gestion des certificats et les points d’entrées, place au reverse proxy. Je propose ici l’utilisation d’ Nginx
. Il permettra d’également sécuriser les accès. En partageant le fichier de logs avec la machine hôte, un autre processus tel que fail2ban
pourra détecter les tentatives d’intrusions9.
Tout le flux entre par le reverse proxy Nginx, et est redirigé vers le relai. Nginx gère le chiffrement des communications entre les clients et le swarm. La connexion interne jusqu’au relai passe alors en clair.
Le fichier access.log sera analysé en temps réel et des protections au niveau IP seront déclenchées si nécessaire. Pour protéger le système, il est nécessaire de manipuler le firewall et fail2ban est déjà en cours d’utilisation sur la machine hôte. On lui configurera quelques jails supplémentaires, qui ajouteront leurs rules dans la chain DOCKER-USER
10.
➡ Avec cette architecture, on répond aux besoins de confidentialité et de chiffrement, puis aux nécessités de protection face aux attaques extérieures classiques.
Le reverse proxy
L’image utilisée sera Nginx:1.25.3
11. On montera plusieurs fichiers dans le conteneur associé:
Description | Nom de la ressource | Chemin dans le conteneur | Commentaires |
---|---|---|---|
Fichier de configuration Nginx global | nginx_system.conf | /etc/nginx/nginx.conf | Fichier global de Nginx séparé du reste. |
Fichier de configuration Nginx dédié au relai | nginx_user.conf | /etc/nginx/conf.d/user.conf | Ne contient que la partie dédiée au relai. |
Dossier des certificats | ./certs/ | /etc/nginx/certs/ | C’est dans ce dossier que l’on génère manuellement les certificats. Le conteneur les utilisera. |
Fichier de logs | ./log/access.log | /var/log/nginx.access.log/ | Avoir accès au fichier de logs est utile pour détecter les intrusions. |
On peut tout à fait joindre les deux fichiers de configuration. Cependant pour la suite, on va devoir les garder séparés.
Résolution de conteneurs dans le swarm
La conguration d’Nginx en tant que proxy se fait de la manière suivante, via le fichier nginx_user.conf
:
server {
[...]
location / {
proxy_pass http://nostr_pyrelay:8001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # for websockets
proxy_set_header Connection $connection_upgrade; # for websockets
proxy_read_timeout 86400; # for websockets
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Le nom de domaine interne au swarm nostr_pyrelay
, pointant vers le service du même nom, est résolu par le resolver Docker 127.0.0.11
. On peut le renseigner dans le fichier de configuration système nginx_system.conf
.
http {
[...]
resolver 127.0.0.11 ipv6=off valid=10s;
[...]
}
Une courte durée d’invalidité de résolution permet d’être plus réactif sur les mises à jour des IP des conteneurs, notamment lors des redémarrages fréquents. Pratique pour du prototypage.
Gestion des certificats
Dans un premier temps, partons sur la création d’un certificat auto-signé. La connexion sera alors chiffrée ; même si l’identité du relai ne sera pas vérifiée. On exécute le code suivant.
#!/bin/bash
export DOMAIN_NAME="antoine.local"
export DAYS_VALID=720
openssl \
req -x509 \
-nodes \
-subj "/CN=${DOMAIN_NAME}}" \
-addext "subjectAltName=DNS:${DOMAIN_NAME}" \
-days ${DAYS_VALID} \
-newkey rsa:2048 -keyout ./self-signed.key \
-out ./self-signed.crt
On placera ce certificat ainsi généré dans le dossier ./certs/
.
Lancement des services
J’utilise un outil homemade pour déployer les services Docker. Le fichier de description ne se conforme donc pas à un docker-compose.yml
. Néanmoins, l’idée reste la même. Une précision sur la configuration réseau du conteneur Nginx en mode “Virtual IP” (vip
).
{
"image": "nginx",
"tag":"1.25.3",
"name": "nginx_reverse_proxy",
"hostname":"nginx_reverse_proxy",
"mounts": ["./nginx/nginx_system.conf:/etc/nginx/nginx.conf/:ro", "./nginx/nginx_user.conf/:/etc/nginx/conf.d/:ro", "./nginx/certs:/etc/nginx/certs/:ro", "./nginx/log/access.log:/var/log/nginx/access.log:rw"],
"endpoints":{
"mode":"vip",
"ports": {"443":443}
},
{
"image": "pyrelay",
"tag":"latest",
"name": "nostr_pyrelay",
"command": "python pyrelay/relay/server.py",
"env":["SERVER_NAME=antoine.local"],
"hostname":"nostr_pyrelay",
"endpoints":{
"mode":"dnsrr"
},
"network":{
"name":"network_swarm",
"aliases":["nostr_pyrelay"]
}
}
J’utilise Portainer12 pour visualiser graphiquement les ressources Docker mises en place.
On expose le port 443
du reverse proxy. Le relai quant à lui n’est pas visible de l’extérieur.
On peut se connecter sur le relai et voir l’établissement de la connexion sur le serveur
# sur ma machine personnelle
$ websocat wss://antoine.local --insecure
Sur le serveur:
On retrouve le certificat auto-signé, que l’on peut récupérer à partir d’une machine tierce. Le champantoine.local
correspond au nom de domaine public de la seule machine hébergeant le swarm.Le nom de domaine utilisé dans cet article
antoine.local
n’est pas officiel et en réalité j’en utilise un autre pour la réalisation de ces étapes. Néanmoins pour la compréhension du déroulé, on supposera que le nom de domaine réel est bienantoine.local
.
$ openssl s_client -showcerts -servername antoine.local antoine.local:443 </dev/null
CONNECTED(00000003)
depth=0 CN = antoine.local}
verify error:num=18:self-signed certificate
verify return:1
depth=0 CN = antoine.local}
verify return:1
---
Certificate chain
0 s:CN = antoine.local}
i:CN = antoine.local}
a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
v:NotBefore: Jan 3 15:18:53 2024 GMT; NotAfter: Dec 26 15:18:53 2025 GMT
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
---
Server certificate
subject=CN = antoine.local}
issuer=CN = antoine.local}
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 1380 bytes and written 397 bytes
Verification error: self-signed certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 18 (self-signed certificate)
---
DONE
➡ Cependant la vérification des certificats est obligatoire; la plupart des clients Nostr ne supportent pas les certificats auto-signés. On va devoir faire valider notre certificat par une autorité reconnue.
Let’s encrypt
On se tourne vers le service bien connu Let’s Encrypt pour facilement mettre en place une certification. Pour cela, on va utiliser l’image Docker nginx-certbot:5.0.0
13 qui automatise la gestion de certificats14.
Nginx-certbot docker image
On lance le conteneur avec quelques modifications et configurations :
- Montage des bons volumes avec les bons modes (read-only pour certains15).
Description | Nom de la ressource | Chemin dans le conteneur | Commentaires |
---|---|---|---|
Fichier de configuration Nginx global | nginx_system.conf | /etc/nginx/nginx.conf | Fichier global de Nginx séparé du reste. |
Dossier de configuration Nginx dédié au relai | ./user_conf.d/ | /etc/nginx/user_conf.d/ | Ne contient que les fichiers dédiés au relai. Doit être monté en lecture seule. C’est un répertoire spécial pour cette image. |
Dossier des certificats | ./letsencrypt/ | /etc/letsencrypt/ | C’est dans ce dossier que certbot va générer les certificats. |
Fichier de logs | ./log/access.log | /var/log/nginx.access.log/ | Avoir accès au fichier de logs est utile pour détecter les intrusions. |
- Changement du nom de domaine par défaut dans le fichier
nginx_user.conf
présent dans le dossier./user_conf.d/
.
server {
# Listen to port 443 on both IPv4 and IPv6.
listen 443 ssl default_server reuseport;
listen [::]:443 ssl default_server reuseport;
# Domain names this server should respond to.
server_name antoine.local;
# Load the certificate files.
ssl_certificate /etc/letsencrypt/live/antoine.local/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/antoine.local/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/antoine.local/chain.pem;
# Load the Diffie-Hellman parameter.
ssl_dhparam /etc/letsencrypt/dhparams/dhparam.pem;
return 200 'Let\'s Encrypt certificate successfully installed!';
add_header Content-Type text/plain;
}
Notons l’absence de configuration proxy : en effet la première étape avec cette image est de passer la procédure via l’utilitaire
certbot
pour obtenir un certificat vérifié, que l’on retrouvera dans le répertoire persistent./letsencrypt/
.
Il démarre et demande un certificat via l’utilitaire certbot
.
[...]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% >>>>> Diffie-Hellman parameter creation done! <<<<< %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2024/01/08 15:50:24 [info] Starting certificate renewal process
2024/01/08 15:50:24 [info] Requesting an ECDSA certificate for 'antoine.local' (http-01 through webroot)
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Account registered.
Requesting a certificate for antoine.local
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/antoine.local/fullchain.pem
Key is saved at: /etc/letsencrypt/live/antoine.local/privkey.pem
This certificate expires on 2024-04-07.
[...]
Une fois le certificat obtenu, on relance le conteneur avec la configuration finale du reverse proxy. On édite une dernière fois le fichier présent dans le répertoire ./user_conf.d/
, pour y configurer la redirection de connexion, et la gestion des websocket.
server {
# Listen to port 443 on both IPv4 and IPv6.
listen 443 ssl default_server reuseport;
listen [::]:443 ssl default_server reuseport;
# Domain names this server should respond to.
server_name antoine.local;
# Load the certificate files.
ssl_certificate /etc/letsencrypt/live/antoine.local/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/antoine.local/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/antoine.local/chain.pem;
# Load the Diffie-Hellman parameter.
ssl_dhparam /etc/letsencrypt/dhparams/dhparam.pem;
location / {
proxy_pass http://nostr_pyrelay:8001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # for websockets
proxy_set_header Connection $connection_upgrade; # for websockets
proxy_read_timeout 86400; # for websockets
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Cerbot staging mode
On peut passer la variable STAGING=1
au conteneur nginx-certbot
pour obtenir un certificat de test et ainsi valider le fonctionnement de l’image. Voici le certificat résultant.
$ openssl s_client -showcerts -servername antoine.local antoine.local:443 </dev/null
CONNECTED(00000003)
depth=2 C = US, O = (STAGING) Internet Security Research Group, CN = (STAGING) Bogus Broccoli X2
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=1 C = US, O = (STAGING) Let's Encrypt, CN = (STAGING) Ersatz Edamame E1
verify return:1
depth=0 CN = antoine.local
verify return:1
---
Certificate chain
0 s:CN = antoine.local
i:C = US, O = (STAGING) Let's Encrypt, CN = (STAGING) Ersatz Edamame E1
a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA384
v:NotBefore: Jan 10 07:21:37 2024 GMT; NotAfter: Apr 9 07:21:36 2024 GMT
-----BEGIN CERTIFICATE-----
[...]
Lancement final
On lance le tout, et après quelques secondes, le relai est accessible !
On peut le configurer dans le client mobile Nostur.
Une requête curl
montre bien la disponibilité du relai.
$ curl https://antoine.local
Failed to open a WebSocket connection: invalid Connection header: close.
You cannot access a WebSocket server directly with a browser. You need a WebSocket client.
Le certificat est bien vérifié :
$ openssl s_client -showcerts -servername antoine.local antoine.local:443 </dev/null
CONNECTED(00000003)
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = antoine.local
verify return:1
---
Certificate chain
0 s:CN = antoine.local
i:C = US, O = Let's Encrypt, CN = R3
a:PKEY: id-ecPublicKey, 256 (bit); sigalg: RSA-SHA256
v:NotBefore: Jan 10 07:37:07 2024 GMT; NotAfter: Apr 9 07:37:06 2024 GMT
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
---
Server certificate
subject=CN = antoine.local
issuer=C = US, O = Let's Encrypt, CN = R3
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 4118 bytes and written 391 bytes
Verification: OK
En configurant un client tel que Gossip16 sur notre machine personnelle, on peut débugguer les paquets réseaux avec Wireshark17.
On peut voir l’établissement de la connexion chiffrée avec le relai:
id | Timestamp | SRC | DST | Protocol | Details |
---|---|---|---|---|---|
60 | 11.442065691 | 192.168.1.78 | antoine.local | TLSv1.3 | Client Hello |
61 | 11.460031100 | antoine.local | 192.168.1.78 | TCP | 443 → 58538 [ACK] Seq=1 Ack=241 Win=64128 Len=0 TSval=4147355995 TSecr=3356805992 |
62 | 11.460031506 | antoine.local | 192.168.1.78 | TLSv1.3 | Server Hello, Change Cipher Spec, Application Data |
➡ La mise en place du relai public est terminée ! On peut s’en servir pour communiquer au sein du réseau Nostr. Ce relai est accessible et n’importe quel client Nostr peut l’inclure dans sa liste de relais.
Protection des intrusions
Après quelques dizaines de minutes, on peut déjà voir des requêtes non souhaitées sur les ports 80
et 443
, visibles dans les logs du conteneur Nginx:
$ tail -f ./log/access.log -n 100
[...]
10.10.0.4 - - [Jan/2024:09:21:41 +0000] "GET / HTTP/1.1" 101 4 "-" "-"
10.10.0.4 - - [Jan/2024:09:21:41 +0000] 101 "GET / HTTP/1.1" 4 "-" "-" "-"
10.10.0.4 - - [Jan/2024:09:21:46 +0000] "GET / HTTP/1.1" 426 165 "-" "curl/7.81.0"
10.10.0.4 - - [Jan/2024:09:21:46 +0000] 426 "GET / HTTP/1.1" 165 "-" "curl/7.81.0" "-"
10.10.0.4 - - [Jan/2024:09:31:14 +0000] "\x03\x00\x00/*\xE0\x00\x00\x00\x00\x00Cookie: mstshash=Administr" 400 150 "-" "-"
10.10.0.4 - - [Jan/2024:09:31:14 +0000] 400 "\x03\x00\x00/*\xE0\x00\x00\x00\x00\x00Cookie: mstshash=Administr" 150 "-" "-" "-"
10.10.0.4 - - [Jan/2024:09:45:32 +0000] "GET / HTTP/1.1" 301 162 "-" "Hello World"
10.10.0.4 - - [Jan/2024:09:45:32 +0000] 301 "GET / HTTP/1.1" 162 "-" "Hello World" "-"
10.10.0.4 - - [Jan/2024:09:45:53 +0000] "H\x00\x00\x00tj\xA8\x9E#D\x98+\xCA\xF0\xA7\xBBl\xC5\x19\xD7\x8D\xB6\x18\xEDJ\x1En\xC1\xF9xu[l\xF0E\x1D-j\xEC\xD4xL\xC9r\xC9\x15\x10u\xE0%\x86Rtg\x05fv\x86]%\xCC\x80\x0C\xE8\xCF\xAE\x00\xB5\xC0f\xC8\x8DD\xC5\x09\xF4" 400 150 "-" "-"
10.10.0.4 - - [Jan/2024:09:45:53 +0000] 400 "H\x00\x00\x00tj\xA8\x9E#D\x98+\xCA\xF0\xA7\xBBl\xC5\x19\xD7\x8D\xB6\x18\xEDJ\x1En\xC1\xF9xu[l\xF0E\x1D-j\xEC\xD4xL\xC9r\xC9\x15\x10u\xE0%\x86Rtg\x05fv\x86]%\xCC\x80\x0C\xE8\xCF\xAE\x00\xB5\xC0f\xC8\x8DD\xC5\x09\xF4" 150 "-" "-" "-"
10.10.0.4 - - [Jan/2024:09:46:18 +0000] "GET / HTTP/1.1" 426 165 "-" "-"
On peut à ce moment détecter quelles requêtes sont issues d’outils de scanning et bannir au niveau firewall les IPs correspondantes. Pour autant, on note que toutes les IPs sont identiques et viennent du réseau ingress de Docker swarm.
Nginx permet de récupérer l’adresse IP source du client Nostr avec la directive proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
. Cependant cela ne fonctionne pas avec Docker swarm actuellement, et plusieurs tickets ont été ouverts à ce sujet 18 19 20.
Voici la version de Docker Engine avec laquelle ce problème s’observe.
$ docker version
Server:
Engine:
Version: 24.0.7
➡ Une solution de secours serait alors de mettre en place le reverse proxy sur la machine hôte et gérer les certificats à cet endroit, puis de rediriger les flux 80
et 443
vers le reverse proxy du swarm sur des ports non-standards. De toute façon, si cette machine devait héberger plusieurs applications dialoguant en HTTP/HTTPS
, un reverse proxy au niveau du host serait nécessaire.
Conclusion
À travers cet article, on a mis en place un relai Nostr public, avec des fonctionnalités de chiffrement et de gestion de cycle de vie. La mise en place de protection impliquant fail2ban
reste à finaliser suite aux investigations techniques sur Docker swarm.
Même si la mise en place et la maintenance opérationnelle du relai reste légère (pas de base de données à gérer par exemple), un développement logiciel de fond est nécessaire pour profiter des fonctionnalités grandissantes que les NIPs proposent (transfert d’images, de vidéo, groupes de discussions,…).
De plus, il serait intéressant de qualifier les communications via les relais Nostr, notamment de déterminer les temps d’attentes liés aux récupérations des messages.
https://medium.com/notrustverify/nostr-x-nym-b1602320ecf0 ↩︎ ↩︎
https://web.archive.org/web/20230325103248/https://www.linuxcapable.com/nginx-custom-fail2ban-filters-and-jails-10-examples/ ↩︎
https://docs.docker.com/network/packet-filtering-firewalls/#add-iptables-policies-before-dockers-rules ↩︎
https://github.com/JonasAlfredsson/docker-nginx-certbot/blob/master/docs/good_to_know.md#good-to-know ↩︎