Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • gromit/signstar
  • wiktor/signstar
  • klausenbusk/signstar
  • archlinux/signstar
4 results
Show changes
Commits on Source (3)
......@@ -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/
......@@ -1742,7 +1742,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
"windows-targets 0.52.6",
"windows-targets 0.48.5",
]
[[package]]
......@@ -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"
......@@ -3140,6 +3148,7 @@ dependencies = [
"clap",
"nethsm-config",
"nix",
"signstar-common",
"strum 0.27.1",
"sysinfo 0.33.0",
"thiserror 2.0.11",
......@@ -3733,9 +3742,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.15.1"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
dependencies = [
"getrandom 0.3.1",
]
......
......@@ -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"
......
[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
# 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
//! 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(())
}
//! 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)
}
//! 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,
})
}
//! 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;
//! 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;
//! 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)
}
//! 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(),
))
}
......@@ -13,6 +13,7 @@ version = "0.1.2"
clap = { workspace = true, features = ["cargo", "derive", "env"] }
nethsm-config.workspace = true
nix = { version = "0.29.0", features = ["user"] }
signstar-common.workspace = true
strum.workspace = true
sysinfo = "0.33.0"
thiserror.workspace = true
use clap::{Parser, crate_name};
use signstar_common::{
config::{
get_default_config_file_path,
get_etc_override_config_file_path,
get_run_override_config_file_path,
get_usr_local_override_config_file_path,
},
ssh::{get_ssh_authorized_key_base_dir, get_sshd_config_dropin_dir},
system_user::get_home_base_dir_path,
};
use strum::VariantNames;
use crate::{
ConfigPath,
DEFAULT_CONFIG_FILE,
ETC_OVERRIDE_CONFIG_FILE,
HOME_BASE_DIR,
RUN_OVERRIDE_CONFIG_FILE,
SSH_AUTHORIZED_KEY_BASE_DIR,
SSHD_DROPIN_CONFIG_DIR,
SshForceCommand,
USR_LOCAL_OVERRIDE_CONFIG_FILE,
};
use crate::{ConfigPath, SshForceCommand};
pub const BIN_NAME: &str = crate_name!();
const SSH_FORCE_COMMAND_VARIANTS: &[&str] = SshForceCommand::VARIANTS;
......@@ -30,25 +30,32 @@ It creates system users and their integration based on a central configuration f
By default, one of the following configuration files is used if it exists, in the following order:
- \"{USR_LOCAL_OVERRIDE_CONFIG_FILE}\"
- {:?}
- \"{RUN_OVERRIDE_CONFIG_FILE}\"
- {:?}
- \"{ETC_OVERRIDE_CONFIG_FILE}\"
- {:?}
If none of the above are found, the default location \"{DEFAULT_CONFIG_FILE}\" is used.
If none of the above are found, the default location {:?} is used.
Alternatively a custom configuration file location can be specified using the \"--config\"/ \"-c\" option.
System users, if they don't exist already, are created with the help of `useradd`.
The users are created without a passphrase and setup with a home below \"{HOME_BASE_DIR}\".
The users are created without a passphrase and setup with a home below {:?}.
However, their home directory is not created automatically.
The system user accounts are then unlocked with the help of `usermod`.
For each system user a tmpfiles.d integration is provided below \"/usr/lib/tmpfiles.d\", to allow automatic creation of their home directory.
If the used configuration file associates the system user with SSH public keys, a dedicated \"authorized_keys\" file containing the SSH public keys for the user is created below \"{SSH_AUTHORIZED_KEY_BASE_DIR}\".
Additionally, an \"sshd_config\" drop-in configuration is created below \"{SSHD_DROPIN_CONFIG_DIR}\".
If the used configuration file associates the system user with SSH public keys, a dedicated \"authorized_keys\" file containing the SSH public keys for the user is created below {:?}.
Additionally, an \"sshd_config\" drop-in configuration is created below {:?}.
This \"sshd_config\" drop-in configuration enforces the use of the user's \"authorized_keys\" and the use of a specific command (i.e. one of {SSH_FORCE_COMMAND_VARIANTS:?}) depending on the user's role.",
),
get_usr_local_override_config_file_path(),
get_run_override_config_file_path(),
get_etc_override_config_file_path(),
get_default_config_file_path(),
get_home_base_dir_path(),
get_ssh_authorized_key_base_dir(),
get_sshd_config_dropin_dir(),
)
)]
pub struct Cli {
#[arg(
......@@ -61,14 +68,18 @@ If specified, the custom configuration file is used instead of the default confi
If unspecified, one of the following configuration files is used if it exists, in the following order:
- \"{USR_LOCAL_OVERRIDE_CONFIG_FILE}\"
- {:?}
- \"{RUN_OVERRIDE_CONFIG_FILE}\"
- {:?}
- \"{ETC_OVERRIDE_CONFIG_FILE}\"
- {:?}
If none of the above are found, the default location \"{DEFAULT_CONFIG_FILE}\" is used.
"),
If none of the above are found, the default location {:?} is used.",
get_usr_local_override_config_file_path(),
get_run_override_config_file_path(),
get_etc_override_config_file_path(),
get_default_config_file_path(),
),
long,
short
)]
......
......@@ -8,18 +8,15 @@ use std::{
use nethsm_config::{HermeticParallelConfig, SystemUserId, UserMapping};
use nix::unistd::User;
use signstar_common::{
config::get_config_file_or_default,
ssh::{get_ssh_authorized_key_base_dir, get_sshd_config_dropin_dir},
system_user::get_home_base_dir_path,
};
use sysinfo::{Pid, System};
pub mod cli;
pub static ETC_OVERRIDE_CONFIG_FILE: &str = "/etc/signstar/config.toml";
pub static RUN_OVERRIDE_CONFIG_FILE: &str = "/run/signstar/config.toml";
pub static USR_LOCAL_OVERRIDE_CONFIG_FILE: &str = "/usr/local/share/signstar/config.toml";
pub static DEFAULT_CONFIG_FILE: &str = "/usr/share/signstar/config.toml";
pub static SSH_AUTHORIZED_KEY_BASE_DIR: &str = "/etc/ssh";
pub static SSHD_DROPIN_CONFIG_DIR: &str = "/etc/ssh/sshd_config.d";
pub static HOME_BASE_DIR: &str = "/var/lib/signstar/home";
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// A config error
......@@ -64,6 +61,16 @@ pub enum Error {
#[error("The string {0} could not be converted to a \"sysinfo::Uid\"")]
SysUidFromStr(String),
/// A `Path` value for a tmpfiles.d integration is not valid.
#[error(
"The Path value {path} for the tmpfiles.d integration for {user} is not valid:\n{reason}"
)]
TmpfilesDPath {
path: String,
user: SystemUserId,
reason: &'static str,
},
/// Adding a user failed
#[error("Adding user {user} failed:\n{source}")]
UserAdd {
......@@ -109,12 +116,7 @@ pub enum Error {
/// The configuration file path for the application.
///
/// If the path exists and is a file, one of the following configuration file locations is used:
/// - [`ETC_OVERRIDE_CONFIG_FILE`]
/// - [`RUN_OVERRIDE_CONFIG_FILE`]
/// - [`USR_LOCAL_OVERRIDE_CONFIG_FILE`]
///
/// If none of the above is found, [`DEFAULT_CONFIG_FILE`] is used (even if it doesn't exist!).
/// The configuration file location is defined by the behavior of [`get_config_file_or_default`].
#[derive(Clone, Debug)]
pub struct ConfigPath(PathBuf);
......@@ -133,25 +135,10 @@ impl AsRef<Path> for ConfigPath {
impl Default for ConfigPath {
/// Returns the default [`ConfigPath`].
///
/// If the path exists and is a file, one of the following configuration file locations is used:
/// - [`ETC_OVERRIDE_CONFIG_FILE`]
/// - [`RUN_OVERRIDE_CONFIG_FILE`]
/// - [`USR_LOCAL_OVERRIDE_CONFIG_FILE`]
///
/// If none of the above is found, [`DEFAULT_CONFIG_FILE`] is used (even if it doesn't exist!).
/// Uses [`get_config_file_or_default`] to find the first usable configuration file path, or the
/// default if none is found.
fn default() -> Self {
for config_file in [
ETC_OVERRIDE_CONFIG_FILE,
RUN_OVERRIDE_CONFIG_FILE,
USR_LOCAL_OVERRIDE_CONFIG_FILE,
] {
let config = PathBuf::from(config_file);
if config.is_file() {
return Self(config);
}
}
Self(PathBuf::from(DEFAULT_CONFIG_FILE))
Self(get_config_file_or_default())
}
}
......@@ -308,8 +295,8 @@ pub fn ensure_root() -> Result<(), Error> {
///
/// Works on the [`UserMapping`]s of the provided `config` and creates system users for all
/// mappings, that define system users, if they don't exist on the system yet.
/// System users are created unlocked, without passphrase, with their homes located in
/// [`HOME_BASE_DIR`].
/// System users are created unlocked, without passphrase, with their homes located in the directory
/// returned by [`get_home_base_dir_path`].
/// The home directories of users are not created upon user creation, but instead a [tmpfiles.d]
/// configuration is added for them to automate their creation upon system boot.
///
......@@ -354,17 +341,17 @@ pub fn create_system_users(config: &HermeticParallelConfig) -> Result<(), Error>
continue;
}
let home_base_dir = get_home_base_dir_path();
// add user, but do not create its home
print!("Creating user \"{user}\"...");
let user_add = Command::new("useradd")
.args([
"--base-dir",
HOME_BASE_DIR,
"--user-group",
"--shell",
"/usr/bin/bash",
user.as_ref(),
])
.arg("--base-dir")
.arg(home_base_dir.as_path())
.arg("--user-group")
.arg("--shell")
.arg("/usr/bin/bash")
.arg(user.as_ref())
.output()
.map_err(|error| Error::UserAdd {
user: user.clone(),
......@@ -407,8 +394,25 @@ pub fn create_system_users(config: &HermeticParallelConfig) -> Result<(), Error>
user: user.clone(),
source,
})?;
// ensure that the `Path` component in the tmpfiles.d file
// - has whitespace replaced with a c-style escape
// - does not contain specifiers
let home_dir = {
let home_dir =
format!("{}/{user}", home_base_dir.to_string_lossy()).replace(" ", "\\x20");
if home_dir.contains("%") {
return Err(Error::TmpfilesDPath {
path: home_dir.clone(),
user: user.clone(),
reason: "Specifiers (%) are not supported at this point.",
});
}
home_dir
};
buffer
.write_all(format!("d {HOME_BASE_DIR}/{user} 700 {user} {user}\n").as_bytes())
.write_all(format!("d {home_dir} 700 {user} {user}\n",).as_bytes())
.map_err(|source| Error::WriteTmpfilesD {
user: user.clone(),
source,
......@@ -422,14 +426,14 @@ pub fn create_system_users(config: &HermeticParallelConfig) -> Result<(), Error>
// add SSH authorized keys file user in system-wide location
print!("Adding SSH authorized_keys file for user \"{user}\"...");
{
let filename = format!(
"{SSH_AUTHORIZED_KEY_BASE_DIR}/signstar-user-{user}.authorized_keys"
);
let mut buffer =
File::create(filename).map_err(|source| Error::WriteAuthorizedKeys {
user: user.clone(),
source,
})?;
let mut buffer = File::create(
get_ssh_authorized_key_base_dir()
.join(format!("signstar-user-{user}.authorized_keys")),
)
.map_err(|source| Error::WriteAuthorizedKeys {
user: user.clone(),
source,
})?;
buffer
.write_all(
(authorized_keys
......@@ -450,9 +454,9 @@ pub fn create_system_users(config: &HermeticParallelConfig) -> Result<(), Error>
// add sshd_config drop-in configuration for user
print!("Adding sshd_config drop-in configuration for user \"{user}\"...");
{
let mut buffer = File::create(format!(
"{SSHD_DROPIN_CONFIG_DIR}/10-signstar-user-{user}.conf"
))
let mut buffer = File::create(
get_sshd_config_dropin_dir().join(format!("10-signstar-user-{user}.conf")),
)
.map_err(|source| Error::WriteSshdConfig {
user: user.clone(),
source,
......