Secure a Shared DevOps Directory

Configure Linux file permissions to secure a shared multi-user project workspace.

Introduction

30 Second Summary

Think about sharing a folder at work. Some people need to edit files, others should only read them, and certain people should have no access at all. On Linux servers, this is exactly the problem DevOps teams solve every day with file permissions.

In this project, you will simulate a three-person team on Linux, build their shared workspace, and progressively solve access problems until the system enforces security automatically.

What You'll Build

A secure project directory where your simulated team collaborates freely, new files automatically inherit the right group ownership, and outsiders are locked out completely.

By the end of this project, you'll have:

  • A shared /projects directory where developers collaborate and new files automatically inherit the correct group ownership thanks to SGID.
  • Permission schemes enforcing least-privilege access so developers can read and write, QA can read only, and outsiders are locked out.
  • Automatic secure defaults via umask so new files are never accidentally world-readable.
  • Secret Mission: Fine-grained ACL rules granting specific users targeted access without changing group membership.

Are there any prerequisites?

You need WSL2 with Ubuntu already installed on your Windows machine. You should also be comfortable with basic Linux commands like cd, ls, mkdir, and touch.

Before We Start

Before We Start...

Before we dive in, let's lock in what you're about to build and why it matters. This step is your moment to commit to the goal before any hands-on work begins.

Set Up Your Team Environment

Linux permissions only make sense when multiple users are trying to access the same files. On a single-user system, you never hit "permission denied" because there is only one identity.

In this step, you will create three test users and organize them into two groups to simulate a real DevOps team. Alice and bob are developers. Charlie handles QA.

In this step, get ready to:
  • Create three test users with home directories and passwords.
  • Create two groups and assign team members.
  • Verify group memberships are correct.
Create your test users

Each person on a Linux server has their own user account. To see how permissions work between people, you need multiple user accounts on your system.

  • Create three test users with home directories by running these commands in your WSL terminal:
sudo useradd -m alice
sudo useradd -m bob
sudo useradd -m charlie

What does this command do?

  • useradd creates a new user account on the system.
  • The -m flag tells Linux to create a home directory for each user (e.g. /home/alice).
  • sudo runs the command as the root administrator, which is required for creating users.
  • Confirm the users were created by checking their home directories:
ls /home

You should see alice, bob, and charlie listed alongside your own username.

Seeing 'user already exists'?

If you ran this project before, the users may already exist. Remove them first with sudo userdel -r alice (repeat for each user), then run the useradd commands again.

Help me resolve a user-already-exists error in my Linux terminal.

  • Set a password for each user. Run each of the following commands one at a time:
sudo passwd alice
sudo passwd bob
sudo passwd charlie

These commands are interactive

Each passwd command prompts you to type a new password, then type it again to confirm. You will not see characters appear as you type. This is normal Linux security behavior.

Use a simple password like test123 since these are local test accounts that you will delete at the end of the project.

After each command completes, you should see a confirmation message that the password was updated successfully.

Create groups and assign members

Groups let you manage permissions for multiple users at once. Instead of setting permissions for alice and bob individually, you set them once on the developers group.

  • Create two groups and add users to them by running these commands:
sudo groupadd developers
sudo groupadd qa
sudo usermod -aG developers alice
sudo usermod -aG developers bob
sudo usermod -aG qa charlie

What does this code do?

  • groupadd creates a new group on the system.
  • usermod -aG adds a user to a supplementary group. The -a means "append" so existing group memberships are preserved. The -G specifies which group to add them to.
  • Alice and bob join the developers group. Charlie joins the qa group.
  • Verify the group memberships by running:
getent group developers
getent group qa

You should see developers:x:GID:alice,bob and qa:x:GID:charlie (the GID number will vary on your system).

Not seeing users listed in the group?

Double-check you typed -aG (capital G). A lowercase -g changes the user's primary group instead of appending a supplementary one.

Run id alice to see all of alice's groups. If developers is missing, re-run sudo usermod -aG developers alice.

Why aren't my users showing up in the group when I run getent group?

