Skip to content
FreeBSDDeep Dive Published Updated 7 min readViews unavailable

FreeBSD devfs Rulesets: Control Device Nodes Predictably

Operate FreeBSD devfs rulesets for device-node visibility and ownership, reload safely, scope jail mounts, and verify hot-plug behavior.

FreeBSD’s device filesystem presents kernel device nodes in a namespace, normally mounted at /dev. A driver can create and remove nodes as hardware or kernel modules change state. The owner, group, mode, and visibility of those nodes affect which programs can use the associated devices. devfs rules let an administrator apply policy as nodes appear, rather than repeatedly changing individual node metadata by hand.

This is distinct from devd. devd reacts to device events and can run an action; devfs rules control attributes and visibility on a particular devfs mount. A devd rule does not replace a devfs ruleset, and changing a node with chmod does not make the change persistent across node recreation. The correct approach depends on whether the requirement is host-wide, specific to a jail, or an event-driven action.

Identify the mount and current rules before editing

Check the host’s devfs mount, the active ruleset number, and the rules already loaded:

mount -t devfs
devfs rule showsets
devfs rule show
sysrc -s devfs -A | grep -E '^devfs_(system_ruleset|load_rulesets)='

The rule commands default to /dev unless an alternate mount point is supplied. A jail or chroot can have a different devfs mount and ruleset. Inspect that mount directly instead of assuming host rules apply. Keep the output with the change record; ruleset identifiers alone are not descriptive enough to establish which policy is active.

The devfs.rules file supports named, numbered rulesets. A section begins with a declaration such as [appdevices=20], and following rule lines belong to that ruleset until another section starts. Rule specifications are interpreted by devfs(8). In devfs.rules, quote path patterns containing glob characters so they are not misinterpreted by the shell or file parser.

Rulesets in /etc/devfs.rules are merged with the defaults according to the manual’s rules. A local ruleset should have a unique name and number, and should not reuse a default ruleset number unless the replacement is intentional and reviewed. Use a number assigned by local configuration management rather than choosing one independently on every host.

Design narrow attribute rules

A rule can match a path pattern or device type and apply actions such as mode, group, or visibility. Conditions in one rule are combined; if an OR relationship is required, write separate rules. Prefer a narrow path or documented device type over changing every device. Do not use world-writable modes as a shortcut for access failures.

For example, a host that intentionally exposes serial USB device nodes to a dedicated group could use:

[serial_devices=20]
add path 'cuaU*' mode 0660 group dialout

This is only a pattern example. Confirm the actual node names produced by the installed driver, create the dialout group through the site’s account policy, and ensure membership is limited to the intended operators. A wildcard can match more devices than expected after new hardware is added. Review the match set on a test machine before deploying it.

For a named filesystem class or another hardware family, use a type match only after checking the devfs(8) list of supported types. A pattern that matches a transient name may fail when enumeration order changes. Where available, prefer stable naming and explicit device ownership policy over matching a bus number that can be reassigned.

Hiding and unhiding nodes require similar care. A rule that hides too broadly can remove the only console, storage, or management device available inside a jail. A rule that unhides a device can counter an earlier restriction. Make the ruleset readable as a complete policy, not a collection of unreviewed commands appended over time.

Load and apply rules on the host

To select a host system ruleset at boot, configure the documented rc.conf variable:

sysrc devfs_system_ruleset="serial_devices"

Use the exact ruleset name declared in /etc/devfs.rules. Keep the file and rc.conf update under configuration management. Do not edit /etc/defaults/devfs.rules; that file contains system defaults. The devfs service loads configured rules at boot, and the manual documents service devfs restart as the way to reload modified rules after boot.

After a reload, inspect the active rule list and the resulting node attributes:

service devfs restart
devfs rule show
stat -f '%N %Su:%Sg %Sp' /dev/cuaU0

Replace cuaU0 with a node that actually exists. The service reload makes configured rules available, but existing nodes may need an explicit rule application depending on the change and current mount state. The devfs manual provides rule apply and applyset operations for this reason. Inspect the manual and choose the narrowest appropriate application; do not assume a reload alone changed every existing node.

Compare a newly created matching node with an existing one. A rule can be applied automatically when a driver creates a node, while nodes that existed before the rule was added may retain old attributes until the rule is applied. Unplug/replug can test hot-plug behavior in a lab, but should not be used on production hardware whose consumers would be disrupted.

Use separate rulesets for jails

Each devfs mount point has a ruleset association. This enables a jail’s /dev to have different visibility and attributes from the host /dev. The jail configuration can select a ruleset for the jail’s devfs mount. Inspect jail.conf(5) and jail(8) for the exact parameter syntax on the target release, then verify the jail’s actual mount and current ruleset after start.

Do not expose a host device to a jail merely because the node is visible. Device access also depends on jail configuration, device-specific semantics, ownership, and any additional restrictions. A jail should receive only the nodes its workload requires. In particular, broad disk, raw memory, or control-device access can undermine the isolation model.

When changing a live jail ruleset, identify the jail’s devfs mount and operate on that mount explicitly:

devfs -m /usr/jails/app/dev rule show
devfs -m /usr/jails/app/dev rule -s 20 show

The paths and ruleset numbers are examples. Determine the real mount point with mount output and the actual jail configuration. Applying a policy to the host’s /dev when the affected process uses a jail’s /dev will produce no useful result.

For a new jail, verify three separate outcomes: the intended ruleset was selected, the needed node is present with expected attributes, and the workload can open only what its design requires. A visible node with mode 0660 is not evidence that a jail is correctly isolated, and a hidden node may still exist on the host mount.

Diagnose common failures

If a permissions change disappears after reconnecting a USB device, the driver probably removed and recreated the devfs node. Replace ad hoc chmod with an appropriate persistent rule. If a rule does not match, first inspect the literal node path, pattern quoting, ruleset number, and mount point. A pattern for da0s1 will not match a provider named differently by GPT labels.

If a node exists but an application receives permission denied, inspect numeric UID/GID and mode from stat, the process’s effective groups, the active ruleset, and whether the application has been restarted after group membership changes. Also check that the application opens the same path you inspected. Do not broaden the mode until those facts agree.

If a rule appears in the file but not in devfs rule show, check whether the ruleset loaded, whether the file contains a syntax error, whether rc configuration selects the ruleset, and whether a jail is using another mount. The file is desired configuration; the kernel’s current rule list is runtime state.

If a previously working device disappears after reload, restore the saved ruleset or select the known-good ruleset, then restart the specific affected service. Avoid removing all rules or setting an empty ruleset on a remote host without an independent console. Preserve the before-state output for investigation.

Acceptance criteria

An operational change is complete when the named ruleset is selected on the intended mount, the exact matching nodes have the expected owner/group/mode/visibility, and a controlled consumer test succeeds after both boot and device recreation. For jail policy, confirm the host mount remains unchanged and the jail sees only its intended device surface.

Record the ruleset file, rc.conf or jail configuration, rule numbers, matched device paths, test identity, before-and-after attributes, and rollback command. Keep device-event automation separate from node policy. That separation makes it possible to tell whether a failure is in enumeration, ruleset selection, permissions, or the consumer itself.

Related:

Sources:

Comments