README.md (6599B)
1 Privilege separation library for Unix-likes OSes 2 ================================================ 3 4 [<img alt="git" src="https://git.philomathiclife.com/badges/priv_sep.svg" height="20">](https://git.philomathiclife.com/priv_sep/log.html) 5 [<img alt="crates.io" src="https://img.shields.io/crates/v/priv_sep.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/priv_sep) 6 [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-priv_sep-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/priv_sep/latest/priv_sep/) 7 8 `priv_sep` is a library that uses the system's libc to perform privilege separation and privilege reduction 9 for Unix-like platforms. The following `target_os` values are supported: 10 11 * `dragonfly` 12 * `freebsd` 13 * `linux` 14 * `macos` 15 * `netbsd` 16 * `openbsd` 17 18 ## `priv_sep` in action 19 20 ```rust 21 use core::convert::Infallible; 22 use priv_sep::{PrivDropErr, UserInfo}; 23 use std::{ 24 io::Error, 25 net::{Ipv6Addr, SocketAddrV6}, 26 }; 27 use tokio::net::TcpListener; 28 #[tokio::main(flavor = "current_thread")] 29 async fn main() -> Result<Infallible, PrivDropErr<Error>> { 30 // Get the user ID and group ID for nobody from `passwd(5)`. 31 // `chroot(2)` to `/path/chroot/` and `chdir(2)` to `/`. 32 // Bind to TCP `[::1]:443` as root. 33 // `setgroups(2)` to drop all supplementary groups. 34 // `setresgid(2)` to the group ID associated with nobody. 35 // `setresuid(2)` to the user ID associated with nobody. 36 let listener = 37 UserInfo::chroot_then_priv_drop_async(c"nobody", c"/path/chroot/", false, async || { 38 TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)).await 39 }) 40 .await?; 41 // At this point, the process is running under nobody. 42 loop { 43 // Handle TCP connections. 44 if let Ok((_, ip)) = listener.accept().await { 45 assert!(ip.is_ipv6()); 46 } 47 } 48 } 49 ``` 50 51 <details> 52 <summary>Incorporating <a href="https://man.openbsd.org/pledge.2"><code>pledge(2)</code></a> and <a href="https://man.openbsd.org/unveil.2"><code>unveil(2)</code></a> on OpenBSD</summary> 53 54 ```rust 55 use core::convert::Infallible; 56 use priv_sep::{Permissions, PrivDropErr, Promise, Promises}; 57 use std::{ 58 fs, 59 io::Error, 60 net::{Ipv6Addr, SocketAddrV6}, 61 }; 62 use tokio::net::TcpListener; 63 #[tokio::main(flavor = "current_thread")] 64 async fn main() -> Result<Infallible, PrivDropErr<Error>> { 65 /// Config file. 66 const CONFIG: &str = "config"; 67 // Get the user ID and group ID for nobody from `passwd(5)`. 68 // `chroot(2)` to `/path/chroot/` and `chdir(2)` to `/`. 69 // `pledge(2)` `id`, `inet`, `rpath`, `stdio`, and `unveil`. 70 // Bind to TCP `[::1]:443` as root. 71 // `setgroups(2)` to drop all supplementary groups. 72 // `setresgid(2)` to the group ID associated with nobody. 73 // `setresuid(2)` to the user ID associated with nobody. 74 // Remove `id` from our `pledge(2)`d promises. 75 let (listener, mut promises) = Promises::new_chroot_then_priv_drop_async( 76 c"nobody", 77 c"/path/chroot/", 78 [Promise::Inet, Promise::Rpath, Promise::Unveil], 79 false, 80 async || TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)).await, 81 ) 82 .await?; 83 // At this point, the process is running under nobody. 84 // Only allow file system access to `config` and only allow read access to it. 85 Permissions::READ.unveil(CONFIG)?; 86 // Read `config`. 87 // This will of course fail if the file does not exist or nobody does not 88 // have read permissions. 89 let config = fs::read(CONFIG).map_err(PrivDropErr::Other)?; 90 // Remove file system access. 91 Permissions::NONE.unveil(CONFIG)?; 92 // Remove `rpath` and `unveil` from our `pledge(2)`d promises 93 // (i.e., only have `inet` and `stdio` abilities when we begin accepting TCP connections). 94 promises.remove_promises_then_pledge([Promise::Rpath, Promise::Unveil])?; 95 loop { 96 // Handle TCP connections. 97 if let Ok((_, ip)) = listener.accept().await { 98 assert!(ip.is_ipv6()); 99 } 100 } 101 } 102 ``` 103 </details> 104 105 ## Cargo "features" 106 107 ### `alloc` 108 109 Enables `alloc` support. While "typical" use of `priv_sep` should work without `alloc`, there are cases 110 where one may desire heap allocation. For example if a database entry associated with a user requires 111 more than 1 KiB of space, `UserInfo::new` will error with `Errno::ERANGE` when `alloc` is not enabled. 112 113 Additional `CStrHelper` `impl`s are exposed as well (e.g., `String`). 114 115 ### `std` 116 117 Enables `std` support. This is useful for additional `CStrHelper` `impl`s (e.g., `OsStr`) as well as 118 `TryFrom<Error>` and `From<Errno>`. 119 120 This feature implies [`alloc`](#alloc) and is enabled by default via the `default` feature. 121 122 ## Minimum Supported Rust Version (MSRV) 123 124 This will frequently be updated to be the same as stable. Specifically, any time stable is updated and that 125 update has "useful" features or compilation no longer succeeds (e.g., due to new compiler lints), then MSRV 126 will be updated. 127 128 MSRV changes will correspond to a SemVer minor version bump. 129 130 ## SemVer Policy 131 132 * All on-by-default features of this library are covered by SemVer 133 * MSRV is considered exempt from SemVer as noted above 134 135 ## License 136 137 Licensed under either of 138 139 * Apache License, Version 2.0 ([LICENSE-APACHE](https://www.apache.org/licenses/LICENSE-2.0)) 140 * MIT license ([LICENSE-MIT](https://opensource.org/licenses/MIT)) 141 142 at your option. 143 144 ## Contribution 145 146 Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, 147 as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 148 149 Before any PR is sent, `cargo clippy --all-targets`, `cargo test --all-targets`, and `cargo test --doc` should be 150 run _for each possible combination of "features"_ using the stable and MSRV toolchains. One easy way to achieve this 151 is by invoking [`ci-cargo`](https://crates.io/crates/ci-cargo) as `ci-cargo clippy --all-targets test --all-targets` 152 in the `priv_sep` directory. 153 154 Additionally, one should test all `ignore` tests as both root and non-root for both toolchains. These tests should 155 be run individually since they may interfere with each other. 156 157 Last, `RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features -Zbuild-std=std` should be run to ensure 158 documentation can be built. 159 160 ### Status 161 162 The crate is only tested on the `x86_64-unknown-linux-gnu`, `x86_64-unknown-openbsd`, and `aarch64-apple-darwin` 163 targets; but it should work on most of the supported platforms.