Skip to main content

kipuka/config/
est.rs

1//! EST protocol configuration.
2//!
3//! The `[est]` section controls which EST operations are enabled globally,
4//! and `[[est.label]]` entries define per-label enrollment profiles with
5//! CA routing and authentication policies.
6//!
7//! # EST labels (RFC 7030 §3.2.2)
8//!
9//! EST labels provide a namespace mechanism for multiple enrollment profiles
10//! under the same server.  Each label maps to a URL path segment:
11//!
12//! ```text
13//! https://est.example.com/.well-known/est/{label}/simpleenroll
14//! ```
15//!
16//! When no label is specified in the URL, the default label configuration
17//! applies.
18
19use serde::Deserialize;
20
21/// Authentication method for EST enrollment requests.
22///
23/// RFC 7030 §3.2.3 defines several client authentication mechanisms.
24/// Each EST label can require a specific method or accept multiple.
25#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
26#[serde(rename_all = "lowercase")]
27pub enum EstAuthMethod {
28    /// mTLS client certificate authentication (RFC 7030 §3.3.2).
29    Mtls,
30    /// HTTP Basic authentication with OTP (RHELBU-3536 R7).
31    Otp,
32    /// HTTP Basic authentication with static credentials.
33    Basic,
34    /// Certificate-based re-enrollment (existing certificate proves identity).
35    Certificate,
36}
37
38/// `[est]` section — global EST protocol settings.
39#[derive(Debug, Clone, Deserialize)]
40#[serde(deny_unknown_fields)]
41pub struct EstConfig {
42    /// Enable the `/simpleenroll` endpoint (RFC 7030 §4.2).
43    #[serde(default = "bool_true")]
44    pub simpleenroll: bool,
45
46    /// Enable the `/simplereenroll` endpoint (RFC 7030 §4.2.2).
47    #[serde(default = "bool_true")]
48    pub simplereenroll: bool,
49
50    /// Enable the `/fullcmc` endpoint (RFC 7030 §4.3).
51    ///
52    /// Full CMC is rarely needed; disabled by default.
53    #[serde(default)]
54    pub fullcmc: bool,
55
56    /// Enable the `/serverkeygen` endpoint (RFC 7030 §4.4).
57    ///
58    /// Server-side key generation requires HSM integration.
59    /// Disabled by default.
60    #[serde(default)]
61    pub serverkeygen: bool,
62
63    /// Enable the `/csrattrs` endpoint (RFC 7030 §4.5).
64    #[serde(default = "bool_true")]
65    pub csrattrs: bool,
66
67    /// Default enrollment profile applied when no label is specified.
68    ///
69    /// When absent, enrollment requests without a label use the default
70    /// CA and authentication policy.
71    #[serde(default)]
72    pub default_profile: Option<String>,
73
74    /// CSR attribute hints returned by `/csrattrs`.
75    ///
76    /// Each entry is an OID string (e.g., `"1.2.840.113549.1.9.14"` for
77    /// the Certificate Extensions Request attribute).
78    #[serde(default)]
79    pub csr_attributes: Vec<String>,
80
81    /// Per-label enrollment configurations.
82    #[serde(default, rename = "label")]
83    pub labels: Vec<EstLabelConfig>,
84
85    /// Disconnected mode: accept enrollment requests without upstream
86    /// CA connectivity (RHELBU-3536 R7-Disconnected).
87    ///
88    /// When `true`, the server queues CSRs for deferred signing and
89    /// returns `202 Accepted` with a `Retry-After` header instead of
90    /// the signed certificate.
91    #[serde(default)]
92    pub disconnected: bool,
93
94    /// Retry-After value (seconds) returned in disconnected mode.
95    /// Default: 300 (5 minutes).
96    #[serde(default = "default_retry_after_secs")]
97    pub disconnected_retry_after_secs: u64,
98}
99
100/// `[[est.label]]` — per-label enrollment profile.
101///
102/// Each label provides an independent enrollment namespace with its own
103/// CA routing, authentication requirements, and CSR attribute set.
104///
105/// ```toml
106/// [[est.label]]
107/// name = "devices"
108/// ca_id = "device-ca"
109/// auth_methods = ["mtls", "otp"]
110/// require_cn_match = true
111/// ```
112#[derive(Debug, Clone, Deserialize)]
113#[serde(deny_unknown_fields)]
114pub struct EstLabelConfig {
115    /// Label name used in the URL path.
116    ///
117    /// Must be a non-empty string matching `^[a-z0-9][a-z0-9_-]*$`.
118    pub name: String,
119
120    /// CA identifier to use for enrollments under this label.
121    ///
122    /// Must reference a `[[ca]]` entry by its `id` field.
123    /// When absent, the default CA is used.
124    pub ca_id: Option<String>,
125
126    /// Allowed authentication methods for this label.
127    ///
128    /// When empty, all globally-enabled auth methods are accepted.
129    #[serde(default)]
130    pub auth_methods: Vec<EstAuthMethod>,
131
132    /// Per-label CSR attribute hints (overrides global `csr_attributes`
133    /// for this label).
134    #[serde(default)]
135    pub csr_attributes: Vec<String>,
136
137    /// Require that the CSR Common Name matches the authenticated identity.
138    ///
139    /// When `true`, the server rejects CSRs where the CN does not match
140    /// the client's authenticated principal name.
141    #[serde(default)]
142    pub require_cn_match: bool,
143
144    /// Maximum validity period (days) for certificates issued under this label.
145    ///
146    /// Overrides the CA's default `validity_days` for this label.
147    pub max_validity_days: Option<u32>,
148
149    /// Enable disconnected mode for this specific label.
150    /// Overrides the global `[est].disconnected` setting.
151    pub disconnected: Option<bool>,
152}
153
154fn bool_true() -> bool {
155    true
156}
157
158fn default_retry_after_secs() -> u64 {
159    300
160}
161
162impl Default for EstConfig {
163    fn default() -> Self {
164        Self {
165            simpleenroll: true,
166            simplereenroll: true,
167            fullcmc: false,
168            serverkeygen: false,
169            csrattrs: true,
170            default_profile: None,
171            csr_attributes: Vec::new(),
172            labels: Vec::new(),
173            disconnected: false,
174            disconnected_retry_after_secs: default_retry_after_secs(),
175        }
176    }
177}