1use std::{
4 collections::HashSet,
5 fs::{File, Permissions, create_dir_all, read_to_string, set_permissions},
6 io::Write,
7 os::unix::fs::{PermissionsExt, chown},
8 path::{Path, PathBuf},
9 process::{Command, Stdio},
10};
11
12#[cfg(doc)]
13use nethsm::NetHsm;
14use nethsm::{FullCredentials, KeyId, NamespaceId, Passphrase, UserId, UserRole};
15use rand::{Rng, distributions::Alphanumeric, thread_rng};
16use serde::{Deserialize, Serialize};
17use signstar_common::{
18 common::SECRET_FILE_MODE,
19 system_user::{
20 get_home_base_dir_path,
21 get_plaintext_secret_file,
22 get_systemd_creds_secret_file,
23 get_user_secrets_dir,
24 },
25};
26use signstar_crypto::key::SigningKeySetup;
27
28use crate::{
29 AdministrativeSecretHandling,
30 AuthorizedKeyEntry,
31 CredentialsLoading,
32 CredentialsLoadingError,
33 CredentialsLoadingErrors,
34 Error,
35 NonAdministrativeSecretHandling,
36 SignstarConfig,
37 SystemUserId,
38 SystemWideUserId,
39 config::base::BackendConnection,
40 nethsm::config::{FilterUserKeys, NetHsmMetricsUsers},
41 utils::{
42 fail_if_not_root,
43 fail_if_root,
44 get_command,
45 get_current_system_user,
46 get_system_user_pair,
47 match_current_system_user,
48 },
49};
50
51#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
53pub enum UserMapping {
54 #[serde(rename = "nethsm_only_admin")]
56 NetHsmOnlyAdmin(UserId),
57
58 #[serde(rename = "system_nethsm_backup")]
60 SystemNetHsmBackup {
61 nethsm_user: SystemWideUserId,
63 ssh_authorized_key: AuthorizedKeyEntry,
65 system_user: SystemUserId,
67 },
68
69 #[serde(rename = "system_nethsm_metrics")]
73 SystemNetHsmMetrics {
74 nethsm_users: NetHsmMetricsUsers,
77 ssh_authorized_key: AuthorizedKeyEntry,
79 system_user: SystemUserId,
81 },
82
83 #[serde(rename = "system_nethsm_operator_signing")]
88 SystemNetHsmOperatorSigning {
89 nethsm_user: UserId,
91 key_id: KeyId,
93 nethsm_key_setup: SigningKeySetup,
95 ssh_authorized_key: AuthorizedKeyEntry,
97 system_user: SystemUserId,
99 tag: String,
101 },
102
103 #[cfg(feature = "yubihsm2")]
108 #[serde(rename = "system_yubihsm_operator_signing")]
109 SystemYubiHsmOperatorSigning {
110 authentication_key_id: u16,
112 backend_key_setup: SigningKeySetup,
114 backend_key_id: u16,
116 backend_key_domain: usize,
118 ssh_authorized_key: AuthorizedKeyEntry,
120 system_user: SystemUserId,
122 },
123
124 #[serde(rename = "hermetic_system_nethsm_metrics")]
128 HermeticSystemNetHsmMetrics {
129 nethsm_users: NetHsmMetricsUsers,
132 system_user: SystemUserId,
134 },
135
136 #[serde(rename = "system_only_share_download")]
139 SystemOnlyShareDownload {
140 system_user: SystemUserId,
142 ssh_authorized_key: AuthorizedKeyEntry,
144 },
145
146 #[serde(rename = "system_only_share_upload")]
149 SystemOnlyShareUpload {
150 system_user: SystemUserId,
152 ssh_authorized_key: AuthorizedKeyEntry,
154 },
155
156 #[serde(rename = "system_only_wireguard_download")]
159 SystemOnlyWireGuardDownload {
160 system_user: SystemUserId,
162 ssh_authorized_key: AuthorizedKeyEntry,
164 },
165}
166
167impl UserMapping {
168 pub fn get_system_user(&self) -> Option<&SystemUserId> {
188 match self {
189 UserMapping::NetHsmOnlyAdmin(_) => None,
190 UserMapping::SystemNetHsmBackup {
191 nethsm_user: _,
192 ssh_authorized_key: _,
193 system_user,
194 }
195 | UserMapping::SystemNetHsmOperatorSigning {
196 nethsm_user: _,
197 key_id: _,
198 nethsm_key_setup: _,
199 ssh_authorized_key: _,
200 system_user,
201 tag: _,
202 }
203 | UserMapping::SystemNetHsmMetrics {
204 nethsm_users: _,
205 ssh_authorized_key: _,
206 system_user,
207 }
208 | UserMapping::HermeticSystemNetHsmMetrics {
209 nethsm_users: _,
210 system_user,
211 }
212 | UserMapping::SystemOnlyShareDownload {
213 system_user,
214 ssh_authorized_key: _,
215 }
216 | UserMapping::SystemOnlyShareUpload {
217 system_user,
218 ssh_authorized_key: _,
219 }
220 | UserMapping::SystemOnlyWireGuardDownload {
221 system_user,
222 ssh_authorized_key: _,
223 } => Some(system_user),
224 #[cfg(feature = "yubihsm2")]
225 UserMapping::SystemYubiHsmOperatorSigning {
226 authentication_key_id: _,
227 backend_key_setup: _,
228 backend_key_id: _,
229 backend_key_domain: _,
230 system_user,
231 ssh_authorized_key: _,
232 } => Some(system_user),
233 }
234 }
235
236 pub fn get_nethsm_users(&self) -> Vec<UserId> {
257 match self {
258 UserMapping::SystemNetHsmBackup {
259 nethsm_user,
260 system_user: _,
261 ssh_authorized_key: _,
262 } => vec![nethsm_user.clone().into()],
263 UserMapping::NetHsmOnlyAdmin(nethsm_user)
264 | UserMapping::SystemNetHsmOperatorSigning {
265 nethsm_user,
266 key_id: _,
267 nethsm_key_setup: _,
268 system_user: _,
269 ssh_authorized_key: _,
270 tag: _,
271 } => vec![nethsm_user.clone()],
272 UserMapping::SystemNetHsmMetrics {
273 nethsm_users,
274 system_user: _,
275 ssh_authorized_key: _,
276 }
277 | UserMapping::HermeticSystemNetHsmMetrics {
278 nethsm_users,
279 system_user: _,
280 } => nethsm_users.get_users(),
281 UserMapping::SystemOnlyShareDownload {
282 system_user: _,
283 ssh_authorized_key: _,
284 }
285 | UserMapping::SystemOnlyShareUpload {
286 system_user: _,
287 ssh_authorized_key: _,
288 }
289 | UserMapping::SystemOnlyWireGuardDownload {
290 system_user: _,
291 ssh_authorized_key: _,
292 } => vec![],
293 #[cfg(feature = "yubihsm2")]
294 UserMapping::SystemYubiHsmOperatorSigning { .. } => Vec::new(),
295 }
296 }
297
298 pub fn get_nethsm_users_and_roles(&self) -> Vec<(UserId, UserRole)> {
319 match self {
320 UserMapping::SystemNetHsmBackup {
321 nethsm_user,
322 system_user: _,
323 ssh_authorized_key: _,
324 } => vec![(nethsm_user.clone().into(), UserRole::Backup)],
325 UserMapping::NetHsmOnlyAdmin(nethsm_user) => {
326 vec![(nethsm_user.clone(), UserRole::Administrator)]
327 }
328 UserMapping::SystemNetHsmOperatorSigning {
329 nethsm_user,
330 key_id: _,
331 nethsm_key_setup: _,
332 system_user: _,
333 ssh_authorized_key: _,
334 tag: _,
335 } => vec![(nethsm_user.clone(), UserRole::Operator)],
336 UserMapping::SystemNetHsmMetrics {
337 nethsm_users,
338 system_user: _,
339 ssh_authorized_key: _,
340 }
341 | UserMapping::HermeticSystemNetHsmMetrics {
342 nethsm_users,
343 system_user: _,
344 } => nethsm_users.get_users_and_roles(),
345 UserMapping::SystemOnlyShareDownload {
346 system_user: _,
347 ssh_authorized_key: _,
348 }
349 | UserMapping::SystemOnlyShareUpload {
350 system_user: _,
351 ssh_authorized_key: _,
352 }
353 | UserMapping::SystemOnlyWireGuardDownload {
354 system_user: _,
355 ssh_authorized_key: _,
356 } => vec![],
357 #[cfg(feature = "yubihsm2")]
358 UserMapping::SystemYubiHsmOperatorSigning { .. } => Vec::new(),
359 }
360 }
361
362 pub fn get_nethsm_user_role_and_tags(&self) -> Vec<(UserId, UserRole, Vec<String>)> {
402 match self {
403 UserMapping::SystemNetHsmOperatorSigning {
404 nethsm_user,
405 key_id: _,
406 nethsm_key_setup: _,
407 system_user: _,
408 ssh_authorized_key: _,
409 tag,
410 } => vec![(
411 nethsm_user.clone(),
412 UserRole::Operator,
413 vec![tag.to_string()],
414 )],
415 UserMapping::SystemNetHsmBackup {
416 nethsm_user,
417 ssh_authorized_key: _,
418 system_user: _,
419 } => vec![(nethsm_user.clone().into(), UserRole::Backup, Vec::new())],
420 UserMapping::NetHsmOnlyAdmin(user_id) => {
421 vec![(user_id.clone(), UserRole::Administrator, Vec::new())]
422 }
423 UserMapping::SystemNetHsmMetrics {
424 nethsm_users,
425 ssh_authorized_key: _,
426 system_user: _,
427 } => nethsm_users
428 .get_users_and_roles()
429 .iter()
430 .map(|(user, role)| (user.clone(), *role, Vec::new()))
431 .collect(),
432 UserMapping::HermeticSystemNetHsmMetrics {
433 nethsm_users,
434 system_user: _,
435 } => nethsm_users
436 .get_users_and_roles()
437 .iter()
438 .map(|(user, role)| (user.clone(), *role, Vec::new()))
439 .collect(),
440 UserMapping::SystemOnlyShareDownload { .. }
441 | UserMapping::SystemOnlyShareUpload { .. }
442 | UserMapping::SystemOnlyWireGuardDownload { .. } => Vec::new(),
443 #[cfg(feature = "yubihsm2")]
444 UserMapping::SystemYubiHsmOperatorSigning { .. } => Vec::new(),
445 }
446 }
447
448 pub fn get_ssh_authorized_key(&self) -> Option<&AuthorizedKeyEntry> {
472 match self {
473 UserMapping::NetHsmOnlyAdmin(_)
474 | UserMapping::HermeticSystemNetHsmMetrics {
475 nethsm_users: _,
476 system_user: _,
477 } => None,
478 UserMapping::SystemNetHsmBackup {
479 nethsm_user: _,
480 system_user: _,
481 ssh_authorized_key,
482 }
483 | UserMapping::SystemNetHsmMetrics {
484 nethsm_users: _,
485 system_user: _,
486 ssh_authorized_key,
487 }
488 | UserMapping::SystemOnlyShareDownload {
489 system_user: _,
490 ssh_authorized_key,
491 }
492 | UserMapping::SystemOnlyShareUpload {
493 system_user: _,
494 ssh_authorized_key,
495 }
496 | UserMapping::SystemOnlyWireGuardDownload {
497 system_user: _,
498 ssh_authorized_key,
499 }
500 | UserMapping::SystemNetHsmOperatorSigning {
501 nethsm_user: _,
502 key_id: _,
503 nethsm_key_setup: _,
504 system_user: _,
505 ssh_authorized_key,
506 tag: _,
507 } => Some(ssh_authorized_key),
508 #[cfg(feature = "yubihsm2")]
509 UserMapping::SystemYubiHsmOperatorSigning {
510 authentication_key_id: _,
511 backend_key_setup: _,
512 backend_key_id: _,
513 backend_key_domain: _,
514 system_user: _,
515 ssh_authorized_key,
516 } => Some(ssh_authorized_key),
517 }
518 }
519
520 pub fn get_nethsm_key_ids(&self, namespace: Option<&NamespaceId>) -> Vec<KeyId> {
558 match self {
559 UserMapping::SystemNetHsmOperatorSigning {
560 nethsm_user,
561 key_id,
562 nethsm_key_setup: _,
563 system_user: _,
564 ssh_authorized_key: _,
565 tag: _,
566 } => {
567 if nethsm_user.namespace() == namespace {
568 vec![key_id.clone()]
569 } else {
570 vec![]
571 }
572 }
573 UserMapping::SystemNetHsmMetrics {
574 nethsm_users: _,
575 system_user: _,
576 ssh_authorized_key: _,
577 }
578 | UserMapping::NetHsmOnlyAdmin(_)
579 | UserMapping::HermeticSystemNetHsmMetrics {
580 nethsm_users: _,
581 system_user: _,
582 }
583 | UserMapping::SystemNetHsmBackup {
584 nethsm_user: _,
585 system_user: _,
586 ssh_authorized_key: _,
587 }
588 | UserMapping::SystemOnlyShareDownload {
589 system_user: _,
590 ssh_authorized_key: _,
591 }
592 | UserMapping::SystemOnlyShareUpload {
593 system_user: _,
594 ssh_authorized_key: _,
595 }
596 | UserMapping::SystemOnlyWireGuardDownload {
597 system_user: _,
598 ssh_authorized_key: _,
599 } => vec![],
600 #[cfg(feature = "yubihsm2")]
601 UserMapping::SystemYubiHsmOperatorSigning { .. } => Vec::new(),
602 }
603 }
604
605 pub fn get_nethsm_tags(&self, namespace: Option<&NamespaceId>) -> Vec<&str> {
650 match self {
651 UserMapping::SystemNetHsmOperatorSigning {
652 nethsm_user,
653 key_id: _,
654 nethsm_key_setup: _,
655 system_user: _,
656 ssh_authorized_key: _,
657 tag,
658 } => {
659 if nethsm_user.namespace() == namespace {
660 vec![tag.as_str()]
661 } else {
662 vec![]
663 }
664 }
665 UserMapping::SystemNetHsmMetrics {
666 nethsm_users: _,
667 system_user: _,
668 ssh_authorized_key: _,
669 }
670 | UserMapping::NetHsmOnlyAdmin(_)
671 | UserMapping::HermeticSystemNetHsmMetrics {
672 nethsm_users: _,
673 system_user: _,
674 }
675 | UserMapping::SystemNetHsmBackup {
676 nethsm_user: _,
677 system_user: _,
678 ssh_authorized_key: _,
679 }
680 | UserMapping::SystemOnlyShareDownload {
681 system_user: _,
682 ssh_authorized_key: _,
683 }
684 | UserMapping::SystemOnlyShareUpload {
685 system_user: _,
686 ssh_authorized_key: _,
687 }
688 | UserMapping::SystemOnlyWireGuardDownload {
689 system_user: _,
690 ssh_authorized_key: _,
691 } => vec![],
692 #[cfg(feature = "yubihsm2")]
693 UserMapping::SystemYubiHsmOperatorSigning { .. } => Vec::new(),
694 }
695 }
696
697 pub fn get_nethsm_user_key_and_tag(
765 &self,
766 filter: FilterUserKeys,
767 ) -> Vec<(UserId, KeyId, SigningKeySetup, String)> {
768 match self {
769 UserMapping::SystemNetHsmOperatorSigning {
770 nethsm_user,
771 key_id,
772 nethsm_key_setup,
773 system_user: _,
774 ssh_authorized_key: _,
775 tag,
776 } => match filter {
777 FilterUserKeys::All => {
778 vec![(
779 nethsm_user.clone(),
780 key_id.clone(),
781 nethsm_key_setup.clone(),
782 tag.clone(),
783 )]
784 }
785 FilterUserKeys::Namespaced => {
786 if nethsm_user.is_namespaced() {
787 vec![(
788 nethsm_user.clone(),
789 key_id.clone(),
790 nethsm_key_setup.clone(),
791 tag.clone(),
792 )]
793 } else {
794 Vec::new()
795 }
796 }
797 FilterUserKeys::Namespace(namespace) => {
798 if Some(&namespace) == nethsm_user.namespace() {
799 vec![(
800 nethsm_user.clone(),
801 key_id.clone(),
802 nethsm_key_setup.clone(),
803 tag.clone(),
804 )]
805 } else {
806 Vec::new()
807 }
808 }
809 FilterUserKeys::SystemWide => {
810 if !nethsm_user.is_namespaced() {
811 vec![(
812 nethsm_user.clone(),
813 key_id.clone(),
814 nethsm_key_setup.clone(),
815 tag.clone(),
816 )]
817 } else {
818 Vec::new()
819 }
820 }
821 FilterUserKeys::Tag(filter_tag) => {
822 if &filter_tag == tag {
823 vec![(
824 nethsm_user.clone(),
825 key_id.clone(),
826 nethsm_key_setup.clone(),
827 tag.clone(),
828 )]
829 } else {
830 Vec::new()
831 }
832 }
833 },
834 UserMapping::SystemNetHsmMetrics {
835 nethsm_users: _,
836 system_user: _,
837 ssh_authorized_key: _,
838 }
839 | UserMapping::NetHsmOnlyAdmin(_)
840 | UserMapping::HermeticSystemNetHsmMetrics {
841 nethsm_users: _,
842 system_user: _,
843 }
844 | UserMapping::SystemNetHsmBackup {
845 nethsm_user: _,
846 system_user: _,
847 ssh_authorized_key: _,
848 }
849 | UserMapping::SystemOnlyShareDownload {
850 system_user: _,
851 ssh_authorized_key: _,
852 }
853 | UserMapping::SystemOnlyShareUpload {
854 system_user: _,
855 ssh_authorized_key: _,
856 }
857 | UserMapping::SystemOnlyWireGuardDownload {
858 system_user: _,
859 ssh_authorized_key: _,
860 } => vec![],
861 #[cfg(feature = "yubihsm2")]
862 UserMapping::SystemYubiHsmOperatorSigning { .. } => Vec::new(),
863 }
864 }
865
866 pub fn get_nethsm_namespaces(&self) -> Vec<NamespaceId> {
907 match self {
908 UserMapping::NetHsmOnlyAdmin(nethsm_user)
909 | UserMapping::SystemNetHsmOperatorSigning {
910 nethsm_user,
911 key_id: _,
912 nethsm_key_setup: _,
913 system_user: _,
914 ssh_authorized_key: _,
915 tag: _,
916 } => {
917 if let Some(namespace) = nethsm_user.namespace() {
918 vec![namespace.clone()]
919 } else {
920 vec![]
921 }
922 }
923 UserMapping::HermeticSystemNetHsmMetrics {
924 nethsm_users,
925 system_user: _,
926 }
927 | UserMapping::SystemNetHsmMetrics {
928 nethsm_users,
929 system_user: _,
930 ssh_authorized_key: _,
931 } => nethsm_users
932 .get_users()
933 .iter()
934 .filter_map(|user_id| user_id.namespace())
935 .cloned()
936 .collect(),
937 UserMapping::SystemOnlyShareDownload {
938 system_user: _,
939 ssh_authorized_key: _,
940 }
941 | UserMapping::SystemNetHsmBackup {
942 nethsm_user: _,
943 system_user: _,
944 ssh_authorized_key: _,
945 }
946 | UserMapping::SystemOnlyShareUpload {
947 system_user: _,
948 ssh_authorized_key: _,
949 }
950 | UserMapping::SystemOnlyWireGuardDownload {
951 system_user: _,
952 ssh_authorized_key: _,
953 } => vec![],
954 #[cfg(feature = "yubihsm2")]
955 UserMapping::SystemYubiHsmOperatorSigning { .. } => Vec::new(),
956 }
957 }
958
959 pub fn has_system_and_backend_user(&self) -> bool {
964 match self {
965 UserMapping::SystemNetHsmOperatorSigning {
966 nethsm_user: _,
967 key_id: _,
968 nethsm_key_setup: _,
969 system_user: _,
970 ssh_authorized_key: _,
971 tag: _,
972 }
973 | UserMapping::HermeticSystemNetHsmMetrics {
974 nethsm_users: _,
975 system_user: _,
976 }
977 | UserMapping::SystemNetHsmMetrics {
978 nethsm_users: _,
979 system_user: _,
980 ssh_authorized_key: _,
981 }
982 | UserMapping::SystemNetHsmBackup {
983 nethsm_user: _,
984 system_user: _,
985 ssh_authorized_key: _,
986 } => true,
987 #[cfg(feature = "yubihsm2")]
988 UserMapping::SystemYubiHsmOperatorSigning { .. } => true,
989 UserMapping::SystemOnlyShareDownload {
990 system_user: _,
991 ssh_authorized_key: _,
992 }
993 | UserMapping::SystemOnlyShareUpload {
994 system_user: _,
995 ssh_authorized_key: _,
996 }
997 | UserMapping::SystemOnlyWireGuardDownload {
998 system_user: _,
999 ssh_authorized_key: _,
1000 }
1001 | UserMapping::NetHsmOnlyAdmin(_) => false,
1002 }
1003 }
1004}
1005
1006pub(crate) fn check_secrets_file(path: impl AsRef<Path>) -> Result<(), Error> {
1024 let path = path.as_ref();
1025
1026 if !path.exists() {
1028 return Err(crate::non_admin_credentials::Error::SecretsFileMissing {
1029 path: path.to_path_buf(),
1030 }
1031 .into());
1032 }
1033
1034 if !path.is_file() {
1036 return Err(Error::NonAdminSecretHandling(
1037 crate::non_admin_credentials::Error::SecretsFileNotAFile {
1038 path: path.to_path_buf(),
1039 },
1040 ));
1041 }
1042
1043 match path.metadata() {
1045 Ok(metadata) => {
1046 let mode = metadata.permissions().mode();
1047 if mode != SECRET_FILE_MODE {
1048 return Err(Error::NonAdminSecretHandling(
1049 crate::non_admin_credentials::Error::SecretsFilePermissions {
1050 path: path.to_path_buf(),
1051 mode,
1052 },
1053 ));
1054 }
1055 }
1056 Err(source) => {
1057 return Err(Error::NonAdminSecretHandling(
1058 crate::non_admin_credentials::Error::SecretsFileMetadata {
1059 path: path.to_path_buf(),
1060 source,
1061 },
1062 ));
1063 }
1064 }
1065
1066 Ok(())
1067}
1068
1069#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1074pub struct ExtendedUserMapping {
1075 admin_secret_handling: AdministrativeSecretHandling,
1076 non_admin_secret_handling: NonAdministrativeSecretHandling,
1077 connections: HashSet<BackendConnection>,
1078 user_mapping: UserMapping,
1079}
1080
1081impl ExtendedUserMapping {
1082 pub fn new(
1084 admin_secret_handling: AdministrativeSecretHandling,
1085 non_admin_secret_handling: NonAdministrativeSecretHandling,
1086 connections: HashSet<BackendConnection>,
1087 user_mapping: UserMapping,
1088 ) -> Self {
1089 Self {
1090 admin_secret_handling,
1091 non_admin_secret_handling,
1092 connections,
1093 user_mapping,
1094 }
1095 }
1096
1097 pub fn get_admin_secret_handling(&self) -> AdministrativeSecretHandling {
1099 self.admin_secret_handling
1100 }
1101
1102 pub fn get_connections(&self) -> HashSet<BackendConnection> {
1104 self.connections.clone()
1105 }
1106
1107 pub fn get_non_admin_secret_handling(&self) -> NonAdministrativeSecretHandling {
1109 self.non_admin_secret_handling
1110 }
1111
1112 pub fn get_user_mapping(&self) -> &UserMapping {
1114 &self.user_mapping
1115 }
1116
1117 pub fn load_credentials(&self) -> Result<CredentialsLoading, Error> {
1138 let (system_user, user) = get_system_user_pair(self)?;
1140 let current_system_user = get_current_system_user()?;
1141
1142 fail_if_root(¤t_system_user)?;
1144 match_current_system_user(¤t_system_user, &user)?;
1145
1146 let secret_handling = self.get_non_admin_secret_handling();
1147 let mut credentials = Vec::new();
1148 let mut errors = Vec::new();
1149
1150 for user_id in self.get_user_mapping().get_nethsm_users() {
1151 let secrets_file = match secret_handling {
1152 NonAdministrativeSecretHandling::Plaintext => {
1153 get_plaintext_secret_file(system_user.as_ref(), &user_id.to_string())
1154 }
1155 NonAdministrativeSecretHandling::SystemdCreds => {
1156 get_systemd_creds_secret_file(system_user.as_ref(), &user_id.to_string())
1157 }
1158 };
1159 if let Err(error) = check_secrets_file(secrets_file.as_path()) {
1161 errors.push(CredentialsLoadingError::new(user_id, error));
1162 continue;
1163 };
1164
1165 match secret_handling {
1166 NonAdministrativeSecretHandling::Plaintext => {
1168 match read_to_string(&secrets_file).map_err(|source| {
1170 Error::NonAdminSecretHandling(
1171 crate::non_admin_credentials::Error::SecretsFileRead {
1172 path: secrets_file,
1173 source,
1174 },
1175 )
1176 }) {
1177 Ok(passphrase) => credentials
1178 .push(FullCredentials::new(user_id, Passphrase::new(passphrase))),
1179 Err(error) => {
1180 errors.push(CredentialsLoadingError::new(user_id, error));
1181 continue;
1182 }
1183 }
1184 }
1185 NonAdministrativeSecretHandling::SystemdCreds => {
1187 let creds_command = get_command("systemd-creds")?;
1189 let mut command = Command::new(creds_command);
1190 let command = command
1191 .arg("--user")
1192 .arg("decrypt")
1193 .arg(&secrets_file)
1194 .arg("-");
1195 match command.output().map_err(|source| Error::CommandExec {
1196 command: format!("{command:?}"),
1197 source,
1198 }) {
1199 Ok(command_output) => {
1200 if !command_output.status.success() {
1202 errors.push(CredentialsLoadingError::new(
1203 user_id,
1204 Error::CommandNonZero {
1205 command: format!("{command:?}"),
1206 exit_status: command_output.status,
1207 stderr: String::from_utf8_lossy(&command_output.stderr)
1208 .into_owned(),
1209 },
1210 ));
1211 continue;
1212 }
1213
1214 let creds = match String::from_utf8(command_output.stdout) {
1215 Ok(creds) => creds,
1216 Err(source) => {
1217 errors.push(CredentialsLoadingError::new(
1218 user_id.clone(),
1219 Error::Utf8String {
1220 path: secrets_file,
1221 context: format!(
1222 "converting stdout of {command:?} to string"
1223 ),
1224 source,
1225 },
1226 ));
1227 continue;
1228 }
1229 };
1230
1231 credentials.push(FullCredentials::new(user_id, Passphrase::new(creds)));
1232 }
1233 Err(error) => {
1234 errors.push(CredentialsLoadingError::new(user_id, error));
1235 continue;
1236 }
1237 }
1238 }
1239 }
1240 }
1241
1242 Ok(CredentialsLoading::new(
1243 self.clone(),
1244 credentials,
1245 CredentialsLoadingErrors::new(errors),
1246 ))
1247 }
1248
1249 pub fn create_secrets_dir(&self) -> Result<(), Error> {
1264 let (system_user, user) = get_system_user_pair(self)?;
1266
1267 fail_if_not_root(&get_current_system_user()?)?;
1269
1270 let secrets_dir = get_user_secrets_dir(system_user.as_ref());
1272 create_dir_all(&secrets_dir).map_err(|source| {
1273 crate::non_admin_credentials::Error::SecretsDirCreate {
1274 path: secrets_dir.clone(),
1275 system_user: system_user.clone(),
1276 source,
1277 }
1278 })?;
1279
1280 let home_dir = get_home_base_dir_path().join(PathBuf::from(system_user.as_ref()));
1283 let mut chown_dir = secrets_dir.clone();
1284 while chown_dir != home_dir {
1285 chown(&chown_dir, Some(user.uid.as_raw()), Some(user.gid.as_raw())).map_err(
1286 |source| Error::Chown {
1287 path: chown_dir.to_path_buf(),
1288 user: system_user.to_string(),
1289 source,
1290 },
1291 )?;
1292 if let Some(parent) = &chown_dir.parent() {
1293 chown_dir = parent.to_path_buf()
1294 } else {
1295 break;
1296 }
1297 }
1298
1299 Ok(())
1300 }
1301
1302 pub fn create_non_administrative_secrets(&self) -> Result<(), Error> {
1326 let (system_user, user) = get_system_user_pair(self)?;
1328
1329 fail_if_not_root(&get_current_system_user()?)?;
1331
1332 let secret_handling = self.get_non_admin_secret_handling();
1333
1334 for user_id in self.get_user_mapping().get_nethsm_users() {
1336 let secrets_file = match secret_handling {
1337 NonAdministrativeSecretHandling::Plaintext => {
1338 get_plaintext_secret_file(system_user.as_ref(), &user_id.to_string())
1339 }
1340 NonAdministrativeSecretHandling::SystemdCreds => {
1341 get_systemd_creds_secret_file(system_user.as_ref(), &user_id.to_string())
1342 }
1343 };
1344 println!(
1345 "Create secret for system user {system_user} and backend user {user_id} in file: {secrets_file:?}"
1346 );
1347 let secret = {
1348 let initial_secret: String = thread_rng()
1350 .sample_iter(&Alphanumeric)
1351 .take(30)
1352 .map(char::from)
1353 .collect();
1354 match secret_handling {
1356 NonAdministrativeSecretHandling::Plaintext => {
1357 initial_secret.as_bytes().to_vec()
1358 }
1359 NonAdministrativeSecretHandling::SystemdCreds => {
1360 let creds_command = get_command("systemd-creds")?;
1362 let mut command = Command::new(creds_command);
1363 let command = command
1364 .arg("--user")
1365 .arg("--name=")
1366 .arg("--uid")
1367 .arg(system_user.as_ref())
1368 .arg("encrypt")
1369 .arg("-")
1370 .arg("-")
1371 .stdin(Stdio::piped())
1372 .stdout(Stdio::piped())
1373 .stderr(Stdio::piped());
1374 let mut command_child =
1375 command.spawn().map_err(|source| Error::CommandBackground {
1376 command: format!("{command:?}"),
1377 source,
1378 })?;
1379
1380 command_child
1382 .stdin
1383 .take()
1384 .ok_or(Error::CommandAttachToStdin {
1385 command: format!("{command:?}"),
1386 })?
1387 .write_all(initial_secret.as_bytes())
1388 .map_err(|source| Error::CommandWriteToStdin {
1389 command: format!("{command:?}"),
1390 source,
1391 })?;
1392
1393 let command_output =
1394 command_child.wait_with_output().map_err(|source| {
1395 Error::CommandExec {
1396 command: format!("{command:?}"),
1397 source,
1398 }
1399 })?;
1400
1401 if !command_output.status.success() {
1402 return Err(Error::CommandNonZero {
1403 command: format!("{command:?}"),
1404 exit_status: command_output.status,
1405 stderr: String::from_utf8_lossy(&command_output.stderr)
1406 .into_owned(),
1407 });
1408 }
1409 command_output.stdout
1410 }
1411 }
1412 };
1413
1414 let mut file = File::create(secrets_file.as_path()).map_err(|source| {
1416 {
1417 crate::non_admin_credentials::Error::SecretsFileCreate {
1418 path: secrets_file.clone(),
1419 system_user: system_user.clone(),
1420 source,
1421 }
1422 }
1423 })?;
1424 file.write_all(&secret).map_err(|source| {
1425 crate::non_admin_credentials::Error::SecretsFileWrite {
1426 path: secrets_file.clone(),
1427 system_user: system_user.clone(),
1428 source,
1429 }
1430 })?;
1431 chown(
1432 &secrets_file,
1433 Some(user.uid.as_raw()),
1434 Some(user.gid.as_raw()),
1435 )
1436 .map_err(|source| Error::Chown {
1437 path: secrets_file.clone(),
1438 user: system_user.to_string(),
1439 source,
1440 })?;
1441 set_permissions(
1442 secrets_file.as_path(),
1443 Permissions::from_mode(SECRET_FILE_MODE),
1444 )
1445 .map_err(|source| Error::ApplyPermissions {
1446 path: secrets_file.clone(),
1447 mode: SECRET_FILE_MODE,
1448 source,
1449 })?;
1450 }
1451 Ok(())
1452 }
1453}
1454
1455impl From<SignstarConfig> for Vec<ExtendedUserMapping> {
1456 fn from(value: SignstarConfig) -> Self {
1463 value
1464 .iter_user_mappings()
1465 .filter_map(|mapping| {
1466 if mapping.has_system_and_backend_user() {
1467 Some(ExtendedUserMapping {
1468 admin_secret_handling: value.get_administrative_secret_handling(),
1469 non_admin_secret_handling: value.get_non_administrative_secret_handling(),
1470 connections: value.iter_connections().cloned().collect(),
1471 user_mapping: mapping.clone(),
1472 })
1473 } else {
1474 None
1475 }
1476 })
1477 .collect()
1478 }
1479}
1480
1481#[cfg(test)]
1482mod tests {
1483 use log::{LevelFilter, debug};
1484 use nethsm::{CryptographicKeyContext, OpenPgpUserIdList};
1485 use rstest::rstest;
1486 use signstar_common::logging::setup_logging;
1487 use tempfile::{NamedTempFile, TempDir};
1488 use testresult::TestResult;
1489
1490 use super::*;
1491
1492 #[rstest]
1493 #[case(UserMapping::NetHsmOnlyAdmin("test".parse()?), FilterUserKeys::All, Vec::new())]
1494 #[case(
1495 UserMapping::SystemNetHsmMetrics {
1496 nethsm_users: NetHsmMetricsUsers::new(
1497 SystemWideUserId::new("metrics".to_string())?,
1498 vec![
1499 UserId::new("operator".to_string())?,
1500 ],
1501 )?,
1502 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1503 system_user: "system-metrics".parse()?,
1504 },
1505 FilterUserKeys::All,
1506 Vec::new(),
1507 )]
1508 #[case(
1509 UserMapping::SystemNetHsmBackup {
1510 nethsm_user: "backup".parse()?,
1511 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1512 system_user: "system-backup".parse()?,
1513 },
1514 FilterUserKeys::All,
1515 Vec::new(),
1516 )]
1517 #[case(
1518 UserMapping::SystemNetHsmOperatorSigning {
1519 nethsm_user: "operator".parse()?,
1520 key_id: "key1".parse()?,
1521 nethsm_key_setup: SigningKeySetup::new(
1522 "Curve25519".parse()?,
1523 vec!["EdDsaSignature".parse()?],
1524 None,
1525 "EdDsa".parse()?,
1526 CryptographicKeyContext::OpenPgp{
1527 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
1528 version: "v4".parse()?,
1529 },
1530 )?,
1531 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1532 system_user: "system-operator".parse()?,
1533 tag: "tag1".to_string(),
1534 },
1535 FilterUserKeys::All,
1536 vec![(
1537 "operator".parse()?,
1538 "key1".parse()?,
1539 SigningKeySetup::new(
1540 "Curve25519".parse()?,
1541 vec!["EdDsaSignature".parse()?],
1542 None,
1543 "EdDsa".parse()?,
1544 CryptographicKeyContext::OpenPgp{
1545 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
1546 version: "v4".parse()?,
1547 },
1548 )?,
1549 "tag1".to_string(),
1550 )],
1551 )]
1552 #[case::systemwide_operator_filter_namespaced(
1553 UserMapping::SystemNetHsmOperatorSigning {
1554 nethsm_user: "operator".parse()?,
1555 key_id: "key1".parse()?,
1556 nethsm_key_setup: SigningKeySetup::new(
1557 "Curve25519".parse()?,
1558 vec!["EdDsaSignature".parse()?],
1559 None,
1560 "EdDsa".parse()?,
1561 CryptographicKeyContext::OpenPgp{
1562 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
1563 version: "v4".parse()?,
1564 },
1565 )?,
1566 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1567 system_user: "system-operator".parse()?,
1568 tag: "tag1".to_string(),
1569 },
1570 FilterUserKeys::Namespaced,
1571 Vec::new(),
1572 )]
1573 #[case::systemwide_operator_filter_namespace(
1574 UserMapping::SystemNetHsmOperatorSigning {
1575 nethsm_user: "operator".parse()?,
1576 key_id: "key1".parse()?,
1577 nethsm_key_setup: SigningKeySetup::new(
1578 "Curve25519".parse()?,
1579 vec!["EdDsaSignature".parse()?],
1580 None,
1581 "EdDsa".parse()?,
1582 CryptographicKeyContext::OpenPgp{
1583 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
1584 version: "v4".parse()?,
1585 },
1586 )?,
1587 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1588 system_user: "system-operator".parse()?,
1589 tag: "tag1".to_string(),
1590 },
1591 FilterUserKeys::Namespace("ns1".parse()?),
1592 Vec::new(),
1593 )]
1594 #[case::namespace_operator_filter_namespaced(
1595 UserMapping::SystemNetHsmOperatorSigning {
1596 nethsm_user: "ns1~operator".parse()?,
1597 key_id: "key1".parse()?,
1598 nethsm_key_setup: SigningKeySetup::new(
1599 "Curve25519".parse()?,
1600 vec!["EdDsaSignature".parse()?],
1601 None,
1602 "EdDsa".parse()?,
1603 CryptographicKeyContext::OpenPgp{
1604 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
1605 version: "v4".parse()?,
1606 },
1607 )?,
1608 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1609 system_user: "system-operator".parse()?,
1610 tag: "tag1".to_string(),
1611 },
1612 FilterUserKeys::Namespaced,
1613 vec![(
1614 "ns1~operator".parse()?,
1615 "key1".parse()?,
1616 SigningKeySetup::new(
1617 "Curve25519".parse()?,
1618 vec!["EdDsaSignature".parse()?],
1619 None,
1620 "EdDsa".parse()?,
1621 CryptographicKeyContext::OpenPgp{
1622 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
1623 version: "v4".parse()?,
1624 },
1625 )?,
1626 "tag1".to_string(),
1627 )],
1628 )]
1629 #[case::namespace_operator_filter_namespace(
1630 UserMapping::SystemNetHsmOperatorSigning {
1631 nethsm_user: "ns1~operator".parse()?,
1632 key_id: "key1".parse()?,
1633 nethsm_key_setup: SigningKeySetup::new(
1634 "Curve25519".parse()?,
1635 vec!["EdDsaSignature".parse()?],
1636 None,
1637 "EdDsa".parse()?,
1638 CryptographicKeyContext::OpenPgp{
1639 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
1640 version: "v4".parse()?,
1641 },
1642 )?,
1643 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1644 system_user: "system-operator".parse()?,
1645 tag: "tag1".to_string(),
1646 },
1647 FilterUserKeys::Namespace("ns1".parse()?),
1648 vec![(
1649 "ns1~operator".parse()?,
1650 "key1".parse()?,
1651 SigningKeySetup::new(
1652 "Curve25519".parse()?,
1653 vec!["EdDsaSignature".parse()?],
1654 None,
1655 "EdDsa".parse()?,
1656 CryptographicKeyContext::OpenPgp{
1657 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
1658 version: "v4".parse()?,
1659 },
1660 )?,
1661 "tag1".to_string(),
1662 )],
1663 )]
1664 #[case::namespace_operator_filter_tag(
1665 UserMapping::SystemNetHsmOperatorSigning {
1666 nethsm_user: "ns1~operator".parse()?,
1667 key_id: "key1".parse()?,
1668 nethsm_key_setup: SigningKeySetup::new(
1669 "Curve25519".parse()?,
1670 vec!["EdDsaSignature".parse()?],
1671 None,
1672 "EdDsa".parse()?,
1673 CryptographicKeyContext::OpenPgp{
1674 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
1675 version: "v4".parse()?,
1676 },
1677 )?,
1678 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1679 system_user: "system-operator".parse()?,
1680 tag: "tag1".to_string(),
1681 },
1682 FilterUserKeys::Tag("tag1".parse()?),
1683 vec![(
1684 "ns1~operator".parse()?,
1685 "key1".parse()?,
1686 SigningKeySetup::new(
1687 "Curve25519".parse()?,
1688 vec!["EdDsaSignature".parse()?],
1689 None,
1690 "EdDsa".parse()?,
1691 CryptographicKeyContext::OpenPgp{
1692 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
1693 version: "v4".parse()?,
1694 },
1695 )?,
1696 "tag1".to_string(),
1697 )],
1698 )]
1699 #[case::namespace_operator_filter_wrong_tag(
1700 UserMapping::SystemNetHsmOperatorSigning {
1701 nethsm_user: "ns1~operator".parse()?,
1702 key_id: "key1".parse()?,
1703 nethsm_key_setup: SigningKeySetup::new(
1704 "Curve25519".parse()?,
1705 vec!["EdDsaSignature".parse()?],
1706 None,
1707 "EdDsa".parse()?,
1708 CryptographicKeyContext::OpenPgp{
1709 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
1710 version: "v4".parse()?,
1711 },
1712 )?,
1713 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1714 system_user: "system-operator".parse()?,
1715 tag: "tag2".to_string(),
1716 },
1717 FilterUserKeys::Tag("tag1".parse()?),
1718 Vec::new(),
1719 )]
1720 #[case::hermetic_system_metrics(
1721 UserMapping::HermeticSystemNetHsmMetrics {
1722 nethsm_users: NetHsmMetricsUsers::new(
1723 "metrics".parse()?,
1724 vec!["operator".parse()?],
1725 )?,
1726 system_user: "system-metrics".parse()?,
1727 },
1728 FilterUserKeys::All,
1729 Vec::new(),
1730 )]
1731 #[case::share_download(
1732 UserMapping::SystemOnlyShareDownload {
1733 system_user: "system-share".parse()?,
1734 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1735 },
1736 FilterUserKeys::All,
1737 Vec::new(),
1738 )]
1739 #[case::share_upload(
1740 UserMapping::SystemOnlyShareUpload {
1741 system_user: "system-share".parse()?,
1742 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1743 },
1744 FilterUserKeys::All,
1745 Vec::new(),
1746 )]
1747 #[case::wireguard_download(
1748 UserMapping::SystemOnlyWireGuardDownload {
1749 system_user: "system-wireguard".parse()?,
1750 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1751 },
1752 FilterUserKeys::All,
1753 Vec::new(),
1754 )]
1755 fn usermapping_get_nethsm_user_key_and_tag(
1756 #[case] mapping: UserMapping,
1757 #[case] filter: FilterUserKeys,
1758 #[case] output: Vec<(UserId, KeyId, SigningKeySetup, String)>,
1759 ) -> TestResult {
1760 assert_eq!(mapping.get_nethsm_user_key_and_tag(filter), output);
1761 Ok(())
1762 }
1763
1764 #[rstest]
1765 #[case::systemwide_admin(
1766 UserMapping::NetHsmOnlyAdmin("admin".parse()?),
1767 vec![("admin".parse()?, UserRole::Administrator)],
1768 )]
1769 #[case::namespace_admin(
1770 UserMapping::NetHsmOnlyAdmin("ns1~admin".parse()?),
1771 vec![("ns1~admin".parse()?, UserRole::Administrator)],
1772 )]
1773 #[case::metrics(
1774 UserMapping::SystemNetHsmMetrics {
1775 nethsm_users: NetHsmMetricsUsers::new(
1776 SystemWideUserId::new("metrics".to_string())?,
1777 vec![
1778 UserId::new("operator".to_string())?,
1779 ],
1780 )?,
1781 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1782 system_user: "system-metrics".parse()?,
1783 },
1784 vec![("metrics".parse()?, UserRole::Metrics), ("operator".parse()?, UserRole::Operator)],
1785 )]
1786 #[case::backup(
1787 UserMapping::SystemNetHsmBackup {
1788 nethsm_user: "backup".parse()?,
1789 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1790 system_user: "system-backup".parse()?,
1791 },
1792 vec![("backup".parse()?, UserRole::Backup)],
1793 )]
1794 #[case::systemwide_operator(
1795 UserMapping::SystemNetHsmOperatorSigning {
1796 nethsm_user: "operator".parse()?,
1797 key_id: "key1".parse()?,
1798 nethsm_key_setup: SigningKeySetup::new(
1799 "Curve25519".parse()?,
1800 vec!["EdDsaSignature".parse()?],
1801 None,
1802 "EdDsa".parse()?,
1803 CryptographicKeyContext::OpenPgp{
1804 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
1805 version: "v4".parse()?,
1806 },
1807 )?,
1808 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1809 system_user: "system-operator".parse()?,
1810 tag: "tag1".to_string(),
1811 },
1812 vec![(
1813 "operator".parse()?,
1814 UserRole::Operator,
1815 )],
1816 )]
1817 #[case::namespace_operator(
1818 UserMapping::SystemNetHsmOperatorSigning {
1819 nethsm_user: "ns1~operator".parse()?,
1820 key_id: "key1".parse()?,
1821 nethsm_key_setup: SigningKeySetup::new(
1822 "Curve25519".parse()?,
1823 vec!["EdDsaSignature".parse()?],
1824 None,
1825 "EdDsa".parse()?,
1826 CryptographicKeyContext::OpenPgp{
1827 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
1828 version: "v4".parse()?,
1829 },
1830 )?,
1831 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1832 system_user: "system-operator".parse()?,
1833 tag: "tag1".to_string(),
1834 },
1835 vec![(
1836 "ns1~operator".parse()?,
1837 UserRole::Operator,
1838 )],
1839 )]
1840 #[case::hermetic_system_metrics(
1841 UserMapping::HermeticSystemNetHsmMetrics {
1842 nethsm_users: NetHsmMetricsUsers::new(
1843 "metrics".parse()?,
1844 vec!["operator".parse()?],
1845 )?,
1846 system_user: "system-metrics".parse()?,
1847 },
1848 vec![
1849 ("metrics".parse()?, UserRole::Metrics),
1850 ("operator".parse()?, UserRole::Operator),
1851 ],
1852 )]
1853 #[case::share_download(
1854 UserMapping::SystemOnlyShareDownload {
1855 system_user: "system-share".parse()?,
1856 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1857 },
1858 Vec::new(),
1859 )]
1860 #[case::share_upload(
1861 UserMapping::SystemOnlyShareUpload {
1862 system_user: "system-share".parse()?,
1863 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1864 },
1865 Vec::new(),
1866 )]
1867 #[case::wireguard_download(
1868 UserMapping::SystemOnlyWireGuardDownload {
1869 system_user: "system-wireguard".parse()?,
1870 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
1871 },
1872 Vec::new(),
1873 )]
1874 fn usermapping_get_nethsm_users_and_roles(
1875 #[case] mapping: UserMapping,
1876 #[case] output: Vec<(UserId, UserRole)>,
1877 ) -> TestResult {
1878 assert_eq!(mapping.get_nethsm_users_and_roles(), output);
1879 Ok(())
1880 }
1881
1882 #[test]
1885 fn check_secrets_file_succeeds() -> TestResult {
1886 setup_logging(LevelFilter::Debug)?;
1887
1888 let temp_file = NamedTempFile::new()?;
1889 let path = temp_file.path();
1890 set_permissions(path, Permissions::from_mode(SECRET_FILE_MODE))?;
1891 debug!(
1892 "Created {path:?} with mode {:o}",
1893 path.metadata()?.permissions().mode()
1894 );
1895
1896 check_secrets_file(path)?;
1897
1898 Ok(())
1899 }
1900
1901 #[test]
1903 fn check_secrets_file_fails_on_missing_file() -> TestResult {
1904 setup_logging(LevelFilter::Debug)?;
1905
1906 let temp_file = NamedTempFile::new()?;
1907 let path = temp_file.path().to_path_buf();
1908 temp_file.close()?;
1909
1910 if check_secrets_file(&path).is_ok() {
1911 panic!("The path {path:?} is missing and should not have passed as a secrets file.");
1912 }
1913
1914 Ok(())
1915 }
1916
1917 #[test]
1919 fn check_secrets_file_fails_on_dir() -> TestResult {
1920 setup_logging(LevelFilter::Debug)?;
1921
1922 let temp_file = TempDir::new()?;
1923 let path = temp_file.path();
1924 debug!(
1925 "Created {path:?} with mode {:o}",
1926 path.metadata()?.permissions().mode()
1927 );
1928
1929 if check_secrets_file(path).is_ok() {
1930 panic!("The dir {path:?} should not have passed as a secrets file.");
1931 }
1932
1933 Ok(())
1934 }
1935
1936 #[test]
1938 fn check_secrets_file_fails_on_invalid_permissions() -> TestResult {
1939 setup_logging(LevelFilter::Debug)?;
1940
1941 let temp_file = NamedTempFile::new()?;
1942 let path = temp_file.path();
1943 set_permissions(path, Permissions::from_mode(0o100644))?;
1944 debug!(
1945 "Created {path:?} with mode {:o}",
1946 path.metadata()?.permissions().mode()
1947 );
1948
1949 if check_secrets_file(path).is_ok() {
1950 panic!("The file at {path:?} should not have passed as a secrets file.");
1951 }
1952
1953 Ok(())
1954 }
1955}