10

I want to set up a directory where all new files and directories have a certain access mask and also the directories have the sticky bit set (the t one, which restricts deletion of files inside those directories).

For the first part, my understanding is that I need to set the default ACL for the parent directory. However, new directories do not inherit the t bit from the parent. Hence, non-owners can delete files in the subdirectories. Can I fix that?

Lev Levitsky
  • 1,037

3 Answers3

11

This is a configuration that allows members of a group, acltest, to create and modify group files while disallowing the deletion and renaming of files except by their owner and "others," nothing. Using the username, lev and assuming umask of 022:

groupadd acltest
usermod -a -G acltest lev

Log out of the root account and the lev account. Log in and become root or use sudo:

mkdir /tmp/acltest
chown root:acltest /tmp/acltest
chmod 0770 /tmp/acltest
chmod g+s /tmp/acltest
chmod +t /tmp/acltest

setfacl -d -m g:acltest:rwx /tmp/acltest
setfacl -m g:acltest:rwx /tmp/acltest

ACL cannot set the sticky bit, and the sticky bit is not copied to subdirectories. But, you might use inotify or similar software to detect changes in the file system, such as new directories, and then react accordingly.

For example, in Debian:

apt-get install inotify-tools

Then make a script for inotify, like /usr/local/sbin/set_sticky.sh.

#!/usr/bin/env bash
inotifywait -m -r -e create /tmp/acltest |
while read path event file; do
    case "$event" in
        *ISDIR*)
            chmod +t $path$file
            ;;
    esac
done

Give it execute permission for root: chmod 0700 /usr/local/sbin/set_sticky.sh. Then run it at boot time from, say, /etc/rc.local or whichever RC file is appropriate:

/usr/local/sbin/set_sticky.sh &

Of course, in this example, /tmp/acltest should disappear on reboot. Otherwise, this should work like a charm.

poige
  • 6,231
Christopher
  • 15,911
  • This is what I have, but "disallowing the deletion and renaming of files except by their owner" is only in effect for files immediately under the parent directory, and not in new subdirectories, which I would like. Thank you anyway. – Lev Levitsky Jun 11 '15 at 21:55
  • 1
    This is neat! Looks like in theory I don't even need ACLs in this case, because I can put all chmods and chowns in the script, too. I'm using Arch Linux & systemd, so I'll try to write a service file that would start the script on boot. Also, some care must be taken about file names with spaces, but I see inotifywait has a -c option that is supposed to help with it. – Lev Levitsky Jun 13 '15 at 15:19
  • 1
    You are right - ACLs don't seem to add any benefit. Thanks for the -c reference. Maybe quotes might help, too? chmod +t "$path$file". – Christopher Jun 15 '15 at 12:43
  • 1
    Yes, I ended up just adding quotes, the rest seems to work without -c. – Lev Levitsky Jun 15 '15 at 12:56
  • unfortunately this will not scale for big directory trees. inotify is the problem here. – uli42 Mar 21 '24 at 17:28
2

Setting the sticky bit recursively based on parent

Directories without the sticky bit can be found with below statement whereby the hyphen - in front of the value 1000 should be considered as a prefix which will match this and higher permission values.

$ find . -type d \! -perm -1000

Hence, here is a way to set the sticky bit and setgid of those directories which parent directory has the sticky bit set.

That would call for a nested find statement, if it were not that find statements cannot be nested.

This is why rather a pipe with a recursive conditional will be employed on the directories found without a sticky bit. The -k test returns true if the sticky bit ---in this case, of the parent directory--- is set.

$ find . -type d \! -perm -1000 |while read d; do if [[ -k "$d/.." ]]; then chmod +t,g+s "$d"; fi; done

Look here for more find fun and an explanation about setgid.

0

You can make a function mkdir and append it to the end of your vi ~/.bashrc file as follows:

mkdir(){
    /bin/mkdir "$@"
    find . -type d \! -perm -1000 |while read d; do if [[ -k "$d/.." ]]; then chmod +t,g+s "$d"; fi; done
}

-k option checks if the sticky bit has been set, not GUID, that is set here too with g+s, check man test for these options

Also, you can do this:

unalias mkdir;
source ~/.bashrc

This should set the sticky bit for the new subdirectory, but only if the parent has the sticky bit set.

This method seemed to be working, but not sure for all users!