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}