
Un petit moteur de micro-politique vérifiable, terminant et déterministe
Un petit moteur de micro-politique, vérifiable, terminant et déterministe.
J'ai construit Gate0 parce que j'en avais assez de déboguer des politiques basées sur des expressions régulières en production. Je voulais quelque chose de banal, borné et impossible à planter. Si vous voulez un moteur de politique flexible et généraliste, vous devriez utiliser OPA. Si vous voulez un moteur de politique dont l'évaluation atteint moins de 50 µs et zéro allocation pour les chemins critiques de sécurité, vous voulez Gate0.
Gate0 est conçu pour les environnements à haute assurance où l'évaluation des politiques doit être déterministe et limitée en ressources. Consultez la Politique de sécurité pour signaler les vulnérabilités et le Modèle de sécurité pour le modèle de menace complet et les garanties mécaniques.
Gate0 utilise une stratégie d'évaluation linéaire, Deny-Overrides. Chaque règle se compose d'une Cible (correspondance rapide) et d'une Condition optionnelle (logique profonde).
+----------+ +-------------+ +--------+
| Ephemera | ----> | GateBridge | ----> | Gate0 |
| (Legacy) | | (Normalize) | | (Core) |
+----------+ +-------------+ +--------+
| ^
| |
+----(Shadow Log)----+
L'exactitude et la sécurité de Gate0 sont vérifiées mécaniquement par des tests unitaires couvrant la logique centrale et les cas limites, des tests basés sur les propriétés via proptest avec des centaines de scénarios générés, et la vérification MIRI pour un fonctionnement sans panique et sans comportement indéfini. Les entrées dans le pire des cas sont testées pour garantir une terminaison bornée.
cargo test
cargo +nightly miri test --lib
L'évaluateur de Gate0 utilise des tampons de taille fixe alloués sur la pile pour garantir zéro allocation sur le tas pendant l'évaluation. L'implémentation par défaut utilise MaybeUninit pour éviter d'initialiser les emplacements inutilisés, ce qui donne un coût d'initialisation en O(utilisé) plutôt qu'en O(capacité).
Le code unsafe est confiné à un seul module (fixed_stack.rs) avec des invariants simples : les éléments 0..len sont initialisés, les éléments len..N ne le sont pas. Tous les chemins unsafe sont vérifiés avec MIRI.
Pour les utilisateurs qui préfèrent zéro code unsafe, Gate0 fournit SafeFixedStack derrière l'indicateur de fonctionnalité safe-stack. Cette variante utilise [T; N] avec T: Default + Copy et initialise tous les emplacements à l'avance. Le compromis est une initialisation en O(capacité) à chaque appel d'évaluation.
cargo build --features safe-stack
Les deux implémentations fournissent une sémantique identique et la même garantie de zéro allocation pendant l'évaluation. Le choix se situe entre la performance (O(utilisé)) et la sécurité absolue (O(capacité)). Pour les petites piles avec des types Default peu coûteux comme bool, la différence est négligeable.
Gate0 est conçu pour fonctionner comme un Point de Décision de Politique (PDP) au sein d'une application hôte plus vaste. Pour maintenir le déterminisme et des limites strictes, Gate0 ne gère pas les E/S, la mise en réseau, ni les cycles de vie des objets.
Le modèle d'intégration recommandé sépare les préoccupations en trois couches. L'application hôte (passerelle API, serveur SSH, etc.) gère l'état, l'identité et les effets de bord. Une couche d'adaptation normalise cet état complexe en primitives que Gate0 comprend (chaînes, booléens, entiers). Gate0 évalue le contexte aplati de manière pure et renvoie une Décision.
Host Application (User Request)
│
▼
[Adapter Layer] → Pre-computes context (time, IP ranges, MFA status)
│ Converts "complex" to "primitive"
▼
[Gate0 Engine] → Pure evaluation (0 allocations, bounded stack)
│
▼
Decision::Allow / Deny
Cette séparation explique pourquoi Gate0 n'inclut pas de matchers complexes comme les vérifications de plages IP ou les expressions régulières. La couche d'adaptation gère la logique spécifique au domaine et présente à Gate0 des attributs booléens ou de chaînes précalculés. Gate0 reste petit, vérifiable et déterministe.
use gate0::{Policy, Rule, Target, Request, ReasonCode};
let policy = Policy::builder()
.rule(Rule::allow(Target::any(), ReasonCode(1)))
.build()?;
let decision = policy.evaluate(&Request::new("alice", "read", "doc"))?;
assert!(decision.is_allow());
Le répertoire examples/ contient des scénarios illustratifs démontrant les modèles d'utilisation courants de Gate0 :
Exécutez-les avec :
cargo run --example saas_api
cargo run --example zero_trust_network
cargo run --example complex_overrides
Gate0 est intentionnellement contraint pour rester prévisible et performant.
Pas de matchers complexes : La logique comme le CIDR complet par masque de bits ou les expressions régulières avancées reste la responsabilité de la couche d'adaptation. Gate0 évalue des primitives prétraitées.
Pas de multithreading natif : L'implémentation FFI actuelle pour Python n'est pas thread-safe. Les utilisateurs à forte concurrence devraient utiliser le multiprocessing ou attendre la stabilisation de la Phase 4 de la FFI qui traitera les verrous globaux.
Pas de décisions chevauchantes : Au sein d'une seule classe d'effet (Allow/Deny), seule la première règle correspondante est renvoyée. La résolution des conflits est strictement dépendante de l'ordre.
Les projets suivants étendent le moteur Gate0 pour des cas d'utilisation spécialisés.
MIT