From dbac0dc5301e0b7b2adafbf930e3b1b6f5964421 Mon Sep 17 00:00:00 2001 From: refi64 Date: Sun, 30 Jun 2024 20:16:43 -0500 Subject: [PATCH] hostsfile: Copy the SELinux context to the temp file before overwrite (#273) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * hostsfile: Copy the SELinux context to the temp file before overwrite On SELinux-enabled systems, /etc/hosts has a different type `net_conf_t` than the other files in /etc, so the temporary file that overwrites it ends up with the wrong context, resulting in many system services becoming unable to access the file. To fix this, manually look up the context /etc/hosts has and copy it to the temporary file before the rename. In order to avoid depending on libselinux on systems that don't use it, this support is gated behind the new "selinux" feature. It *is* installed and enabled in the Dockerfile, however, in order to ensure that it still builds. * Appease clippy * Add info about selinux feature to README.md * Remove unused ClientError struct * Reformatted & repositioned and improved doc about selinux --------- Co-authored-by: Brian Schwind Co-authored-by: Jürgen Botz --- Cargo.lock | 205 ++++++++++++++++++++++++++++++- README.md | 6 + client/Cargo.toml | 3 + client/src/main.rs | 18 +-- docker-tests/Dockerfile.innernet | 4 +- hostsfile/Cargo.toml | 1 + hostsfile/src/lib.rs | 42 +++++++ shared/src/prompts.rs | 2 +- 8 files changed, 257 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 97eddba..5e323d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -142,6 +142,29 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.5.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -150,9 +173,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "byteorder" @@ -172,12 +195,32 @@ version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.1" @@ -326,6 +369,18 @@ dependencies = [ "shell-words", ] +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "either" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -450,6 +505,12 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "hashbrown" version = "0.14.3" @@ -496,11 +557,21 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "hostsfile" version = "1.2.0" dependencies = [ "log", + "selinux", "tempfile", ] @@ -605,6 +676,15 @@ dependencies = [ "serde", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -617,12 +697,28 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.4", +] + [[package]] name = "libsqlite3-sys" version = "0.26.0" @@ -680,6 +776,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.2" @@ -814,6 +916,16 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -908,6 +1020,16 @@ dependencies = [ "log", ] +[[package]] +name = "prettyplease" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.78" @@ -954,6 +1076,12 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "reference-counted-singleton" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "242f841f006fa4f35979f74147f6d0be4402c19ca25b62b1c8e4c02e28288cb9" + [[package]] name = "regex" version = "1.10.3" @@ -989,7 +1117,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -1003,6 +1131,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1018,7 +1152,7 @@ version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -1031,12 +1165,47 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "selinux" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53371b1e9bbbfffd65e5ac3c895c786ec35b7695bdc4a67a8b08c29c8d057e0b" +dependencies = [ + "bitflags 2.5.0", + "libc", + "once_cell", + "reference-counted-singleton", + "selinux-sys", + "thiserror", +] + +[[package]] +name = "selinux-sys" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d45498373dc17ec8ebb72e1fd320c015647b0157fc81dddf678e2e00205fec" +dependencies = [ + "bindgen", + "cc", + "dunce", + "walkdir", +] + [[package]] name = "semver" version = "1.0.22" @@ -1151,6 +1320,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "smallvec" version = "1.13.1" @@ -1418,6 +1593,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -1433,6 +1618,18 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/README.md b/README.md index d058ef8..272c978 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,12 @@ Note that you'll be responsible for updating manually. ## Development +### Cargo build feature for SELinux + +If your target system uses SELinux, you will want to enable the 'selinux' feature when building the innernet binary. +This will ensure that innernet maintains the correct selinux context on the /etc/hosts file when adding hosts. To do so add ```--features selinux``` to the ```cargo build``` options. +The `selinux-devel` package will need to be installed for the correct headers. + ### `innernet-server` Build dependencies - `rustc` / `cargo` (version 1.50.0 or higher) diff --git a/client/Cargo.toml b/client/Cargo.toml index 09ffb4e..32abf91 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -40,6 +40,9 @@ wireguard-control = { path = "../wireguard-control" } once_cell = "1.17.1" tempfile = "3" +[features] +selinux = ["hostsfile/selinux"] + [package.metadata.deb] assets = [ ["target/release/innernet", "usr/bin/", "755"], diff --git a/client/src/main.rs b/client/src/main.rs index 0f7f8e6..67cbf1a 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -15,7 +15,7 @@ use shared::{ RedeemContents, RenameCidrOpts, RenamePeerOpts, State, WrappedIoError, REDEEM_TRANSITION_WAIT, }; use std::{ - fmt, io, + io, net::SocketAddr, path::{Path, PathBuf}, thread, @@ -281,22 +281,6 @@ enum Command { }, } -/// Application-level error. -#[derive(Debug, Clone)] -pub(crate) struct ClientError(String); - -impl fmt::Display for ClientError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl std::error::Error for ClientError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - None - } -} - fn update_hosts_file( interface: &InterfaceName, hosts_path: PathBuf, diff --git a/docker-tests/Dockerfile.innernet b/docker-tests/Dockerfile.innernet index 18baab1..d216582 100644 --- a/docker-tests/Dockerfile.innernet +++ b/docker-tests/Dockerfile.innernet @@ -16,13 +16,13 @@ RUN mkdir /repo \ #################################################################################################### FROM rust:slim-bookworm RUN apt-get update && \ - apt-get install -y --no-install-recommends libsqlite3-dev iproute2 iputils-ping build-essential clang libclang-dev && \ + apt-get install -y --no-install-recommends libsqlite3-dev iproute2 iputils-ping build-essential clang libclang-dev libselinux1-dev && \ rm -rf /var/lib/apt/lists/* WORKDIR /app COPY . . -RUN cargo build \ +RUN cargo build --features client/selinux \ && strip /app/target/debug/innernet /app/target/debug/innernet-server \ && cp /app/target/debug/innernet /app/target/debug/innernet-server /usr/bin/ \ && cargo clean diff --git a/hostsfile/Cargo.toml b/hostsfile/Cargo.toml index 77526c0..0fa8bb0 100644 --- a/hostsfile/Cargo.toml +++ b/hostsfile/Cargo.toml @@ -9,6 +9,7 @@ version = "1.2.0" [dependencies] log = "0.4" +selinux = { version = "0.4", optional = true } [dev-dependencies] tempfile = "3" diff --git a/hostsfile/src/lib.rs b/hostsfile/src/lib.rs index 514cc5c..b8844a2 100644 --- a/hostsfile/src/lib.rs +++ b/hostsfile/src/lib.rs @@ -285,6 +285,48 @@ impl HostsBuilder { fn write_and_swap(temp_path: &Path, hosts_path: &Path, contents: &[u8]) -> io::Result<()> { // Copy the file we plan on modifying so its permissions and metadata are preserved. std::fs::copy(hosts_path, temp_path)?; + + #[cfg(feature = "selinux")] + if selinux::current_mode() != selinux::SELinuxMode::NotRunning { + log::trace!("SELinux is running; copying context"); + use selinux::SecurityContext; + + const FOLLOW_SYMBOLIC_LINKS: bool = false; + const RAW_FORMAT: bool = false; + match SecurityContext::of_path(hosts_path, FOLLOW_SYMBOLIC_LINKS, RAW_FORMAT) { + Ok(Some(context)) => { + log::trace!( + "{} context is {:?}", + hosts_path.display(), + context.to_c_string() + ); + if let Err(err) = + context.set_for_path(temp_path, FOLLOW_SYMBOLIC_LINKS, RAW_FORMAT) + { + log::warn!( + "SELinux context of {} ({:?}) could not be set \ + ({} may become inaccessible due to permission errors): {:?}", + temp_path.display(), + context.to_c_string(), + hosts_path.display(), + err + ); + } + }, + Ok(None) => { + log::trace!("Hosts file {} had no SELinux context", hosts_path.display()); + }, + Err(err) => { + log::warn!( + "SELinux context of {} could not be retrieved \ + (file may become inaccessible due to permission errors): {:?}", + hosts_path.display(), + err + ); + }, + } + } + Self::write_clobber(temp_path, contents)?; std::fs::rename(temp_path, hosts_path)?; Ok(()) diff --git a/shared/src/prompts.rs b/shared/src/prompts.rs index 238249a..7855663 100644 --- a/shared/src/prompts.rs +++ b/shared/src/prompts.rs @@ -138,7 +138,7 @@ pub fn rename_cidr( }; let mut new_cidr = old_cidr; - new_cidr.contents.name = new_name.clone(); + new_cidr.contents.name.clone_from(&new_name); Ok( if args.yes