diff --git a/examples/unixd b/examples/unixd index 4058963cd..26576c93a 100644 --- a/examples/unixd +++ b/examples/unixd @@ -54,8 +54,9 @@ version = '2' # home_attr = "uuid" -# Controls the prefix that will be appended to the home alias when mounting home directories. Must -# end with a trailing `/`. If unset, defaults to the value of the `home_prefix`. +# Controls the prefix that will be prepended to the home name when using mounted home directories from +# a location that is outside of them `home_prefix`. Must end with a trailing `/`. If unset then home +# directories will be created in `home_prefix`. # # This is option is useful when implementing a networked home directory layout. A common implementation # is to configure a networked filesystem that contains user directories mounted at `/u/` (eg /u/$user_uuid) @@ -64,12 +65,19 @@ version = '2' # # > home_attr = "uuid" # > home_alias = "name" -# > home_prefix = "/u/" -# > home_mount_prefix = "/home/" +# > home_prefix = "/home/" +# > home_mount_prefix = "/u/" +# +# ⚠️ If you expect directories to be created by kanidm unixd tasks in an alternate mount prefix, then +# you need to run `systemctl edit kanidm-unixd-tasks` to allow the daemon to write to these locations. +# +# > [Service] +# > ReadWritePaths=/home /var/run/kanidm-unixd /u +# # # Default: unset # -# home_mount_prefix = "/home/" +# home_mount_prefix = "/u/" # # The default token attribute used for generating symlinks pointing to the user's home diff --git a/unix_integration/resolver/src/bin/kanidm_unixd_tasks.rs b/unix_integration/resolver/src/bin/kanidm_unixd_tasks.rs index fe1a9510c..5b312f49b 100644 --- a/unix_integration/resolver/src/bin/kanidm_unixd_tasks.rs +++ b/unix_integration/resolver/src/bin/kanidm_unixd_tasks.rs @@ -97,12 +97,15 @@ fn create_home_directory( // be possible, but we assert this to be sure. let name = info.name.trim_start_matches('.').replace(['/', '\\'], ""); - let home_mount_prefix_path_is_set = home_mount_prefix_path.is_some(); + debug!(?home_prefix_path, ?home_mount_prefix_path, ?info); + // This is where the users home dir "is" and aliases from here go to the true storage + // mounts let home_prefix_path = home_prefix_path .canonicalize() .map_err(|e| format!("{:?}", e))?; + // This is where the storage is *mounted*. If not set, falls back to the home_prefix. let home_mount_prefix_path = home_mount_prefix_path .unwrap_or(&home_prefix_path) .canonicalize() @@ -120,27 +123,16 @@ fn create_home_directory( return Err("Invalid home_mount_prefix from configuration - home_prefix path must exist, must be a directory, and must be absolute (not relative)".to_string()); } - // Actually process the request here. - let hd_path = Path::join(&home_prefix_path, &name); - - // Assert the resulting named home path is consistent and correct. This is to ensure that - // the complete home path is not a path traversal outside of the home_prefixes. - if let Some(pp) = hd_path.parent() { - if pp != home_prefix_path { - return Err("Invalid home directory name - not within home_prefix".to_string()); - } - } else { - return Err("Invalid/Corrupt home directory path - no prefix found".to_string()); - } - + // This is now creating the actual home directory in the home_mount path. + // First we want to validate that the path is legitimate and hasn't tried + // to escape the home_mount prefix. let hd_mount_path = Path::join(&home_mount_prefix_path, &name); + debug!(?hd_mount_path); + if let Some(pp) = hd_mount_path.parent() { if pp != home_mount_prefix_path { - return Err( - "Invalid home directory name - not within home_prefix/home_mount_prefix" - .to_string(), - ); + return Err("Invalid home directory name - not within home_mount_prefix".to_string()); } } else { return Err("Invalid/Corrupt home directory path - no prefix found".to_string()); @@ -167,6 +159,7 @@ fn create_home_directory( // Create the dir if let Err(e) = fs::create_dir_all(&hd_mount_path) { let _ = unsafe { umask(before) }; + error!(err = ?e, ?hd_mount_path, "Unable to create directory"); return Err(format!("{:?}", e)); } let _ = unsafe { umask(before) }; @@ -195,9 +188,15 @@ fn create_home_directory( } if entry.path().is_dir() { - fs::create_dir_all(dest).map_err(|e| e.to_string())?; + fs::create_dir_all(dest).map_err(|e| { + error!(err = ?e, ?dest, "Unable to create directory from /etc/skel"); + e.to_string() + })?; } else { - fs::copy(entry.path(), dest).map_err(|e| e.to_string())?; + fs::copy(entry.path(), dest).map_err(|e| { + error!(err = ?e, ?dest, "Unable to copy from /etc/skel"); + e.to_string() + })?; } chown(dest, info.gid)?; @@ -212,15 +211,6 @@ fn create_home_directory( #[cfg(all(target_family = "unix", feature = "selinux"))] labeler.set_default_context_for_fs_objects()?; - // If there is a mount prefix we use it, otherwise we use a relative path - // within the same directory. - let name_rel_path = if home_mount_prefix_path_is_set { - // Use the absolute path. - hd_mount_path.as_ref() - } else { - Path::new(&name) - }; - // Do the aliases exist? for alias in info.aliases.iter() { // Sanity check the alias. @@ -239,9 +229,11 @@ fn create_home_directory( } if alias_path.exists() { + debug!("checking symlink {:?} -> {:?}", alias_path, hd_mount_path); let attr = match fs::symlink_metadata(&alias_path) { Ok(a) => a, Err(e) => { + error!(err = ?e, ?alias_path, "Unable to read alias path metadata"); return Err(format!("{:?}", e)); } }; @@ -249,22 +241,27 @@ fn create_home_directory( if attr.file_type().is_symlink() { // Probably need to update it. if let Err(e) = fs::remove_file(&alias_path) { + error!(err = ?e, ?alias_path, "Unable to remove existing alias path"); return Err(format!("{:?}", e)); } - if let Err(e) = symlink(name_rel_path, &alias_path) { + + debug!("updating symlink {:?} -> {:?}", alias_path, hd_mount_path); + if let Err(e) = symlink(&hd_mount_path, &alias_path) { + error!(err = ?e, ?alias_path, "Unable to update alias path"); return Err(format!("{:?}", e)); } } else { warn!( ?alias_path, - ?alias, - ?name, - "home directory alias is not a symlink, unable to update" + ?hd_mount_path, + "home directory alias path is not a symlink, unable to update" ); } } else { // Does not exist. Create. - if let Err(e) = symlink(name_rel_path, alias_path) { + debug!("creating symlink {:?} -> {:?}", alias_path, hd_mount_path); + if let Err(e) = symlink(&hd_mount_path, &alias_path) { + error!(err = ?e, ?alias_path, "Unable to create alias path"); return Err(format!("{:?}", e)); } }