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}