đŸ›Ąïž windows-wfp : Un wrapper Rust safe pour le firewall kernel Windows

Depuis quelques mois, je travaille sur un projet personnel en Rust liĂ© Ă  la sĂ©curitĂ© rĂ©seau sous Windows. Un projet ambitieux dont je ne dĂ©voilerai pas encore tous les dĂ©tails — disons simplement qu’il nĂ©cessite d’interagir directement avec le firewall kernel-level de Windows. Et quand on plonge dans cet univers, on tombe inĂ©vitablement sur la Windows Filtering Platform (WFP).

Le problĂšme ? Manipuler WFP depuis Rust, c’est du sport. J’ai donc construit un wrapper pour mes propres besoins, et j’ai dĂ©cidĂ© de le partager en open-source sur crates.io : windows-wfp.

🎯 C’est quoi la Windows Filtering Platform ?

La WFP (Windows Filtering Platform) est le framework de filtrage rĂ©seau au niveau kernel intĂ©grĂ© Ă  Windows depuis Vista/Server 2008. C’est le moteur qui fait tourner le Pare-feu Windows lui-mĂȘme, ainsi que la plupart des solutions de sĂ©curitĂ© rĂ©seau : antivirus, EDR, VPN…

ConcrĂštement, WFP vous permet de :

  • Bloquer ou autoriser du trafic rĂ©seau selon des rĂšgles fines
  • Filtrer par application : autoriser Firefox mais bloquer une autre app
  • Filtrer par protocole, port, IP : avec support CIDR pour les plages d’adresses
  • Monitorer en temps rĂ©el les Ă©vĂ©nements rĂ©seau du systĂšme
  • Intervenir au niveau kernel : avant mĂȘme que le trafic n’atteigne l’application

C’est une API extrĂȘmement puissante, utilisĂ©e par les outils de sĂ©curitĂ© professionnels. Mais elle a un gros dĂ©faut : elle est conçue pour le C/C++, et l’utiliser depuis Rust demande de jongler avec des abstractions bas-niveau peu ergonomiques.

⚠ Le problĂšme : manipuler WFP sans wrapper

Quand j’ai commencĂ© Ă  intĂ©grer WFP dans mon projet Rust, je me suis vite retrouvĂ© face Ă  trois obstacles majeurs :

1. Du FFI brut partout

Le FFI (Foreign Function Interface), c’est le mĂ©canisme qui permet Ă  Rust d’appeler les API C de Windows. Le problĂšme, c’est qu’en passant par du FFI brut, on perd toutes les garanties de sĂ©curitĂ© mĂ©moire qui font la force de Rust. On manipule des pointeurs bruts, des structures C, et on se retrouve Ă  Ă©crire du code unsafe (c’est-Ă -dire du code oĂč on dit au compilateur : “fais-moi confiance, je sais ce que je fais” — spoiler : on ne sait pas toujours ce qu’on fait 😅).

2. Des handles Windows non sécurisés

Un handle sous Windows, c’est un identifiant opaque que le systĂšme vous donne quand vous ouvrez une ressource (une session WFP, un fichier, un processus…). Le souci : il faut penser Ă  fermer chaque handle manuellement. Si on oublie → fuite de ressources. Si on l’utilise aprĂšs l’avoir fermĂ© → comportement indĂ©fini. Le genre de bugs silencieux qui ne se manifestent qu’en production, 3 mois plus tard, Ă  2h du matin.

3. Le piĂšge des chemins NT kernel

C’est probablement le piĂšge le plus vicieux. WFP opĂšre au niveau kernel et utilise des chemins NT kernel (\Device\HarddiskVolume3\Windows\System32\notepad.exe), pas les chemins DOS habituels (C:\Windows\System32\notepad.exe).

Le rĂ©sultat ? Vous ajoutez un filtre pour bloquer notepad.exe en utilisant son chemin classique. L’API WFP ne renvoie aucune erreur — tout semble fonctionner. Mais le filtre ne matche jamais le moindre paquet. Vos rĂšgles existent, mais elles sont muettes. 🙃

J’ai perdu un temps considĂ©rable Ă  debugger ce comportement avant de comprendre le problĂšme. C’est d’ailleurs ce genre de frustration qui m’a motivĂ© Ă  encapsuler toute cette complexitĂ© dans un wrapper propre.

🚀 windows-wfp : l’approche Rust idiomatique

Le crate windows-wfp encapsule toute cette complexité derriÚre une API safe, ergonomique et idiomatique en Rust.

Architecture du crate

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
windows-wfp/
├── src/
│   ├── engine.rs       # Session WFP (RAII, auto-cleanup)
│   ├── provider.rs     # Enregistrement de providers
│   ├── sublayer.rs     # Gestion des sublayers (prioritĂ©s)
│   ├── filter.rs       # Builder pattern pour les filtres
│   ├── condition.rs    # Conditions de filtrage
│   ├── path.rs         # Conversion DOS → NT kernel path
│   ├── monitor.rs      # Monitoring rĂ©seau temps rĂ©el
│   └── lib.rs          # Point d'entrĂ©e public

Les principes de conception

✅ RAII pour les handles : Chaque session WFP, chaque provider, chaque filtre est wrappĂ© dans un type Rust qui ferme automatiquement le handle quand l’objet sort du scope. Impossible d’oublier de nettoyer.

✅ Builder pattern pour les filtres : Au lieu de remplir des structures C avec des pointeurs, on construit les rĂšgles avec une API fluide et typĂ©e.

✅ Conversion automatique des chemins : Le crate convertit de façon transparente les chemins DOS en chemins NT kernel. Plus de filtres silencieusement cassĂ©s.

✅ API 100% safe : Le code unsafe (nĂ©cessaire pour parler aux API Windows) est isolĂ© et encapsulĂ© Ă  l’intĂ©rieur du crate. L’utilisateur n’a jamais Ă  Ă©crire d’unsafe.

✅ Monitoring rĂ©seau : Souscription aux Ă©vĂ©nements rĂ©seau en temps rĂ©el via un callback Rust.

Exemple : bloquer une application

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use windows_wfp::{WfpEngine, WfpProvider, WfpSublayer, WfpFilter};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Ouvrir une session WFP (handle fermé automatiquement via RAII)
    let engine = WfpEngine::open()?;

    // Enregistrer un provider (identifie qui crée les rÚgles)
    let provider = WfpProvider::new("Mon App", "RĂšgles de filtrage custom");
    engine.register_provider(&provider)?;

    // Créer un sublayer (groupe de rÚgles avec priorité)
    let sublayer = WfpSublayer::new("Mes Filtres", 1000);
    engine.register_sublayer(&sublayer)?;

    // Bloquer notepad.exe sur TCP
    // Le chemin DOS est automatiquement converti en chemin NT kernel
    let filter = WfpFilter::block()
        .name("Block Notepad")
        .application("C:\\Windows\\System32\\notepad.exe")
        .protocol(Protocol::Tcp)
        .build()?;

    engine.add_filter(&filter)?;

    println!("Filtre actif ! notepad.exe est bloqué sur TCP.");
    Ok(())
}

Le ? Ă  la fin de chaque appel, c’est l’opĂ©rateur de propagation d’erreur en Rust : si l’appel Ă©choue, l’erreur est renvoyĂ©e proprement Ă  l’appelant. Pas de try/catch, pas d’exceptions — tout passe par le systĂšme de types.

Exemple : monitorer le réseau en temps réel

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use windows_wfp::WfpMonitor;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let monitor = WfpMonitor::start(|event| {
        println!("ÉvĂ©nement rĂ©seau : {:?}", event);
    })?;

    // Le monitoring tourne en arriĂšre-plan...
    std::thread::sleep(std::time::Duration::from_secs(60));

    monitor.stop()?;
    Ok(())
}

🔧 CapacitĂ©s de filtrage

Le crate supporte un large éventail de conditions de filtrage, combinables entre elles :

ConditionDescriptionExemple
ApplicationFiltrer par chemin d’exĂ©cutablenotepad.exe, chrome.exe
ProtocoleTCP, UDP, ICMP, ICMPv6Protocol::Tcp
Port distantPort du serveur ciblePort 443 (HTTPS)
Port localPort de l’application localePort 8080
Adresse IPIPv4/IPv6 avec CIDR192.168.1.0/24
Service WindowsFiltrer par nom de servicesvchost, dns
AppContainerSID pour apps UWP/Microsoft StoreEdge, apps packagées

Ces conditions peuvent ĂȘtre combinĂ©es pour crĂ©er des rĂšgles trĂšs prĂ©cises. Par exemple : bloquer chrome.exe uniquement sur le port 80 (HTTP) tout en autorisant le port 443 (HTTPS).

💡 Ce que j’ai appris en construisant ce crate

Le FFI Windows en Rust, c’est un monde

Le crate windows de Microsoft a Ă©normĂ©ment simplifiĂ© l’accĂšs aux API Windows depuis Rust. Mais WFP reste une API kernel complexe avec des structures imbriquĂ©es, des unions C, et des comportements parfois sous-documentĂ©s. Chaque filtre WFP implique de construire des structures FWPM_FILTER0, FWPM_FILTER_CONDITION0, etc., avec des allocations mĂ©moire manuelles et des durĂ©es de vie Ă  gĂ©rer soigneusement.

La conversion de chemins est critique

La fonction FilterAPI.dll!FwpmGetAppIdFromFileName fait la conversion DOS → NT kernel, mais elle a ses subtilitĂ©s. Les chemins rĂ©seau (UNC), les liens symboliques, et les variables d’environnement sont autant de cas Ă  gĂ©rer. C’est le genre de dĂ©tail invisible qui fait la diffĂ©rence entre un filtre qui fonctionne et un filtre fantĂŽme.

Le monitoring réseau révÚle beaucoup de choses

En dĂ©veloppant la fonctionnalitĂ© de monitoring, j’ai Ă©tĂ© surpris de voir le volume et la diversitĂ© du trafic rĂ©seau sur un poste Windows classique. Des dizaines de services communiquent en permanence — c’est un rappel que la surface d’attaque rĂ©seau est bien plus large qu’on ne l’imagine.

🔼 Et la suite ?

windows-wfp est la premiĂšre brique d’un projet plus large sur lequel je travaille actuellement. Un projet liĂ© Ă  la sĂ©curitĂ© rĂ©seau sous Windows, toujours en Rust. Je n’en dis pas plus pour le moment… mais restez connectĂ©s, ça promet d’ĂȘtre intĂ©ressant. 😏

En attendant, le crate est disponible et fonctionnel. Voici la feuille de route pour windows-wfp lui-mĂȘme :

Prochaines évolutions

  • 🔄 Support des callouts : Interception et modification de paquets en temps rĂ©el
  • 📊 Statistiques de filtrage : Compteurs de paquets bloquĂ©s/autorisĂ©s par filtre
  • đŸ§Ș Tests d’intĂ©gration : Suite de tests automatisĂ©s avec crĂ©ation/suppression de filtres rĂ©els
  • 📖 Documentation enrichie : Guides d’utilisation avancĂ©e et cas d’usage concrets

🔗 Ressources et liens

Le crate

Références techniques

Post LinkedIn

🎉 Conclusion

Construire windows-wfp m’a permis de plonger dans les entrailles du rĂ©seau Windows au niveau kernel — un domaine fascinant mais exigeant. Le wrapper transforme une API C complexe et piĂ©geuse en quelque chose de sĂ»r, ergonomique et idiomatique en Rust.

Si vous travaillez sur de la sĂ©curitĂ© rĂ©seau sous Windows, ou si vous ĂȘtes simplement curieux de voir comment on wrappe proprement une API kernel en Rust, n’hĂ©sitez pas Ă  explorer le code, ouvrir des issues, ou proposer des contributions.

Des questions ? Des retours ? Je suis preneur de tous les feedbacks, que ce soit sur l’API, la documentation, ou les cas d’usage que vous aimeriez voir supportĂ©s.

📧 Email : lostyzen@gmail.com 🔐 PGP Key : Download from keys.openpgp.org 📌 Fingerprint : 0205 9854 864D EE62 C2BB 455F D825 3521 EDD1 630B