FreeBSD.software
Home/Guides/FreeBSD User and Permission Management Guide
guide·2026-04-09·9 min read

FreeBSD User and Permission Management Guide

Complete guide to FreeBSD user and permission management: users, groups, ACLs, login classes, capabilities, MAC framework, file flags, and chflags.

FreeBSD User and Permission Management Guide

FreeBSD has a layered permission model that goes far beyond basic Unix file permissions. Users, groups, ACLs, login classes, the MAC framework, and file flags give you fine-grained control over who can do what on the system.

This guide covers the full stack, from basic user administration through advanced mandatory access control. Every command runs on FreeBSD 14.

User Management Fundamentals

Creating Users

The pw command is FreeBSD's user administration tool. It is more scriptable than adduser:

sh
pw useradd -n jsmith -c "John Smith" -m -s /bin/sh -G wheel,staff

Flags:

  • -n -- username
  • -c -- comment (full name)
  • -m -- create home directory
  • -s -- login shell
  • -G -- supplementary groups

Set the password:

sh
passwd jsmith

For batch user creation:

sh
cat > /tmp/users.txt << 'EOF' jsmith:John Smith:staff,wheel mjones:Mary Jones:staff,finance bwilson:Bob Wilson:staff EOF while IFS=: read -r user fullname groups; do pw useradd -n "$user" -c "$fullname" -m -s /bin/sh -G "$groups" echo "changeme" | pw usermod -n "$user" -h 0 done < /tmp/users.txt

Modifying Users

Add a user to additional groups:

sh
pw groupmod finance -m jsmith

Change a user's shell:

sh
pw usermod -n jsmith -s /usr/local/bin/bash

Lock an account (disable login without deleting):

sh
pw lock jsmith

Unlock:

sh
pw unlock jsmith

Deleting Users

Remove a user and their home directory:

sh
pw userdel -n jsmith -r

Without -r, the home directory is preserved. Good practice: lock the account first, verify no running processes, then delete.

sh
pw lock jsmith ps aux | grep jsmith pw userdel -n jsmith -r

Viewing User Information

sh
id jsmith finger jsmith pw usershow jsmith

List all users:

sh
pw usershow -a

Group Management

Creating Groups

sh
pw groupadd -n finance pw groupadd -n engineering pw groupadd -n management

Managing Group Membership

Add users to a group:

sh
pw groupmod finance -m jsmith,mjones

Remove a user from a group:

sh
pw groupmod finance -d bwilson

View group membership:

sh
pw groupshow finance getent group finance

The wheel Group

The wheel group controls su access. Only users in wheel can su to root:

sh
pw groupmod wheel -m jsmith

This is enforced by PAM in /etc/pam.d/su:

sh
auth requisite pam_group.so no_warn group=wheel root_only

Standard Unix Permissions

Permission Basics

Every file has an owner, a group, and three permission sets: owner, group, others. Each set has read (r=4), write (w=2), and execute (x=1).

sh
ls -la /data/shared/

Set permissions:

sh
chmod 750 /data/finance chown root:finance /data/finance

This gives the owner (root) full access, the finance group read and execute, and others nothing.

The Setgid Bit

For shared directories, the setgid bit ensures new files inherit the directory's group:

sh
chmod 2770 /data/finance

Without setgid, new files get the creator's primary group. With setgid, they get the directory's group -- critical for shared workspaces.

Umask

The umask controls default permissions for new files. Set it system-wide in /etc/login.conf or per-user in shell profile:

sh
# In /etc/login.conf under the default class: # :umask=022: # Per-user in ~/.profile: umask 027

A umask of 027 means new files get 640 (rw-r-----) and directories get 750 (rwxr-x---).

POSIX ACLs

Standard Unix permissions are limited to one owner, one group, and everyone else. ACLs add fine-grained access for multiple users and groups.

Enable ACLs

ACLs are supported on UFS and ZFS. On UFS, enable them per-mount:

sh
tunefs -a enable /dev/ada0p2 mount -o acls /dev/ada0p2 /data

On ZFS, ACLs are always available. For NFSv4-style ACLs:

sh
zfs set acltype=nfsv4 zroot/data

Setting ACLs

Grant a specific user read access to a directory:

sh
setfacl -m u:mjones:rx /data/reports

Grant a group write access:

sh
setfacl -m g:finance:rwx /data/budget

Set a default ACL so new files inherit permissions:

