Linux hosts

umask and login.defs, the secure way

You set UMASK 027 in /etc/login.defs, logged in again, typed umask, and it still said 0022. You're not crazy, you heard right: on many systems that line is only read by a module nobody enabled.

The short answer

Set UMASK 027 and HOME_MODE 0700 in /etc/login.defs so new home directories are private. Make sure pam_umask is in the PAM session stack, or the login umask never changes. Set UMask=0027 in each systemd service, because services do not log in. Fix existing home directories by hand.

Updated Houssam Hammoudi, CTOTested with shadow-utils 4.13, Linux-PAM 1.5.2 (Debian 12)

On this page
  1. What goes wrong
  2. What the docs say
  3. The secure configuration
  4. Prove it
  5. Mistakes people make
  6. Checklist

What goes wrong

The umask decides the permissions of every new file and directory. The usual default, 022, means "everyone may read". On a shared host that makes every new file readable by every other user and every other service account: config exports, database dumps, notes with passwords in them.

Home directories have the same problem. On Debian and Ubuntu, useradd -m creates /home/<user> with mode 755, so any local account can list and read another user's files unless each user fixes it.

The trap is where the setting lives. /etc/login.defs has a UMASK line, and it looks like the obvious switch. For logins, it is only applied by pam_umask, and a Debian 12 container image does not load that module at all. Services started by systemd ignore both and use their own default.

What the docs say

The file mode creation mask is initialized to this value. If not specified, the mask will be initialized to 022. [...] It is also used by pam_umask as the default umask value.

Source: login.defs(5), UMASK

The mode for new home directories. If not specified, the UMASK is used to create the mode.

Source: login.defs(5), HOME_MODE

Controls the file mode creation mask. Takes an access mode in octal notation. See umask(2) for details. Defaults to 0022 for system units.

Source: systemd.exec(5), UMask=

login.defs(5) says the mask "is initialized" to UMASK, but it does not say which program does that on your distribution. Check your PAM files before you trust it.

The secure configuration

conf
# /etc/login.defs (edit the existing lines)
# New files: owner read/write, group read, others nothing. New dirs: 750.
UMASK		027
# New home directories: owner only.
HOME_MODE	0700

Make sure logins apply it. On Debian and Ubuntu:

bash
# Is pam_umask in the session stack? 0 means no.
grep -c pam_umask /etc/pam.d/common-session
# If it is missing, add it (reads UMASK from /etc/login.defs):
echo "session optional pam_umask.so" >> /etc/pam.d/common-session

On Rocky Linux 9 the stock /etc/pam.d/postlogin already has session optional pam_umask.so silent and login.defs already sets HOME_MODE 0700, so changing UMASK is enough there. Check with grep -rn pam_umask /etc/pam.d/, and look for umask lines in /etc/profile and /etc/bashrc that could override it.

Services do not log in, so give each one its own mask:

ini
# /etc/systemd/system/app.service.d/umask.conf
[Service]
# Files the service creates: 640, directories: 750.
UMask=0027

Existing home directories keep their old mode. Fix them once:

bash
for d in /home/*/; do chmod 0700 "$d"; done

Prove it

The stock Debian 12 image: UMASK 022, HOME_MODE commented out, and no pam_umask in the session stack. A new user gets a readable home and readable files:

bash
grep -E "^#?(UMASK|HOME_MODE)" /etc/login.defs
grep -c pam_umask /etc/pam.d/common-session
useradd -m -s /bin/bash alice
stat -c "%A %U %n" /home/alice
su - alice -c "umask; touch notes.txt; mkdir reports; ls -ld notes.txt reports"
text
UMASK		022
#HOME_MODE	0700
0
drwxr-xr-x alice /home/alice
0022
-rw-r--r-- 1 alice alice    0 Sep 24 20:36 notes.txt
drwxr-xr-x 2 alice alice 4096 Sep 24 20:36 reports

Change only login.defs. The new home directory is private, but the login umask is still 0022:

bash
useradd -m -s /bin/bash bob
stat -c "%A %U %n" /home/bob
su - bob -c umask
text
drwx------ bob /home/bob
0022

Add pam_umask and log in again:

bash
echo "session optional pam_umask.so" >> /etc/pam.d/common-session
su - bob -c "umask; touch notes.txt; mkdir reports; ls -ld notes.txt reports"
text
0027
-rw-r----- 1 bob bob    0 Sep 24 20:36 notes.txt
drwxr-x--- 2 bob bob 4096 Sep 24 20:36 reports

The difference for other users, and the old home that did not change:

bash
su - bob -c "head -c0 /home/alice/notes.txt && echo bob can read /home/alice/notes.txt"
su - alice -c "ls /home/bob"
stat -c "%a %n" /home/alice
chmod 0700 /home/alice
stat -c "%a %n" /home/alice
text
bob can read /home/alice/notes.txt
ls: cannot open directory '/home/bob': Permission denied
755 /home/alice
700 /home/alice

The test script is secure-tests/umask-login-defs/run.sh.

Mistakes people make

Trusting login.defs on its own

UMASK in login.defs changes the login umask only if pam_umask runs. Always check with umask in a fresh login, not by reading the file.

Forgetting services

A web app or backup job started by systemd uses UMask= from its unit, which defaults to 0022. The files it writes, often the sensitive ones, stay world-readable until you set it.

Going straight to 077 everywhere

077 also removes group read, which breaks setups where a group is meant to share files (a deploy group reading app config, for example). 027 keeps the group and removes everyone else. Use 077 for single-user hosts.

Expecting old files to change

A umask applies when a file is created. Files and home directories that already exist keep their mode. Find them with find /home -maxdepth 1 -type d -perm -o+r.

Checklist

  • /etc/login.defs has UMASK 027 and HOME_MODE 0700.
  • pam_umask is in the PAM session stack, or the shell startup files set umask 027.
  • A fresh login prints 0027 for umask.
  • Each custom systemd service sets UMask=0027 or stricter.
  • Every existing home directory is mode 0700 or 0750.
  • A test file created by a normal user is not readable by another user.

The umask is the quietest security setting on the host. Set it once, prove it once, and every file created afterward is private by default.

FND

Learn it on a live range

Linux 1: the command line to a working, secure host, in Foundation: a real host in your browser, and every objective checked on the machine.

Start free

The Secure Way

More on linux hosts

SSH, sudo, firewalls, mount options, updates and the host basics every engineer should get right.

All linux hosts guides