8 of 8

Linux FilesystemLesson 8 of 8

Capstone: a shared project area

Combine ownership, permissions, setgid, sticky bits, usage reports and cleanup to build and audit a shared directory the way an administrator would.

  • useradd
  • usermod
  • getent
  • tar
  • find
  • du
  • userdel

50 min6 exercises

After this lesson you can

  • Turn a written requirement into an owner, group and mode.
  • Build a team directory where new files inherit the group and members cannot delete each other's files.
  • Test permissions from each user's point of view.
  • Report usage per user, archive and prune old files, and audit for policy violations.

The brief

A web team of two needs a shared directory. This is how the request would arrive:

Create /srv/projects/web. Alice and Bob must both be able to add and edit files. Everything they create should belong to their team group. Nobody outside the team should see it. Bob must not be able to delete Alice’s files, or the other way round.

Administration is mostly this: turn a plain request into settings you can check. The skills from the previous seven lessons are all in it.

Step 1: requirements to settings

Take each sentence and find the setting that enforces it.

RequirementSetting
Alice and Bob add and edit filesA group webteam with both as members; group has rwx on the directory
New files belong to the team groupsetgid on the directory (2)
Nobody outside the team sees itOthers have ---
Members cannot delete each other’s filessticky bit on the directory (1)
Owner of the directoryroot, so members cannot loosen the rules themselves

Assemble the mode. Special bits 2 and 1 add to 3; the user and group get 7; others get 0:

3 7 7 0   →   3770   →   drwxrws--T

The capital T means sticky is set but others have no execute, which is what we want. A lowercase t would mean others can enter.

Step 2: group and users

sudo groupadd webteam
sudo useradd -m -s /bin/bash -G webteam alice
sudo useradd -m -s /bin/bash -G webteam bob
sudo useradd -m -s /bin/bash carol

-m creates a home directory, -s sets the login shell and -G adds supplementary groups. carol is deliberately outside the team so you can test the boundary. Check:

id alice
getent group webteam
uid=1001(alice) gid=1003(alice) groups=1003(alice),1002(webteam)
webteam:x:1002:alice,bob

Step 3: the directory

sudo mkdir -p /srv/projects/web
sudo chown root:webteam /srv/projects/web
sudo chmod 3770 /srv/projects/web
ls -ld /srv/projects/web
drwxrws--T 2 root webteam 4096 Nov  3 11:05 /srv/projects/web

Owner root, group webteam, mode drwxrws--T. /srv/projects was created by mkdir -p with the default mode so that users can reach web through it. Always check that every parent directory on the path has x for the people who need it.

Step 4: prove it, as each user

A permission setup is not finished until you have tried it from every side. su - USER -c 'COMMAND' starts a fresh login for that user, so the groups and umask are the real ones.

Alice creates a file:

sudo su - alice -c 'echo "<h1>hi</h1>" > /srv/projects/web/index.html; mkdir /srv/projects/web/css; ls -l /srv/projects/web'
total 8
drwxrwsr-x 2 alice webteam 4096 Nov  3 11:08 css
-rw-rw-r-- 1 alice webteam   12 Nov  3 11:08 index.html

The group is webteam, not alice: that is setgid at work. The file is group-writable because the login umask is 002. The new css directory inherited setgid too (s in the group position), so the rule carries down the tree.

Bob edits Alice’s file, then tries to delete it:

sudo su - bob -c 'echo "<p>bob</p>" >> /srv/projects/web/index.html && echo append ok'
sudo su - bob -c 'rm /srv/projects/web/index.html'
append ok
rm: cannot remove '/srv/projects/web/index.html': Operation not permitted

Editing works because of the group write bit. Deleting fails because of the sticky bit, even though Bob has write access to the directory. Without it, w on a directory would let him remove anything inside.

Carol, outside the team:

sudo su - carol -c 'ls /srv/projects/web; cat /srv/projects/web/index.html'
ls: cannot open directory '/srv/projects/web': Permission denied
cat: /srv/projects/web/index.html: Permission denied

All four rules are now verified, not assumed.

Step 5: who is using the space?

Counting files per owner shows who is filling a shared area:

sudo su - alice -c 'touch /srv/projects/web/a1 /srv/projects/web/a2'
sudo su - bob -c 'touch /srv/projects/web/b1'
sudo find /srv/projects/web -type f -printf '%u\n' | sort | uniq -c
      3 alice
      1 bob

-printf '%u\n' prints the owner of each match, sort groups equal names together, and uniq -c counts runs. For sizes rather than counts:

