signstar_host/configure/host/
nethsm.rs1use log::{debug, error, info, warn};
4use signstar_common::{backend::BackendType, traits::BackendCheck};
5use signstar_config::{
6 admin_credentials::AdminCredentials,
7 config::{UserBackendConnection, UserBackendConnectionFilter},
8 nethsm::{
9 NetHsmAdminCredentials,
10 NetHsmBackend,
11 NetHsmConfig,
12 nethsm_export::{Connection, FullCredentials, NetHsm, NetHsmError, UserId},
13 },
14};
15
16use crate::{
17 Error,
18 configure::{ConfigurationResult, HostConfiguration},
19};
20
21impl<'config> HostConfiguration<'config> {
22 fn load_nethsm_admin_credentials(&self) -> Result<NetHsmAdminCredentials, Error> {
28 Ok(
29 NetHsmAdminCredentials::load(*self.config().system().admin_secret_handling())
30 .map_err(Box::new)?,
31 )
32 }
33
34 fn available_nethsm_connections(config: &NetHsmConfig) -> Vec<NetHsm> {
36 info!("Query all NetHSM connections for availability.");
37 config
38 .connections()
39 .iter()
40 .filter_map(|connection| {
41 if connection.is_available() {
42 match NetHsm::new(connection.clone(), None, None, None) {
45 Err(error) => {
46 error!(
47 "Skipping NetHSM connection {} due to an error: {error}",
48 connection.url()
49 );
50 None
51 }
52 Ok(nethsm) => {
53 info!("Detected available NetHSM connection {}", connection.url());
54 Some(nethsm)
55 }
56 }
57 } else {
58 warn!(
59 "Skipping unavailable NetHSM connection {}",
60 connection.url()
61 );
62 None
63 }
64 })
65 .collect()
66 }
67
68 fn provisioned_nethsm_connections(config: &NetHsmConfig) -> Vec<&Connection> {
70 info!("Detect all provisioned NetHSM connections.");
71 config
72 .connections()
73 .iter()
74 .filter(|connection| {
75 if connection.is_provisioned() {
76 debug!("Detected provisioned NetHSM connection {connection:?}");
77 true
78 } else {
79 debug!("Skipping unprovisioned NetHSM connection {connection:?}");
80 false
81 }
82 })
83 .collect::<Vec<_>>()
84 }
85
86 fn create_nethsm_non_admin_credentials(&self) -> Result<Vec<FullCredentials>, Error> {
93 info!("Create new non-administrative user credentials for NetHSM backends.");
94 let user_backend_connections = self.config().user_backend_connections(&[
95 UserBackendConnectionFilter::NonAdmin,
96 UserBackendConnectionFilter::Backend(BackendType::NetHsm),
97 ]);
98
99 let credentials_list = {
100 let mut creds_list = Vec::new();
101 for user_backend_connection in user_backend_connections {
102 if let UserBackendConnection::NetHsm { .. } = &user_backend_connection
103 && let Some(creds_per_user) = user_backend_connection
104 .create_non_admin_backend_user_secrets()
105 .map_err(Box::new)?
106 {
107 for credentials in creds_per_user {
108 creds_list.push(FullCredentials::new(
109 UserId::try_from(credentials.user())
112 .map_err(|source| {
113 signstar_config::Error::NetHsm(NetHsmError::User(source))
114 })
115 .map_err(Box::new)?,
116 credentials.passphrase().clone(),
117 ));
118 }
119 }
120 }
121
122 creds_list
123 };
124
125 Ok(credentials_list)
126 }
127
128 fn sync_nethsm_backends(
140 &self,
141 available_connections: Vec<NetHsm>,
142 admin_credentials: &NetHsmAdminCredentials,
143 user_credentials: &[FullCredentials],
144 ) -> Result<(), Error> {
145 let backends = {
146 let mut backends = Vec::new();
147 for nethsm in available_connections.into_iter() {
148 if let Some(backend) = NetHsmBackend::new(nethsm, admin_credentials, self.config())
149 .map_err(Box::new)?
150 {
151 backends.push(backend);
152 }
153 }
154 backends
155 };
156
157 for backend in backends {
158 info!(
159 "Sync the state of the Signstar configuration with the NetHSM backend {}",
160 backend.nethsm().get_url()
161 );
162 backend.sync(user_credentials).map_err(Box::new)?;
163 }
164
165 Ok(())
166 }
167
168 pub fn sync_nethsm(&self) -> Result<ConfigurationResult, Error> {
177 info!("Sync the state of the Signstar configuration with all available NetHSM backends.");
178 let Some(nethsm_config) = self.config().nethsm() else {
179 warn!("There is no NetHSM section in the Signstar configuration. Skipping...");
180 return Ok(ConfigurationResult::MissingConfigurationForBackend);
181 };
182 info!("Found NetHSM section in the Signstar configuration.");
183
184 let available_connections = {
185 let available_connections = Self::available_nethsm_connections(nethsm_config);
186 if available_connections.is_empty() {
187 error!("There are no available NetHSM connections. Aborting...");
188 return Ok(ConfigurationResult::NoAvailableBackendConnection);
189 }
190
191 available_connections
192 };
193 let provisioned_backends = Self::provisioned_nethsm_connections(nethsm_config);
194
195 let (admin_credentials, new_admin_credentials) = match self.load_nethsm_admin_credentials()
196 {
197 Ok(admin_credentials) => {
198 info!("Found administrative credentials for NetHSM.");
199 (admin_credentials, false)
200 }
201 Err(Error::SignstarConfig(signstar_config_error)) => {
202 if !matches!(
203 *signstar_config_error,
204 signstar_config::Error::AdminSecretHandling(
205 signstar_config::admin_credentials::Error::CredsFileMissing { .. },
206 )
207 ) {
208 return Err(signstar_config_error.into());
209 }
210 if !provisioned_backends.is_empty() {
211 error!(
212 "There are provisioned backends, but administrative credentials are not present. You probably want to restore from a backup. Aborting..."
213 );
214 return Ok(ConfigurationResult::ProvisionedBackendsButNoAdminCreds);
215 }
216 if available_connections.len() != nethsm_config.connections().len() {
217 error!(
218 "Not all configured connections are (yet) available ({}/{}) and administrative credentials are not present. Aborting...",
219 available_connections.len(),
220 nethsm_config.connections().len()
221 );
222 return Ok(ConfigurationResult::NotAllConnectionsAvailableAndNoAdminCreds);
223 }
224
225 (
228 NetHsmAdminCredentials::try_from(self.config()).map_err(Box::new)?,
229 true,
230 )
231 }
232 Err(error) => return Err(error),
233 };
234
235 if new_admin_credentials {
237 admin_credentials
238 .store(*self.config().system().admin_secret_handling())
239 .map_err(Box::new)?;
240 }
241
242 let user_credentials = self.create_nethsm_non_admin_credentials()?;
244
245 self.sync_nethsm_backends(
246 available_connections,
247 &admin_credentials,
248 user_credentials.as_slice(),
249 )?;
250
251 Ok(ConfigurationResult::SyncSucceeded)
252 }
253}