Allow 2+ Users to Be Able to Share a Linux Directory with 2775 vs 0775
This is handy if you have 2 different users deploying code to the same directory, such as a deploy user and CI user.
Prefer video? There’s a video version of this blog post on YouTube that goes into a bit more detail about certain topics listed below.
This could be used for a number of things but let’s focus on 1 concrete example.
Going Over the Permission Problem
For example, let’s say you
git push your code to your server and it ultimately gets checked out to
/srv/mysite. This could be a static site or your web application, it doesn’t matter.
Now let’s say you sometimes deploy your code from your dev box by pushing your code as the
deploy user but you also have CI set up which can push code to your server.
For security reasons it would be a good idea to use a heavily restricted
ci user that can only
git push code to your server and nothing more. This user wouldn’t even have an ability to run a shell like Bash, instead you can use git-shell which provides restricted git access.
It’s worth pointing out when you git push to your server, part of that process will run a
git checkout in perhaps a git post receive hook. The details aren’t important for this post’s topic but it’s important to know that files will be created as the user who performed the git push.
That’s because when you git push your code, you’re SSH’ing into your server as that user. The post receive hook runs and that script runs all commands as that user, such as
git checkout and whatever else you want to do.
/srv/mysite (the checkout directory) needs to be writeable by both users. You can do this without doing anything fancy with permissions by simply creating a common group such as
sshusers or whatever you decide to name it and add both the
ci users to that group.
You can create a new system group with
groupadd --system sshusers. The
--system flag ensures your group will be created with a group id less than 1000 or more specifically what’s defined in
If you run
chown deploy:sshusers /srv/mysite then your directory will allow both the
ci users to be able to write to it. That would look like this:
drwxrwxr-x 2 deploy sshusers 4096 Sep 24 22:46 mysite
Then as the
deploy user you could do
touch /srv/mysite/test and you’ll end up with:
-rw-rw-r-- 1 deploy deploy 0 Sep 24 23:04 test
Then let’s say you git push your code as the
ci user. You’ll end up with:
-rw-rw-r-- 1 ci ci 0 Sep 24 23:07 index.html
This technically works, but as the
ci user you’ll get a permission error if you try to overwrite a file owned by
deploy:deploy, such as if you were able to run
touch /srv/mysite/test (assuming the user had permission to run the
touch command, I’m only doing this for an example). That will produce
touch: cannot touch 'test': Permission denied.
Basically what this means is if you git push as the
deploy user you’ll get permission errors when git pushing as the
ci user and vice versa. Neither user will be able to write to files owned by the other user.
That leads us to the problem. How can we have 2 different users be able to git push or more generally write to each others’ files in a shared directory?
0775 vs 2775 Permissions for a Directory
By default when you create a directory on Linux, unless you modified the default behavior then you’ll get
0775 as the permissions, as seen below:
$ stat /srv/mysite File: /srv/mysite Size: 4096 Blocks: 8 IO Block: 4096 directory Device: fd00h/64768d Inode: 134418 Links: 2 Access: (0775/drwxrwxr-x) Uid: ( 0/ root) Gid: ( 998/sshusers) Access: 2022-09-24 23:04:55.682777452 +0000 Modify: 2022-09-24 23:04:53.682799318 +0000 Change: 2022-09-24 23:04:53.682799318 +0000 Birth: 2022-09-24 22:46:03.543155216 +0000
The important line is
Access: (0775/...) which is the 4th line of output.
If you don’t specify the
0 then it defaults to 0 which gives us the default behavior we all know. If a user creates a file in this directory then it will own the file at the user and group level.
Here’s a little chart of the possible values you can set:
0: setuid, setgid, sticky bits are unset 1: sticky bit is in place 2: setgid bit is in place 3: setgid and sticky bits are in place 4: setuid bit is in place 5: setuid and sticky bits are in place 6: setuid and setgid bits are on 7: setuid, setgid, sticky bits are activated
We can use
2775 which will set the
setgid bit. Now if a user creates a file in this directory then it will own the file but the group will be set to whatever group owns the parent directory. That’ll solve our problem!
That means we can do this:
chmod 2775 /srv/mysite
And now we can see the new access permissions:
$ stat /srv/mysite File: /srv/mysite Size: 4096 Blocks: 8 IO Block: 4096 directory Device: fd00h/64768d Inode: 134418 Links: 2 Access: (2775/drwxrwsr-x) Uid: ( 1000/ nick) Gid: ( 998/sshusers) Access: 2022-09-24 23:04:55.682777452 +0000 Modify: 2022-09-24 23:04:53.682799318 +0000 Change: 2022-09-24 23:36:15.845483124 +0000 Birth: 2022-09-24 22:46:03.543155216 +0000
Now as the
deploy user you could do
touch /srv/mysite/cool and you’ll end up with:
-rw-rw-r-- 1 deploy sshusers 0 Sep 24 23:38 cool
Notice how the group is set to
ci user and any other users in the
sshusers group will be able to write to this file without getting a permission denied error. Now we can safely deploy our code to our server as 2+ different users.
Note worthy topics
In the video I incorrectly said that
setgid is a type of sticky bit, that’s not the case. Both
sticky are their own types of bits.
- 0:05 – A use case for wanting to do this
- 2:07 – Creating a directory to demo things out
- 3:10 – Getting a permission denied error as a different user
- 3:59 – Making a common group that both users belong to
- 5:06 – Now a different user can create files in the directory
- 5:23 – We’re good to go right? Not quite
- 6:31 – setgid to the rescue
- 8:02 – Setting our directory to 2775
- 9:04 – Everything works how we want it to
Do you use this pattern on any of your servers? Let me know below.