spiffe_rustls/
policy.rs

1//! Trust domain policy for federation control.
2//!
3//! This module provides [`TrustDomainPolicy`], which allows you to restrict which
4//! trust domains from the bundle set are actually used during certificate verification.
5//!
6//! # Examples
7//!
8//! ```rust
9//! use spiffe_rustls::{AllowList, AnyInBundleSet, LocalOnly, TrustDomainPolicy};
10//! use std::collections::BTreeSet;
11//!
12//! // Default: use all bundles from the Workload API (using re-exported variant)
13//! let policy = AnyInBundleSet;
14//!
15//! // Restrict to specific trust domains (using re-exported variant)
16//! let mut allowed = BTreeSet::new();
17//! allowed.insert("broker.example".try_into().unwrap());
18//! allowed.insert("stockmarket.example".try_into().unwrap());
19//! let policy = AllowList(allowed);
20//!
21//! // Only trust a single trust domain (using re-exported variant)
22//! let policy = LocalOnly("example.org".try_into().unwrap());
23//!
24//! // You can also use the full path if preferred
25//! let policy = TrustDomainPolicy::AnyInBundleSet;
26//! ```
27
28use spiffe::TrustDomain;
29use std::collections::BTreeSet;
30
31/// Policy for selecting which trust domains to trust during certificate verification.
32///
33/// When SPIFFE federation is configured, the Workload API delivers trust bundles
34/// for multiple trust domains. This policy allows you to restrict which of those
35/// bundles are actually used during certificate verification.
36///
37/// This is a **defense-in-depth** mechanism. The primary trust model comes from
38/// the bundle set delivered by the SPIFFE Workload API. This policy provides an
39/// additional layer of control over which trust domains are accepted.
40///
41/// **Default**: `AnyInBundleSet` - use all bundles provided by the Workload API.
42///
43/// # Examples
44///
45/// ```rust
46/// use spiffe_rustls::{AllowList, AnyInBundleSet, TrustDomainPolicy};
47/// use std::collections::BTreeSet;
48///
49/// // Default: trust any domain in the bundle set
50/// let policy = AnyInBundleSet;
51///
52/// // Restrict to specific trust domains (using re-exported variant)
53/// let mut allowed = BTreeSet::new();
54/// allowed.insert("broker.example".try_into().unwrap());
55/// let policy = AllowList(allowed);
56///
57/// // You can also use the full path if preferred
58/// let policy = TrustDomainPolicy::default();
59/// ```
60#[derive(Debug, Clone, Default)]
61pub enum TrustDomainPolicy {
62    /// Default: use all trust domain bundles provided by the Workload API.
63    ///
64    /// When SPIFFE federation is configured, the Workload API delivers bundles
65    /// for multiple trust domains. This policy accepts all of them, allowing
66    /// the verifier to automatically select the correct bundle based on the
67    /// peer's SPIFFE ID. No additional configuration is needed for federation
68    /// to work.
69    #[default]
70    AnyInBundleSet,
71
72    /// Restrict to these trust domains only.
73    ///
74    /// Only bundles for these trust domains will be used, even if other bundles
75    /// are present in the bundle set.
76    AllowList(BTreeSet<TrustDomain>),
77
78    /// Only trust the specified trust domain.
79    ///
80    /// Only bundles for this trust domain will be used, even if the Workload API
81    /// provides bundles for other trust domains. This restricts certificate
82    /// verification to a single trust domain.
83    LocalOnly(TrustDomain),
84}
85
86impl TrustDomainPolicy {
87    /// Checks if a trust domain is allowed by this policy.
88    pub fn allows(&self, trust_domain: &TrustDomain) -> bool {
89        match self {
90            Self::AnyInBundleSet => true,
91            Self::AllowList(allowed) => allowed.contains(trust_domain),
92            Self::LocalOnly(local) => trust_domain == local,
93        }
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn test_any_in_bundle_set() {
103        let policy = TrustDomainPolicy::AnyInBundleSet;
104        let td1 = TrustDomain::new("example.org").unwrap();
105        let td2 = TrustDomain::new("other.org").unwrap();
106
107        assert!(policy.allows(&td1));
108        assert!(policy.allows(&td2));
109    }
110
111    #[test]
112    fn test_allow_list() {
113        let td1 = TrustDomain::new("example.org").unwrap();
114        let td2 = TrustDomain::new("other.org").unwrap();
115        let td3 = TrustDomain::new("third.org").unwrap();
116
117        let mut allowed = BTreeSet::new();
118        allowed.insert(td1.clone());
119        allowed.insert(td2.clone());
120
121        let policy = TrustDomainPolicy::AllowList(allowed);
122        assert!(policy.allows(&td1));
123        assert!(policy.allows(&td2));
124        assert!(!policy.allows(&td3));
125    }
126
127    #[test]
128    fn test_local_only() {
129        let local = TrustDomain::new("example.org").unwrap();
130        let other = TrustDomain::new("other.org").unwrap();
131
132        let policy = TrustDomainPolicy::LocalOnly(local.clone());
133        assert!(policy.allows(&local));
134        assert!(!policy.allows(&other));
135    }
136}