Skip to content
FreeBSDDeep Dive Published Updated 8 min readViews unavailable

FreeBSD Login Class Operations: Apply Session Limits and Environment Policy

Configure FreeBSD login classes with validated resource limits, cap_mkdb rebuilds, per-user assignment, effective-limit checks, and rollback.

FreeBSD login classes group session policy such as environment settings, process priorities, session accounting, and resource limits. A class is attached to an account record and is interpreted when a login environment is created. The mechanism is useful for consistent human or service-account defaults, but it is not a replacement for jail boundaries, RCTL policy, application quotas, or a system-wide capacity plan.

Login class changes are easy to make ineffective: editing the text database without rebuilding its capability database leaves consumers reading stale values. They can also be too restrictive for real workloads or too permissive to meet policy. Treat the change as a controlled rollout: inspect the current class, define one narrow purpose, validate syntax, compile the database, assign a test account, open a fresh session, and measure the resulting process limits.

Inventory current policy and session state

Start by capturing the existing account classes, the active login.conf database, and the limits seen by a representative session:

grep -v '^[[:space:]]*#' /etc/login.conf
ls -l /etc/login.conf /etc/login.conf.db
limits -U alice
limits -C default
id alice

Replace alice with a nonprivileged test account. Avoid dumping password hashes or other sensitive account fields into tickets. The seventh field in the system account record is the login class, but prefer the supported account tools rather than hand-editing the master password database.

Resource limits shown for an existing shell are the limits of that already-running process tree. Changing login.conf or a user’s assigned class does not retroactively rebuild all current process environments. Establish a fresh login session after assignment and compare effective values. Keep an existing administrative session available while testing, particularly if you are changing limits for an operator account.

Classes are not containers. A process can inherit a login class and its limits, but a login class does not provide filesystem isolation, network isolation, a private kernel, or a complete service sandbox. Use jails or another isolation boundary for those requirements. RCTL can apply resource rules to users, jails, and processes at runtime; login classes provide session-start policy. The two mechanisms solve related but different administrative problems.

Design a narrowly scoped class

An example class called ops can set a restrictive umask and define process resource limits while inheriting other defaults:

ops:\
    :umask=027:\
    :openfiles=2048:\
    :maxproc=256:\
    :tc=default:

The example values are a starting point for a test environment, not universal production limits. openfiles is the maximum number of open files per process, not a simple per-user descriptor pool. maxproc and other resource capabilities must be sized for the actual workload, including shells, editors, monitoring agents, and service children. A limit that is too low can break a login or application in ways that appear unrelated to the class change.

The login.conf syntax is termcap-like: capabilities are separated by colons, records can inherit using tc, and a literal colon in a value requires escaping. Keep a class’s purpose and owner in a comment or configuration-management record. Avoid copying the entire default class into a custom record; inheritance reduces drift and makes default behavior easier to understand.

For resource limits, the documented capabilities include coredumpsize, cputime, datasize, filesize, kqueues, maxproc, memorylocked, memoryuse, openfiles, pipebuf, pseudoterminals, sbsize, stacksize, swapuse, umtxp, and vmemoryuse. The manual states these entries specify both current and maximum values unless a -cur or -max suffix is used. Decide whether users should be able to raise the soft limit to the hard limit, and set the two independently only when that distinction is intentional.

Do not mistake a login-class value for a complete accounting budget. For example, openfiles is per process, and memory-related resource limits do not necessarily measure the same thing as ARC usage, VM object size, jail memory, or application RSS. Read getrlimit(2), login.conf(5), and the resource’s own semantics before relying on it as an enforcement boundary.

Validate and compile without losing the prior database

Back up the text file and database before editing. Make one small change, retain the original file mode and owner, and review the diff:

cp -p /etc/login.conf /etc/login.conf.pre-ops
cap_mkdb -v /etc/login.conf
ls -l /etc/login.conf /etc/login.conf.db
limits -C ops

