Skip to main content

kipuka/config/
admin.rs

1//! Admin API configuration.
2//!
3//! The `[admin]` section controls the administrative REST API used for
4//! operator management, OTP provisioning, CA health monitoring, and
5//! audit log queries.
6
7use serde::Deserialize;
8
9/// Admin authentication method.
10#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
11#[serde(rename_all = "lowercase")]
12#[derive(Default)]
13pub enum AdminAuthMethod {
14    /// Configured opaque Bearer credential (operator/auditor).
15    Bearer,
16    /// mTLS client certificate authentication.
17    #[default]
18    Mtls,
19    /// HTTP Basic authentication.
20    Basic,
21    /// GSSAPI/SPNEGO (Kerberos) authentication.
22    Gssapi,
23}
24
25/// `[admin]` section — administrative API configuration.
26///
27/// When this section is absent, admin endpoints return 404.
28///
29/// ```toml
30/// [admin]
31/// enabled = true
32/// listen_addr = "127.0.0.1:8444"
33/// auth_method = "mtls"
34/// allowed_operators = ["admin@example.com"]
35/// ```
36#[derive(Debug, Clone, Deserialize)]
37#[serde(deny_unknown_fields)]
38pub struct AdminConfig {
39    /// Enable the admin API.
40    #[serde(default)]
41    pub enabled: bool,
42
43    /// Listen address for the admin API.
44    ///
45    /// When absent, admin endpoints are served on the main EST listener.
46    /// Setting a separate address allows binding the admin API to a
47    /// management network interface.
48    pub listen_addr: Option<String>,
49
50    /// Authentication method for admin API access.
51    #[serde(default)]
52    pub auth_method: AdminAuthMethod,
53
54    /// List of allowed operator identities.
55    ///
56    /// The format depends on `auth_method`:
57    /// - `mtls` — Subject DN or SAN email of the client certificate.
58    /// - `basic` — Username (passwords stored in the database).
59    /// - `gssapi` — Kerberos principal name.
60    #[serde(default)]
61    pub allowed_operators: Vec<String>,
62
63    /// List of allowed auditor identities (read-only role).
64    ///
65    /// NIAP CA PP FMT_SMR.1: an auditor may query audit and status endpoints
66    /// but is refused all mutating operations.  Format matches
67    /// `allowed_operators` (per `auth_method`).  An identity listed in both
68    /// `allowed_operators` and here is treated as an operator (operator
69    /// precedence).
70    #[serde(default)]
71    pub allowed_auditors: Vec<String>,
72
73    /// Path to the CA certificate bundle (PEM) for admin mTLS.
74    ///
75    /// RHELBU-3536 R18: separate truststore from the EST client truststore.
76    /// Required when `auth_method = "mtls"`.
77    pub admin_ca_file: Option<String>,
78
79    /// Session TTL in seconds.
80    ///
81    /// Admin sessions expire after this duration of inactivity.
82    /// Default: 3600 (1 hour).
83    #[serde(default = "default_session_ttl_secs")]
84    pub session_ttl_secs: u64,
85
86    /// Maximum concurrent admin sessions.
87    /// Default: 16.
88    #[serde(default = "default_max_sessions")]
89    pub max_sessions: usize,
90
91    /// Bearer token for admin API authentication.
92    ///
93    /// When set, `Authorization: Bearer <token>` requests are validated
94    /// against this value using constant-time comparison.  Required when
95    /// operators authenticate via Bearer tokens (e.g., CI pipelines,
96    /// monitoring systems).
97    pub bearer_token: Option<super::SecretRef>,
98
99    /// Bearer token for read-only auditor API access.
100    ///
101    /// When set, `Authorization: Bearer <token>` requests matching this value
102    /// (constant-time) are granted the [`crate::routes::admin::AdminRole::Auditor`]
103    /// role.  A token matching `bearer_token` takes precedence (operator).
104    pub auditor_bearer_token: Option<super::SecretRef>,
105
106    /// GSSAPI configuration (required when `auth_method = "gssapi"`).
107    pub gssapi: Option<AdminGssapiConfig>,
108}
109
110/// GSSAPI/SPNEGO configuration for admin authentication.
111#[derive(Debug, Clone, Deserialize)]
112#[serde(deny_unknown_fields)]
113pub struct AdminGssapiConfig {
114    /// Path to the Kerberos keytab file.
115    pub keytab_file: Option<String>,
116
117    /// Service principal name.
118    /// Default: `"HTTP"` (the hostname is appended automatically).
119    #[serde(default = "default_service_name")]
120    pub service_name: String,
121
122    /// Use gssproxy for credential management instead of a keytab.
123    #[serde(default)]
124    pub gssproxy: bool,
125
126    /// Require cryptographic verification of Kerberos tickets.
127    ///
128    /// When `true` (the default), GSSAPI authentication is rejected unless
129    /// libgssapi integration is compiled in and a valid keytab/gssproxy is
130    /// available to decrypt and verify the ticket.  When `false`, structural
131    /// parsing of the SPNEGO token is allowed — the service name (sname) is
132    /// extracted from the cleartext portion of the ticket, but no
133    /// cryptographic verification is performed and the client identity
134    /// cannot be authenticated.
135    ///
136    /// **Security warning:** Setting this to `false` should only be used
137    /// for development, logging, or environments where TLS client
138    /// certificates provide the primary authentication.
139    #[serde(default = "default_require_crypto_verification")]
140    pub require_crypto_verification: bool,
141}
142
143fn default_require_crypto_verification() -> bool {
144    true
145}
146
147fn default_session_ttl_secs() -> u64 {
148    3600
149}
150
151fn default_max_sessions() -> usize {
152    16
153}
154
155fn default_service_name() -> String {
156    "HTTP".to_string()
157}
158
159impl Default for AdminConfig {
160    fn default() -> Self {
161        Self {
162            enabled: false,
163            listen_addr: None,
164            auth_method: AdminAuthMethod::default(),
165            allowed_operators: Vec::new(),
166            allowed_auditors: Vec::new(),
167            admin_ca_file: None,
168            session_ttl_secs: default_session_ttl_secs(),
169            max_sessions: default_max_sessions(),
170            bearer_token: None,
171            auditor_bearer_token: None,
172            gssapi: None,
173        }
174    }
175}
176
177impl AdminConfig {
178    /// Validate admin configuration constraints.
179    pub fn validate(&self) -> std::result::Result<(), String> {
180        if !self.enabled {
181            return Ok(());
182        }
183
184        if self.auth_method == AdminAuthMethod::Mtls && self.admin_ca_file.is_none() {
185            return Err("[admin].admin_ca_file is required when auth_method = \"mtls\"".into());
186        }
187
188        if self.auth_method == AdminAuthMethod::Gssapi {
189            match &self.gssapi {
190                None => {
191                    return Err(
192                        "[admin].gssapi section is required when auth_method = \"gssapi\"".into(),
193                    );
194                }
195                Some(g) => {
196                    if !g.gssproxy && g.keytab_file.is_none() {
197                        return Err(
198                            "[admin.gssapi]: set `keytab_file` or enable `gssproxy = true`".into(),
199                        );
200                    }
201                    if g.gssproxy && g.keytab_file.is_some() {
202                        return Err(
203                            "[admin.gssapi]: `keytab_file` and `gssproxy = true` are mutually exclusive".into(),
204                        );
205                    }
206                }
207            }
208        }
209
210        if self.session_ttl_secs == 0 {
211            return Err("[admin].session_ttl_secs must be at least 1".into());
212        }
213
214        Ok(())
215    }
216}