sh
setfacl -d -m g:finance:rwx /data/budget

Viewing ACLs

sh
getfacl /data/budget

Output:

sh
# file: /data/budget # owner: root # group: finance user::rwx group::rwx group:finance:rwx mask::rwx other::--- default:user::rwx default:group::rwx default:group:finance:rwx default:mask::rwx default:other::---

Removing ACLs

Remove a specific entry:

sh
setfacl -x u:mjones /data/reports

Remove all ACLs:

sh
setfacl -b /data/reports

NFSv4 ACLs on ZFS

ZFS supports the richer NFSv4 ACL model with inheritance, deny entries, and more granular permissions:

sh
setfacl -m u:mjones:rwxpDdaARWcCos:fd:allow /data/project

This is verbose but powerful. The flags control individual permissions like delete, read attributes, write ACL, and more.

Login Classes

Login classes define resource limits and environment settings for groups of users. They are configured in /etc/login.conf.

Viewing the Default Class

sh
cat /etc/login.conf | head -40

Creating a Custom Login Class

Add a restricted class for external contractors:

sh
cat >> /etc/login.conf << 'EOF' contractor:\ :maxproc=50:\ :openfiles=256:\ :stacksize=8M:\ :memoryuse=512M:\ :cputime=4h:\ :umask=077:\ :tc=default: EOF

Rebuild the login database:

sh
cap_mkdb /etc/login.conf

Assign the class to a user:

sh
pw usermod -n contractor1 -L contractor

Resource Limits via Login Classes

Common limits:

| Limit | Description | Example |

|-------|-------------|---------|

| maxproc | Max processes | 100 |

| openfiles | Max open files | 512 |

| memoryuse | Max memory per process | 1G |

| cputime | Max CPU time per process | 8h |

| filesize | Max file size | 2G |

| coredumpsize | Max core dump size | 0 (disable) |

Create a developer class with generous limits:

sh
cat >> /etc/login.conf << 'EOF' developer:\ :maxproc=1024:\ :openfiles=8192:\ :stacksize=64M:\ :memoryuse=4G:\ :cputime=unlimited:\ :umask=022:\ :tc=default: EOF cap_mkdb /etc/login.conf

Capabilities and Privilege Escalation

sudo Configuration

Install and configure sudo for controlled privilege escalation:

sh
pkg install sudo visudo

Common configurations:

sh
# Allow wheel group full sudo access %wheel ALL=(ALL:ALL) ALL # Allow deploy user to restart services without password deploy ALL=(root) NOPASSWD: /usr/sbin/service nginx restart, /usr/sbin/service php-fpm restart # Allow backup user to run ZFS commands backup ALL=(root) NOPASSWD: /sbin/zfs snapshot *, /sbin/zfs send *, /sbin/zfs list *

doas: A Simpler Alternative

sh
pkg install doas

Configure /usr/local/etc/doas.conf:

sh
cat > /usr/local/etc/doas.conf << 'EOF' permit persist :wheel permit nopass deploy as root cmd /usr/sbin/service args nginx restart permit nopass deploy as root cmd /usr/sbin/service args php-fpm restart deny :contractor EOF

The persist keyword remembers authentication for 5 minutes, like sudo.

MAC Framework (Mandatory Access Control)

FreeBSD's MAC framework enforces security policies that even root cannot override (when configured properly).

MAC Modules

FreeBSD includes several MAC modules:

| Module | Purpose |

|--------|---------|

| mac_bsdextended | File system firewall rules |

| mac_portacl | Allow non-root port binding |

| mac_seeotheruids | Hide other users' processes |

| mac_partition | Process partition isolation |

mac_seeotheruids: Hide Processes

Prevent users from seeing other users' processes:

sh
kldload mac_seeotheruids echo 'mac_seeotheruids_load="YES"' >> /boot/loader.conf sysctl security.mac.seeotheruids.enabled=1

Now ps aux run by a normal user only shows their own processes. Root still sees everything.

mac_portacl: Non-Root Port Binding

Allow a specific user to bind to privileged ports without running as root:

sh
kldload mac_portacl echo 'mac_portacl_load="YES"' >> /boot/loader.conf

Allow user www (UID 80) to bind to ports 80 and 443:

sh
sysctl security.mac.portacl.rules="uid:80:tcp:80,uid:80:tcp:443" echo 'security.mac.portacl.rules=uid:80:tcp:80,uid:80:tcp:443' >> /etc/sysctl.conf

