diff --git a/.lycheeignore b/.lycheeignore index 26f8b06f527a14cb0f54ddd012ffa986fad0aebd..75cbd1325b4cdc47db91dd3b78d4df018fca3dde 100644 --- a/.lycheeignore +++ b/.lycheeignore @@ -3,3 +3,7 @@ # an URL fragment that's used in format! macro # see: https://github.com/lycheeverse/lychee/issues/1492 https://raw.githubusercontent.com/Nitrokey/nethsm-sdk-py/main/tests/%7B%7D + +# URLs that only become available after release of a component +https://signstar.archlinux.page/rustdoc/signstar_common/ +https://docs.rs/signstar_common/latest/signstar_common/ diff --git a/Cargo.lock b/Cargo.lock index fa094f4d97c2347f0641f80d00c8cfd368a6575c..d9cdcaef775aa0b025c6eab01f31406b06c0313a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3133,6 +3133,14 @@ dependencies = [ "rand_core", ] +[[package]] +name = "signstar-common" +version = "0.1.0" +dependencies = [ + "nethsm", + "thiserror 2.0.11", +] + [[package]] name = "signstar-configure-build" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index 54407d2a65690cf4cdba9a5135730c73a6c73411..6b970a364d09697fa3fdafbde79925457794a9fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "nethsm-config", "nethsm-tests", "signstar-configure-build", + "signstar-common", "signstar-request-signature", ] @@ -31,6 +32,7 @@ serde = { version = "1.0.215", features = ["derive"] } # Then adjust the tests in signstar-request-signature/src/lib.rs to additionally test # inputs that reference the "old_sha2". sha2 = "0.11.0-pre.4" +signstar-common = { path = "signstar-common", version = "0.1.0" } signstar-request-signature = { path = "signstar-request-signature", version = "0.1.0" } strum = { version = "0.27.0", features = ["derive"] } testdir = "0.9.3" diff --git a/signstar-common/Cargo.toml b/signstar-common/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..c07c559c0e42f1a7ce8baa561deffb5f98bdb8e5 --- /dev/null +++ b/signstar-common/Cargo.toml @@ -0,0 +1,14 @@ +[package] +authors.workspace = true +description = "Common components for Signstar libraries and command-line interfaces" +edition.workspace = true +homepage.workspace = true +keywords = ["user", "signstar", "nethsm"] +license.workspace = true +name = "signstar-common" +repository.workspace = true +version = "0.1.0" + +[dependencies] +nethsm.workspace = true +thiserror.workspace = true diff --git a/signstar-common/README.md b/signstar-common/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9f04895b81e171a5d96eed5c187c2b2978f180d5 --- /dev/null +++ b/signstar-common/README.md @@ -0,0 +1,22 @@ +# Signstar common + +Shared components and data types for Signstar tools and libraries + +## Documentation + +- <https://signstar.archlinux.page/rustdoc/signstar_common/> for development version of the crate +- <https://docs.rs/signstar_common/latest/signstar_common/> for released versions of the crate + +## Contributing + +Please refer to the [contributing guidelines] to learn how to contribute to this project. + +## License + +This project may be used under the terms of the [Apache-2.0] or [MIT] license. + +Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses. + +[Apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0 +[MIT]: https://opensource.org/licenses/MIT +[contributing guidelines]: ../CONTRIBUTING.md diff --git a/signstar-common/src/admin_credentials.rs b/signstar-common/src/admin_credentials.rs new file mode 100644 index 0000000000000000000000000000000000000000..eedc834e77bf5d494c801f53d8364631f6946cc2 --- /dev/null +++ b/signstar-common/src/admin_credentials.rs @@ -0,0 +1,113 @@ +//! Data and functions for using administrative credentials on a Signstar host. +//! +//! # Examples +//! +//! ``` +//! use signstar_common::admin_credentials::get_credentials_dir; +//! +//! // Get the directory path in which administrative credentials reside. +//! println!("{:?}", get_credentials_dir()); +//! ``` + +use std::{ + fs::{Permissions, create_dir_all, set_permissions}, + os::unix::fs::{PermissionsExt, chown}, + path::PathBuf, +}; + +use crate::common::{CREDENTIALS_DIR_MODE, get_data_home}; + +/// File name of plaintext administrative credentials. +const PLAINTEXT_CREDENTIALS_FILE: &str = "admin-credentials.toml"; + +/// File name of systemd-creds encrypted administrative credentials. +const SYSTEMD_CREDS_CREDENTIALS_FILE: &str = "admin-credentials.creds"; + +/// The directory for administrative credentials (encrypted and unencrypted). +const CREDENTIALS_DIR: &str = "creds/"; + +/// An error that may occur when handling credentials. +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// Applying permissions to a file failed. + #[error("Unable to apply permissions {permissions} to {path}:\n{source}")] + ApplyPermissions { + permissions: u32, + path: PathBuf, + source: std::io::Error, + }, + + /// No plaintext administrative credentials file can be found + #[error("Unable to create directory {dir}:\n{source}")] + CreateDirectory { + dir: &'static str, + source: std::io::Error, + }, + + /// The ownership of a directory can not be set. + #[error("Ownership of directory {dir} can not be changed to user {system_user}: {source}")] + DirChangeOwner { + dir: PathBuf, + system_user: String, + source: std::io::Error, + }, +} + +/// Returns the path of the directory in which administrative credentials reside. +pub fn get_credentials_dir() -> PathBuf { + get_data_home().join(PathBuf::from(CREDENTIALS_DIR)) +} + +/// Returns the file path for plaintext administrative credentials. +pub fn get_plaintext_credentials_file() -> PathBuf { + get_credentials_dir().join(PathBuf::from(PLAINTEXT_CREDENTIALS_FILE)) +} + +/// Returns the file path for systemd-creds encrypted administrative credentials. +pub fn get_systemd_creds_credentials_file() -> PathBuf { + get_credentials_dir().join(PathBuf::from(SYSTEMD_CREDS_CREDENTIALS_FILE)) +} + +/// Creates the directory for administrative credentials. +/// +/// # Errors +/// +/// Returns an error if the directory or one of its parents can not be created. +/// Refer to [`create_dir_all`] for further information on failure scenarios. +pub fn create_credentials_dir() -> Result<(), Error> { + let credentials_dir = get_credentials_dir(); + create_dir_all(credentials_dir.as_path()).map_err(|source| Error::CreateDirectory { + dir: CREDENTIALS_DIR, + source, + })?; + + // Set the permissions of the credentials directory to `CREDENTIALS_DIR_MODE`. + set_permissions( + credentials_dir.as_path(), + Permissions::from_mode(CREDENTIALS_DIR_MODE), + ) + .map_err(|source| Error::ApplyPermissions { + permissions: CREDENTIALS_DIR_MODE, + path: credentials_dir.clone(), + source, + })?; + + // Recursively chown all directories to root, until `DATA_HOME` is + // reached. + let data_home = get_data_home(); + let mut chown_dir = credentials_dir.clone(); + while chown_dir != data_home { + chown(&chown_dir, Some(0), Some(0)).map_err(|source| Error::DirChangeOwner { + dir: chown_dir.to_path_buf(), + system_user: "root".to_string(), + source, + })?; + if let Some(parent) = &chown_dir.parent() { + chown_dir = parent.to_path_buf() + } else { + break; + } + } + + Ok(()) +} diff --git a/signstar-common/src/common.rs b/signstar-common/src/common.rs new file mode 100644 index 0000000000000000000000000000000000000000..8dd86eb9b2e64180346145e114c36497127b356e --- /dev/null +++ b/signstar-common/src/common.rs @@ -0,0 +1,26 @@ +//! Common data for all Signstar functionalities. +//! +//! # Examples +//! +//! ``` +//! use signstar_common::common::get_data_home; +//! +//! // Get the directory below which all Signstar related data is stored. +//! println!("{:?}", get_data_home()); +//! ``` + +use std::path::PathBuf; + +/// The directory below which o store all Signstar related data. +const DATA_HOME: &str = "/var/lib/signstar/"; + +/// The file mode of directories containing credentials. +pub const CREDENTIALS_DIR_MODE: u32 = 0o100700; + +/// The file mode of secret files. +pub const SECRET_FILE_MODE: u32 = 0o100600; + +/// Get the directory below which all Signstar related data is stored. +pub fn get_data_home() -> PathBuf { + PathBuf::from(DATA_HOME) +} diff --git a/signstar-common/src/config.rs b/signstar-common/src/config.rs new file mode 100644 index 0000000000000000000000000000000000000000..5cba4ce1913a78b03d864ed2c1b0129f989aacce --- /dev/null +++ b/signstar-common/src/config.rs @@ -0,0 +1,205 @@ +//! Default locations for Signstar configuration files. +//! +//! # Examples +//! +//! ``` +//! use signstar_common::config::{ +//! get_config_file_or_default, +//! get_config_file_paths, +//! get_config_file, +//! get_default_config_dir_path, +//! get_default_config_file_path, +//! get_etc_override_config_file_path, +//! get_etc_override_dir_path, +//! get_run_override_config_file_path, +//! get_run_override_dir_path, +//! get_usr_local_override_config_file_path, +//! get_usr_local_override_dir_path, +//! }; +//! +//! // Get directory paths for Signstar configuration files. +//! println!("{:?}", get_etc_override_dir_path()); +//! println!("{:?}", get_run_override_dir_path()); +//! println!("{:?}", get_usr_local_override_dir_path()); +//! println!("{:?}", get_default_config_dir_path()); +//! +//! // Get file paths for Signstar configuration files. +//! println!("{:?}", get_etc_override_config_file_path()); +//! println!("{:?}", get_run_override_config_file_path()); +//! println!("{:?}", get_usr_local_override_config_file_path()); +//! println!("{:?}", get_default_config_file_path()); +//! +//! // Get the first config file found, according to directory precedence. +//! println!("{:?}", get_config_file()); +//! +//! // Get the first config file found, according to directory precedence, or the default if none are found. +//! println!("{:?}", get_config_file_or_default()); +//! +//! // Get all configuration file paths, sorted by directory precedence. +//! println!("{:?}", get_config_file_paths()); +//! ``` + +use std::{fs::create_dir_all, path::PathBuf}; + +/// The default config directory below "/usr" for Signstar hosts. +const DEFAULT_CONFIG_DIR: &str = "/usr/share/signstar/"; + +/// The override config directory below "/etc" for Signstar hosts. +const ETC_OVERRIDE_CONFIG_DIR: &str = "/etc/signstar/"; + +/// The override config directory below "/run" for Signstar hosts. +const RUN_OVERRIDE_CONFIG_DIR: &str = "/run/signstar/"; + +/// The override config directory below "/usr/local" for Signstar hosts. +const USR_LOCAL_OVERRIDE_CONFIG_DIR: &str = "/usr/local/share/signstar/"; + +/// The filename of a Signstar configuration file. +const CONFIG_FILE: &str = "config.toml"; + +/// An error that may occur when handling configuration directories or files. +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// A directory can not be created. + #[error("Unable to create directory {dir}:\n{source}")] + CreateDirectory { dir: String, source: std::io::Error }, +} + +/// Returns the first Signstar configuration file available, or [`None`] if none found. +/// +/// Considers files named `config.toml` in the following directories in descending priority: +/// - `/etc/signstar` +/// - `/run/signstar` +/// - `/usr/local/share/signstar` +/// - `/usr/share/signstar` +/// +/// The first existing file is returned. +/// If no file is found [`None`] is returned. +pub fn get_config_file() -> Option<PathBuf> { + [ + get_etc_override_config_file_path(), + get_run_override_config_file_path(), + get_usr_local_override_config_file_path(), + get_default_config_file_path(), + ] + .into_iter() + .find(|file| file.is_file()) +} + +/// Returns the first Signstar configuration file available, or the default if none found. +/// +/// Considers files named `config.toml` in the following directories in descending priority: +/// - `/etc/signstar` +/// - `/run/signstar` +/// - `/usr/local/share/signstar` +/// - `/usr/share/signstar` +/// +/// The first existing file is returned. +/// If no file is found, the default location `/usr/share/signstar/config.toml` is returned. +pub fn get_config_file_or_default() -> PathBuf { + let Some(config) = get_config_file() else { + return get_default_config_file_path(); + }; + config +} + +/// Returns a list of all configuration file locations, sorted by precedence. +pub fn get_config_file_paths() -> Vec<PathBuf> { + vec![ + get_etc_override_config_file_path(), + get_run_override_config_file_path(), + get_usr_local_override_config_file_path(), + get_default_config_file_path(), + ] +} + +/// Returns the file path of the configuration file override below /etc. +pub fn get_etc_override_config_file_path() -> PathBuf { + PathBuf::from([ETC_OVERRIDE_CONFIG_DIR, CONFIG_FILE].concat()) +} + +/// Returns the directory path of the configuration override directory below /etc. +pub fn get_etc_override_dir_path() -> PathBuf { + PathBuf::from(ETC_OVERRIDE_CONFIG_DIR) +} + +/// Returns the file path of the configuration file override below /run. +pub fn get_run_override_config_file_path() -> PathBuf { + PathBuf::from([RUN_OVERRIDE_CONFIG_DIR, CONFIG_FILE].concat()) +} + +/// Returns the directory path of the configuration override directory below /run. +pub fn get_run_override_dir_path() -> PathBuf { + PathBuf::from(RUN_OVERRIDE_CONFIG_DIR) +} + +/// Returns the file path of the configuration file override below /usr/local. +pub fn get_usr_local_override_config_file_path() -> PathBuf { + PathBuf::from([USR_LOCAL_OVERRIDE_CONFIG_DIR, CONFIG_FILE].concat()) +} + +/// Returns the directory path of the configuration override directory below /usr/local. +pub fn get_usr_local_override_dir_path() -> PathBuf { + PathBuf::from(USR_LOCAL_OVERRIDE_CONFIG_DIR) +} + +/// Returns the file path of the default configuration file /usr. +pub fn get_default_config_file_path() -> PathBuf { + PathBuf::from([DEFAULT_CONFIG_DIR, CONFIG_FILE].concat()) +} + +/// Returns the directory path of the default configuration directory below /usr. +pub fn get_default_config_dir_path() -> PathBuf { + PathBuf::from(DEFAULT_CONFIG_DIR) +} + +/// Creates the default configuration directory below /usr. +/// +/// # Errors +/// +/// Returns an error if the directory or one of its parents can not be created. +/// Refer to [`create_dir_all`] for further information on failure scenarios. +pub fn create_default_config_dir() -> Result<(), Error> { + create_dir_all(get_default_config_dir_path()).map_err(|source| Error::CreateDirectory { + dir: DEFAULT_CONFIG_DIR.to_string(), + source, + }) +} + +/// Creates the configuration override dir below /etc. +/// +/// # Errors +/// +/// Returns an error if the directory or one of its parents can not be created. +/// Refer to [`create_dir_all`] for further information on failure scenarios. +pub fn create_etc_override_config_dir() -> Result<(), Error> { + create_dir_all(get_etc_override_dir_path()).map_err(|source| Error::CreateDirectory { + dir: ETC_OVERRIDE_CONFIG_DIR.to_string(), + source, + }) +} + +/// Creates the configuration override dir below /run. +/// +/// # Errors +/// +/// Returns an error if the directory or one of its parents can not be created. +/// Refer to [`create_dir_all`] for further information on failure scenarios. +pub fn create_run_override_config_dir() -> Result<(), Error> { + create_dir_all(get_run_override_dir_path()).map_err(|source| Error::CreateDirectory { + dir: RUN_OVERRIDE_CONFIG_DIR.to_string(), + source, + }) +} + +/// Creates the configuration override dir below /usr/local. +/// +/// # Errors +/// +/// Returns an error if the directory or one of its parents can not be created. +/// Refer to [`create_dir_all`] for further information on failure scenarios. +pub fn create_usr_local_override_config_dir() -> Result<(), Error> { + create_dir_all(get_usr_local_override_dir_path()).map_err(|source| Error::CreateDirectory { + dir: USR_LOCAL_OVERRIDE_CONFIG_DIR.to_string(), + source, + }) +} diff --git a/signstar-common/src/lib.rs b/signstar-common/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..0f446ff0cd68b48c5bc7b20a9767b89852f7079c --- /dev/null +++ b/signstar-common/src/lib.rs @@ -0,0 +1,8 @@ +//! Common components and data for Signstar crates. + +pub mod admin_credentials; +pub mod common; +pub mod config; +pub mod nethsm; +pub mod ssh; +pub mod system_user; diff --git a/signstar-common/src/nethsm.rs b/signstar-common/src/nethsm.rs new file mode 100644 index 0000000000000000000000000000000000000000..a746d85c36094c00d2eb13e7b9f177657ae33ee4 --- /dev/null +++ b/signstar-common/src/nethsm.rs @@ -0,0 +1,26 @@ +//! Defaults for NetHSM backends. + +use std::net::Ipv4Addr; + +use nethsm::LogLevel; + +/// The default admin user name of newly provisioned NetHSM. +pub const USER_DEFAULT_ADMIN: &str = "admin"; + +/// The default IP address of an unprovisioned NetHSM. +pub const NETWORK_DEFAULT_IP_ADDRESS: Ipv4Addr = Ipv4Addr::new(192, 168, 1, 1); + +/// The default netmask of an unprovisioned NetHSM. +pub const NETWORK_DEFAULT_NETMASK: Ipv4Addr = Ipv4Addr::new(255, 255, 255, 0); + +/// The default gateway of an unprovisioned NetHSM. +pub const NETWORK_DEFAULT_GATEWAY: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0); + +/// The default logging IP address of an unprovisioned NetHSM. +pub const LOGGING_DEFAULT_IP_ADDRESS: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0); + +/// The default logging port of an unprovisioned NetHSM. +pub const LOGGING_DEFAULT_PORT: u32 = 514; + +/// The default logging level of an unprovisioned NetHSM. +pub const LOGGING_DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Info; diff --git a/signstar-common/src/ssh.rs b/signstar-common/src/ssh.rs new file mode 100644 index 0000000000000000000000000000000000000000..9ca2d3fe13135c08af245888a97cf7834f2ccaad --- /dev/null +++ b/signstar-common/src/ssh.rs @@ -0,0 +1,31 @@ +//! Defaults for SSH. +//! +//! # Examples +//! +//! ``` +//! use signstar_common::ssh::{get_ssh_authorized_key_base_dir, get_sshd_config_dropin_dir}; +//! +//! // Get directory path for SSH authorized_keys files for Signstar users. +//! println!("{:?}", get_ssh_authorized_key_base_dir()); +//! +//! // Get directory path for sshd_config drop-in files. +//! println!("{:?}", get_sshd_config_dropin_dir()); +//! ``` + +use std::path::PathBuf; + +/// The base directory below which SSH authorized_keys files for users are located. +const SSH_AUTHORIZED_KEY_BASE_DIR: &str = "/etc/ssh/"; + +/// The directory below which sshd_config drop-in files are located. +const SSHD_CONFIG_DROPIN_DIR: &str = "/etc/ssh/sshd_config.d/"; + +/// Returns the directory path below which SSH authorized_keys files for Signstar users are located. +pub fn get_ssh_authorized_key_base_dir() -> PathBuf { + PathBuf::from(SSH_AUTHORIZED_KEY_BASE_DIR) +} + +/// Returns the directory path below which SSH authorized_keys files for Signstar users are located. +pub fn get_sshd_config_dropin_dir() -> PathBuf { + PathBuf::from(SSHD_CONFIG_DROPIN_DIR) +} diff --git a/signstar-common/src/system_user.rs b/signstar-common/src/system_user.rs new file mode 100644 index 0000000000000000000000000000000000000000..b0406e10a74e106bf10df473cac78e3d88164ff9 --- /dev/null +++ b/signstar-common/src/system_user.rs @@ -0,0 +1,62 @@ +//! Defaults for system users. +//! +//! ``` +//! use signstar_common::system_user::{get_home_base_dir_path, get_relative_user_secrets_dir}; +//! +//! // Get the base directory below which Signstar system user homes are located. +//! println!("{:?}", get_home_base_dir_path()); +//! +//! // Get the relative directory below which Signstar secrets are located per system user. +//! println!("{:?}", get_relative_user_secrets_dir()); +//! ``` + +use std::path::PathBuf; + +use crate::common::get_data_home; + +/// The relative base directory below which system user homes are located. +/// +/// This directory resides relative to the data home on the system. +const HOME_BASE_DIR: &str = "home/"; + +/// The directory name below which credentials files are stored. +/// +/// The directory is evaluated relative to a user's home. +const USER_SECRETS_DIR: &str = ".local/state/signstar/secrets/"; + +/// The file extension of plaintext credential files. +const PLAINTEXT_SECRETS_EXTENSION: &str = "txt"; + +/// The file extension of systemd-creds encrypted credential files. +const SYSTEMD_CREDS_SECRETS_EXTENSION: &str = "creds"; + +/// Returns the base directory below which Signstar system user homes are located. +pub fn get_home_base_dir_path() -> PathBuf { + get_data_home().join(PathBuf::from(HOME_BASE_DIR)) +} + +/// Returns the relative directory below which Signstar secrets are located per system user. +pub fn get_relative_user_secrets_dir() -> PathBuf { + PathBuf::from(USER_SECRETS_DIR) +} + +/// Returns the path to the secrets directory for a specific system user. +pub fn get_user_secrets_dir(system_user: &str) -> PathBuf { + get_home_base_dir_path() + .join(PathBuf::from(system_user)) + .join(get_relative_user_secrets_dir()) +} + +/// Returns the path to a plaintext secrets file for a system user and backend user. +pub fn get_plaintext_secret_file(system_user: &str, backend_user: &str) -> PathBuf { + get_user_secrets_dir(system_user).join(PathBuf::from( + [backend_user, ".", PLAINTEXT_SECRETS_EXTENSION].concat(), + )) +} + +/// Returns the path to a systemd-creds encrypted secrets file for a system user and backend user. +pub fn get_systemd_creds_secret_file(system_user: &str, backend_user: &str) -> PathBuf { + get_user_secrets_dir(system_user).join(PathBuf::from( + [backend_user, ".", SYSTEMD_CREDS_SECRETS_EXTENSION].concat(), + )) +}