Verify your complete team setup

Before you move on, confirm that every user exists and belongs to the correct group.

  • Before running the next commands, predict: what groups do you expect to see listed for alice? What about charlie?
  • Check each user's identity and group memberships by running:
id alice
id bob
id charlie
groups alice

What should I see?

Each id command shows the user's UID, primary group (GID), and supplementary groups. You should see developers listed in the groups for alice and bob, and qa listed for charlie.

The groups alice command outputs a simpler list of just the group names alice belongs to.

Seeing 'no such user'?

The user was not created successfully. Run sudo useradd -m alice (or whichever user is missing) and try again.

Help me figure out why my Linux user was not created.

Your simulated team is ready. Next up, you will create the shared project workspace and discover firsthand what happens when permissions are wrong.

Create the Workspace and Hit the Permission Wall

Your team of three users across two groups is ready. Now it's time to build the workspace where they'll actually collaborate on project files.

A shared directory seems simple to create. But who actually owns it, and can your team members use it?

In this step, get ready to:
  • Create the /projects directory structure with test files.
  • Check who owns the directories and what permissions they have.
  • Test whether alice and bob can work in the shared workspace.
Create the shared directory structure

Every team needs a place to store project files. You'll create a /projects directory with two subdirectories: one for application code and one for configuration files.

  • Create the directory structure by running these commands:
sudo mkdir -p /projects/app
sudo mkdir /projects/configs

What do these commands do?

The -p flag on mkdir creates parent directories as needed. So mkdir -p /projects/app creates both /projects and /projects/app in one command.

The second command creates /projects/configs inside the already-existing /projects directory.

  • Confirm the directories exist by running:
ls /projects

You should see app and configs listed in the output.

  • Now create some test files inside the directories by running:
sudo touch /projects/app/main.py /projects/configs/db.conf

Why use sudo for these commands?

The /projects directory lives at the root of the filesystem, and only the root user can create directories there. Using sudo runs the command with root privileges, which means root becomes the owner of everything you create.

  • Verify the files exist by running:
ls /projects/app /projects/configs

You should see main.py listed under /projects/app and db.conf listed under /projects/configs.

Don't see the directories or files?

Make sure you included sudo before each command. Without it, you'll get a permission error because only root can write to /.

If you see a typo in a directory name, remove it with sudo rm -rf /projects and start the commands again from the top.

Help me troubleshoot why my directories weren't created

Check the current permissions

Before testing whether your team can access these files, take a closer look at who owns them and what Linux file permissions are currently set.

  • Inspect the ownership and permissions of the /projects directory by running:
ls -la /projects/

You should see output similar to this:

drwxr-xr-x  root root  app
drwxr-xr-x  root root  configs

What does this output mean?

Every file and directory shows root root in the owner and group columns. This means root owns everything.

The permission string drwxr-xr-x breaks down as: d (directory), rwx (owner can read, write, execute), r-x (group can read and execute), r-x (others can read and execute). Nobody except root has write permission.

Alice and bob fall into the "others" category here. They are not root, and they are not in the root group. They can read and list these directories, but they cannot write to them.

Test whether your team can access the workspace

Alice is in the developers group. The /projects/app directory exists. Let's see if she can actually do her job and create a file there.

  • Switch to alice by running:
su - alice

Enter the password you set for alice in the previous step when prompted.

Before you run the next command, make a prediction. Alice is a developer on this team. Do you think she can create a file in /projects/app?

  • Try to create a file as alice by running:
touch /projects/app/test.txt

You'll see this error:

touch: cannot touch '/projects/app/test.txt': Permission denied

Alice is locked out of her own project directory. Even though she's a developer, the system does not care about her group membership here. The directory is owned by root:root, and alice is just "others" with no write access.

  • Exit alice's session by running:
exit
  • Switch to bob to confirm he has the same problem:
su - bob
  • Try to create a file as bob by running:
touch /projects/app/test.txt

Same result. Permission denied. Both team members are completely locked out of their own project workspace.

  • Exit bob's session by running:
