Skip to main content

spiffe_rustls/
policy.rs

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