Home / Alt manpages / pam_localuser(8)

  • pam_localuser(8)
  • Admin command
  • linux

Use pam_localuser to Separate Local and Network Accounts

Letting local accounts through while still checking network ones more carefully is a common PAM policy. pam_localuser is the module that answers whether the current user counts as local, so the rest of the stack can act on that. This guide combines it with a group check so local users or an approved administrator group can use a service. It uses Linux-PAM from Ubuntu package libpam-modules:amd64, version 1.5.3-5ubuntu5.7.

Allow about fifteen minutes for a configuration review and a controlled test. You need root access to edit a PAM service file, and a second administrative path for recovery. PAM mistakes can lock out logins, remote access or privilege escalation, so this guide does not edit a live file for you.

1. Check the module and the account source

Run these read-only commands as your ordinary user:

$ dpkg-query -W -f='${Package} ${Version}\n' libpam-modules:amd64
libpam-modules 1.5.3-5ubuntu5.7
$ ls -l /lib/x86_64-linux-gnu/security/pam_localuser.so
-rw-r--r-- 1 root root ... /lib/x86_64-linux-gnu/security/pam_localuser.so

The exact module path and file metadata can differ on another architecture. The module's default account source is /etc/passwd: it asks PAM for the current user, then checks that name against the chosen passwd file. It does not authenticate a password, and it does not decide group membership either.

Checkpoint

Pick one existing local test account and one that is not in /etc/passwd, if your system has both. Do not create or delete accounts just to run this test. On a system using LDAP, SSSD or another directory, do not assume a directory user counts as local just because getent passwd can resolve it.

2. Understand the PAM line before editing it

A service file under /etc/pam.d/ uses the form type control module arguments. For pam_localuser, the common module line is:

account sufficient pam_localuser.so

The module supplies all four PAM management types, but account is the useful one for an access policy. With sufficient, a successful local-user check can finish the whole account stack, as long as no earlier required module has already failed. A failed sufficient check is simply ignored, leaving later modules free to handle or deny network users.

This is a policy decision, not a harmless status check: a local account that clears this line may be accepted before any later account rule even runs. Read the whole target stack first, included files and all, and check how the service's own application interprets a PAM result.

3. Back up the target service file

Choose the service deliberately. The manual's own example uses su, so start by inspecting that file without changing it:

$ sudo sed -n '1,220p' /etc/pam.d/su

Do not paste the example into every file under /etc/pam.d/: add the rule only to the service whose access policy you are actually changing. Before editing, make a root-owned backup:

$ sudo cp --preserve=all /etc/pam.d/su /etc/pam.d/su.before-pam-localuser
$ sudo ls -l /etc/pam.d/su /etc/pam.d/su.before-pam-localuser

Confirm both files exist and the backup predates the edit. That backup is your recovery path, so keep an already-open root shell or console session while you test: do not close the only route back into the machine.

4. Allow local users or the wheel group

For a service where that policy is genuinely intended, the manual gives this stack:

account sufficient pam_localuser.so
account required pam_wheel.so

The first line lets a user listed in the chosen passwd file pass without ever reaching the wheel check. Anyone not in that file falls through to pam_wheel, which then requires membership of its configured wheel group. This is an OR policy: it does not make every local user a member of wheel.

Insert the lines at the right point in the service's existing account stack, preserving vendor comments and included rules. Use a privileged editor, for example:

$ sudoedit /etc/pam.d/su

Avoid sudo sh -c with an unquoted, generated configuration string, and never replace the entire file with the two-line example above. A malformed line or an unexpected control flag can change access for every user of that service.

5. Use a different passwd file only for a deliberate policy

The file= option changes which file the module checks:

account sufficient pam_localuser.so file=/etc/security/service-users

That path is a module argument, not a shell command, and the file must hold passwd-style account entries the installed PAM implementation can read. Treat it as security-sensitive: root-owned, write access restricted, and reviewed for who can alter it. A writable allow-list is an access-control bypass waiting to happen.

Check the file and its permissions before turning the rule on:

$ sudo stat -c '%U:%G %a %n' /etc/security/service-users
root:root 640 /etc/security/service-users
$ sudo sed -n '1,20p' /etc/security/service-users

The module also accepts debug, which writes diagnostic detail to the system log: useful temporarily during a controlled test, then worth removing again. Unrecognised options get logged as errors, so spell file= exactly right.

6. Test without taking away your recovery path

There is no standalone pam_localuser executable: the module only runs inside a PAM-aware application. Test through the actual service, with a disposable or already-approved account, from a second session. For a change to su, keep the current root or administrator shell open and test from another terminal:

$ su - TEST_LOCAL_USER -c 'id -un'
TEST_LOCAL_USER
$ su - TEST_NETWORK_USER -c 'id -un'
su: Authentication failure

The exact failure text, and whether a password gets requested at all, depends on the rest of the service's stack. A successful id command for one account proves nothing about whether every PAM path shares the same policy, so test both an intended local user and an intended non-local one, and check the system journal if the result diverges from the stack you reviewed.

Wrong result? Stop testing and restore the backup rather than improvising another rule on top:

$ sudo cp --preserve=all /etc/pam.d/su.before-pam-localuser /etc/pam.d/su
$ sudo sed -n '1,220p' /etc/pam.d/su

Restoring the file undoes this example's change, but not any unrelated edit made after the backup, so compare the file before restoring if other administrators might have touched it since.

7. Know the failure boundaries

A user missing from the chosen passwd file returns PAM_PERM_DENIED. An unavailable passwd file or invalid username returns a service error, and if PAM cannot even get the username through its conversation method, the module can return a conversation or incomplete error instead. These are all module results; the control flag in the service file is what decides whether each one gets ignored, recorded as a failure, or ends the stack outright.

pam_localuser is not a stand-in for pam_wheel, pam_listfile, directory authentication or password verification. It answers exactly one narrow question: is the current name present in the configured passwd file? Reach for a different module when the policy actually depends on group membership, a maintained allow-list, a directory, or credentials.

Done means

  • Confirmed the install. The installed Linux-PAM package and module path were checked.
  • Read the whole stack. One PAM service was chosen and its complete account stack read before editing.
  • Backed up first. The service file was backed up and a separate recovery session stayed open.
  • Used the right control flag. account sufficient pam_localuser.so was used only where local-user policy is actually intended.
  • Secured any custom file. Any file= passwd file is root-owned and not writable by the accounts it protects.
  • Tested both paths. An allowed local account and the intended non-local path were both tested, and the backup was restored if the result was wrong.