Linux hosts

Linux users, groups and file permissions, the secure way

Two people needed to share a folder, the permissions got in the way, and someone typed chmod 777. It worked on the first try. So did the next person's attempt to delete everything in it.

The short answer

Give each person their own account, put people who share files in a group, and make the shared directory owned by that group with mode 2770 (setgid, no access for others). Add the sticky bit (3770) if members must not delete each other's files. Use an ACL for one extra reader. Never use 777.

Updated Houssam Hammoudi, CTOTested with coreutils 9.1, acl 2.3.1 (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

Linux decides access with three sets of bits on every file: one for the owner, one for the file's group, and one for everyone else ("other"). Each set has read (r), write (w) and execute (x). For a directory, w means "may create, rename and delete entries", and x means "may enter".

chmod 777 sets all nine bits. On a directory, that means every account on the host, including service accounts and a compromised web app, can create, replace and delete files there. Deleting a file needs write access to the directory, not to the file. So even files that are read-only for others can be removed and replaced.

Shared accounts cause the second problem. When three people log in as deploy, the logs cannot tell you who did what, and removing one person means changing a password everyone knows.

What the docs say

For a directory, it indicates that BSD semantics are to be used for that directory: files created there inherit their group ID from the directory, not from the effective group ID of the creating process

Source: inode(7), The file type and mode

The sticky bit (S_ISVTX) on a directory means that a file in that directory can be renamed or deleted only by the owner of the file, by the owner of the directory, and by a privileged process.

Source: inode(7), The file type and mode

The ACL_MASK entry denotes the maximum access rights that can be granted by entries of type ACL_USER, ACL_GROUP_OBJ, or ACL_GROUP.

Source: acl(5)

The man pages describe each bit exactly, but they never put them together into "how to share a folder". That missing recipe is why 777 is so common.

The secure configuration

bash
# One account per person. Service accounts get no shell and no home.
useradd -m -s /bin/bash alice
useradd -m -s /bin/bash bob
useradd -r -s /usr/sbin/nologin -d /nonexistent app

# A group for the people who share files.
groupadd team-a
usermod -aG team-a alice
usermod -aG team-a bob

# The shared directory:
#   2 = setgid: new files get group team-a, not the creator's own group
#   7 = owner (root) full access
#   7 = group team-a may list, enter, create and delete
#   0 = everyone else: nothing
install -d -o root -g team-a -m 2770 /srv/team-a

# A drop directory where members may add files but not delete each other's:
#   3 = setgid + sticky bit
install -d -o root -g team-a -m 3770 /srv/team-a-drop

# One extra reader (the app service) without adding it to the group.
setfacl -m u:app:rx /srv/team-a
setfacl -m u:app:r  /srv/team-a/report.txt

Members need a umask that keeps group write, such as 007, when they work in the shared directory (umask 007 in their shell, or UMask=0007 for a service). Group membership changes apply at the next login.

Prove it

The accounts:

bash
getent passwd alice app | cut -d: -f1,3,4,6,7
id alice; id mallory
text
alice:1000:1001:/home/alice:/bin/bash
app:999:999:/nonexistent:/usr/sbin/nologin
uid=1000(alice) gid=1001(alice) groups=1001(alice),1000(team-a)
uid=1002(mallory) gid=1003(mallory) groups=1003(mallory)

The 777 directory. Alice writes a report; mallory cannot edit it, but can delete it, because deleting is a write to the directory:

bash
mkdir -p /srv/share-777 && chmod 777 /srv/share-777
su alice -c "echo q3 numbers > /srv/share-777/report.txt"
su mallory -c "echo tampered > /srv/share-777/report.txt; rm /srv/share-777/report.txt && echo mallory deleted the report"
text
bash: line 1: /srv/share-777/report.txt: Permission denied
mallory deleted the report

The group directory. New files get the team-a group from the setgid bit, Bob can edit, Mallory cannot even read:

bash
stat -c "%A %U:%G %n" /srv/team-a
su alice -c "umask 007; echo q3 numbers > /srv/team-a/report.txt"
stat -c "%A %U:%G %n" /srv/team-a/report.txt
su bob -c "cat /srv/team-a/report.txt; echo bob edit >> /srv/team-a/report.txt && echo bob can edit"
su mallory -c "cat /srv/team-a/report.txt"
text
drwxrws--- root:team-a /srv/team-a
-rw-rw---- alice:team-a /srv/team-a/report.txt
q3 numbers
bob can edit
cat: /srv/team-a/report.txt: Permission denied

The sticky bit stops Bob from deleting Alice's file in the drop directory:

bash
su alice -c "umask 007; echo draft > /srv/team-a-drop/alice.txt"
su bob -c "rm -f /srv/team-a-drop/alice.txt"
stat -c "%A %n" /srv/team-a-drop
text
rm: cannot remove '/srv/team-a-drop/alice.txt': Operation not permitted
drwxrws--T /srv/team-a-drop

One ACL entry lets the app service read the report, and nobody else:

bash
getfacl -p /srv/team-a/report.txt
su -s /bin/sh app -c "head -1 /srv/team-a/report.txt"
text
# file: /srv/team-a/report.txt
# owner: alice
# group: team-a
user::rw-
user:app:r--
group::rw-
mask::rw-
other::---

q3 numbers

The audit: world-writable entries and files whose owner no longer exists:

bash
find /srv -xdev \( -perm -0002 -a ! -type l \) -o \( -nouser -o -nogroup \)
text
/srv/leftover
/srv/share-777

The test script is secure-tests/linux-users-groups-file-permissions/run.sh.

Mistakes people make

chmod 777 to make an error go away

It fixes the error by giving the whole host write access. Find out which account needs which access, and give that one account or group exactly that.

Protecting the file but not the directory

A file with mode 644 in a 777 directory can be deleted and replaced by anyone. Check the directory's mode as well as the file's.

Shared logins

sudo -u deploy for three people is still one identity in the logs. Give each person an account and use groups and sudo rules for the shared parts.

Adding a service account to a people group

The service then gets everything the group can touch. Give it an ACL entry for the one file or directory it needs.

Forgetting to clean up after people leave

Files owned by a deleted user keep the old numeric ID. The next account that gets that ID owns them. Find them with find / -xdev -nouser and reassign them before you reuse IDs.

Checklist

  • Every person has their own account; no shared logins.
  • Service accounts have /usr/sbin/nologin as shell.
  • Shared directories are group-owned with mode 2770, or 3770 with the sticky bit.
  • No directory outside /tmp and /var/tmp is world-writable.
  • find / -xdev -perm -0002 ! -type l has been reviewed.
  • find / -xdev -nouser -o -nogroup returns nothing.
  • Extra readers use ACL entries, reviewed with getfacl.
  • Group membership is reviewed when people change teams or leave.

Permissions are a conversation about who needs what. `777` ends that conversation by answering "everybody, everything", which is rarely what anyone meant.

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