In a real rollout, edit the file between the backup and database rebuild, then inspect the compiled record. The sequence above emphasizes the build and check points; do not rebuild before the intended class exists. cap_mkdb reads capability records, expands tc inheritance, and produces a hashed database. A successful command exit is necessary but should be paired with an inspection of the intended class and its inherited values.

If cap_mkdb reports a syntax or expansion problem, stop and correct the text before assigning the class. Do not delete the existing .db file as a speculative repair. Keeping the prior database and text file provides a direct rollback path if the new record is malformed or incomplete.

The capability database also supports a user’s ~/.login_conf, subject to documented restrictions on which capabilities can be overridden. Keep host-wide administrative policy in /etc/login.conf and avoid assuming that a per-user file can override every resource or security setting. For centrally managed systems, source the file from version-controlled configuration management and rebuild the database idempotently.

Assign and test one account

After the class compiles, assign it to a disposable or explicitly approved test account:

pw usermod alice -L ops
limits -U alice

The pw command modifies the account’s login-class assignment; it does not terminate the user’s current processes or change their active limits. Open a new login session and inspect the resulting shell:

su - alice
limits
ulimit -a

Use the exact supported shell and account workflow at the site. If alice is currently logged in through SSH, keep the existing session until the new session is verified. Test the intended application under the class, not just an empty shell. Confirm that it can open expected files, create ordinary child processes, allocate memory, start required pseudo-terminals, and write any necessary core file.

Run both positive and negative tests. Positive tests show normal tasks still work. A controlled negative test should demonstrate that the limit is enforced without endangering the system or production data. Avoid deliberately exhausting machine-wide resources; test a bounded single-user operation and stop it at the configured threshold.

Check environment settings separately from resource limits. A class can influence PATH, locale-related values, terminal behavior, umask, and other session characteristics, while PAM, shell startup files, SSH configuration, and the user’s own dotfiles may also affect the final environment. If a value differs from the class record, inspect the complete login path instead of assuming login.conf is ignored.

Understand precedence, inheritance, and ongoing sessions

The default record is a parent for many classes. A class can inherit settings from another record with tc, and the resulting database expands those capabilities. Review the complete resolved class, not just the lines you edited. Multiple inheritance or local aliases can make a seemingly small change affect more accounts than expected.

The account database chooses the class for a new login. A process inherits its class context from its parent, and some programs may deliberately establish another login class. A system daemon started by rc.d, cron, at, or a service manager may not receive the same class as a user’s interactive login. Verify the actual launching mechanism and process context before assuming the policy applies.

Existing sessions are a common rollout trap. Updating the class does not revise limits already installed in a long-lived shell or daemon. For service processes, schedule a controlled restart when needed and verify the new process limits after the restart. Avoid killing every user’s session as a shortcut.

Distinguish login class from RCTL and jail controls

Login classes are convenient for defaults applied at session creation. RCTL offers rules evaluated against processes, users, jails, and login classes, including runtime actions. A jail is a stronger namespace and administration boundary. Choose the layer that matches the requirement and do not duplicate limits without understanding how current and maximum values interact.

If a service must remain within a memory or process budget regardless of how it starts, a class assignment may be insufficient if the startup path does not use that class. A jail or RCTL policy may provide the intended scope, but each has its own semantics and failure behavior. Document which layer is authoritative and test enforcement through the actual launch path.

Roll back with evidence

If the test account cannot log in or the workload fails, preserve the error and current values. Restore the previous login.conf from the controlled backup, rebuild the database with cap_mkdb, and return the test account to its prior class using pw usermod. Open a new session to confirm rollback. Do not merely change the text file and assume the old policy is active.

Record the change owner, purpose, class definition, assignment scope, before-and-after values, tested login path, application tests, and rollback result. Review the policy after FreeBSD upgrades or application changes because workload needs and default class definitions can change.

Acceptance means the resolved class is the one intended, a fresh session receives the expected environment and hard/soft limits, the application completes its normal workload, and a bounded enforcement test behaves as planned. A successful cap_mkdb exit alone does not prove any of these end-to-end conditions.

Related:

Sources:

Comments