Apache – Access a file in PHP through a symbolic link from a user that normally doesn’t have permission

apache-httpdpermissionsPHPsymlinkusers

I have this website, where if the user submits a form, a python script is executed through a php page, and the python script creates a zip file and should offer it to the user for download through a link. The file could be huge (a few GB).

As I'm working on a university server, I'm strictly bound to their server rules and capabilities. Here's the problem:

The website is stored in /data/mywebsite, which has limited diskspace. Of course this is owned by www-data as it's mainly accessible by my Apache server.

I'm offered 1 TB storage in /experimentdata/, which is ONLY ACCESSIBLE by a single, specific user, say theuser. This is because this folder is a samba mount that can be accessed by a single and specific user-id.

To create the file in /experimentdata, I use a sudo -u theuser command that will create the file /experimentdata/downloadme.zip as user theuser. Now my problem is: How can I offer this file through a link for download through Apache?

I thought of using a symbolic link that I put in, e.g., /data/mywebsite/download/downloadme.zip. The problem with that is that the user www-data has absolutely no permission to read the file!

How can I let the user download the file /experimentdata/downloadme.zip with the user www-data through the user theuser?

I would like to explicitly say that involving sudo -u theuser is absolutely fine. But I don't know how to make a link out of that to somewhere outside my website folder.

PS: If you require any additional information, please ask.

Best Answer

I think the thing to do is have your php/python return the data directly instead of apache. Your code can do the same thing that apache does. In my experience this is much better than opening up another directory and/or using sudo, or changing file permissions for apache, etc.

If the program produces the large file faster than the internet connection, then you can stream the data directly from your program, which eliminates the extra data file and the code to manage it and the mechanisms to remember it.

This answer on Stack Overflow shows how the code works in php. https://stackoverflow.com/a/4357904/5484716.

For programs that will be called this way, eliminate all stderr stream output and make sure the return code from your python process accurately reflects the success or failure of the process.

The examples below show the popen() calls you would use in the above example scenario from stackoverflow. I've prepended exec 2>/dev/null; to the shell command. This ensures that no output goes to stdandard error, even from the shell itself, because having data coming on both stderr and stdout can be a source of deadlocks with popen().

If you want to download the disk file to your user:

$fp = popen('exec 2>/dev/null; sudo -u theuser cat yourfile.zip', 'r');

If you want to download the data from the active process:

$fp = popen('exec 2>/dev/null; sudo yourpythonscript arg argN', 'r');

These command lines are shell commands and need to be quoted appropriately for shell meta characters.

In the second method, the server would begin sending the data immediately. When the user successfully submits the form, they immediately see a "save as" dialog from their browser. As soon as the user selects the output file, your php script transmits the data directly across the wire and into the remote file.

The python script should print only the zip data on standard output, and return an exit code that accurately represents the success or failure of the zip process. In python the script should write the output on sys.stdout, for example zf = ZipFile(sys.stdout, ....

It is critical to call pclose() and check the return value, because that will be the only way you know if the zip succeeded or not. If pclose() returns anything other than 0, something is wrong.

How the file is handled by the client depends on the settings of these response headers and others: content-type:, content-encoding:, and content-disposition: See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html, look at the response-header and the entity-header information.

Related Question