mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
Improve selinux in tasks daemon (#1847)
This commit is contained in:
parent
6e01c4802f
commit
07580cf57a
|
@ -27,11 +27,35 @@ To build the Web UI you'll need [wasm-pack](https://rustwasm.github.io/wasm-pack
|
||||||
You will need to install rustup and our build dependencies with:
|
You will need to install rustup and our build dependencies with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
zypper in rustup git libudev-devel sqlite3-devel libopenssl-3-devel
|
zypper in rustup git libudev-devel sqlite3-devel libopenssl-3-devel libselinux-devel pam-devel tpm2-0-tss-devel
|
||||||
```
|
```
|
||||||
|
|
||||||
You can then use rustup to complete the setup of the toolchain.
|
You can then use rustup to complete the setup of the toolchain.
|
||||||
|
|
||||||
|
In some cases you may need to build other vendored components, or use an alternate linker. In these
|
||||||
|
cases we advise you to also install.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
zypper in clang lld make sccache
|
||||||
|
```
|
||||||
|
|
||||||
|
You should also adjust your environment with:
|
||||||
|
```bash
|
||||||
|
export RUSTC_WRAPPER=sccache
|
||||||
|
export CC="sccache /usr/bin/clang"
|
||||||
|
export CXX="sccache /usr/bin/clang++"
|
||||||
|
```
|
||||||
|
|
||||||
|
And add the following to a cargo config of your choice (such as ~/.cargo/config), adjusting for cpu arch
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
linker = "clang"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-fuse-ld=lld",
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
### Fedora
|
### Fedora
|
||||||
|
|
||||||
You will need [rustup](https://rustup.rs/) to install a Rust toolchain.
|
You will need [rustup](https://rustup.rs/) to install a Rust toolchain.
|
||||||
|
|
|
@ -35,7 +35,8 @@ async fn get_webdriver_client() -> fantoccini::Client {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// trying the default chromedriver port
|
// trying the default chromedriver port
|
||||||
eprintln!("Couldn't connect on 4444, trying 9515");
|
eprintln!("Couldn't connect on 4444, trying 9515");
|
||||||
fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new()).connect("http://localhost:9515")
|
fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new())
|
||||||
|
.connect("http://localhost:9515")
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
@ -48,8 +49,11 @@ async fn get_webdriver_client() -> fantoccini::Client {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let cap: Capabilities = serde_json::from_value(cap).unwrap();
|
let cap: Capabilities = serde_json::from_value(cap).unwrap();
|
||||||
fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new()).capabilities(cap).connect("http://localhost:9515").await.unwrap()
|
fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new())
|
||||||
|
.capabilities(cap)
|
||||||
|
.connect("http://localhost:9515")
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
use users::get_user_by_uid;
|
||||||
|
|
||||||
use selinux::{
|
use selinux::{
|
||||||
current_mode, kernel_support, label::back_end::File, label::Labeler, KernelSupport, SELinuxMode,
|
current_mode, kernel_support, label::back_end::File, label::Labeler, KernelSupport,
|
||||||
|
SELinuxMode, SecurityContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn supported() -> bool {
|
pub fn supported() -> bool {
|
||||||
|
@ -16,18 +20,7 @@ pub fn supported() -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_labeler() -> Result<Labeler<File>, String> {
|
fn do_setfscreatecon_for_path(path_raw: &String, labeler: &Labeler<File>) -> Result<(), String> {
|
||||||
if let Ok(v) = Labeler::new(&[], true) {
|
|
||||||
Ok(v)
|
|
||||||
} else {
|
|
||||||
Err("Failed getting handle for SELinux labeling".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn do_setfscreatecon_for_path(
|
|
||||||
path_raw: &String,
|
|
||||||
labeler: &Labeler<File>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
match labeler.look_up(&CString::new(path_raw.to_owned()).unwrap(), 0) {
|
match labeler.look_up(&CString::new(path_raw.to_owned()).unwrap(), 0) {
|
||||||
Ok(context) => {
|
Ok(context) => {
|
||||||
if context.set_for_new_file_system_objects(true).is_err() {
|
if context.set_for_new_file_system_objects(true).is_err() {
|
||||||
|
@ -40,3 +33,98 @@ pub fn do_setfscreatecon_for_path(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_labeler() -> Result<Labeler<File>, String> {
|
||||||
|
if let Ok(v) = Labeler::new(&[], true) {
|
||||||
|
Ok(v)
|
||||||
|
} else {
|
||||||
|
Err("Failed getting handle for SELinux labeling".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SelinuxLabeler {
|
||||||
|
None,
|
||||||
|
Enabled {
|
||||||
|
labeler: Labeler<File>,
|
||||||
|
sel_lookup_path_raw: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelinuxLabeler {
|
||||||
|
pub fn new(gid: u32, home_prefix: &str) -> Result<Self, String> {
|
||||||
|
let labeler = get_labeler()?;
|
||||||
|
|
||||||
|
// Construct a path for SELinux context lookups.
|
||||||
|
// We do this because the policy only associates a home directory to its owning
|
||||||
|
// user by the name of the directory. Since the real user's home directory is (by
|
||||||
|
// default) their uuid or spn, its context will always be the policy default
|
||||||
|
// (usually user_u or unconfined_u). This lookup path is used to ask the policy
|
||||||
|
// what the context SHOULD be, and we will create policy equivalence rules below
|
||||||
|
// so that relabels in the future do not break it.
|
||||||
|
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
||||||
|
// Yes, gid, because we use the GID number for both the user's UID and primary GID
|
||||||
|
let sel_lookup_path_raw = match get_user_by_uid(gid) {
|
||||||
|
Some(v) => format!("{}{}", home_prefix, v.name().to_str().unwrap()),
|
||||||
|
None => {
|
||||||
|
return Err("Failed looking up username by uid for SELinux relabeling".to_string());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(SelinuxLabeler::Enabled {
|
||||||
|
labeler,
|
||||||
|
sel_lookup_path_raw,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_noop() -> Self {
|
||||||
|
SelinuxLabeler::None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn do_setfscreatecon_for_path(&self) -> Result<(), String> {
|
||||||
|
match &self {
|
||||||
|
SelinuxLabeler::None => Ok(()),
|
||||||
|
SelinuxLabeler::Enabled {
|
||||||
|
labeler,
|
||||||
|
sel_lookup_path_raw,
|
||||||
|
} => do_setfscreatecon_for_path(sel_lookup_path_raw, labeler),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn label_path<P: AsRef<Path>>(&self, path: P) -> Result<(), String> {
|
||||||
|
match &self {
|
||||||
|
SelinuxLabeler::None => Ok(()),
|
||||||
|
SelinuxLabeler::Enabled {
|
||||||
|
labeler,
|
||||||
|
sel_lookup_path_raw,
|
||||||
|
} => {
|
||||||
|
let sel_lookup_path = Path::new(&sel_lookup_path_raw).join(path.as_ref());
|
||||||
|
do_setfscreatecon_for_path(&sel_lookup_path.to_str().unwrap().to_string(), &labeler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_equivalence_rule(&self, path: &str) -> Result<(), String> {
|
||||||
|
match &self {
|
||||||
|
SelinuxLabeler::None => Ok(()),
|
||||||
|
SelinuxLabeler::Enabled {
|
||||||
|
labeler: _,
|
||||||
|
sel_lookup_path_raw,
|
||||||
|
} => Command::new("semanage")
|
||||||
|
.args(["fcontext", "-ae", sel_lookup_path_raw, &path])
|
||||||
|
.spawn()
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| "Failed creating SELinux policy equivalence rule".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_default_context_for_fs_objects(&self) -> Result<(), String> {
|
||||||
|
match &self {
|
||||||
|
SelinuxLabeler::None => Ok(()),
|
||||||
|
SelinuxLabeler::Enabled { .. } => {
|
||||||
|
SecurityContext::set_default_context_for_new_file_system_objects()
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| "Failed resetting SELinux file creation contexts".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -36,12 +36,6 @@ use walkdir::WalkDir;
|
||||||
|
|
||||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
||||||
use kanidm_unix_common::selinux_util;
|
use kanidm_unix_common::selinux_util;
|
||||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
|
||||||
use selinux::SecurityContext;
|
|
||||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
|
||||||
use std::process::Command;
|
|
||||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
|
||||||
use users::get_user_by_uid;
|
|
||||||
|
|
||||||
struct TaskCodec;
|
struct TaskCodec;
|
||||||
|
|
||||||
|
@ -96,6 +90,7 @@ fn create_home_directory(
|
||||||
info: &HomeDirectoryInfo,
|
info: &HomeDirectoryInfo,
|
||||||
home_prefix: &str,
|
home_prefix: &str,
|
||||||
use_etc_skel: bool,
|
use_etc_skel: bool,
|
||||||
|
use_selinux: bool,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// Final sanity check to prevent certain classes of attacks.
|
// Final sanity check to prevent certain classes of attacks.
|
||||||
let name = info.name.trim_start_matches('.').replace(['/', '\\'], "");
|
let name = info.name.trim_start_matches('.').replace(['/', '\\'], "");
|
||||||
|
@ -121,33 +116,22 @@ fn create_home_directory(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a handle to the SELinux labeling interface
|
// Get a handle to the SELinux labeling interface
|
||||||
|
debug!(?use_selinux, "selinux for home dir labeling");
|
||||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
||||||
let labeler = selinux_util::get_labeler()?;
|
let labeler = if use_selinux {
|
||||||
|
selinux_util::SelinuxLabeler::new(info.gid, home_prefix)?
|
||||||
// Construct a path for SELinux context lookups.
|
} else {
|
||||||
// We do this because the policy only associates a home directory to its owning
|
selinux_util::SelinuxLabeler::new_noop()
|
||||||
// user by the name of the directory. Since the real user's home directory is (by
|
|
||||||
// default) their uuid or spn, its context will always be the policy default
|
|
||||||
// (usually user_u or unconfined_u). This lookup path is used to ask the policy
|
|
||||||
// what the context SHOULD be, and we will create policy equivalence rules below
|
|
||||||
// so that relabels in the future do not break it.
|
|
||||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
|
||||||
// Yes, gid, because we use the GID number for both the user's UID and primary GID
|
|
||||||
let sel_lookup_path_raw = match get_user_by_uid(info.gid) {
|
|
||||||
Some(v) => format!("{}{}", home_prefix, v.name().to_str().unwrap()),
|
|
||||||
None => {
|
|
||||||
return Err("Failed looking up username by uid for SELinux relabeling".to_string());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Does the home directory exist?
|
// Does the home directory exist?
|
||||||
if !hd_path.exists() {
|
if !hd_path.exists() {
|
||||||
// Set a umask
|
|
||||||
let before = unsafe { umask(0o0027) };
|
|
||||||
|
|
||||||
// Set the SELinux security context for file creation
|
// Set the SELinux security context for file creation
|
||||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
||||||
selinux_util::do_setfscreatecon_for_path(&sel_lookup_path_raw, &labeler)?;
|
labeler.do_setfscreatecon_for_path()?;
|
||||||
|
|
||||||
|
// Set a umask
|
||||||
|
let before = unsafe { umask(0o0027) };
|
||||||
|
|
||||||
// Create the dir
|
// Create the dir
|
||||||
if let Err(e) = fs::create_dir_all(hd_path) {
|
if let Err(e) = fs::create_dir_all(hd_path) {
|
||||||
|
@ -172,17 +156,11 @@ fn create_home_directory(
|
||||||
|
|
||||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
||||||
{
|
{
|
||||||
// Look up the correct SELinux context of this object
|
let p = entry
|
||||||
let sel_lookup_path = Path::new(&sel_lookup_path_raw).join(
|
|
||||||
entry
|
|
||||||
.path()
|
.path()
|
||||||
.strip_prefix(skel_dir)
|
.strip_prefix(skel_dir)
|
||||||
.map_err(|e| e.to_string())?,
|
.map_err(|e| e.to_string())?;
|
||||||
);
|
labeler.label_path(p)?;
|
||||||
selinux_util::do_setfscreatecon_for_path(
|
|
||||||
&sel_lookup_path.to_str().unwrap().to_string(),
|
|
||||||
&labeler,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if entry.path().is_dir() {
|
if entry.path().is_dir() {
|
||||||
|
@ -194,22 +172,14 @@ fn create_home_directory(
|
||||||
|
|
||||||
// Create equivalence rule in the SELinux policy
|
// Create equivalence rule in the SELinux policy
|
||||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
||||||
if Command::new("semanage")
|
labeler.setup_equivalence_rule(&hd_path_raw)?;
|
||||||
.args(["fcontext", "-ae", &sel_lookup_path_raw, &hd_path_raw])
|
|
||||||
.spawn()
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
return Err("Failed creating SELinux policy equivalence rule".to_string());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset object creation SELinux context to default
|
// Reset object creation SELinux context to default
|
||||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
||||||
if SecurityContext::set_default_context_for_new_file_system_objects().is_err() {
|
labeler.set_default_context_for_fs_objects()?;
|
||||||
return Err("Failed resetting SELinux file creation contexts".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
let name_rel_path = Path::new(&name);
|
let name_rel_path = Path::new(&name);
|
||||||
// Does the aliases exist
|
// Does the aliases exist
|
||||||
|
@ -264,7 +234,12 @@ async fn handle_tasks(stream: UnixStream, cfg: &KanidmUnixdConfig) {
|
||||||
Some(Ok(TaskRequest::HomeDirectory(info))) => {
|
Some(Ok(TaskRequest::HomeDirectory(info))) => {
|
||||||
debug!("Received task -> HomeDirectory({:?})", info);
|
debug!("Received task -> HomeDirectory({:?})", info);
|
||||||
|
|
||||||
let resp = match create_home_directory(&info, &cfg.home_prefix, cfg.use_etc_skel) {
|
let resp = match create_home_directory(
|
||||||
|
&info,
|
||||||
|
&cfg.home_prefix,
|
||||||
|
cfg.use_etc_skel,
|
||||||
|
cfg.selinux,
|
||||||
|
) {
|
||||||
Ok(()) => TaskResponse::Success,
|
Ok(()) => TaskResponse::Success,
|
||||||
Err(msg) => TaskResponse::Error(msg),
|
Err(msg) => TaskResponse::Error(msg),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue