mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +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:
|
||||
|
||||
```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.
|
||||
|
||||
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
|
||||
|
||||
You will need [rustup](https://rustup.rs/) to install a Rust toolchain.
|
||||
|
|
|
@ -28,18 +28,19 @@ async fn get_webdriver_client() -> fantoccini::Client {
|
|||
};
|
||||
if !in_ci {
|
||||
match fantoccini::ClientBuilder::native()
|
||||
.connect("http://localhost:4444")
|
||||
.await
|
||||
{
|
||||
Ok(val) => val,
|
||||
Err(_) => {
|
||||
// trying the default chromedriver port
|
||||
eprintln!("Couldn't connect on 4444, trying 9515");
|
||||
fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new()).connect("http://localhost:9515")
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
.connect("http://localhost:4444")
|
||||
.await
|
||||
{
|
||||
Ok(val) => val,
|
||||
Err(_) => {
|
||||
// trying the default chromedriver port
|
||||
eprintln!("Couldn't connect on 4444, trying 9515");
|
||||
fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new())
|
||||
.connect("http://localhost:9515")
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("In CI setting headless and assuming Chrome");
|
||||
let cap = json!({
|
||||
|
@ -48,8 +49,11 @@ async fn get_webdriver_client() -> fantoccini::Client {
|
|||
}
|
||||
});
|
||||
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::path::Path;
|
||||
use std::process::Command;
|
||||
use users::get_user_by_uid;
|
||||
|
||||
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 {
|
||||
|
@ -16,18 +20,7 @@ pub fn supported() -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub 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 fn do_setfscreatecon_for_path(
|
||||
path_raw: &String,
|
||||
labeler: &Labeler<File>,
|
||||
) -> Result<(), String> {
|
||||
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) {
|
||||
Ok(context) => {
|
||||
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"))]
|
||||
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;
|
||||
|
||||
|
@ -96,6 +90,7 @@ fn create_home_directory(
|
|||
info: &HomeDirectoryInfo,
|
||||
home_prefix: &str,
|
||||
use_etc_skel: bool,
|
||||
use_selinux: bool,
|
||||
) -> Result<(), String> {
|
||||
// Final sanity check to prevent certain classes of attacks.
|
||||
let name = info.name.trim_start_matches('.').replace(['/', '\\'], "");
|
||||
|
@ -121,33 +116,22 @@ fn create_home_directory(
|
|||
}
|
||||
|
||||
// Get a handle to the SELinux labeling interface
|
||||
debug!(?use_selinux, "selinux for home dir labeling");
|
||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
||||
let labeler = selinux_util::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(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());
|
||||
}
|
||||
let labeler = if use_selinux {
|
||||
selinux_util::SelinuxLabeler::new(info.gid, home_prefix)?
|
||||
} else {
|
||||
selinux_util::SelinuxLabeler::new_noop()
|
||||
};
|
||||
|
||||
// Does the home directory exist?
|
||||
if !hd_path.exists() {
|
||||
// Set a umask
|
||||
let before = unsafe { umask(0o0027) };
|
||||
|
||||
// Set the SELinux security context for file creation
|
||||
#[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
|
||||
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"))]
|
||||
{
|
||||
// Look up the correct SELinux context of this object
|
||||
let sel_lookup_path = Path::new(&sel_lookup_path_raw).join(
|
||||
entry
|
||||
.path()
|
||||
.strip_prefix(skel_dir)
|
||||
.map_err(|e| e.to_string())?,
|
||||
);
|
||||
selinux_util::do_setfscreatecon_for_path(
|
||||
&sel_lookup_path.to_str().unwrap().to_string(),
|
||||
&labeler,
|
||||
)?;
|
||||
let p = entry
|
||||
.path()
|
||||
.strip_prefix(skel_dir)
|
||||
.map_err(|e| e.to_string())?;
|
||||
labeler.label_path(p)?;
|
||||
}
|
||||
|
||||
if entry.path().is_dir() {
|
||||
|
@ -194,22 +172,14 @@ fn create_home_directory(
|
|||
|
||||
// Create equivalence rule in the SELinux policy
|
||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
||||
if Command::new("semanage")
|
||||
.args(["fcontext", "-ae", &sel_lookup_path_raw, &hd_path_raw])
|
||||
.spawn()
|
||||
.is_err()
|
||||
{
|
||||
return Err("Failed creating SELinux policy equivalence rule".to_string());
|
||||
}
|
||||
labeler.setup_equivalence_rule(&hd_path_raw)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset object creation SELinux context to default
|
||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
||||
if SecurityContext::set_default_context_for_new_file_system_objects().is_err() {
|
||||
return Err("Failed resetting SELinux file creation contexts".to_string());
|
||||
}
|
||||
labeler.set_default_context_for_fs_objects()?;
|
||||
|
||||
let name_rel_path = Path::new(&name);
|
||||
// Does the aliases exist
|
||||
|
@ -264,7 +234,12 @@ async fn handle_tasks(stream: UnixStream, cfg: &KanidmUnixdConfig) {
|
|||
Some(Ok(TaskRequest::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,
|
||||
Err(msg) => TaskResponse::Error(msg),
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue