| Crates.io | priv_sep |
| lib.rs | priv_sep |
| version | 3.0.0-alpha.4.0 |
| created_at | 2023-07-25 06:19:55.703818+00 |
| updated_at | 2026-01-11 01:24:06.719083+00 |
| description | FFI for chroot(2), pledge(2), setgroups(2), setresgid(2), setresuid(2), and unveil(2). |
| homepage | |
| repository | https://git.philomathiclife.com/repos/priv_sep/ |
| max_upload_size | |
| id | 925219 |
| size | 251,116 |
priv_sep is a library that uses the system's libc to perform privilege separation and privilege reduction
for Unix-like platforms. The following target_os values are supported:
dragonflyfreebsdlinuxmacosnetbsdopenbsdpriv_sep in actionuse core::convert::Infallible;
use priv_sep::{PrivDropErr, UserInfo};
use std::{
io::Error,
net::{Ipv6Addr, SocketAddrV6},
};
use tokio::net::TcpListener;
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<Infallible, PrivDropErr<Error>> {
// Get the user ID and group ID for nobody from `passwd(5)`.
// `chroot(2)` to `/path/chroot/` and `chdir(2)` to `/`.
// Bind to TCP `[::1]:443` as root.
// `setgroups(2)` to drop all supplementary groups.
// `setresgid(2)` to the group ID associated with nobody.
// `setresuid(2)` to the user ID associated with nobody.
let listener =
UserInfo::chroot_then_priv_drop_async(c"nobody", c"/path/chroot/", false, async || {
TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)).await
})
.await?;
// At this point, the process is running under nobody.
loop {
// Handle TCP connections.
if let Ok((_, ip)) = listener.accept().await {
assert!(ip.is_ipv6());
}
}
}
pledge(2) and unveil(2) on OpenBSDuse core::convert::Infallible;
use priv_sep::{Permissions, PrivDropErr, Promise, Promises};
use std::{
fs,
io::Error,
net::{Ipv6Addr, SocketAddrV6},
};
use tokio::net::TcpListener;
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<Infallible, PrivDropErr<Error>> {
/// Config file.
const CONFIG: &str = "config";
// Get the user ID and group ID for nobody from `passwd(5)`.
// `chroot(2)` to `/path/chroot/` and `chdir(2)` to `/`.
// `pledge(2)` `id`, `inet`, `rpath`, `stdio`, and `unveil`.
// Bind to TCP `[::1]:443` as root.
// `setgroups(2)` to drop all supplementary groups.
// `setresgid(2)` to the group ID associated with nobody.
// `setresuid(2)` to the user ID associated with nobody.
// Remove `id` from our `pledge(2)`d promises.
let (listener, mut promises) = Promises::new_chroot_then_priv_drop_async(
c"nobody",
c"/path/chroot/",
[Promise::Inet, Promise::Rpath, Promise::Unveil],
false,
async || TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)).await,
)
.await?;
// At this point, the process is running under nobody.
// Only allow file system access to `config` and only allow read access to it.
Permissions::READ.unveil(CONFIG)?;
// Read `config`.
// This will of course fail if the file does not exist or nobody does not
// have read permissions.
let config = fs::read(CONFIG).map_err(PrivDropErr::Other)?;
// Remove file system access.
Permissions::NONE.unveil(CONFIG)?;
// Remove `rpath` and `unveil` from our `pledge(2)`d promises
// (i.e., only have `inet` and `stdio` abilities when we begin accepting TCP connections).
promises.remove_promises_then_pledge([Promise::Rpath, Promise::Unveil])?;
loop {
// Handle TCP connections.
if let Ok((_, ip)) = listener.accept().await {
assert!(ip.is_ipv6());
}
}
}
allocEnables alloc support. While "typical" use of priv_sep should work without alloc, there are cases
where one may desire heap allocation. For example if a database entry associated with a user requires
more than 1 KiB of space, UserInfo::new will error with Errno::ERANGE when alloc is not enabled.
Additional CStrHelper impls are exposed as well (e.g., String).
stdEnables std support. This is useful for additional CStrHelper impls (e.g., OsStr) as well as
TryFrom<Error> and From<Errno>.
This feature implies alloc and is enabled by default via the default feature.
This will frequently be updated to be the same as stable. Specifically, any time stable is updated and that update has "useful" features or compilation no longer succeeds (e.g., due to new compiler lints), then MSRV will be updated.
MSRV changes will correspond to a SemVer minor version bump.
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Before any PR is sent, cargo clippy --all-targets, cargo test --all-targets, and cargo test --doc should be
run for each possible combination of "features" using the stable and MSRV toolchains. One easy way to achieve this
is by invoking ci-cargo as ci-cargo clippy --all-targets test --all-targets
in the priv_sep directory.
Additionally, one should test all ignore tests as both root and non-root for both toolchains. These tests should
be run individually since they may interfere with each other.
Last, RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features -Zbuild-std=std should be run to ensure
documentation can be built.
The crate is only tested on the x86_64-unknown-linux-gnu, x86_64-unknown-openbsd, and aarch64-apple-darwin
targets; but it should work on most of the supported platforms.