postrust_proxy/saas/options.rs
1//! Settings for the SaaS control plane.
2//!
3//! Gathered into one place so the defaults are visible together and an
4//! embedder changes them by naming them, rather than by finding which
5//! constructor took which positional argument.
6
7use crate::saas::validation::ReservedDomains;
8use std::sync::Arc;
9use std::time::Duration;
10
11/// The audience a proxy JWT must name when none is configured.
12///
13/// Required, rather than optional, because the main Postrust server may be
14/// configured with the same signing secret: without an audience, a token
15/// minted for it would be accepted here too.
16pub const DEFAULT_JWT_AUDIENCE: &str = "postrust-proxy";
17
18/// Everything [`SaasState`](crate::saas::handlers::SaasState) is configured
19/// with.
20///
21/// `#[non_exhaustive]`, so build it from [`Default`] and set fields:
22///
23/// ```
24/// # use postrust_proxy::saas::SaasOptions;
25/// let mut options = SaasOptions::default();
26/// options.jwt_secret = Some("secret".into());
27/// ```
28#[derive(Clone, Debug)]
29#[non_exhaustive]
30pub struct SaasOptions {
31 /// HMAC secret for proxy JWTs. `None` refuses every JWT.
32 pub jwt_secret: Option<String>,
33 /// The `aud` a JWT must carry. Defaults to [`DEFAULT_JWT_AUDIENCE`].
34 pub jwt_audience: String,
35 /// The `iss` a JWT must carry, if set. Unchecked by default.
36 pub jwt_issuer: Option<String>,
37 /// Rules for domains: what may be registered and how verification runs.
38 pub domains: DomainPolicy,
39}
40
41impl Default for SaasOptions {
42 fn default() -> Self {
43 Self {
44 jwt_secret: None,
45 jwt_audience: DEFAULT_JWT_AUDIENCE.to_string(),
46 jwt_issuer: None,
47 domains: DomainPolicy::default(),
48 }
49 }
50}
51
52/// Rules for registering, verifying and certifying domains.
53#[derive(Clone, Debug)]
54#[non_exhaustive]
55pub struct DomainPolicy {
56 /// Names no tenant may register. Put the platform's own hostnames here.
57 pub reserved: ReservedDomains,
58 /// How long an unverified registration holds its name.
59 ///
60 /// After this a pending (or failed) row stops blocking the name: the next
61 /// registration of it by anyone removes the stale row, and the stale row
62 /// can no longer be verified. Otherwise a tenant could register a name it
63 /// does not control and hold it against its real owner indefinitely.
64 /// The verification challenge expires at the same moment. Seven days.
65 pub pending_ttl: Duration,
66 /// The shortest gap allowed between two verification attempts for one
67 /// domain. Each attempt makes outbound DNS or HTTP requests. Ten seconds.
68 pub verification_min_interval: Duration,
69 /// How many verification attempts one registration gets. After that, the
70 /// tenant has to delete and re-add the domain. Fifty.
71 pub verification_max_attempts: i32,
72 /// What an uploaded certificate chain must chain to.
73 pub upload_trust: UploadTrust,
74}
75
76impl Default for DomainPolicy {
77 fn default() -> Self {
78 Self {
79 reserved: ReservedDomains::default(),
80 pending_ttl: Duration::from_secs(7 * 24 * 60 * 60),
81 verification_min_interval: Duration::from_secs(10),
82 verification_max_attempts: 50,
83 upload_trust: UploadTrust::PublicRoots,
84 }
85 }
86}
87
88/// What an uploaded certificate chain has to be trusted by.
89#[derive(Clone, Debug)]
90pub enum UploadTrust {
91 /// The Mozilla root programme, as bundled by `webpki-roots`. The default:
92 /// the certificate is served to browsers, which will trust nothing else.
93 PublicRoots,
94 /// These roots, for a platform whose tenants use an internal CA.
95 Roots(Arc<rustls::RootCertStore>),
96 /// No chain check. The key must still match, the leaf must still be in
97 /// date and cover the domain, and the domain must still be verified.
98 Unchecked,
99}