Skip to content
Search Fedora

    Browse this documentation

    Users and permissions on Fedora: wheel, sudo and password policy

    Fedora’s permission model has two layers that are easy to conflate: file permissions (rwx) and sudo escalation (who may become root). Both are independent of SELinux; this page covers the first two.

    Measured on F45.

    Terminal window
    id
    getent passwd labuser
    uid=1000(labuser) gid=1000(labuser) groups=1000(labuser),4(adm),10(wheel),190(systemd-journal)
    labuser:x:1000:1000:fedora Cloud User:/home/labuser:/bin/bash

    In the id output, 10(wheel) is what grants sudo. The getent passwd line has seven colon-separated fields: name, password placeholder, UID, GID, comment, home directory, login shell.

    Fedora grants sudo to the wheel group by default; the rules live in /etc/sudoers:

    Terminal window
    sudo grep -nE "^(%wheel|Defaults)" /etc/sudoers | head -5
    55:Defaults !visiblepw
    64:Defaults always_set_home
    65:Defaults match_group_by_gid
    73:Defaults always_query_group_plugin
    75:Defaults env_reset

    The granting line is %wheel ALL=(ALL) ALL (commented on some images, where # %wheel is offered as an alternative). Always edit sudoers with visudo — it checks syntax; editing by hand can lock you out of sudo.

    Put your own rules in /etc/sudoers.d/, one file per rule:

    Terminal window
    sudo ls /etc/sudoers.d/
    90-cloud-init-users
    Terminal window
    sudo tee /etc/sudoers.d/90-labadmin >/dev/null <<'EOF'
    labadmin ALL=(ALL) NOPASSWD:ALL
    EOF
    sudo chmod 440 /etc/sudoers.d/90-labadmin
    Terminal window
    sudo useradd -m -G wheel -s /bin/bash alice
    sudo passwd alice

    Each flag matters:

    FlagEffectIf omitted
    -mcreate the home directorythe user can log in but has no ~
    -G wheeljoin the wheel groupno sudo
    -s /bin/bashset the login shelltakes the default from /etc/default/useradd

    Fedora’s defaults:

    Terminal window
    grep -E "^(SHELL|INACTIVE|CREATE_MAIL_SPOOL)" /etc/default/useradd
    grep -E "^(UID_MIN|UID_MAX|PASS_MAX_DAYS|PASS_WARN_AGE)" /etc/login.defs
    SHELL=/bin/bash
    INACTIVE=-1
    CREATE_MAIL_SPOOL=yes
    UID_MIN 1000
    UID_MAX 60000
    PASS_MAX_DAYS 99999
    PASS_WARN_AGE 7

    Two defaults stand out: PASS_MAX_DAYS 99999 means no expiry, and INACTIVE=-1 means the account is not locked even after expiry. Production usually tightens both; see below.

    Adding a supplementary group: never drop the -a

    Section titled “Adding a supplementary group: never drop the -a”
    Terminal window
    sudo usermod -aG wheel alice # correct: append
    sudo usermod -G wheel alice # wrong: replaces the whole list

    -G means “set the supplementary groups to exactly these”; -aG means “append”. Dropping the -a silently removes membership in adm, systemd-journal and friends — the most insidious kind of permission problem.

    Verify:

    Terminal window
    id alice
    getent group wheel
    Terminal window
    sudo chage -l alice # current policy
    sudo chage -M 90 -W 7 -I 14 alice # expire in 90 days, warn 7 days ahead, lock 14 days after expiry
    sudo passwd -l alice # lock the account
    sudo passwd -u alice # unlock

    -I 14 fills exactly the gap left by INACTIVE=-1: the account locks automatically 14 days after the password expires.

    Check the lock state:

    Terminal window
    sudo passwd -S alice
    alice LK 2026-10-28 0 90 7 14 (Password locked.)

    The second field: P usable, LK locked, NP no password.

    Terminal window
    sudo userdel -r alice # -r removes home directory and mail spool too

    Without -r the home directory stays behind — a later user with the same name may get a different UID, leaving orphaned files and mismatched SELinux labels.

    • Check for running processes first: ps -u alice, loginctl list-sessions;
    • sudo -l shows what you are allowed to do — faster than guessing;
    • sudo -i versus sudo -s: the first gives a login shell (reads /etc/profile), the second keeps the current environment;
    • SELinux relation: the context=unconfined_u:... at the end of id is the SELinux user context; see SELinux.