Advanced User and Access Management

Advanced User and Access Management

Bitnesia Aug 28, 2026 2 ID

Throughout the first three Chapters, the server we managed still felt like a personal machine. One account, one sudo, one person holding full control. As soon as the server begins to be used by a larger team, the situation changes drastically. Developers need access to deploy applications, the team managing the database needs direct access without needing to know network configuration details, and freelance contractors need temporary access that must automatically terminate as soon as their contract period ends. Managing all of this individually through a manually patched sudo line, sooner or later, creates a mess and opens up security vulnerabilities we might not realize.

This chapter brings user and access management to a more mature level. We begin with a brief review of the foundations of users, groups, and permissions, then move on to sudo-rs, which is now the default in Ubuntu Server 26.04 LTS, along with how it compares to classic sudo. After that, we discuss strategies for managing multiple accounts simultaneously through role-based groups and password policies, concluding with an introduction to the concepts of centralized authentication via PAM and LDAP.

4.1 Brief Review of Users, Groups, and Permissions

Every process and file in Linux is always bound to the identity of its owner. We should already be familiar with this concept from the Linux 101 material, but it is worth refreshing our memory before moving on to more complex topics, as the entire advanced access strategy in this Chapter is built on the same foundation.

Its three basic elements are user (the individual identity logging into the system), group (a collection of users managed together), and permission (read, write, and execute rights attached to each file and directory). The combination of these three determines who is allowed to perform what actions on which resources on the server.

Quick Verification

Before proceeding, ensure we still remember how to check the identity and group membership of an account.

id username
groups username

The id command displays the UID, primary GID, and all supplementary groups owned by that user, while groups displays only the list of group names. We will frequently use both commands throughout this Chapter to verify configuration results.

4.2 sudo-rs vs Classic sudo (sudo.ws)

Chapter 1 briefly mentioned that Ubuntu Server 26.04 LTS uses sudo-rs as the default, which is an implementation of sudo rewritten in the Rust language for memory safety. Now it is time for us to unpack what this means in practical terms for a Sysadmin's daily work, especially when we encounter legacy automation scripts that suddenly behave strangely after migrating to a new server.

