đŸ’Ÿ Archived View for heyplzlookat.me â€ș articles â€ș mehari-serveur.gmi captured on 2024-08-18 at 17:25:29. Gemini links have been rewritten to link to archived content

View Raw

More Information

âŹ…ïž Previous capture (2024-03-21)

-=-=-=-=-=-=-

Comment faire un serveur TLS en OCaml qui soit compatible Mirage (dans Mehari)

⚠ TW : Article trĂšs moche avec de nombreuses profanitĂ©s, malgrĂ© tout publiĂ©

Le principal problĂšme est la stack TCP/IP puisqu’on ne peut pas utiliser la librairie Unix (prĂ©sente dans la librairie standard) sinon ce n’est pas compatible Mirage.

On peut donc aller la chercher dans le type de module `Tcpip.Stack.V4V6` (celui que Tim a choisi dans `server_impl`)

on y trouve :

val listen_tcp :
  ?keepalive:Tcp.Keepalive.t ->
  t ->
  port:int ->
  ( TCP.flow -> unit Lwt.t ) ->
  unit

(Tcpip) V4V6

Mais elle est dépréciée je ne sais pas pourquoi.

Donc on cherche mieux et on tombe sur ça :

val listen :
  t ->
  port:int ->
  ?keepalive:Tcp.Keepalive.t ->
  ( flow -> unit Lwt.t ) ->
  unit

(Tcpip) V4V6.TCP

Ok super elle n’est pas dĂ©prĂ©ciĂ©e mais maintenant il faut une valeur de type `t` !!

OĂč trouver ça ???

Il n’y a aucune fonction pour crĂ©er un `t`



Sauf que si, détrompez-vous.

Il y a `tcpip.stack-direct` et `tcpip.stack-socket` qui sont deux implĂ©mentations du truc de ce que j’ai compris (lisez le readme sinon, je sais pas).

Tim a séparé en deux Mehari :

J’ai choisi la premiĂšre implĂ©mentation de `tcpip` qui venait de base (`socket`) mais elle s’est finalement avĂ©rĂ©e ĂȘtre un bon choix puisqu’on l’utilise uniquement dans `mehari.unix` et que `direct` est un bordel que je n’ai pas envie d’aborder.

Si on regarde dans le module `V4V6` on y trouve :

val connect : Udpv4v6_socket.t -> Tcpv4v6_socket.t -> t Lwt.t

(Tcpip) Tcpip_stack_socket.V4V6

Ce n’est pas ce qu’il nous faut (pas besoin d’`Udpv4v6_socket.t`) et il nous faut trouver comment crĂ©er un `Tcpv4v6_socket.t` de toute façon.

On regarde Ă©galement du cĂŽtĂ© de `TCP` (mĂȘme type de module) et ici se trouve notre bonheur :

val listen :
  t ->
  port:int ->
  ?keepalive:Tcp.Keepalive.t ->
  ( flow -> unit Lwt.t ) ->
  unit

(Tcpip) Tcpip_stack_socket.V4V6.TCP

Mais elle n’est pas incluse dans le type de module, j’ai donc Ă©tĂ© renvoyĂ© Ă  la triste rĂ©alitĂ© qu’on ne peut pas l’utiliser en dehors de `mehari.unix`.

Comment faire donc ?

AprĂšs un soir Ă  se lamenter devant Tim que je n’arrive Ă  rien il a donc posĂ© la question Ă  Dinosaure (illustre membre de la communautĂ© Mirage OS) sur le Discord OCaml (on a essayĂ© de faire notre meilleur anglais possible pour l’impressioner) :

Le condor du plateau — 17/11/2022 18:56
hi, I use mirage Tcpip to listen on a socket so I am using Tcpip.Stack.V4V6.TCP.listen

https://mirage.github.io/mirage-tcpip/tcpip/Tcpip/Stack/module-type-V4V6/index.html#val-listen

For this usage, I have to pass a value of type Tcpip.Stack.V4V6.TCP.t and a callback of type TLS.FLOW.flow -> unit Lwt.t but I don’t understand how can I get a value of type Tcpip.Stack.V4V6.t.
I must specify that I work on a library and not on a unikernel
from what I understand I can obtain a stack value at the construction of the unikernel like here

https://github.com/mirage/mirage-tcpip/blob/4bae5497c5ef76c1dc3532bb0e4d6ad466051c50/examples/unikernel/config.ml#L7

but I still don’t know how to get it in another context than unikernel
dinosaure — 18/11/2022 11:50
you can have an example here: https://github.com/roburio/http-mirage-client/blob/d2de9c59824a621959a4dbaceca24bedb26ca374/test/test.ml#L116 🙂

Super !

