Bindfs to solve file permissions

27 Septembre 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 by owner
  • replace force-group by group

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.