priv_sep

Crates.iopriv_sep
lib.rspriv_sep
version3.0.0-alpha.1.3
created_at2023-07-25 06:19:55.703818+00
updated_at2025-08-26 14:15:54.694125+00
descriptionFFI for setresuid(2), setresgid(2), chroot(2), pledge(2), and unveil(2).
homepage
repositoryhttps://git.philomathiclife.com/repos/priv_sep/
max_upload_size
id925219
size134,454
philomathic_life (zacknewman)

documentation

https://docs.rs/priv_sep/latest/priv_sep/

README

priv_sep

git crates.io docs.rs

priv_sep is a library that uses the system's libc to perform privilege separation and privilege reduction.

priv_sep in action for OpenBSD

use 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.
    // `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(
        "nobody",
        "/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)?;
    // 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());
        }
    }
}

priv_sep in action for Unix-like OSes

use core::convert::Infallible;
use priv_sep::{UserInfo, PrivDropErr};
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.
    // `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("nobody", "/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());
        }
    }
}

Minimum Supported Rust Version (MSRV)

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.

SemVer Policy

  • All on-by-default features of this library are covered by SemVer
  • MSRV is considered exempt from SemVer as noted above

License

Licensed under either of

at your option.

Contribution

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 and cargo t should be run. Additionally RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features should be run to ensure documentation can be built.

Status

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 platforms.

Commit count: 0

cargo fmt