Mais on a toujours le mĂȘme problĂšme, on peut pas l’utiliser dans `mehari`.

Condor va donc gentiment lui répondre :

Le condor du plateau — 18/11/2022 12:36
thank you for your reply, however the connect function seems to be hidden by the signature that I apply here to the Stack module:
module Make (Stack : Tcpip.Stack.V4V6) = struct


end
include Lib.Make (Tcpip_stack_socket.V4V6)
if it was not clear, I use the this module type because I try to make a mirage library compatible while having a default implementation 🙂

(Notez l’utilisation de l’emote de faquin) (pas Dinosaure je parle de Tim)

On en reparle donc en message privĂ© avant que Dinosaure ne rĂ©ponde et lĂ  on se rend compte de notre bĂȘtise : si tous les unikernels Mirage passaient leur stack via `main $ stack` et pas directement dans la fonction c’est qu’il y avait une raison


Le condor du plateau — 18/11/2022 12:46
so I just have to make an entry point and then adapt it in the default implementation of the library
I was doing it wrong, thank you đŸ„č

(Lire : « éjacule-moi dessus »)

dinosaure — 18/11/2022 12:56
the reason behind is: the connect function (for any device) depends on the implementation (and should not be exposed by the interface)
then, the mirage tool knows how to “connect” a device and compose implementations, values which come from the connect and your unikernel 🙂
I just would like to notice that Hannes wrote an article about Albatross, our tool to deploy unikernels easily:

https://hannes.nqsb.io/Posts/Albatross

Merci Dinosaure pour cette explication.

Donc on va suivre son exemple et override les fonctions `run` et `run_lwt` dans `mehari.unix`.

Je mets par dĂ©faut `Ipaddr.V4.Prefix.loopback` pour l’adresse IPv4 et lĂ  encore des problĂšmes surviennent :

Fatal error: exception Unix.Unix_error(Unix.EADDRNOTAVAIL, "bind", "")

Étrange Ă©tant donnĂ© qu’on utilise l’adresse loopback.

Je me retrouve plongĂ© dans la mĂȘme situation : plusieurs soirs de tristesse et d’impuissance.

Mais cette fois j’eu la prĂ©sence d’esprit de vĂ©rifier si c’était la bonne adresse :

127.0.0.0/8

« Tiens tiens tiens »

Il manque un 1 !

J’essaie donc de remplacer par `127.0.0.1/8` (avec `Ipaddr.V4.Prefix.of_string_exn` parce que flemme de propager le `result`).

Là oui ça marche youpi !

Sauf qu’il y a _encore_ un problĂšme, notre fonction `serve` qui est la main loop du seveur n’est pas exĂ©cutĂ©e.

Donc pareil je demande à Tim de poser la question sur le Discord :

Le condor du plateau — 22/11/2022 22:46
Hi, I keep hacking with Tcpip and I have a new problem.
The callback doesn’t seem to run in the following snippet:
let callback _ = Lwt_io.printl "Executed"
Stack.TCP.listen stack serve;


I’m using this function :

https://mirage.github.io/mirage-tcpip/tcpip/Tcpip/Stack/module-type-V4V6/TCP/index.html#val-listen

Vous remarquerez que l’on s’est trompĂ© de nom de fonction et il manque un `in` donc on aura droit Ă  un message d’étonnement de la part de « anmonteiro » (autre illustre membre de la communautĂ© (rĂ©alisation d’un serveur HTTP en OCaml !)).

Étant de vĂ©ritables singes, on ne corrigera l’erreur que trop tard aprĂšs humiliation.

« mseri » (illustre membre de la communauté OCaml également) nous aura proposé une piste un peu plus tÎt :

mseri — 22/11/2022 23:44
Have you tried to flush before listening?
Le condor du plateau — 22/11/2022 23:48
how can I do that?

Pas de réponse à ce jour.

C’est là que Dinosaure vint à notre rescousse et proposa deux pistes :

dinosaure — 23/11/2022 15:35
it’s probably related to this issue (which is fixed):

https://github.com/mirage/mirage-tcpip/issues/438

I would like to say that you should to listen 
 ; Lwt.return_unit instead of listen 
 |> Lwt.return first
if it does not solve your issue, I will probably try to go deeper and reproduce your case 🙂

Pour la premiĂšre option, avec un peu d’espoir notre version de `tcpip` n’était peut-ĂȘtre tout simplement que trop peu rĂ©cente, mais aprĂšs vĂ©rification cette issue date d’il y a presque 2 ans.

Pour la deuxiÚme option :

Le condor du plateau — 23/11/2022 15:44
I just tried this fix but it doesn’t solve the issue 😔
dinosaure — 23/11/2022 15:56
hmmhmm
ok, I will try to reproduce

