1use std::{
4 fs::{File, Permissions, create_dir_all, read_dir, set_permissions, write},
5 io::Write,
6 os::{linux::fs::MetadataExt, unix::fs::PermissionsExt},
7 path::{Path, PathBuf},
8 process::{Child, Command},
9 str::FromStr,
10 thread,
11 time,
12};
13
14use change_user_run::{create_users, get_command};
15use log::debug;
16#[cfg(feature = "nethsm")]
17use nethsm::{FullCredentials, UserId};
18#[cfg(feature = "nethsm")]
19use rand::{Rng, distributions::Alphanumeric, thread_rng};
20use signstar_common::system_user::get_home_base_dir_path;
21#[cfg(feature = "nethsm")]
22use signstar_crypto::AdministrativeSecretHandling;
23#[cfg(feature = "nethsm")]
24use signstar_crypto::passphrase::Passphrase;
25use tempfile::NamedTempFile;
26
27use crate::config::{Config, ConfigSystemUserIds, MappingAuthorizedKeyEntry};
28#[cfg(feature = "nethsm")]
29use crate::{admin_credentials::AdminCredentials, nethsm::NetHsmAdminCredentials};
30#[cfg(any(feature = "nethsm", feature = "yubihsm2"))]
32pub mod impl_any {
33 use super::*;
34 use crate::config::UserBackendConnectionFilter;
35
36 impl SystemUserConfig {
37 pub fn apply(&self, config: &Config) -> Result<(), crate::Error> {
43 if self.create_secrets {
44 let user_backend_connections =
45 config.user_backend_connections(UserBackendConnectionFilter::NonAdmin);
46
47 for user_backend_connection in user_backend_connections {
48 user_backend_connection.create_non_admin_backend_user_secrets()?;
49 }
50 }
51
52 if self.create_ssh_authorized_keys {
53 let user_backend_connections =
54 config.user_backend_connections(UserBackendConnectionFilter::NonAdmin);
55 for user_backend_connection in user_backend_connections {
56 user_backend_connection.write_authorized_key_entry()?;
57 }
58
59 for mapping in config.system().mappings() {
60 mapping.write_authorized_key_entry()?;
61 }
62 }
63
64 Ok(())
65 }
66 }
67}
68
69#[cfg(not(any(feature = "nethsm", feature = "yubihsm2")))]
71mod impl_none {
72 use super::*;
73
74 impl SystemUserConfig {
75 pub fn apply(&self, config: &Config) -> Result<(), crate::Error> {
85 if self.create_ssh_authorized_keys {
86 for mapping in config.system().mappings() {
87 mapping.write_authorized_key_entry()?;
88 }
89 }
90
91 Ok(())
92 }
93 }
94}
95
96const NO_BACKEND_ADMIN_PLAINTEXT_NON_ADMIN_PLAINTEXT: &[u8] =
101 include_bytes!("../../fixtures/config/no_backend/admin-plaintext-non-admin-plaintext.yaml");
102
103const NO_BACKEND_ADMIN_PLAINTEXT_NON_ADMIN_SYSTEMD_CREDS: &[u8] =
108 include_bytes!("../../fixtures/config/no_backend/admin-plaintext-non-admin-systemd-creds.yaml");
109
110const NO_BACKEND_ADMIN_SYSTEMD_CREDS_NON_ADMIN_PLAINTEXT: &[u8] =
115 include_bytes!("../../fixtures/config/no_backend/admin-systemd-creds-non-admin-plaintext.yaml");
116
117const NO_BACKEND_ADMIN_SYSTEMD_CREDS_NON_ADMIN_SYSTEMD_CREDS: &[u8] = include_bytes!(
122 "../../fixtures/config/no_backend/admin-systemd-creds-non-admin-systemd-creds.yaml"
123);
124
125const NO_BACKEND_ADMIN_SSS_NON_ADMIN_PLAINTEXT: &[u8] =
130 include_bytes!("../../fixtures/config/no_backend/admin-sss-non-admin-plaintext.yaml");
131
132const NO_BACKEND_ADMIN_SSS_NON_ADMIN_SYSTEMD_CREDS: &[u8] =
137 include_bytes!("../../fixtures/config/no_backend/admin-sss-non-admin-systemd-creds.yaml");
138
139const ONLY_NETHSM_ADMIN_PLAINTEXT_NON_ADMIN_PLAINTEXT: &[u8] =
144 include_bytes!("../../fixtures/config/nethsm_backend/admin-plaintext-non-admin-plaintext.yaml");
145
146const ONLY_NETHSM_ADMIN_PLAINTEXT_NON_ADMIN_SYSTEMD_CREDS: &[u8] = include_bytes!(
151 "../../fixtures/config/nethsm_backend/admin-plaintext-non-admin-systemd-creds.yaml"
152);
153
154const ONLY_NETHSM_ADMIN_SYSTEMD_CREDS_NON_ADMIN_PLAINTEXT: &[u8] = include_bytes!(
159 "../../fixtures/config/nethsm_backend/admin-systemd-creds-non-admin-plaintext.yaml"
160);
161
162const ONLY_NETHSM_ADMIN_SYSTEMD_CREDS_NON_ADMIN_SYSTEMD_CREDS: &[u8] = include_bytes!(
167 "../../fixtures/config/nethsm_backend/admin-systemd-creds-non-admin-systemd-creds.yaml"
168);
169
170const ONLY_NETHSM_ADMIN_SSS_NON_ADMIN_PLAINTEXT: &[u8] =
175 include_bytes!("../../fixtures/config/nethsm_backend/admin-sss-non-admin-plaintext.yaml");
176
177const ONLY_NETHSM_ADMIN_SSS_NON_ADMIN_SYSTEMD_CREDS: &[u8] =
182 include_bytes!("../../fixtures/config/nethsm_backend/admin-sss-non-admin-systemd-creds.yaml");
183
184const ONLY_YUBIHSM2_ADMIN_PLAINTEXT_NON_ADMIN_PLAINTEXT: &[u8] = include_bytes!(
189 "../../fixtures/config/yubihsm2_backend/admin-plaintext-non-admin-plaintext.yaml"
190);
191
192const ONLY_YUBIHSM2_ADMIN_PLAINTEXT_NON_ADMIN_SYSTEMD_CREDS: &[u8] = include_bytes!(
197 "../../fixtures/config/yubihsm2_backend/admin-plaintext-non-admin-systemd-creds.yaml"
198);
199
200const ONLY_YUBIHSM2_ADMIN_SYSTEMD_CREDS_NON_ADMIN_PLAINTEXT: &[u8] = include_bytes!(
205 "../../fixtures/config/yubihsm2_backend/admin-systemd-creds-non-admin-plaintext.yaml"
206);
207
208const ONLY_YUBIHSM2_ADMIN_SYSTEMD_CREDS_NON_ADMIN_SYSTEMD_CREDS: &[u8] = include_bytes!(
213 "../../fixtures/config/yubihsm2_backend/admin-systemd-creds-non-admin-systemd-creds.yaml"
214);
215
216const ONLY_YUBIHSM2_ADMIN_SSS_NON_ADMIN_PLAINTEXT: &[u8] =
221 include_bytes!("../../fixtures/config/yubihsm2_backend/admin-sss-non-admin-plaintext.yaml");
222
223const ONLY_YUBIHSM2_ADMIN_SSS_NON_ADMIN_SYSTEMD_CREDS: &[u8] =
228 include_bytes!("../../fixtures/config/yubihsm2_backend/admin-sss-non-admin-systemd-creds.yaml");
229
230const ONLY_YUBIHSM2_MOCKHSM_ADMIN_PLAINTEXT_NON_ADMIN_PLAINTEXT: &[u8] = include_bytes!(
235 "../../fixtures/config/yubihsm2_mockhsm_backend/admin-plaintext-non-admin-plaintext.yaml"
236);
237
238const ONLY_YUBIHSM2_MOCKHSM_ADMIN_PLAINTEXT_NON_ADMIN_SYSTEMD_CREDS: &[u8] = include_bytes!(
243 "../../fixtures/config/yubihsm2_mockhsm_backend/admin-plaintext-non-admin-systemd-creds.yaml"
244);
245
246const ONLY_YUBIHSM2_MOCKHSM_ADMIN_SYSTEMD_CREDS_NON_ADMIN_PLAINTEXT: &[u8] = include_bytes!(
251 "../../fixtures/config/yubihsm2_mockhsm_backend/admin-systemd-creds-non-admin-plaintext.yaml"
252);
253
254const ONLY_YUBIHSM2_MOCKHSM_ADMIN_SYSTEMD_CREDS_NON_ADMIN_SYSTEMD_CREDS: &[u8] = include_bytes!(
259 "../../fixtures/config/yubihsm2_mockhsm_backend/admin-systemd-creds-non-admin-systemd-creds.yaml"
260);
261
262const ONLY_YUBIHSM2_MOCKHSM_ADMIN_SSS_NON_ADMIN_PLAINTEXT: &[u8] = include_bytes!(
267 "../../fixtures/config/yubihsm2_mockhsm_backend/admin-sss-non-admin-plaintext.yaml"
268);
269
270const ONLY_YUBIHSM2_MOCKHSM_ADMIN_SSS_NON_ADMIN_SYSTEMD_CREDS: &[u8] = include_bytes!(
275 "../../fixtures/config/yubihsm2_mockhsm_backend/admin-sss-non-admin-systemd-creds.yaml"
276);
277
278const ALL_BACKENDS_ADMIN_PLAINTEXT_NON_ADMIN_PLAINTEXT: &[u8] =
283 include_bytes!("../../fixtures/config/all_backends/admin-plaintext-non-admin-plaintext.yaml");
284
285const ALL_BACKENDS_ADMIN_PLAINTEXT_NON_ADMIN_SYSTEMD_CREDS: &[u8] = include_bytes!(
290 "../../fixtures/config/all_backends/admin-plaintext-non-admin-systemd-creds.yaml"
291);
292
293const ALL_BACKENDS_ADMIN_SYSTEMD_CREDS_NON_ADMIN_PLAINTEXT: &[u8] = include_bytes!(
298 "../../fixtures/config/all_backends/admin-systemd-creds-non-admin-plaintext.yaml"
299);
300
301const ALL_BACKENDS_ADMIN_SYSTEMD_CREDS_NON_ADMIN_SYSTEMD_CREDS: &[u8] = include_bytes!(
306 "../../fixtures/config/all_backends/admin-systemd-creds-non-admin-systemd-creds.yaml"
307);
308
309const ALL_BACKENDS_ADMIN_SSS_NON_ADMIN_PLAINTEXT: &[u8] =
314 include_bytes!("../../fixtures/config/all_backends/admin-sss-non-admin-plaintext.yaml");
315
316const ALL_BACKENDS_ADMIN_SSS_NON_ADMIN_SYSTEMD_CREDS: &[u8] =
321 include_bytes!("../../fixtures/config/all_backends/admin-sss-non-admin-systemd-creds.yaml");
322
323#[derive(Debug, thiserror::Error)]
325pub enum Error {
326 #[error("Timeout of {timeout}ms reached while {context}")]
328 Timeout {
329 timeout: u64,
331
332 context: String,
334 },
335}
336
337#[derive(Clone, Copy, Debug, Default)]
339pub enum ConfigFileLocation {
340 Run,
342
343 Etc,
345
346 #[default]
348 UsrShare,
349}
350
351impl ConfigFileLocation {
352 pub fn to_parent_dir_path(&self) -> PathBuf {
354 match self {
355 ConfigFileLocation::Run => PathBuf::from(Config::RUN_OVERRIDE_CONFIG_DIR),
356 ConfigFileLocation::Etc => PathBuf::from(Config::ETC_OVERRIDE_CONFIG_DIR),
357 ConfigFileLocation::UsrShare => PathBuf::from(Config::DEFAULT_CONFIG_DIR),
358 }
359 }
360}
361
362impl From<ConfigFileLocation> for PathBuf {
363 fn from(value: ConfigFileLocation) -> Self {
364 value
365 .to_parent_dir_path()
366 .join(format!("{}.yaml", Config::CONFIG_NAME))
367 }
368}
369
370#[derive(Clone, Copy, Debug, Default)]
372pub enum ConfigFileVariant {
373 NoBackendAdminPlaintextNonAdminPlaintext,
378
379 NoBackendAdminPlaintextNonAdminSystemdCreds,
384
385 NoBackendAdminSystemdCredsNonAdminPlaintext,
390
391 NoBackendAdminSystemdCredsNonAdminSystemdCreds,
396
397 NoBackendAdminSssNonAdminPlaintext,
402
403 NoBackendAdminSssNonAdminSystemdCreds,
408
409 OnlyNetHsmBackendAdminPlaintextNonAdminPlaintext,
414
415 OnlyNetHsmBackendAdminPlaintextNonAdminSystemdCreds,
420
421 OnlyNetHsmBackendAdminSystemdCredsNonAdminPlaintext,
426
427 OnlyNetHsmBackendAdminSystemdCredsNonAdminSystemdCreds,
432
433 OnlyNetHsmBackendAdminSssNonAdminPlaintext,
438
439 OnlyNetHsmBackendAdminSssNonAdminSystemdCreds,
444
445 OnlyYubiHsm2BackendAdminPlaintextNonAdminPlaintext,
450
451 OnlyYubiHsm2BackendAdminPlaintextNonAdminSystemdCreds,
456
457 OnlyYubiHsm2BackendAdminSystemdCredsNonAdminPlaintext,
462
463 OnlyYubiHsm2BackendAdminSystemdCredsNonAdminSystemdCreds,
468
469 OnlyYubiHsm2BackendAdminSssNonAdminPlaintext,
474
475 OnlyYubiHsm2BackendAdminSssNonAdminSystemdCreds,
480
481 OnlyYubiHsm2MockHsmBackendAdminPlaintextNonAdminPlaintext,
486
487 OnlyYubiHsm2MockHsmBackendAdminPlaintextNonAdminSystemdCreds,
492
493 OnlyYubiHsm2MockHsmBackendAdminSystemdCredsNonAdminPlaintext,
498
499 OnlyYubiHsm2MockHsmBackendAdminSystemdCredsNonAdminSystemdCreds,
504
505 OnlyYubiHsm2MockHsmBackendAdminSssNonAdminPlaintext,
510
511 OnlyYubiHsm2MockHsmBackendAdminSssNonAdminSystemdCreds,
516
517 AllBackendsAdminPlaintextNonAdminPlaintext,
522
523 AllBackendsAdminPlaintextNonAdminSystemdCreds,
528
529 AllBackendsAdminSystemdCredsNonAdminPlaintext,
534
535 AllBackendsAdminSystemdCredsNonAdminSystemdCreds,
540
541 AllBackendsAdminSssNonAdminPlaintext,
546
547 #[default]
552 AllBackendsAdminSssNonAdminSystemdCreds,
553}
554
555impl ConfigFileVariant {
556 pub fn as_config_bytes(&self) -> &[u8] {
558 match self {
559 ConfigFileVariant::NoBackendAdminPlaintextNonAdminPlaintext => {
560 NO_BACKEND_ADMIN_PLAINTEXT_NON_ADMIN_PLAINTEXT
561 }
562 ConfigFileVariant::NoBackendAdminPlaintextNonAdminSystemdCreds => {
563 NO_BACKEND_ADMIN_PLAINTEXT_NON_ADMIN_SYSTEMD_CREDS
564 }
565 ConfigFileVariant::NoBackendAdminSystemdCredsNonAdminPlaintext => {
566 NO_BACKEND_ADMIN_SYSTEMD_CREDS_NON_ADMIN_PLAINTEXT
567 }
568 ConfigFileVariant::NoBackendAdminSystemdCredsNonAdminSystemdCreds => {
569 NO_BACKEND_ADMIN_SYSTEMD_CREDS_NON_ADMIN_SYSTEMD_CREDS
570 }
571 ConfigFileVariant::NoBackendAdminSssNonAdminPlaintext => {
572 NO_BACKEND_ADMIN_SSS_NON_ADMIN_PLAINTEXT
573 }
574 ConfigFileVariant::NoBackendAdminSssNonAdminSystemdCreds => {
575 NO_BACKEND_ADMIN_SSS_NON_ADMIN_SYSTEMD_CREDS
576 }
577 ConfigFileVariant::OnlyNetHsmBackendAdminPlaintextNonAdminPlaintext => {
578 ONLY_NETHSM_ADMIN_PLAINTEXT_NON_ADMIN_PLAINTEXT
579 }
580 ConfigFileVariant::OnlyNetHsmBackendAdminPlaintextNonAdminSystemdCreds => {
581 ONLY_NETHSM_ADMIN_PLAINTEXT_NON_ADMIN_SYSTEMD_CREDS
582 }
583 ConfigFileVariant::OnlyNetHsmBackendAdminSystemdCredsNonAdminPlaintext => {
584 ONLY_NETHSM_ADMIN_SYSTEMD_CREDS_NON_ADMIN_PLAINTEXT
585 }
586 ConfigFileVariant::OnlyNetHsmBackendAdminSystemdCredsNonAdminSystemdCreds => {
587 ONLY_NETHSM_ADMIN_SYSTEMD_CREDS_NON_ADMIN_SYSTEMD_CREDS
588 }
589 ConfigFileVariant::OnlyNetHsmBackendAdminSssNonAdminPlaintext => {
590 ONLY_NETHSM_ADMIN_SSS_NON_ADMIN_PLAINTEXT
591 }
592 ConfigFileVariant::OnlyNetHsmBackendAdminSssNonAdminSystemdCreds => {
593 ONLY_NETHSM_ADMIN_SSS_NON_ADMIN_SYSTEMD_CREDS
594 }
595 ConfigFileVariant::OnlyYubiHsm2BackendAdminPlaintextNonAdminPlaintext => {
596 ONLY_YUBIHSM2_ADMIN_PLAINTEXT_NON_ADMIN_PLAINTEXT
597 }
598 ConfigFileVariant::OnlyYubiHsm2BackendAdminPlaintextNonAdminSystemdCreds => {
599 ONLY_YUBIHSM2_ADMIN_PLAINTEXT_NON_ADMIN_SYSTEMD_CREDS
600 }
601 ConfigFileVariant::OnlyYubiHsm2BackendAdminSystemdCredsNonAdminPlaintext => {
602 ONLY_YUBIHSM2_ADMIN_SYSTEMD_CREDS_NON_ADMIN_PLAINTEXT
603 }
604 ConfigFileVariant::OnlyYubiHsm2BackendAdminSystemdCredsNonAdminSystemdCreds => {
605 ONLY_YUBIHSM2_ADMIN_SYSTEMD_CREDS_NON_ADMIN_SYSTEMD_CREDS
606 }
607 ConfigFileVariant::OnlyYubiHsm2BackendAdminSssNonAdminPlaintext => {
608 ONLY_YUBIHSM2_ADMIN_SSS_NON_ADMIN_PLAINTEXT
609 }
610 ConfigFileVariant::OnlyYubiHsm2BackendAdminSssNonAdminSystemdCreds => {
611 ONLY_YUBIHSM2_ADMIN_SSS_NON_ADMIN_SYSTEMD_CREDS
612 }
613 ConfigFileVariant::OnlyYubiHsm2MockHsmBackendAdminPlaintextNonAdminPlaintext => {
614 ONLY_YUBIHSM2_MOCKHSM_ADMIN_PLAINTEXT_NON_ADMIN_PLAINTEXT
615 }
616 ConfigFileVariant::OnlyYubiHsm2MockHsmBackendAdminPlaintextNonAdminSystemdCreds => {
617 ONLY_YUBIHSM2_MOCKHSM_ADMIN_PLAINTEXT_NON_ADMIN_SYSTEMD_CREDS
618 }
619 ConfigFileVariant::OnlyYubiHsm2MockHsmBackendAdminSystemdCredsNonAdminPlaintext => {
620 ONLY_YUBIHSM2_MOCKHSM_ADMIN_SYSTEMD_CREDS_NON_ADMIN_PLAINTEXT
621 }
622 ConfigFileVariant::OnlyYubiHsm2MockHsmBackendAdminSystemdCredsNonAdminSystemdCreds => {
623 ONLY_YUBIHSM2_MOCKHSM_ADMIN_SYSTEMD_CREDS_NON_ADMIN_SYSTEMD_CREDS
624 }
625 ConfigFileVariant::OnlyYubiHsm2MockHsmBackendAdminSssNonAdminPlaintext => {
626 ONLY_YUBIHSM2_MOCKHSM_ADMIN_SSS_NON_ADMIN_PLAINTEXT
627 }
628 ConfigFileVariant::OnlyYubiHsm2MockHsmBackendAdminSssNonAdminSystemdCreds => {
629 ONLY_YUBIHSM2_MOCKHSM_ADMIN_SSS_NON_ADMIN_SYSTEMD_CREDS
630 }
631 ConfigFileVariant::AllBackendsAdminPlaintextNonAdminPlaintext => {
632 ALL_BACKENDS_ADMIN_PLAINTEXT_NON_ADMIN_PLAINTEXT
633 }
634 ConfigFileVariant::AllBackendsAdminPlaintextNonAdminSystemdCreds => {
635 ALL_BACKENDS_ADMIN_PLAINTEXT_NON_ADMIN_SYSTEMD_CREDS
636 }
637 ConfigFileVariant::AllBackendsAdminSystemdCredsNonAdminPlaintext => {
638 ALL_BACKENDS_ADMIN_SYSTEMD_CREDS_NON_ADMIN_PLAINTEXT
639 }
640 ConfigFileVariant::AllBackendsAdminSystemdCredsNonAdminSystemdCreds => {
641 ALL_BACKENDS_ADMIN_SYSTEMD_CREDS_NON_ADMIN_SYSTEMD_CREDS
642 }
643 ConfigFileVariant::AllBackendsAdminSssNonAdminPlaintext => {
644 ALL_BACKENDS_ADMIN_SSS_NON_ADMIN_PLAINTEXT
645 }
646 ConfigFileVariant::AllBackendsAdminSssNonAdminSystemdCreds => {
647 ALL_BACKENDS_ADMIN_SSS_NON_ADMIN_SYSTEMD_CREDS
648 }
649 }
650 }
651
652 pub fn to_config(&self) -> Result<Config, crate::Error> {
661 Config::from_str(
662 &String::from_utf8(self.as_config_bytes().to_vec()).map_err(|source| {
663 crate::Error::Utf8String {
664 path: PathBuf::from("/dev/null"),
665 context: "creating a Signstar config object from config fixture bytes"
666 .to_string(),
667 source,
668 }
669 })?,
670 )
671 }
672}
673
674#[derive(Clone, Copy, Debug, Default)]
676pub struct SystemUserConfig {
677 #[cfg(any(feature = "nethsm", feature = "yubihsm2"))]
679 pub create_secrets: bool,
680
681 pub create_ssh_authorized_keys: bool,
683}
684
685#[derive(Clone, Copy, Debug, Default)]
687pub struct ConfigFileConfig {
688 pub location: Option<ConfigFileLocation>,
692
693 pub variant: ConfigFileVariant,
695
696 pub system_user_config: Option<SystemUserConfig>,
702}
703
704fn create_config(
717 location: ConfigFileLocation,
718 variant: ConfigFileVariant,
719) -> Result<(), crate::Error> {
720 create_dir_all(location.to_parent_dir_path()).map_err(|source| crate::Error::IoPath {
721 path: location.to_parent_dir_path(),
722 context: "creating the parent directory for the Signstar config",
723 source,
724 })?;
725 let path = PathBuf::from(location);
726
727 let mut file = File::create(&path).map_err(|source| crate::Error::IoPath {
728 path: path.clone(),
729 context: "creating a Signstar configuration file",
730 source,
731 })?;
732 let config_bytes = variant.as_config_bytes();
733 file.write_all(config_bytes)
734 .map_err(|source| crate::Error::IoPath {
735 path,
736 context: "writing data to a Signstar configuration file",
737 source,
738 })?;
739
740 Ok(())
741}
742
743fn create_unix_users_and_homes(config: &Config) -> Result<(), crate::Error> {
749 let users = config
750 .system_user_ids()
751 .iter()
752 .cloned()
753 .map(|id| id.as_ref())
754 .collect::<Vec<_>>();
755 Ok(create_users(&users, Some(&get_home_base_dir_path()), None)?)
756}
757
758#[derive(Clone, Copy, Debug)]
760pub struct SystemPrepareConfig {
761 pub machine_id: bool,
763
764 pub credentials_socket: bool,
766
767 pub signstar_config: ConfigFileConfig,
769}
770
771impl SystemPrepareConfig {
772 pub fn apply(&self) -> Result<Option<BackgroundProcess>, crate::Error> {
788 if self.machine_id {
789 write_machine_id()?;
790 }
791
792 let background_process = if self.credentials_socket {
793 Some(start_credentials_socket()?)
794 } else {
795 None
796 };
797
798 if let Some(config_file_location) = self.signstar_config.location {
799 create_config(config_file_location, self.signstar_config.variant)?;
800
801 if let Some(system_user_config) = self.signstar_config.system_user_config {
802 let config = Config::from_str(&String::from_utf8_lossy(
803 self.signstar_config.variant.as_config_bytes(),
804 ))?;
805 create_unix_users_and_homes(&config)?;
806 system_user_config.apply(&config)?;
807 }
808 }
809
810 Ok(background_process)
811 }
812}
813
814impl Default for SystemPrepareConfig {
815 fn default() -> Self {
816 Self {
817 machine_id: true,
818 credentials_socket: true,
819 signstar_config: ConfigFileConfig::default(),
820 }
821 }
822}
823
824pub fn list_files_in_dir(path: impl AsRef<Path>) -> Result<(), crate::Error> {
826 let path = path.as_ref();
827 let entries = read_dir(path).map_err(|source| crate::Error::IoPath {
828 path: path.to_path_buf(),
829 context: "reading its children",
830 source,
831 })?;
832
833 for entry in entries {
834 let entry = entry.map_err(|source| crate::Error::IoPath {
835 path: path.to_path_buf(),
836 context: "getting an entry below it",
837 source,
838 })?;
839 let meta = entry.metadata().map_err(|source| crate::Error::IoPath {
840 path: path.to_path_buf(),
841 context: "getting metadata",
842 source,
843 })?;
844
845 debug!(
846 "{} {}/{} {entry:?}",
847 meta.permissions().mode(),
848 meta.st_uid(),
849 meta.st_gid()
850 );
851
852 if meta.is_dir() {
853 list_files_in_dir(entry.path())?;
854 }
855 }
856
857 Ok(())
858}
859
860pub fn get_tmp_config(data: &[u8]) -> Result<NamedTempFile, crate::Error> {
862 let tmp_config = NamedTempFile::new().map_err(|source| crate::Error::Io {
863 context: "creating a temporary configuration file".to_string(),
864 source,
865 })?;
866 write(&tmp_config, data).map_err(|source| crate::Error::IoPath {
867 path: tmp_config.path().to_path_buf(),
868 context: "writing full signstar configuration to temporary file",
869 source,
870 })?;
871 Ok(tmp_config)
872}
873
874pub fn write_machine_id() -> Result<(), crate::Error> {
883 debug!("Write dummy /etc/machine-id, required for systemd-creds");
884 let machine_id = PathBuf::from("/etc/machine-id");
885 std::fs::write(&machine_id, "d3b07384d113edec49eaa6238ad5ff00").map_err(|source| {
886 crate::Error::IoPath {
887 path: machine_id.to_path_buf(),
888 context: "writing machine-id",
889 source,
890 }
891 })?;
892
893 let metadata = machine_id
894 .metadata()
895 .map_err(|source| crate::Error::IoPath {
896 path: machine_id,
897 context: "getting metadata of file",
898 source,
899 })?;
900 debug!(
901 "/etc/machine-id\nmode: {}\nuid: {}\ngid: {}",
902 metadata.permissions().mode(),
903 metadata.st_uid(),
904 metadata.st_gid()
905 );
906 Ok(())
907}
908
909#[derive(Debug)]
914pub struct BackgroundProcess {
915 child: Child,
916 command: String,
917}
918
919impl BackgroundProcess {
920 pub fn kill(&mut self) -> Result<(), crate::Error> {
926 self.child.kill().map_err(|source| crate::Error::Io {
927 context: format!("killing process of command \"{}\"", self.command),
928 source,
929 })
930 }
931}
932
933impl Drop for BackgroundProcess {
934 fn drop(&mut self) {
936 if let Err(error) = self.child.kill() {
937 log::debug!(
938 "Unable to kill background process of command {}:\n{error}",
939 self.command
940 )
941 }
942 }
943}
944
945pub fn start_credentials_socket() -> Result<BackgroundProcess, crate::Error> {
958 let systemd_run_path = PathBuf::from("/run/systemd");
959 let socket_path = PathBuf::from("/run/systemd/io.systemd.Credentials");
960 create_dir_all(&systemd_run_path).map_err(|source| crate::Error::IoPath {
961 path: systemd_run_path.clone(),
962 context: "creating the directory",
963 source,
964 })?;
965
966 let mut command = Command::new(get_command("systemd-socket-activate")?);
968 let command = command.args([
969 "--listen",
970 "/run/systemd/io.systemd.Credentials",
971 "--accept",
972 "--fdname=varlink",
973 "systemd-creds",
974 ]);
975 let child = command.spawn().map_err(|source| crate::Error::IoPath {
976 path: PathBuf::from("/run/systemd/io.systemd.Credentials"),
977 context: "creating a socket using systemd-socket-activate",
978 source,
979 })?;
980
981 let timeout = 10000;
983 let step = 100;
984 let mut elapsed = 0;
985 let mut permissions_set = false;
986 while elapsed < timeout {
987 if socket_path.exists() {
988 debug!("Found {socket_path:?}");
989 set_permissions(socket_path.as_path(), Permissions::from_mode(0o666)).map_err(
990 |source| crate::Error::IoPath {
991 path: socket_path.to_path_buf(),
992 context: "applying permissions",
993 source,
994 },
995 )?;
996 permissions_set = true;
997 break;
998 } else {
999 thread::sleep(time::Duration::from_millis(step));
1000 elapsed += step;
1001 }
1002 }
1003 if !permissions_set {
1004 return Err(Error::Timeout {
1005 timeout,
1006 context: format!("waiting for {socket_path:?}"),
1007 }
1008 .into());
1009 }
1010
1011 Ok(BackgroundProcess {
1012 child,
1013 command: format!("{command:?}"),
1014 })
1015}
1016
1017#[cfg(feature = "nethsm")]
1028pub fn nethsm_admin_credentials(
1029 config_data: &[u8],
1030) -> Result<NetHsmAdminCredentials, crate::Error> {
1031 let config_file = get_tmp_config(config_data)?;
1032 NetHsmAdminCredentials::load_from_file(
1033 config_file.path(),
1034 AdministrativeSecretHandling::Plaintext,
1035 )
1036}
1037
1038#[cfg(feature = "nethsm")]
1043pub fn create_full_credentials(users: &[UserId]) -> Vec<FullCredentials> {
1044 fn create_passphrase() -> String {
1046 thread_rng()
1047 .sample_iter(&Alphanumeric)
1048 .take(30)
1049 .map(char::from)
1050 .collect()
1051 }
1052
1053 users
1054 .iter()
1055 .map(|user| FullCredentials::new(user.clone(), Passphrase::new(create_passphrase())))
1056 .collect()
1057}