exit

Why did this happen?

You created the directories with sudo, which made root the owner. The default permissions (755) only give write access to the owner. Alice and bob are not root, and they are not in root's group. They are "others" with read-only and execute access.

This is the fundamental permission problem in shared Linux environments. Creating a directory is not enough. You also need the right ownership and permissions for your team to use it.

Didn't see Permission denied?

If alice could create the file, you may have run the touch command without fully switching users. Make sure you used su - alice (with the dash) to load alice's environment, not just su alice.

Run whoami to confirm you are logged in as alice before retrying the touch command.

Help me figure out why I didn't get Permission denied

This is the exact problem that chmod solves. In the next step, you'll use it to open up access for your team.

Fix Access with chmod

In the last step, alice and bob tried to create files in /projects/app/ and hit "Permission denied." The default permissions lock everyone except root out of the project workspace.

The chmod command fixes this. It controls who can read, write, and execute each file and directory. You will use both octal notation (numbers like 770) and symbolic notation (letters like u=rw,g=r,o=) because DevOps roles require fluency in both.

In this step, get ready to:
  • Set directory permissions to allow full access for owner and group using octal notation.
  • Set file permissions using both symbolic and octal notation.
  • Test whether alice can now create files in the project directory.
Set directory permissions with octal chmod

Octal notation uses three digits to represent permissions for owner, group, and others. Each digit is a sum: read (4) + write (2) + execute (1). So 770 means owner gets full access (7), group gets full access (7), and others get nothing (0).

  • Set the /projects directory and its subdirectories to 770 by running these commands:
sudo chmod 770 /projects
sudo chmod 770 /projects/app
sudo chmod 770 /projects/configs

What does 770 mean?

Each digit represents a permission set: owner, group, others.

  • 7 = read (4) + write (2) + execute (1) = full access.
  • 0 = no permissions at all.
  • So 770 gives the owner and group full read/write/execute, while everyone else is completely locked out.
  • Verify the updated permissions by running:
ls -la /projects/

You should see drwxrwx--- for each subdirectory. The first d means directory, the next three characters (rwx) are owner permissions, the next three (rwx) are group permissions, and the last three (---) are others.

Still seeing 755 permissions?

Make sure you included sudo before each chmod command. Only root can change permissions on root-owned files and directories.

Double-check you targeted the correct paths. Run ls -la /projects/ to confirm the directory names match what you typed.

Help me debug why my chmod 770 command did not change the permissions.

Set file permissions with symbolic and octal notation

Symbolic notation uses letters and operators instead of numbers. It reads more naturally: u is user (owner), g is group, o is others. The = operator sets exact permissions.

  • Set main.py to owner read/write, group read-only, others nothing using symbolic notation:
sudo chmod u=rw,g=r,o= /projects/app/main.py

How does symbolic notation work?

The command u=rw,g=r,o= reads as: set user to read+write, set group to read-only, set others to nothing.

Symbolic notation is useful for targeted changes. You can add a single permission with + (like g+w) or remove one with - (like o-r). Octal always sets all permissions at once.

  • Now set db.conf to owner-only access using octal notation. Sensitive config files should be locked down tightly:
sudo chmod 600 /projects/configs/db.conf

Why 600 for config files?

Config files often contain database credentials, API keys, or connection strings. Permission 600 (-rw-------) means only the file owner can read or write it. Nobody else on the system can even peek at the contents.

  • Verify both file permissions by running:
ls -l /projects/app/main.py
ls -l /projects/configs/db.conf

You should see -rw-r----- for main.py and -rw------- for db.conf.

Permissions don't match?

If main.py shows -rw-r--r-- instead of -rw-r-----, the o= part of your symbolic command may have been missed. Re-run the full command including o= (with nothing after the equals sign) to clear others' permissions.

Help me understand why my file permissions are not showing the expected values.

Test access as alice

The directories are now set to 770 and the files have appropriate permissions. Time to test whether the team can actually work in this directory.

  • Switch to alice by running:
su - alice

Enter alice's password when prompted.

Before you run the next command, do you think alice can now create files in /projects/app/? The directory permissions allow group read/write/execute. What do you predict will happen?

  • Try creating a file as alice:
touch /projects/app/test.txt

You see touch: cannot touch '/projects/app/test.txt': Permission denied.

The permissions are correct on paper. But look at who owns the directory. It is still root:root. The "group" in those permission bits refers to root's group. Alice is in the developers group, not root's group. Permissions alone are not enough.

Why did 770 not fix it?

Permissions define what actions (read, write, execute) are allowed for three categories: the file owner, the file's group, and everyone else. But which users fall into those categories is determined by ownership.

Right now the directory's group is root. Alice is not a member of the root group, so she falls into "others" which has zero permissions. The fix is to change the group ownership to developers so alice actually belongs to the group that has access.

  • Exit back to your sudo user by running:
exit
  • Confirm the final state of your permissions by running:
ls -l /projects/app/main.py

You should see -rw-r-----. The permissions are correctly configured. The ownership problem is what you will fix next.

Permissions are half the puzzle. The other half is ownership. In the next step, you will transfer group ownership to developers and enable automatic group inheritance so new files always belong to the right group.

Fix Ownership and Enable Group Inheritance

In the last step, you set directory permissions to 770. In theory, that gives the group full access. But alice and bob are still locked out.

The problem is ownership. Every directory still belongs to root:root. Alice is not in root's group, so the group permissions do nothing for her. You need chown to transfer group ownership to developers. And without SGID, every file alice creates will belong to her personal group instead of developers. That breaks collaboration for bob.

In this step, get ready to:
  • Transfer group ownership of the project directories to the developers group.
  • Enable SGID so new files automatically inherit the correct group.
  • Test that alice and bob can collaborate on shared files.
Transfer group ownership to developers

The chown command changes who owns a file or directory. The :groupname syntax targets just the group without changing the user owner.

  • Transfer group ownership of both project directories to developers by running these commands:
sudo chown -R :developers /projects/app
sudo chown -R :developers /projects/configs

What do these commands do?

The -R flag applies the change recursively, meaning every file and subdirectory inside also gets the new group.

The colon before developers tells chown to change only the group owner, leaving the user owner (root) untouched.

  • Verify the ownership change by running:
ls -la /projects/

You should see root developers as the owner:group for both app and configs.

Still seeing root:root?

Make sure you included the colon before developers in the command. Without the colon, chown tries to change the user owner instead of the group.

Also confirm the group exists by running getent group developers. If nothing appears, the group was not created in Step 1.

help me fix chown group ownership

Enable SGID on the project directories

Ownership is fixed, but there is a hidden problem. When alice creates a new file, it will belong to her personal group (alice) by default. Bob cannot access files in alice's personal group. SGID solves this by forcing new files to inherit the directory's group instead of the creator's personal group.

  • Enable SGID on both project directories by running:
sudo chmod g+s /projects/app
sudo chmod g+s /projects/configs

What is SGID on a directory?

SGID (Set Group ID) on a directory means every new file or subdirectory created inside automatically inherits the directory's group ownership. Without it, new files get the creator's primary group.

This is the standard mechanism for shared team directories on Linux servers. It saves teams from having to manually chown every new file.

  • Verify SGID is set by running:
ls -la /projects/

You should see drwxrws--- for both directories. The s in the group execute position confirms SGID is active.

Seeing x instead of s?

If you see drwxrwx--- (lowercase x instead of s), the SGID command did not apply. Re-run sudo chmod g+s /projects/app and check again.

If you see a capital S instead of lowercase s, the directory lacks group execute permission. Fix it with sudo chmod g+x /projects/app first.

help me understand the SGID bit in ls output

Test collaboration between alice and bob

Ownership and SGID are configured. Time to prove they actually work by having alice create a file and bob edit it.

  • Switch to alice and create a new file by running:
su - alice
touch /projects/app/feature.py

Why su - alice?