Quoi !? Dinosaure va cloner (ou _a_ cloné !) notre code (que dis-je, notre torchon) sur sa machine personnelle et le compiler.

Malgré les difficultés, on y a vu une petite victoire.

AprĂšs une semaine d’attente impatiente, Dinosaure est sorti de sa longue rĂ©flexion pour rĂ©pondre Ă  Tim :

dinosaure — 2022/11/29 13:54
I finally found why your `listen` does not work. With `mirage-tcpip`, you must allocate a `Stack.t` (not only a `Stack.TCP.t`) and call the `Stack.listen` then. `Stack.TCP.listen` just adds internally a notification to `listen` but it does not really `listen()`
I made a little example for you here:

Exemple de Dinosaure

Bien Ă©videmment, son petit exemple est parfait.

On remplace tout comme il faut, et lĂ  encore un problĂšme : le client envoie un « hello » mais le serveur, au lieu d’envoyer le sien, envoie un paquet FIN et la connexion se termine lĂ -dessus.

C’est relativement problĂ©matique, mais comme Ă  notre habitude :

Le condor du plateau — 2022/11/30 22:42
Hi, I want to upgrade the TCP flow to a TLS flow with `TLS.server_of_flow`

https://docs.mirage.io/tls-mirage/Tls_mirage/Make/index.html#val-server_of_flow

but when running the server, while the client successfully send a client hello the server shutdowns the connection with FIN
dinosaure — 2022/12/01 16:13
Can you show where you use `server_of_flow`?
Le condor du plateau — 2022/12/01 16:15

https://github.com/Psi-Prod/Mehari/blob/wip-merge/mehari/server_impl.ml#L43

just here

Pas de rĂ©ponse jusqu’à aujourd’hui.

Alors que cela fait environ une semaine d’attente, Tim dĂ©cide d’ouvrir une issue chez mirage/mirage-tcp-ip (qui n’est pas le bon repo mdr) avec un titre Ă  peine anglais :

https://github.com/mirage/mirage-tcpip/issues/500

hannesm rĂ©pond alors que `server_of_flow` est sensĂ© fonctionner (comme sur tout les autres repo) mais ne comprends pas `match%lwt` (ou n’a pas compris pourquoi on l’a mis lĂ , au choix). Tim s’empresse de lui rĂ©pondre pour au final ne rien recevoir encore 2 semaines aprĂšs. Dur


Le 6 dĂ©cembre, la release candidate d’OCaml 5 sort !

Avec elle on peut utiliser la librairie Eio que Tim va utiliser pour écrire une nouvelle implémentation, puisque `mehari.mirage` est toujours en panne.

Mehari change donc de forme et devient :

Avant d’arriver Ă  la vraie solution, en cherchant un peu une des causes « habituelles » (mĂȘme s’il faut chercher pour se taper un tel truc) est que le serveur ne trouve pas d’algorithme qu’il supporte dans la liste fournie par le « client hello » et FIN directement. Seulement ça n’arrive uniquement (j’imagine) quand on utilise un client SSL ou TLSv1 donc bon.

Le 21 décembre, vers 4h et demi du matin, je comprends enfin comment activer le mode de débug avec `ocaml-tls` !

Grande nouvelle puisqu’à la premiùre connection :

2022-12-21 04:29:36 +01:00: WRN [tcpv4v6-socket] error The default generator is not yet initialized.
To initialize the RNG with a default generator, and set up entropy collection and periodic reseeding as a background task, do the following:
If you are using MirageOS, use the random device in config.ml: `let main = Mirage.foreign "Unikernel.Main" (random @-> job)`, and `let () = register "my_unikernel" [main $ default_random]`.
If you are using Lwt, execute `Mirage_crypto_rng_lwt.initialize ()` at startup.
If you are using Async, execute `Mirage_crypto_eng_async.initialize (module Mirage_crypto_rng.Fortuna)` at startup.
If you are using Eio, execute in one of the fibers `Mirage_crypto_rng_eio.run (module Fortuna) env` (`env` from `Eio_main.run`).
Otherwise, there is no periodic reseeding. For an initial seed from getrandom(), execute `Mirage_crypto_rng_unix.initialize ()`. You can use `Mirage_crypto_rng.accumulate` and `Mirage_crypto_rng.reseed` to reseed the RNG manually.

On a juste Ă  ajouter

Mirage_crypto_rng_lwt.initialize ()

chez `mehari_unix` et le tour est joué, tout fonctionne correctement (ou presque).

Solution qui marche mais trĂšs dĂ©cevante, sachant l’attente que ça a gĂ©nĂ©rĂ©.

FIN
 de
 OCaml

Retour au gemlog

Retour à l’accueil

Commentaires (0)

Pas encore de commentaires

Écrire un commentaire