hostsfile: Copy the SELinux context to the temp file before overwrite (#273)

* 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 <brianmschwind@gmail.com>
Co-authored-by: Jürgen Botz <jurgen@botz.org>
This commit is contained in:
refi64 2024-06-30 20:16:43 -05:00 committed by GitHub
parent 85c8cc37ec
commit dbac0dc530
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 257 additions and 24 deletions

205
Cargo.lock generated
View file

@ -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"

View file

@ -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)

View file

@ -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"],

View file

@ -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,

View file

@ -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

View file

@ -9,6 +9,7 @@ version = "1.2.0"
[dependencies]
log = "0.4"
selinux = { version = "0.4", optional = true }
[dev-dependencies]
tempfile = "3"

View file

@ -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(())

View file

@ -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