Limiting total size of uploads in Jirafeau

Introduction

Jirafeau is a simple webapp for people to share files. You can drag and drop a file or click to choose a file to upload, then the file gets uploaded and you get a link that you can share with whoever you want to share the file. There are options to add a password that people need to enter before they can download the file and you can set when the link should expire. Jirafeau is a simple and lightweight FLOSS app which means that you can self host it. As an admin you'll want to be able to have some control over what's possible and Jirafeau allows for several things. You can choose what periods of time people can choose before the link expires for example (from 1 minute, 1 hour... to one month, a year or never) and also the maximum size a file is allowed to have. One problem I have is that there is currently no way to limit the total size of uploads which means that people could technically flood your server with files. This is problematic especially if your server has other services as well. I looked how I could limit the size for an instance I run and found two options. Either we put the files in a separate filesystem or we limit the amount jirafeau's system user can have.

Putting the uploads in a separate filesystem

Linux can use a file as a filesystem. We can create a file, make it a filesystem of a certain size and mount that at the location of the upload folder. This way is the easiest to set up.

Setup

I run Jirafeau on Yunohost, so the commands are written for that. You should be able to trivially adapt them to your own installation. On Yunohost the files are in /home/yunohost.app/jirafeau. We'll create a filesystem file called jirafeau-filesystem in /home/yunohost.app/ and mount that to /home/yunohost.app/jirafeau. We will also copy over the files from the current folder to the new one. The systemuser that runs jirafeau is aptly named Jirafeau.

First we create a filesystem file of the maximum size we want to allow. In my case that's 3G. I used 4k for the bs option because I read somewhere that this is a good choice, but I have no clue if this is an actual good choice or why exactly so feel free to use something else if you think that's better. bs is the block size while count is the total amount of blocks. I want a 3G filesystem, so that's 3145728k. Since I have 4k blocks, I need (3145728k)/(4k/block)=786432 blocks.

dd if=/dev/zero of=/home/yunohost.app/jirafeau-filesystem bs=4k count=786432
mkfs -t ext4 /home/yunohost.app/jirafeau-filesystem

Once the filesystem is created we'll rename the folder with the uploads to a temporary folder. That way we still have our files while the path we need to mount the new filesystem is free.

mv /home/yunohost.app/jirafeau /home/yunohost.app/jirafeau_tmp

Then we mount the filesystem to the correct location

mkdir /home/yunohost.app/jirafeau
mount /home/yunohost.app/jirafeau-filesystem /home/yunohost.app/jirafeau

And we put files back and remove the temporary folder we created.

mv /home/yunohost.app/jirafeau_tmp/* /home/yunohost.app/jirafeau
chown -R jirafeau:jirafeau /home/yunohost.app/jirafeau
chmod 700 /home/yunohost.app/jirafeau
rm -r /home/yunohost.app/jirafeau_tmp

Now the Jirafeau app will already work with the new filesystem, but it won't be mounted when we reboot the server. For that reason we open /etc/fstab.

nano /etc/fstab

And add the following line

/home/yunohost.app/jirafeau-filesystem /home/yunohost.app/jirafeau ext4 defaults 0 2

And we're done! Good job!

Limit the total use the Jirafeau user can create

Linux can limit how much space a user or group can use on a filesystem using quotas. It should be noted that this isn't something most distro's do by default so it does take more work to set up. A guide I used to figure this out is https://www.thegeekstuff.com/2010/07/disk-quota/ . The size also isn't limited to only the uploads, but to the user that runs the application (unless the uploads are already on a separate filesystem than the code). I assume that you use a separate systemuser for each application you run, if you don't have a separate user for Jirafeau, then this option is already out of the question. I'm not sure if there's really an advantage in this method, but I want to share it anyway. Note that I've tested this method on my local machine as a proof of concept, but not actually did it on a Jirafeau instance. I'll try to make it as clear as possible what should happen, but be aware that it hasn't been tested.

First of all we'll need two applications that can set these limits.

apt install quota quotatool

Then we need to find the correct mountpoint and add the option usrquota in /etc/fstab. The correct mountpoint the the one that has your Jirafeau upload folder, in the case of yunohost /home/yunohost.app/jirafeau

nano /etc/fstab

An example entry is

/dev/sda1 /home ext4 defaults,usrquota 0 2

Note where we added the usrquota option as a comma separated list. This option only allows for limits on users, for groups there is grpquota.

Then we'll remount the correct drive and mountpoint with the correct option.

mount -o remount,usrquota /dev/sda1 /home

The following command looks for quota files and creates them if needed (and in this case it's needed).

quotacheck -cfmvF vfsv0 /home

Then we can activate the possibility of using user quotas.

quotaon -avu

By now we can only use these quota on users on the filesystem we told it to use. We still have to tell the system what quotas there are for which user (in this case the user is called jirafeau). The following command will open a textfile in your systems preferred editor (if you don't know which one it is, do echo $EDITOR. If you want to change it to, say nano, you can do EDITOR=nano. Note that this will only last for this session. The moment you open a new shell, it's back to the default editor).

edquota jirafeau

In the file you can see for the user per filesystem what usage and quotas there are. In this case the only thing that is relevant is the column blocks, this is the number of 1k blocks already used by this user. The first hard column is the number of maximum blocks that the user may use. If we want to allow 3G, we change the value of the first hard column to 3145728 (see also https://wiki.archlinux.org/index.php/Disk_quota )

An example with a 3G limit is

Disk quotas for user jirafeau (uid 1002):
  Filesystem                   blocks       soft       hard     inodes     soft     hard
  /dev/sda1                      8812          0    3145728         18        0        0

After this you are done and Jirafeau won't be able to use more that 3G on the target filesystem. Awesome!

Conclusion

The first option seems best, but feel free to choose what you think is best for you and your use case. In both cases when the limit is reached Jirafeau will throw an error Error 6 FIX ME: Internal error during file creation. effectively limiting the maximum amount of disk space used for uploads.

Updates

2020-06-20 I thought the first option would always occupy the full size of the filesystem-file on the system. It doesn't seem to do that, so I changed accordingly.