signstar_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::{ConfigurationResult, Error, HostConfiguration};
17
18impl<'config> HostConfiguration<'config> {
19 fn load_nethsm_admin_credentials(&self) -> Result<NetHsmAdminCredentials, Error> {
25 Ok(NetHsmAdminCredentials::load(
26 *self.config().system().admin_secret_handling(),
27 )?)
28 }
29
30 fn available_nethsm_connections(config: &NetHsmConfig) -> Vec<NetHsm> {
32 info!("Query all NetHSM connections for availability.");
33 config
34 .connections()
35 .iter()
36 .filter_map(|connection| {
37 if connection.is_available() {
38 match NetHsm::new(connection.clone(), None, None, None) {
41 Err(error) => {
42 error!(
43 "Skipping NetHSM connection {} due to an error: {error}",
44 connection.url()
45 );
46 None
47 }
48 Ok(nethsm) => {
49 info!("Detected available NetHSM connection {}", connection.url());
50 Some(nethsm)
51 }
52 }
53 } else {
54 warn!(
55 "Skipping unavailable NetHSM connection {}",
56 connection.url()
57 );
58 None
59 }
60 })
61 .collect()
62 }
63
64 fn provisioned_nethsm_connections(config: &NetHsmConfig) -> Vec<&Connection> {
66 info!("Detect all provisioned NetHSM connections.");
67 config
68 .connections()
69 .iter()
70 .filter(|connection| {
71 if connection.is_provisioned() {
72 debug!("Detected provisioned NetHSM connection {connection:?}");
73 true
74 } else {
75 debug!("Skipping unprovisioned NetHSM connection {connection:?}");
76 false
77 }
78 })
79 .collect::<Vec<_>>()
80 }
81
82 fn create_nethsm_non_admin_credentials(&self) -> Result<Vec<FullCredentials>, Error> {
89 info!("Create new non-administrative user credentials for NetHSM backends.");
90 let user_backend_connections = self.config().user_backend_connections(&[
91 UserBackendConnectionFilter::NonAdmin,
92 UserBackendConnectionFilter::Backend(BackendType::NetHsm),
93 ]);
94
95 let credentials_list = {
96 let mut creds_list = Vec::new();
97 for user_backend_connection in user_backend_connections {
98 if let UserBackendConnection::NetHsm { .. } = &user_backend_connection
99 && let Some(creds_per_user) =
100 user_backend_connection.create_non_admin_backend_user_secrets()?
101 {
102 for credentials in creds_per_user {
103 creds_list.push(FullCredentials::new(
104 UserId::try_from(credentials.user()).map_err(|source| {
107 signstar_config::Error::NetHsm(NetHsmError::User(source))
108 })?,
109 credentials.passphrase().clone(),
110 ));
111 }
112 }
113 }
114
115 creds_list
116 };
117
118 Ok(credentials_list)
119 }
120
121 fn sync_nethsm_backends(
133 &self,
134 available_connections: Vec<NetHsm>,
135 admin_credentials: &NetHsmAdminCredentials,
136 user_credentials: &[FullCredentials],
137 ) -> Result<(), Error> {
138 let backends = {
139 let mut backends = Vec::new();
140 for nethsm in available_connections.into_iter() {
141 if let Some(backend) = NetHsmBackend::new(nethsm, admin_credentials, self.config())?
142 {
143 backends.push(backend);
144 }
145 }
146 backends
147 };
148
149 for backend in backends {
150 info!(
151 "Sync the state of the Signstar configuration with the NetHSM backend {}",
152 backend.nethsm().get_url()
153 );
154 backend.sync(user_credentials)?;
155 }
156
157 Ok(())
158 }
159
160 pub fn sync_nethsm(&self) -> Result<ConfigurationResult, Error> {
169 info!("Sync the state of the Signstar configuration with all available NetHSM backends.");
170 let Some(nethsm_config) = self.config().nethsm() else {
171 warn!("There is no NetHSM section in the Signstar configuration. Skipping...");
172 return Ok(ConfigurationResult::MissingConfigurationForBackend);
173 };
174 info!("Found NetHSM section in the Signstar configuration.");
175
176 let available_connections = {
177 let available_connections = Self::available_nethsm_connections(nethsm_config);
178 if available_connections.is_empty() {
179 error!("There are no available NetHSM connections. Aborting...");
180 return Ok(ConfigurationResult::NoAvailableBackendConnection);
181 }
182
183 available_connections
184 };
185 let provisioned_backends = Self::provisioned_nethsm_connections(nethsm_config);
186
187 let admin_credentials = match self.load_nethsm_admin_credentials() {
188 Ok(admin_credentials) => {
189 info!("Found administrative credentials for NetHSM.");
190 admin_credentials
191 }
192 Err(Error::SignstarConfig(signstar_config::Error::AdminSecretHandling(
193 signstar_config::admin_credentials::Error::CredsFileMissing { .. },
194 ))) => {
195 if !provisioned_backends.is_empty() {
196 error!(
197 "There are provisioned backends, but administrative credentials are not present. You probably want to restore from a backup. Aborting..."
198 );
199 return Ok(ConfigurationResult::ProvisionedBackendsButNoAdminCreds);
200 }
201 if available_connections.len() != nethsm_config.connections().len() {
202 error!(
203 "Not all configured connections are (yet) available ({}/{}) and administrative credentials are not present. Aborting...",
204 available_connections.len(),
205 nethsm_config.connections().len()
206 );
207 return Ok(ConfigurationResult::NotAllConnectionsAvailableAndNoAdminCreds);
208 }
209
210 NetHsmAdminCredentials::try_from(self.config())?
213 }
214 Err(error) => return Err(error),
215 };
216
217 let user_credentials = self.create_nethsm_non_admin_credentials()?;
219
220 self.sync_nethsm_backends(
221 available_connections,
222 &admin_credentials,
223 user_credentials.as_slice(),
224 )?;
225
226 Ok(ConfigurationResult::SyncSucceeded)
227 }
228}