signstar_configure/host/
yubihsm2.rs1use std::str::FromStr;
4
5use log::{debug, error, info, warn};
6use signstar_common::{backend::BackendType, traits::BackendCheck};
7use signstar_config::{
8 admin_credentials::AdminCredentials,
9 config::{UserBackendConnection, UserBackendConnectionFilter},
10 yubihsm2::{
11 YubiHsm2Backend,
12 YubiHsm2Config,
13 admin_credentials::YubiHsm2AdminCredentials,
14 signstar_yubihsm2_export::{Connection, Credentials},
15 yubihsm2_export::{Connector, Id},
16 },
17};
18
19use crate::{ConfigurationResult, Error, HostConfiguration};
20
21impl<'config> HostConfiguration<'config> {
22 fn load_yubihsm2_admin_credentials(&self) -> Result<YubiHsm2AdminCredentials, Error> {
28 Ok(YubiHsm2AdminCredentials::load(
29 *self.config().system().admin_secret_handling(),
30 )?)
31 }
32
33 fn create_yubihsm2_non_admin_credentials(&self) -> Result<Vec<Credentials>, Error> {
40 info!("Create new non-administrative user credentials for YubiHSM2.");
41 let user_backend_connections = self.config().user_backend_connections(&[
42 UserBackendConnectionFilter::NonAdmin,
43 UserBackendConnectionFilter::Backend(BackendType::YubiHsm2),
44 ]);
45
46 let credentials_list = {
47 let mut creds_list = Vec::new();
48 for user_backend_connection in user_backend_connections {
49 if let UserBackendConnection::YubiHsm2 { .. } = &user_backend_connection
50 && let Some(creds_per_user) =
51 user_backend_connection.create_non_admin_backend_user_secrets()?
52 {
53 for credentials in creds_per_user {
54 creds_list.push(Credentials::new(
55 Id::from_str(&credentials.user())?,
58 credentials.passphrase().clone(),
59 ));
60 }
61 }
62 }
63
64 creds_list
65 };
66 debug!(
67 "Created non-administrative credentials for the following IDs: {}",
68 credentials_list
69 .iter()
70 .map(|creds| creds.id().to_string())
71 .collect::<Vec<_>>()
72 .join(", ")
73 );
74
75 Ok(credentials_list)
76 }
77
78 fn available_yubihsm2_connections(config: &YubiHsm2Config) -> Vec<Connector> {
80 info!("Query all YubiHSM2 connections for availability.");
81 config
82 .connections()
83 .iter()
84 .filter_map(|connection| {
85 if connection.is_available() {
86 debug!("Detected available YubiHSM2 connection {connection:?}");
87 Some(Connector::from(connection))
88 } else {
89 warn!("Skipping unavailable YubiHSM2 connection {connection:?}");
90 None
91 }
92 })
93 .collect::<Vec<_>>()
94 }
95
96 fn provisioned_yubihsm2_connections(config: &YubiHsm2Config) -> Vec<&Connection> {
98 info!("Detect all provisioned YubiHSM2 connections.");
99 config
100 .connections()
101 .iter()
102 .filter(|connection| {
103 if connection.is_provisioned() {
104 debug!("Detected provisioned YubiHSM2 connection {connection:?}");
105 true
106 } else {
107 debug!("Skipping unprovisioned YubiHSM2 connection {connection:?}");
108 false
109 }
110 })
111 .collect::<Vec<_>>()
112 }
113
114 fn sync_yubihsm2_backends(
126 &self,
127 available_connections: Vec<Connector>,
128 admin_credentials: &YubiHsm2AdminCredentials,
129 user_credentials: &[Credentials],
130 ) -> Result<(), Error> {
131 let backends = {
132 let mut backends = Vec::new();
133 for nethsm in available_connections.into_iter() {
134 if let Some(backend) =
135 YubiHsm2Backend::new(nethsm, admin_credentials, self.config())?
136 {
137 backends.push(backend);
138 }
139 }
140 backends
141 };
142
143 for backend in backends {
144 info!(
145 "Sync the state of the Signstar configuration with the YubiHSM2 backend {backend:?}",
146 );
147 backend.sync(user_credentials)?;
148 }
149
150 Ok(())
151 }
152
153 pub fn sync_yubihsm2(&self) -> Result<ConfigurationResult, Error> {
162 info!("Sync the state of the Signstar configuration with all available YubiHSM2 backends.");
163 let Some(yubihsm2_config) = self.config().yubihsm2() else {
164 warn!("There is no YubiHSM2 section in the Signstar configuration. Skipping...");
165 return Ok(ConfigurationResult::MissingConfigurationForBackend);
166 };
167 info!("Found YubiHSM2 section in the Signstar configuration.");
168
169 let available_connections = {
170 let available_connections = Self::available_yubihsm2_connections(yubihsm2_config);
171 if available_connections.is_empty() {
172 error!("There are no available YubiHSM2 connections. Aborting...");
173 return Ok(ConfigurationResult::NoAvailableBackendConnection);
174 }
175
176 available_connections
177 };
178 let provisioned_backends = Self::provisioned_yubihsm2_connections(yubihsm2_config);
179
180 let admin_credentials = match self.load_yubihsm2_admin_credentials() {
181 Ok(admin_credentials) => {
182 info!("Found administrative credentials for YubiHSM2.");
183 admin_credentials
184 }
185 Err(Error::SignstarConfig(signstar_config::Error::AdminSecretHandling(
186 signstar_config::admin_credentials::Error::CredsFileMissing { .. },
187 ))) => {
188 if !provisioned_backends.is_empty() {
189 error!(
190 "There are provisioned backends, but administrative credentials are not present. You probably want to restore from a backup. Aborting..."
191 );
192 return Ok(ConfigurationResult::ProvisionedBackendsButNoAdminCreds);
193 }
194 if available_connections.len() != yubihsm2_config.connections().len() {
195 error!(
196 "Not all configured connections are (yet) available ({}/{}) and administrative credentials are not present. Aborting...",
197 available_connections.len(),
198 yubihsm2_config.connections().len()
199 );
200 return Ok(ConfigurationResult::NotAllConnectionsAvailableAndNoAdminCreds);
201 }
202
203 YubiHsm2AdminCredentials::try_from(self.config())?
206 }
207 Err(error) => return Err(error),
208 };
209
210 let user_credentials = self.create_yubihsm2_non_admin_credentials()?;
212
213 self.sync_yubihsm2_backends(
214 available_connections,
215 &admin_credentials,
216 user_credentials.as_slice(),
217 )?;
218
219 Ok(ConfigurationResult::SyncSucceeded)
220 }
221}