sudo find /srv/projects/web -user alice -type f -exec du -ch {} + | tail -1
sudo du -sh /srv/projects/web
12K	total
16K	/srv/projects/web

Step 6: archive, then prune

Cleaning up is where data gets lost. The safe order is always: list, archive, verify, delete.

Backdate two files to simulate old data:

sudo touch -d '45 days ago' /srv/projects/web/old1.log /srv/projects/web/old2.log
  1. List what would be removed. -mtime +30 matches files last modified more than 30 days ago:
sudo find /srv/projects/web -type f -mtime +30
/srv/projects/web/old2.log
/srv/projects/web/old1.log
  1. Archive exactly those files:
sudo tar -czf ~/lab/web-old.tar.gz -C /srv/projects/web $(sudo find /srv/projects/web -type f -mtime +30 -printf '%P\n')

c creates, z compresses, f names the archive. -C makes tar change into the directory first, so the archive stores short names, and -printf '%P\n' prints each match relative to the directory being searched.

  1. Verify the archive. t lists:
tar -tzf ~/lab/web-old.tar.gz
old2.log
old1.log
  1. Delete with the same selection:
sudo find /srv/projects/web -type f -mtime +30 -delete
sudo ls -l /srv/projects/web
total 12
-rw-rw-r-- 1 alice webteam  0 Nov  3 11:12 a1
-rw-rw-r-- 1 alice webteam  0 Nov  3 11:12 a2
-rw-rw-r-- 1 bob   webteam  0 Nov  3 11:12 b1
drwxrwsr-x 2 alice webteam 4096 Nov  3 11:08 css
-rw-rw-r-- 1 alice webteam 23 Nov  3 11:09 index.html

Keeping the selection identical between the listing, the archive and the deletion is the point. Run the same find with -delete replaced by -print first, every time.

Step 7: audit

Months later, someone will have changed something. A few find expressions find the usual drift:

ProblemCommand
Anything writable by everyonefind /srv/projects -perm -o+w -not -type l
Files in the wrong groupfind /srv/projects/web -not -group webteam
setuid or setgid filesfind /srv/projects -type f -perm /6000
Files whose owner was deletedfind /srv/projects -nouser -o -nogroup

Plant a mistake and catch it:

sudo chmod o+w /srv/projects/web/index.html
sudo find /srv/projects -perm -o+w -not -type l
/srv/projects/web/index.html

Repair it, and run the audit again to confirm it prints nothing:

sudo chmod o-w /srv/projects/web/index.html
sudo find /srv/projects -perm -o+w -not -type l

Silence from an audit command is the pass condition. For a recurring job, put these commands in a script and run it from cron or a systemd timer, and mail yourself anything it prints.

Step 8: tear it down

Remove everything you created, in the reverse order:

sudo rm -r /srv/projects
sudo userdel -r alice
sudo userdel -r bob
sudo userdel -r carol
sudo groupdel webteam

userdel -r removes the home directory too. Confirm that nothing is left:

getent group webteam
ls /srv

The routine

The same sequence solves most filesystem requests:

  1. Write down who needs what, in plain words.
  2. Pick owner, group and mode for each sentence, and check parents are enterable.
  3. Create it. Prefer root as owner of shared areas.
  4. Test as each kind of user, including one who should be refused.
  5. Measure: df, du, find.
  6. Keep an audit that prints nothing when all is well.

Practice

0 of 6 done

