Bindfs to solve file permissions
27 September 2019
When developing web applications and deploying them on Linux servers (development or production environments), you often encounter problems with file permissions on the application files.
The problem
Both the web server and the developers require file permissions on the same files to operate properly, but they might need different permissions on the same files.
The developer requires the ability to read and write every file, while the web server might require only write permissions only on some files.
This constrained shared-resource problem has always been a nightmare for me when I am in charge of a server.
I named it "the www-data struggle".
The user~group "traditional" solution
One common solution is to give the developer the same user group as the web server (something like www-data
), and then to juggle with ownership on files.
While this works in some ways, it's flawed in others:
- concurrency problem: when the developer creates a file, the file is owned by the developer, and might not be readable (nor writable if needed) properly by the web server process.
- impermeability problem: if you have several web applications running side-by-side on your web server, you'll have to juggle a lot more with groups and sub-groups just to prevent Developer A to be able to see and modify Application B.
- stability problem over time, and accordingly with the principle of increase of entropy, file permissions will become a mess and perms related bugs will occur.
The bindfs solution
Wouldn't it be great if a developer could read and write files of an application with its own user/group, while allowing the web server to have its permissions?
Well, that's exactly what bindfs is meant for.
With bindfs, developers access applications via dedicated filesystem mountpoints (placed in their home dir), acting as file-permission filters, presenting files like they're owned by themselves, whereas the files are in fact owned by the web server user (like www-data
).
Advantages:
- it solves the concurrency problem, as every user in the equation (every developers and the web server) sees the permissions that he requires to operate properly and safely.
- it solves the impermeability problem, as if you need one developer to access a particular application, you have to add a mountpoint in its home directory, but on the contrary, if the you need one developer to not access a particular application, you just have to not add such a mountpoint in it's home directory.
- it solves the stability problem, as developers will never be able to change the file permissions set on the application files, as required by the web server to operate properly and safely.
- best of all, it's simple to understand and to setup!
How to use this
For Ubuntu / Debian systems
In the next example, we assume that the developer user is devone
, the web server user is www-data
and the application is stored at /var/www/application1
.
# Installing bindfs (just the first time)
root@entropie$ apt-get update
root@entropie$ apt-get -y install bindfs
# Creating the application mountpoint
root@entropie$ mkdir -p /home/devone/websites/application1
root@entropie$ chown -Rf devone:devone /home/devone/websites
root@entropie$ chmod -Rf 770 /home/devone/websites
Then, edit the content of /etc/fstab and add this line (just one line, without line wraps):
bindfs#/var/www/application1 /home/devone/websites/application1 fuse force-user=devone,force-group=devone,create-for-user=www-data,create-for-group=www-data,create-with-perms=0770,chgrp-ignore,chown-ignore,chmod-ignore 0 0
Save the file, and proceed with mounting application (will mount automatically at system load):
root@entropie$ mount /home/devone/websites/application1
If your system yells about force-user
or force-group
not being defined:
- replace
force-user
byowner
- replace
force-group
bygroup
Testing the solution
Once the application is mounted, you can test it by creating a file in the application mountpoint using the devone
account, and verifying the file perms:
# as root
root@entropie$ su - devone
# as devone
devone@entropie$ cd ~/websites/application1
devone@entropie$ touch helloworld.txt
devone@entropie$ ls -l helloworld.txt
-rwxrwx--- 1 devone devone 0 sept. 10 17:15 helloworld.txt
devone@entropie$ exit
# as root again
root@entropie$ cd /var/www/application1
root@entropie$ ls -l helloworld.txt
-rwxrwx--- 1 www-data www-data 0 sept. 10 17:15 helloworld.txt
The file is owned by www-data:www-data
whereas we created it as devone:devone
! It worked!
References / external links
Bindfs is an open-source project so if this is useful to you, please give them some love and feel free to contribute to it.
This article was written while listening to Eating Hooks remixed by Siriusmo and Solomun.