mac_bsdextended: Filesystem Firewall

Create rules that restrict file access at the kernel level:

sh
kldload mac_bsdextended echo 'mac_bsdextended_load="YES"' >> /boot/loader.conf

Prevent the contractor group from accessing /data/finance:

sh
ugidfw add subject gid contractor object path /data/finance mode n

List active rules:

sh
ugidfw list

These rules are enforced by the kernel and cannot be bypassed even by file permission changes.

File Flags: chflags

FreeBSD file flags provide protection beyond standard permissions. They work at the filesystem level and can prevent modification even by root.

Common Flags

| Flag | Meaning |

|------|---------|

| schg | System immutable (root cannot modify in securelevel > 0) |

| uchg | User immutable (owner cannot modify) |

| sappnd | System append-only |

| uappnd | User append-only |

| sunlnk | System undeletable |

| uunlnk | User undeletable |

Making Files Immutable

Protect critical configuration files:

sh
chflags schg /etc/rc.conf chflags schg /boot/loader.conf chflags schg /etc/pf.conf

Now even root cannot modify these files:

sh
echo "test" >> /etc/rc.conf # rc.conf: Operation not permitted

To modify them, remove the flag first:

sh
chflags noschg /etc/rc.conf

Append-Only Logs

Make log files append-only so they cannot be truncated or overwritten:

sh
chflags sappnd /var/log/auth.log chflags sappnd /var/log/security

New log entries can be written, but existing entries cannot be modified or deleted.

Securelevel

Combine file flags with securelevel for defense-in-depth. At securelevel 1 or higher, system flags (schg, sappnd) cannot be removed even by root:

sh
sysctl kern.securelevel=1

Set in /etc/rc.conf to enforce at boot:

sh
sysrc kern_securelevel_enable="YES" sysrc kern_securelevel="1"

At securelevel 1: system flags are locked, kernel modules cannot be loaded/unloaded, and /dev/mem is read-only. This is powerful for production servers where the configuration should not change.

Warning: To modify system-flagged files after setting securelevel, you must reboot into single-user mode.

Auditing Permission Changes

Track who changes what:

sh
sysrc auditd_enable="YES" service auditd start

Configure audit policy in /etc/security/audit_control:

sh
cat > /etc/security/audit_control << 'EOF' dir:/var/audit flags:lo,aa,fc,fd,fw,fm minfree:5 naflags:lo,aa policy:cnt,argv filesz:2M expire-after:30d EOF

Review audit logs:

sh
praudit /var/audit/current | tail -20

The flags fc,fd,fw,fm audit file creation, deletion, write, and attribute modification respectively.

FAQ

Q: What is the difference between pw and adduser?

A: pw is the low-level tool for scripting. adduser is an interactive wrapper around pw. Use pw in scripts and automation, adduser for one-off interactive user creation.

Q: Can I use LDAP or Active Directory for user authentication?

A: Yes. Install nss_ldap and pam_ldap for LDAP integration, or use sssd for Active Directory. Configure /etc/nsswitch.conf to add ldap as a passwd/group source.

Q: How do ACLs interact with standard Unix permissions?

A: The ACL mask limits the effective permissions of group and named user/group entries. Standard permissions show the ACL mask as the group permission. If the mask is r-x, no ACL entry can grant write access.

Q: Should I use sudo or doas?

A: doas is simpler and has a smaller attack surface. Use it for straightforward "allow user X to run command Y as root" policies. Use sudo when you need logging, command aliases, or complex per-host rules.

Q: How do I prevent users from running cron jobs?

A: Create /var/cron/deny and add one username per line. Or create /var/cron/allow to whitelist only specific users.

Q: What securelevel should I use in production?

A: Securelevel 1 for most production servers. It prevents kernel module loading and protects system-flagged files. Securelevel 2 adds protection against disk device writes. Securelevel 3 adds packet filter rule protection.

Q: How do I audit all file access by a specific user?

A: Add the user to the audit system. In /etc/security/audit_user, add: username:fc,fd,fr,fw,fm:no. This audits all file operations for that user.

Q: Can I limit disk space per user?

A: Yes. On UFS, use traditional quotas (edquota -u username). On ZFS, create per-user datasets with quotas: zfs create -o quota=10G zroot/home/jsmith.

Get more FreeBSD guides

Weekly tutorials, security advisories, and package updates. No spam.