Signal9Signal9 — home
online
all lessons

Default File Permissions: umask and Fixing Exposed Files

Beginner20 min5 steps
What you'll learn
  • Explain how the starting mode and the umask combine into a new file's permissions
  • Read and set the umask, and predict the modes it produces
  • Make one user's umask persistent and test it from a real interactive login
  • Find files a loose umask exposed and fix them with symbolic chmod without breaking directories
  1. The mental model: a starting mode minus a mask

    Understand how a new file's permissions are calculated before you change anything.

    Every file and directory you create gets permission bits immediately, and you never typed them. They come from two numbers working together.

    • The program asks the kernel for a starting mode. Tools like touch, editors and shell redirection ask for 666 (rw-rw-rw-) for regular files. mkdir asks for 777 (rwxrwxrwx) for directories.
    • The kernel then removes every bit that is set in the process's umask (user file-creation mask). Whatever survives is the new file's mode.

    So the umask is not a mode. It's a list of permissions to take away. A umask of 022 takes write away from group and other: files become 644 and directories 755. A umask of 027 takes write from group and everything from other: files become 640 and directories 750.

    Notice that new regular files never get the execute bit from the default mode, whatever the umask is. That's deliberate: a freshly written file shouldn't become a runnable program by accident. You grant execute explicitly with chmod +x when you mean it.

    The arithmetic is a bitwise AND with the inverse of the mask, not a subtraction. With a mask of 027, the file calculation is 666 AND NOT 027 = 640. For most masks plain subtraction gives the same answer, but not always (try 666 with a mask of 033), so think of it as "remove these bits".

    Check yourself

    A process with umask 077 runs mkdir private. What mode does the directory get?

  2. Reading and setting the umask

    Read the current mask, change it for a shell, and predict the result.

    umask is a shell builtin, because the mask belongs to the shell process and is inherited by every program it starts. Run it alone to print the mask in octal. The leading zero is just the special-bits digit (setuid, setgid, sticky), which you'll almost never mask. Add -S to see the permissions that are left allowed, in symbolic form.

    umask; umask -S
    0022
    u=rwx,g=rx,o=rx
    -S
    Print the mask symbolically as the permissions that remain allowed.
    The octal value is the mask, the bits being removed. The symbolic form shows the opposite: what new directories are allowed to get. Most systems default to 0022 for ordinary accounts.
    mkdir -p /tmp/umask-lesson && cd /tmp/umask-lesson && rm -rf * && (umask 027; touch report.txt; mkdir archive; ls -ld report.txt archive)
    drwxr-x--- 2 root root 4096 Oct  6 16:26 archive
    -rw-r----- 1 root root    0 Oct  6 16:26 report.txt
    ( ... )
    Run the commands in a subshell, so settings like umask don't leak into your shell.
    Inside the parentheses the commands run in a subshell, so the umask 027 only applies there and your own shell keeps its mask. The file comes out rw-r----- (640) and the directory rwxr-x--- (750), exactly what the arithmetic predicted.
    cd /tmp/umask-lesson && (umask 077; touch secret.key; ls -l secret.key)
    -rw------- 1 root root 0 Oct  6 16:26 secret.key
    077 is the private mask: nothing for group or other. It's the right choice for a shell that creates key material or credentials. It's usually the wrong choice for a whole login, because files you meant to share with your team come out unreadable to them.

    Check yourself

    Your umask is 0002. What mode does a new file from touch get?

  3. Making a user's default persistent

    Set one account's umask so every new session gets it, and test it honestly.

    Typing umask 027 lasts until the shell exits. To make it the account's default, the line has to run at the start of every session, and that means a shell startup file. Which file depends on what kind of shell starts.

    • A login shell (console login, SSH, su -) reads /etc/profile, the scripts in /etc/profile.d/, and then the user's ~/.bash_profile or, if that doesn't exist, ~/.profile.
    • An interactive non-login shell (a new terminal tab inside a session) reads ~/.bashrc.

    On RHEL, and on Ubuntu, the stock login file also sources ~/.bashrc, so a line in ~/.bashrc ends up in both kinds of interactive shell. That's why it's the usual per-user answer.

    id analyst >/dev/null 2>&1 || useradd -m -s /bin/bash analyst; echo 'umask 027' >> /home/analyst/.bashrc; tail -1 /home/analyst/.bashrc
    umask 027
    >>
    Append to the file instead of overwriting it.
    This creates a demo account if it doesn't exist and appends the umask to its ~/.bashrc. Only analyst is affected: other users have their own startup files.
    su - analyst -c 'umask'; su - analyst -c 'bash -ic umask' 2>/dev/null
    0002
    0027
    su - USER -c CMD
    Run CMD in a non-interactive login shell as USER.
    bash -ic CMD
    Run CMD in an interactive shell, which reads ~/.bashrc.
    Two answers, and the difference is the most common trap in this objective. su - analyst -c starts a login shell that runs one command non-interactively. Ubuntu's ~/.bashrc stops early for non-interactive shells, so the first line still shows the old mask. The second runs an interactive shell, which is what a person who logs in actually gets, and shows 0027.

    Check yourself

    Which file sets a single user's umask for their interactive shells on a stock RHEL system?

  4. Diagnosing and fixing permissions that are already wrong

    Find files a loose umask exposed and fix them without breaking directories.

    Changing the umask protects files created from now on. Everything a loose mask already created stays exposed until you fix it. Find them first, then fix them with symbolic chmod, which changes only the bits you name and leaves the rest alone.

    mkdir -p /tmp/perm-lesson /tmp/perm-lesson/2026/q3 && cd /tmp/perm-lesson && touch 2026/q3/sales.csv 2026/summary.csv && chmod -R a+rwX . && find . -perm '/o=w'
    .
    ./2026
    ./2026/summary.csv
    ./2026/q3
    ./2026/q3/sales.csv
    -perm /MODE
    Match if ANY of the given permission bits are set.
    -perm -MODE
    Match only if ALL of the given bits are set.
    This builds a small tree the way a umask of 000 would have, world-writable everywhere, then asks find for everything that other users can write. -perm /o=w means "any of these bits is set", so it lists every file and directory with other-write.
    cd /tmp/perm-lesson && chmod -R o-rwx . && find . -perm '/o=rwx' | wc -l && ls -ld 2026 2026/summary.csv
    0
    drwxrwx--- 3 root root 4096 Oct  6 16:26 2026
    -rw-rw---- 1 root root    0 Oct  6 16:26 2026/summary.csv
    -R
    Apply recursively to the whole tree.
    o-rwx
    Remove read, write and execute for other users only.
    o-rwx removes every permission for other and touches nothing else, so directories keep their execute bits and the owner and group keep their access. Zero items still match, and the listing shows directories at 770 and files at 660.

    Check yourself

    Which command removes all access for other users from every file and directory under /srv/reports, while leaving directories enterable for the owner and group?

  5. Now do it — in your live terminal

    Set a user's default permissions and verify them on the sandbox to your right.

    Put it together on the sandbox. Before you press Enter on each command, write down the two modes you expect the new file and the new directory to get, then compare with what stat prints. If a result surprises you, work the arithmetic again: start from 666 or 777 and remove the bits in the mask.

    Checking a user that should not change is as important as checking the one that should. A change that leaks to every account is a different, and often worse, problem than the one you were asked to fix. That before-and-after check on two accounts is exactly how the lab is graded.

    useradd -m -s /bin/bash auditor; su - auditor -c 'bash -ic "touch f; mkdir d; stat -c %a f d"' 2>/dev/null
    Note auditor's defaults before you change anything.
    echo 'umask 077' >> /home/auditor/.bashrc; su - auditor -c 'bash -ic "rm -rf f d; touch f; mkdir d; stat -c %a f d"' 2>/dev/null
    The file should now come out 600 and the directory 700.
    su - analyst -c 'bash -ic "rm -rf g; touch g; stat -c %a g"' 2>/dev/null
    analyst has its own startup file, so it's unaffected by auditor's change.
Recap
  • New files start from 666 and directories from 777, and the kernel removes every bit set in the umask. 022 gives 644/755, 027 gives 640/750 and 077 gives 600/700.
  • umask is a per-process shell setting inherited by children. umask prints it and umask -S shows what remains allowed.
  • A single user's default belongs in their own ~/.bashrc (or ~/.bash_profile). /etc/profile, /etc/profile.d and the UMASK line in /etc/login.defs change it for everyone.
  • Test from an interactive login (su - user -c 'bash -ic umask'), because a non-interactive su - user -c may skip ~/.bashrc.
  • The umask never touches existing files. Fix those with symbolic chmod (chmod -R o-rwx), not an octal mode that strips execute from directories.
umask at a glance
umaskNew file / new directory
0022644 / 755 (common default)
0002664 / 775 (user-private-group default)
0027640 / 750 (group can read, others nothing)
0077600 / 700 (private)
find DIR -perm /o=rwxList anything other users can access
chmod -R o-rwx DIRRemove other's access, keep everything else