Do these on your own system. They get harder. Check each result, then tick it off.

  1. Warm-up

    From requirement to mode

    Without any users or sudo, write the mode for a directory that satisfies all four rules, apply it to ~/lab/spec, and show it.

    • The owner and the group may read, write and enter it.
    • Others get no access.
    • Files created inside belong to the directory's group.
    • Only a file's owner can delete or rename it.
    Check your result
    ls -ld ~/lab/spec
    drwxrws--T 2 alex alex 4096 Nov  3 11:02 /home/alex/lab/spec
    Hint

    Four digits: special bits, then user, group, others. Setgid is 2 and sticky is 1.

    Solution
    mkdir -p ~/lab/spec
    chmod 3770 ~/lab/spec
    ls -ld ~/lab/spec
  2. Practice

    Create the group, users and directory

    On a disposable machine or container, create the group webteam, the users alice and bob as members, and carol who is not a member. Create /srv/projects/web owned by root:webteam with the mode from the previous exercise. Show the group's members and the directory's listing.

    Check your result
    getent group webteam
    ls -ld /srv/projects/web
    webteam:x:1001:alice,bob
    drwxrws--T 2 root webteam 4096 Nov  3 11:05 /srv/projects/web
    Hint

    useradd -m -G GROUP USER creates a user with a home directory and puts them in a supplementary group.

    Solution
    sudo groupadd webteam
    sudo useradd -m -s /bin/bash -G webteam alice
    sudo useradd -m -s /bin/bash -G webteam bob
    sudo useradd -m -s /bin/bash carol
    sudo mkdir -p /srv/projects/web
    sudo chown root:webteam /srv/projects/web
    sudo chmod 3770 /srv/projects/web
  3. Practice

    Test it as each user

    Show with real commands, run as each user, that: alice can create a file and its group is webteam; bob can append to it; bob cannot delete it; carol cannot list the directory or read the file.

    Check your result

    alice's file is -rw-rw-r-- alice webteam. bob's append succeeds. bob's rm fails with Operation not permitted (the sticky bit). carol gets Permission denied on both the listing and the read.

    Hint

    sudo su - USER -c 'COMMAND' runs a command as that user with a fresh login, including the right groups and umask.

    Solution
    sudo su - alice -c 'echo "<h1>hi</h1>" > /srv/projects/web/index.html; ls -l /srv/projects/web'
    sudo su - bob   -c 'echo "<p>bob</p>" >> /srv/projects/web/index.html && echo append ok'
    sudo su - bob   -c 'rm /srv/projects/web/index.html'
    sudo su - carol -c 'ls /srv/projects/web; cat /srv/projects/web/index.html'
  4. Practice

    Report usage per user

    Have alice create two more small files and bob one. Print how many files each user owns in /srv/projects/web, and the total disk usage of the directory.

    Check your result
          3 alice
          1 bob

    followed by a size line such as 16K /srv/projects/web.

    Hint

    find -type f -printf '%u\n' prints each file's owner. Count repeated lines with sort | uniq -c.

    Solution
    sudo su - alice -c 'touch /srv/projects/web/a1 /srv/projects/web/a2'
    sudo su - bob   -c 'touch /srv/projects/web/b1'
    sudo find /srv/projects/web -type f -printf '%u\n' | sort | uniq -c
    sudo du -sh /srv/projects/web
  5. Challenge

    Archive, check, then delete

    Create two files in the area dated 45 days ago (old1.log, old2.log). Safely remove everything older than 30 days in this order: list what matches, write it to a compressed archive in ~/lab/web-old.tar.gz, list the archive to prove it contains exactly those files, and only then delete them from the area. Everything newer must stay.

    Check your result

    The archive lists old1.log and old2.log and nothing else. Afterwards find /srv/projects/web -type f -mtime +30 prints nothing and index.html is still there.

    Hint

    touch -d '45 days ago' backdates a file. find -mtime +30 matches files modified more than 30 days ago. tar -czf ARCHIVE -C DIR FILES... writes an archive, tar -tzf lists one.

    Solution
    sudo touch -d '45 days ago' /srv/projects/web/old1.log /srv/projects/web/old2.log
    sudo find /srv/projects/web -type f -mtime +30
    sudo tar -czf ~/lab/web-old.tar.gz -C /srv/projects/web $(sudo find /srv/projects/web -type f -mtime +30 -printf '%P\n')
    tar -tzf ~/lab/web-old.tar.gz
    sudo find /srv/projects/web -type f -mtime +30 -delete
  6. Challenge

    Audit for policy violations

    Plant three violations: make index.html writable by everyone, change the group of css to carol, and make a new file tool.sh setuid. Write one find command for each that reports exactly that violation, run them, repair all three, and run them again to show they now print nothing.

    Check your result

    Before the repair the three commands print index.html, the css directory and tool.sh. After it, all three print nothing.

    Hint

    find -perm -o+w means others can write. -not -group NAME finds the wrong group. -perm /4000 matches setuid.

    Solution
    sudo chmod o+w /srv/projects/web/index.html
    sudo chgrp carol /srv/projects/web/css
    sudo touch /srv/projects/web/tool.sh && sudo chmod u+s /srv/projects/web/tool.sh
    
    sudo find /srv/projects -type f -perm -o+w
    sudo find /srv/projects/web -not -group webteam
    sudo find /srv/projects -perm /4000
    
    sudo chmod o-w /srv/projects/web/index.html
    sudo chgrp webteam /srv/projects/web/css
    sudo chmod u-s /srv/projects/web/tool.sh