sudo-rs was originally initiated through Prossimo, an ISRG initiative (the same non-profit organization behind Let's Encrypt), and was worked on by engineers from Tweede golf and Ferrous Systems before its long-term management was handed over to the Trifecta Tech Foundation in 2024. This project began becoming the default in Ubuntu 25.10 (Questing Quokka) before finally being brought into Ubuntu 26.04 LTS. To prevent this transition from causing inconvenience to legacy users, Canonical still includes classic sudo in the system, but uses a different binary name: sudo.ws (referring to the domain of its official project, sudo.ws, owned by Todd Miller). Both implementations coexist via Debian/Ubuntu's alternatives system, with sudo-rs receiving a higher priority (50) compared to classic sudo (40), making it automatically selected on new installations.

4.2.1 Differences and Default Provider Configuration

In terms of daily functionality, sudo-rs feels identical to classic sudo. However, there are several behavioral differences that are important for a Sysadmin to know before discovering them accidentally in the middle of production.

AspectClassic sudo (sudo.ws)sudo-rs
Authentication timestampCan be global (one authentication valid across all terminals)Always per-TTY, each terminal session authenticates independently
Wildcard in command pathAllowed in any positionSupported only as the last argument
Resource limit and umaskCan be configured via sudoersMust be configured via PAM
I/O logging (sudoreplay)SupportedNot supported
Sudoers via LDAP (sudoers.ldap)SupportedNot supported, use LDAP authentication via PAM

In practice, the difference that most frequently causes confusion is the per-TTY timestamp. Automation scripts relying on the assumption "once authenticated via sudo, valid across all sessions" for the same user might suddenly prompt for a password again in another terminal after the server is migrated to sudo-rs. This is not a bug, but rather a design decision aimed at stricter security.

Practical Steps

  1. Check which sudo provider is currently active on our server.
    update-alternatives --display sudo
    The first line shows the link currently in use, while the lines below it display all candidates along with their priority values.
  2. If we need to temporarily switch to classic sudo, for instance to audit compatibility with legacy scripts, use interactive mode.
    sudo update-alternatives --config sudo
  3. For automation or provisioning needs, use non-interactive mode.
    sudo update-alternatives --set sudo /usr/bin/sudo.ws
  4. Switch back to sudo-rs at any time using the same method.
    sudo update-alternatives --set sudo /usr/bin/sudo-rs

Verification and Troubleshooting

  • After switching providers, run sudo -V to ensure the active version and implementation match expectations.
  • If Expect-based scripts fail to detect the password prompt due to changed text formatting (sudo-rs displays [sudo: authenticate] instead of [sudo] password for username like the classic version), use the --prompt "" option on the sudo call so that the prompt is custom and more predictable for scripts.
  • Canonical plans to fully deprecate classic sudo in Ubuntu 26.10, so take advantage of Ubuntu 26.04 LTS as a transition period to audit the compatibility of our sudoers and automation scripts before the option to revert to sudo.ws completely disappears.

4.2.2 /etc/sudoers and sudoers.d/

Both sudo-rs and classic sudo read the same configuration files, namely /etc/sudoers and all files inside the /etc/sudoers.d/ directory. This concept is identical to the pattern we used in sshd_config.d/ in Chapter 3: instead of directly editing the main sudoers, we place custom rules in separate files to make them easier to track and to avoid interfering with the system's default configuration.

One rule that must never be broken: never edit /etc/sudoers or the contents of sudoers.d/ using a standard text editor like nano directly. Always use visudo, because this tool locks the file while it is being edited and automatically validates syntax before saving. A single misplaced parenthesis character in sudoers can cause all sudo functionality on the server to stop working, including for our own account.

Practical Steps

  1. Create a new rule file for the developer group via visudo, not a standard editor.
    sudo visudo -f /etc/sudoers.d/10-developer
  2. Populate it with a rule granting full access to all members of the developer group.
    %developer ALL=(ALL:ALL) ALL
    The % sign in front of the name indicates that this rule applies to a group, not an individual user.
  3. For accounts with more restricted needs, such as a CI/CD pipeline's deploy account that is only allowed to restart a specific service without being prompted for a password, create a separate file.
    sudo visudo -f /etc/sudoers.d/20-deploy-restart
    deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp.service
  4. Validate all sudoers files at once, including those in sudoers.d/.
    sudo visudo -c

Verification and Troubleshooting

  • Check the permissions that actually apply to a user using the following command, without needing to log in as that user.
    sudo -l -U username
  • If sudo refuses to read a file in sudoers.d/ with a permission error message, check its permissions and ownership. Files in this directory must have 0440 mode and be owned by root.
    sudo chmod 0440 /etc/sudoers.d/10-developer
    sudo chown root:root /etc/sudoers.d/10-developer
  • Before closing a terminal session after modifying sudoers rules, always open a separate session and test sudo access there first. This habit is just as important as what we applied during SSH hardening in Chapter 3, because a mistake in sudoers can lock everyone, including ourselves, out of administrative access.

4.3 Account Management for Multiple Users

Adding sudoers lines one by one for every new user is sufficient when the team consists of only two or three people. Once the number of developers and other staff needing access grows into the dozens, this approach turns into an auditing bottleneck. Who has access to what? Which rules remain relevant after a developer changes teams? The answer lies in two habits: managing access via role-based groups and enforcing consistent password policies.

4.3.1 Role-Based User Groups

Role-based access means we grant access permissions to groups representing job roles or functions, such as developer, dbadmin, or support, rather than to individuals. When someone joins or switches roles, we simply change their group membership. When someone leaves the team, simply remove them from that group, and all their access rights automatically revoke without needing to trace individual sudoers lines that may have been created years ago by a previous Sysadmin.

Practical Steps

  1. Create a group for each relevant role in our environment.
    sudo groupadd developer
    sudo groupadd dbadmin
  2. Add users to the group corresponding to their role. The -a (append) option must be used so that pre-existing group memberships are not removed.
    sudo usermod -aG developer alice
    sudo usermod -aG dbadmin bob
  3. Pair these groups with the sudoers rules we created in section 4.2.2, or create a new dbadmin-specific rule limiting access strictly to database management commands.

Verification and Troubleshooting

  • Confirm that the user's group memberships are correct.
    groups alice
  • If group changes do not reflect immediately when the user tries sudo, ask the user to log out and log back in. New group memberships only take effect upon a new login session, not an active running session.
  • In production, avoid the temptation of adding users directly to Ubuntu's default sudo group as a shortcut. This default sudo group grants unrestricted full access, making it difficult to audit who is permitted to do what, and a single account compromised by an attacker automatically grants full control over the server. Custom role-based groups are far easier to account for during security audits, as we will discuss in Chapter 34.

4.3.2 Password Policy and Expiry (chage)

By default, Ubuntu Server does not force passwords to expire. Default values in /etc/login.defs set PASS_MAX_DAYS to 99999 days (practically never expiring), PASS_MIN_DAYS to 0 days, and PASS_WARN_AGE to 7 days. These values apply only to new accounts created after the file is modified, not retroactively to existing accounts. For environments with stricter security policies, such as servers storing sensitive client data, we need to enforce password expiration policies explicitly via chage.

Practical Steps

  1. View the password policy currently in effect for an account.
    sudo chage -l alice
  2. Set the password to require changing every 90 days, with a minimum delay of 7 days before it can be changed again, and a warning 14 days prior to expiration.
    sudo chage -M 90 -m 7 -W 14 alice
  3. For freelance contractor accounts working only until a specific date, set the account expiration date directly.
    sudo chage -E 2026-12-31 alice
  4. If we have just created an account and want to force the user to change the default password on their first login, use the following option.
    sudo chage -d 0 alice

Verification and Troubleshooting

  • Run sudo chage -l alice again to ensure all values are saved according to plan.
  • chage restricts almost all of its options exclusively to root, except for -l, which regular users may run to view their own password policy without sudo.
  • If a user is suddenly unable to log in and receives a message stating that their password has expired, it means PASS_MAX_DAYS has been exceeded. Perform a reset via chage -d 0 to force a password change on the next login, or extend the limit via -M if the policy requires revision.
  • Password complexity policies themselves, such as minimum length and character combinations, are not within the scope of chage. Those rules are enforced via the pam_pwquality module, which we will touch upon in the following PAM section.

4.4 Introduction to Centralized Authentication

Everything we have discussed so far remains centered on a single server. As soon as a Sysadmin must manage dozens or hundreds of servers simultaneously, manually creating the same account on every machine is clearly unrealistic. This is where the concept of centralized authentication comes into play, where user identities are stored in a single location and shared across multiple servers. This section is purely an introduction to the concepts, as full implementation details of a directory service fall outside the scope of this book.

4.4.1 PAM: Basic Concepts

PAM (Pluggable Authentication Modules) is a framework that separates authentication logic from the applications using it. Thanks to PAM, applications like sudo, sshd, or the login process do not need to know the details of how to verify a password. The application simply calls PAM, and PAM determines which module to use to verify the identity, whether it is a local password, LDAP, or another method.

PAM configuration in Ubuntu Server is stored in the /etc/pam.d/ directory, with four common files serving as shared references for many applications: common-auth (authentication), common-account (account status validation, such as expiration status), common-password (password change rules), and common-session (actions executed when a session opens or closes, such as automatically creating a home directory).

Practical Steps: Viewing the PAM Stack

  1. View the contents of the authentication stack applying to almost all applications on the server.
    cat /etc/pam.d/common-auth
  2. Pay attention to the second column on each line, which contains control flags like required, requisite, or sufficient. These flags determine what happens if a module fails, whether PAM immediately denies access or continues to the next module.
  3. To enable or disable specific PAM profiles (such as the SSSD integration we will touch upon in the next section) without manually editing each file, Ubuntu provides an interactive tool.
    sudo pam-auth-update

Never edit common-auth, common-account, or common-password manually without careful calculation. A single wrong line here can lock the entire login mechanism on the server, including via console, not just via SSH or sudo like the risks discussed earlier. Use pam-auth-update to enable or disable modules, and always test from a separate session before closing the active session.

The password complexity rules mentioned in the chage section earlier, such as minimum length and character mix, are enforced via the pam_pwquality module installed in common-password. Configuration details are stored in /etc/security/pwquality.conf.

4.4.2 LDAP as an Identity Source

LDAP (Lightweight Directory Access Protocol) is a protocol for accessing directory services, a type of structured database optimized for storing identity information such as users, groups, and organizational attributes. In a server context, LDAP is used as a centralized identity source: a single directory acting as the identity reference for many servers at once, allowing a Sysadmin to manage accounts in one place.

In modern Ubuntu Server, LDAP integration no longer uses the legacy libnss-ldap package, but instead relies on SSSD (System Security Services Daemon), which bridges PAM and NSS (Name Service Switch) to the LDAP server. On the client side, this integration comes via the sssd, libnss-sss, and libpam-sss packages, each connecting identity resolution and authentication to the SSSD daemon. Its lookup route is defined in /etc/nsswitch.conf, for example, the following lines tell the system to query user and group data via SSSD first, falling back to local files if not found.

passwd: sss files systemd
group:  sss files systemd

One important thing to note according to the changes in Chapter 1: the sudo-ldap package has been removed alongside the transition to sudo-rs. This means sudoers rules that could previously be stored directly on an LDAP server (via sudoers.ldap) are no longer supported. The current recommended practice is to decouple the two: LDAP/SSSD handles identity and authentication (user identity and password validity), while sudoers rules remain managed locally on each server or distributed via automation tools like Ansible, which we will discuss in Chapter 36.

Since full implementation of an LDAP server lies outside the scope of this book, understand the conceptual flow first. Once sssd is installed and connected to the LDAP server, we can test identity resolution using the following commands.

getent passwd ldap_username
getent group ldap_groupname

If the results display user or group details from the LDAP directory, it means the chain from NSS to SSSD is functioning correctly as a centralized identity source.

This chapter concludes the foundations of advanced user and access management. Now that users, groups, sudo, and centralized authentication concepts have matured, we are ready to move on to Chapter 5 to discuss advanced systemd, ranging from creating custom service units to migrating from cgroup v1 to cgroup v2.