The su - alice command switches you to alice's user session. The - flag loads alice's full environment, including her home directory and shell settings. You will be prompted for alice's password (the one you set in Step 1).

Before you check the result, think about this: what group do you expect to own this new file? Alice's personal group, or developers?

  • Check who owns the file by running:
ls -l /projects/app/feature.py

The file is owned by alice:developers. SGID did its job. Even though alice created the file, it belongs to the developers group automatically.

  • Exit alice's session, switch to bob, and have bob edit the file by running:
exit
su - bob
echo "# Bob's code" >> /projects/app/feature.py
cat /projects/app/feature.py

You will be prompted for bob's password when switching users. After the cat command, you should see # Bob's code in the output. Bob can edit alice's file because they share the developers group.

  • Exit bob's session to return to your main user by running:
exit

Bob getting Permission denied?

Check that feature.py has group write permission. Run ls -l /projects/app/feature.py and look for rw in the group position. If the group only has read permission, the default umask stripped write access. Fix it with chmod g+w /projects/app/feature.py as alice or with sudo.

Also confirm bob is in the developers group by running id bob. You should see developers in the groups list.

help me fix bob's write access to shared files

Alice and bob can now create and edit shared files without any manual ownership fixes. Next up, you will lock down the defaults so new files are never accidentally world-readable, and prevent team members from deleting each other's work.

Secure Defaults with umask and the Sticky Bit

Your team can now collaborate on files thanks to SGID and group ownership. But two gaps remain.

First, any file alice creates may still be readable by everyone on the system because of the default umask value. Second, bob can delete alice's files in the shared directory because group write permission allows it. In this step, you will close both gaps using umask and the sticky bit.

In this step, get ready to:
  • Expose how default umask makes new files world-readable.
  • Set a restrictive umask so new files are locked down automatically.
  • Apply the sticky bit to prevent developers from deleting each other's files.
Expose the world-readable default

Linux uses a value called umask to determine the default permissions on every new file. The default umask on most systems is 0022, which means new files get 644 permissions (owner read/write, everyone else read). For project files on a shared server, "everyone else can read" is too permissive.

  • Switch to alice by running this command:
su - alice

Enter alice's password when prompted.

  • Check alice's current umask by running:
umask

You should see 0022 printed. This means new files lose write for group and write+read for others (subtracted from 666), resulting in 644.

  • Create a test file and check its permissions by running:
touch /projects/app/test_default.py
ls -l /projects/app/test_default.py

You should see -rw-r--r-- in the output. The r-- at the end means anyone on the system can read this project file.

Why is world-readable a problem?

On a shared server, other users and services can read files with 644 permissions. Sensitive project files (API keys in config, proprietary code) would be exposed to anyone with shell access.

Set a restrictive umask

You can override the default by setting umask to 0027. This means new files get 640 (owner read/write, group read, others nothing) and new directories get 750.

  • Set the restrictive umask for this session by running:
umask 0027
  • Create a new file to test the effect by running:
touch /projects/app/test_secure.py
ls -l /projects/app/test_secure.py

Before you look at the output, what permissions do you expect to see on this file?

You should see -rw-r----- (640). Group members can still read, but others get nothing.

What changed between the two files?

  • test_default.py was created with umask 0022, so it ended up 644 (world-readable).
  • test_secure.py was created with umask 0027, so it ended up 640 (others locked out).
  • To make this permanent across sessions, you would add umask 0027 to ~/.bashrc.

Both files show the same permissions?

  • Make sure you ran umask 0027 before creating test_secure.py. If both files show 644, the umask change was not applied.
  • Confirm the umask is active by running umask again. It should print 0027.

Having trouble with umask?

Apply the sticky bit to prevent file deletion

Right now, bob can delete alice's files because the /projects/app directory grants group write permission. The sticky bit prevents users from deleting files they do not own, even when they have write access to the directory.

  • Exit alice's session to return to your sudo user by running:
exit
  • Apply the sticky bit to the app directory by running:
sudo chmod +t /projects/app
  • Verify the sticky bit is set by running:
