Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

posix-acls: POSIX ACL Modeling

Your team shares a Linux server. Source code, build artifacts, and documentation all live on the filesystem, and different people need different levels of access. Traditional Unix permissions (chmod 750) give you three slots – owner, group, everyone else – but you need to give one contractor read access and one DBA read-write access without touching the group membership of every file. That is the problem POSIX ACLs (POSIX.1e) solve.

posix-acls provides Rust types that model the full POSIX.1e ACL system – from individual rwx permission triples up through complete ACLs with named user and group entries, default ACLs for directories, mask computation, and the access-check algorithm. On top of that core model, a higher-level policy layer lets you declare permissions against a user/group directory and derive concrete ACLs automatically.

Two layers

The crate is organised into two layers, each building on the previous:

  1. Typed POSIX ACL model – the rwx permission triple (PosixPerm) with lattice algebra, the six ACL tag types (AclTag), ACL entries (AclEntry), and the complete ACL type (PosixAcl) with fluent construction (AclBuilder), getfacl(1) parsing and generation, Unix mode word conversion, and the POSIX.1e access-check algorithm.

  2. User/group model and policy – an abstract directory of users and groups (UserGroupModel) with a policy system (AclPolicy) that assigns permissions to principals and derives validated PosixAcl values. The model ensures that every named grant references a real user or group.

An acls-rs bridge connects both layers to the algebraic permission system in acls-rs, so you can mix POSIX file permissions with application-level access control.

Quick start

Add posix-acls to your Cargo.toml:

[dependencies]
posix-acls = "0.1"

With serde support:

[dependencies]
posix-acls = { version = "0.1", features = ["serde"] }

A minimal example that builds an ACL, serialises it to getfacl text, and checks access:

#![allow(unused)]
fn main() {
use posix_acls::prelude::*;

// Build an ACL for a shared project directory
let acl = AclBuilder::new("alice", "devs")
    .owner_perms(PosixPerm::RWX)
    .owning_group_perms(PosixPerm::RX)
    .other_perms(PosixPerm::NONE)
    .add_user("bob", PosixPerm::RW)
    .build();

// Serialise to the getfacl(1) text format
let text = generate::<GetfaclGenerator>(&acl).unwrap();
assert!(text.contains("user:bob:rw-"));

// Parse it back
let recovered = parse::<GetfaclParser>(&text).unwrap();
assert_eq!(acl.owner(), recovered.owner());

// Check access -- bob can read, but not execute
let r = acl.check_access("bob", &["staff"] as &[&str], PosixPerm::R);
assert!(r.granted);

let r = acl.check_access("bob", &["staff"] as &[&str], PosixPerm::X);
assert!(!r.granted);
}

Policy-driven ACLs

When your organisation has dozens of users across multiple teams, building ACLs by hand is error-prone. The policy layer lets you declare intent and validate it against a user/group directory:

#![allow(unused)]
fn main() {
use posix_acls::prelude::*;

// Define the organisation's users and groups
let mut model = UserGroupModel::new();
model.add_user(UserEntry::new("alice").with_groups(["devs"]));
model.add_user(UserEntry::new("bob").with_groups(["devs"]));
model.add_user(UserEntry::new("carol").with_groups(["ops"]));
model.add_group(GroupEntry::new("devs"));
model.add_group(GroupEntry::new("ops"));

// Declare a policy for the project directory
let policy = AclPolicy::builder()
    .owner("alice")
    .owning_group("devs")
    .owner_perms(PosixPerm::RWX)
    .owning_group_perms(PosixPerm::RX)
    .other_perms(PosixPerm::NONE)
    .add_user("bob", PosixPerm::RW)
    .add_group("ops", PosixPerm::R)
    .build()
    .unwrap();

// Generate a concrete ACL -- the model validates that "bob" and "ops" exist
let acl = policy.generate(&model).unwrap();
assert!(acl.is_valid().is_ok());
assert!(acl.is_extended());
}

Mode word interoperability

POSIX ACLs and traditional Unix mode words (chmod 755) are two views of the same permission model. posix-acls converts between them losslessly for minimal (non-extended) ACLs:

#![allow(unused)]
fn main() {
use posix_acls::prelude::*;

// Build from a mode word
let acl = PosixAcl::from_mode("alice", "devs", 0o755);
assert_eq!(acl.to_mode(), 0o755);

// Extended ACLs reflect the mask in stat()'s group bits (POSIX.1e section 23.1.6)
let extended = AclBuilder::new("alice", "devs")
    .owner_perms(PosixPerm::RWX)
    .owning_group_perms(PosixPerm::RX)
    .other_perms(PosixPerm::R)
    .add_user("bob", PosixPerm::RW)
    .build();
assert_eq!((extended.to_mode() >> 3) & 0o7, 0o7); // mask = rwx
}

Integration with acls-rs

The bridge module converts between POSIX permission triples and acls-rs PermissionSet values, so you can combine file-level access control with application-level policies:

#![allow(unused)]
fn main() {
use posix_acls::prelude::*;
use acls_rs::permission::AtomicPermission;

let acl = AclBuilder::new("alice", "devs")
    .owner_perms(PosixPerm::RWX)
    .owning_group_perms(PosixPerm::RX)
    .other_perms(PosixPerm::NONE)
    .build();

// Convert Alice's effective permissions to an acls-rs PermissionSet
let set = acl.to_permission_set("alice", &[] as &[&str]);
assert!(set.contains(&AtomicPermission::new("posix", "read")));
assert!(set.contains(&AtomicPermission::new("posix", "write")));
assert!(set.contains(&AtomicPermission::new("posix", "execute")));

// Or as a GrantDenialPair (POSIX ACLs have no explicit denials)
let gdp = acl.to_grant_denial_pair("alice", &[] as &[&str]);
assert!(gdp.denials.is_empty());
}
  • Permissions – the rwx permission triple, special bits, and the lattice algebra that makes composition well-defined.
  • Building ACLs – tags, entries, the PosixAcl type, fluent construction with AclBuilder, and structural validation.
  • Parsing and Generation – reading and writing the getfacl(1) text format, round-trip guarantees, and extending with custom formats.
  • Access Checks – the full POSIX.1e access-check algorithm, the role of the mask, and mode word conversion.
  • User/Group Model and Policy – declaring users and groups, building policies, deriving validated ACLs, and handling errors.
  • acls-rs Bridge – converting between POSIX permissions and acls-rs types for cross-system access control.
  • Temporal Access – end-to-end example: time-bounded contractor team grants → group-based POSIX ACLs → per-user effective access → setfacl commands.