ls -la /projects/

You should see drwxrws--T for the app directory. The capital T in the last position confirms the sticky bit is active.

Why is it a capital T instead of lowercase t?

A lowercase t means the sticky bit is set AND the "others" execute bit is also set. A capital T means the sticky bit is set but "others" do NOT have execute permission. Since your directory has no permissions for others (--- in the others position), the sticky bit shows as T.

Now test whether bob can delete alice's file.

  • Switch to bob by running:
su - bob

Enter bob's password when prompted.

Before you run the next command, do you think bob will be able to delete alice's file? Why or why not?

  • Attempt to delete alice's file by running:
rm /projects/app/feature.py

You should see:

rm: cannot remove '/projects/app/feature.py': Operation not permitted

Bob has write permission on the directory, but the sticky bit blocks him from deleting a file he does not own. Only alice (the file owner), root, or the directory owner can delete it.

  • Exit bob's session by running:
exit

Where else is the sticky bit used?

The /tmp directory on every Linux system uses the sticky bit for the same reason. Any user can write to /tmp, but no user can delete another user's temporary files.

Bob was able to delete the file?

  • Make sure you applied sudo chmod +t /projects/app before switching to bob.
  • Verify the sticky bit is present by running ls -la /projects/ and looking for the T in the permissions string for app.
  • If you accidentally ran the delete as your sudo user (who owns the directory), the sticky bit would not block it. Make sure you switched to bob first with su - bob.

Why can bob still delete the file even with the sticky bit?

Your shared workspace now enforces three layers of security: group ownership controls who can access files, umask ensures new files are never accidentally world-readable, and the sticky bit prevents developers from deleting each other's work. Next up, you will tackle a more targeted access control challenge using ACLs.

Secret mission

Fine-Grained Access with ACLs

Charlie from QA needs read-only access to the project files for testing. But adding him to the developers group would give him write access, violating least privilege. Can you use POSIX Access Control Lists to grant targeted, per-user permissions without changing group membership?

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally in WSL, so there are no ongoing costs.

Resources you used:

  • Directory: /projects with subdirectories /projects/app and /projects/configs (plus test files inside).
  • Package: acl utilities (getfacl and setfacl commands).

Keep everything running

No action needed. Choose this if you want to keep practising with file permissions, creating new users, or experimenting with ACLs on the existing directory structure.

  • The /projects directory remains on your WSL filesystem for further exploration.
  • The acl package stays installed so you can continue using setfacl and getfacl.
  • The test users and groups were already removed in the Secret Mission cleanup step.

Pause - I'll come back to this later

Nothing to shut down or pause. All resources are local files on your WSL filesystem with no running processes or ongoing costs.

  • Your /projects directory will be right where you left it next time you open WSL.
  • If you want to start fresh later, follow the Delete tab instructions at that time.

Delete - I don't want to use this again

Remove all project resources and start fresh. Run the following commands to delete everything this project created.

  • Remove the entire /projects directory and all its contents by running:
sudo rm -rf /projects
  • Verify the directory is gone by running:
ls /projects

You should see ls: cannot access '/projects': No such file or directory.

  • Remove the acl package if you no longer need ACL utilities by running:
sudo apt remove acl
  • The test users (alice, bob, charlie) and groups (developers, qa) were already deleted during the Secret Mission. No further cleanup is needed for those.

Nice Work!

Nice Work!

You just built a secure, multi-user project workspace from scratch using Linux file permissions. You can now diagnose and fix any permission problem a DevOps engineer encounters on a shared server.

You've learned how to:

  • Diagnose permission failures and fix them with chmod (both octal and symbolic notation) and chown for ownership transfer.
  • Configure SGID for automatic group inheritance, umask for secure-by-default file creation, and the sticky bit to prevent accidental file deletion in shared directories.
  • Build a transferable mental model for diagnosing Linux permission problems: check ownership first, then permissions, then ACLs.
  • Secret Mission: Applied POSIX ACLs for fine-grained per-user access control without modifying group membership.

Ready to quiz yourself?