Restrict filesystem access from scripts in directory

zmippie

Verified User
Joined
Apr 19, 2015
Messages
161
In my continued state of paranoia I'm trying to figure out how to set up the following: how to restrict filesystem access from PHP scripts that are executed from a particular directory. Background: a developer gets limited (S)FTP access to upload his application which will run under a specific URL (let's say mydomain.com/application/). Now this application has not been vetted thoroughly, and may have potential security holes to be exploited. How to restrict this application from accessing files in this (DA) user's home directory? Even Maildir can be read. So that's far from ideal.

The server is running NGINX, php-fpm, php7+.

I thought I had a solution by adding this location to the NGINX conf:

Code:
location ~ ^/application/.*\.php$
{
	try_files $uri =404;
	fastcgi_split_path_info ^(.+\.php)(/.+)$;
	include /etc/nginx/fastcgi_params;
	fastcgi_index index.php;
	fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
	include /etc/nginx/nginx_limits.conf;
	[B]fastcgi_param PHP_VALUE open_basedir=/tmp/:/home/username/domains/mydomain.com/public_html/application/;[/B]
	if (-f $request_filename)
	{
		fastcgi_pass unix:/usr/local/php71/sockets/username.sock;
	}		
}

Apart from the PHP_VALUE line, the block is copied straight from the default config. This actually seemed to work, except that the rest of the user's scripts were now also restricted to the paths in the open_basedir PHP_VALUE. Which was confirmed in the accepted answer here on StackExchange.

The solution which might work for my case, which is also mentioned in the StackExchange accepted answer, is a seperate PHP-FPM pool. The downside is that this will probably not play nice with the standard DA setup. So my question is: can anyone think of another solution?
 
Hello,

I don't see how the guide will help you in your case, unless the second user will host scripts of the developer, and you will add a new location in nginx for them.
 
Alex, the issue is that I want the developer to be able to run his application under the domain name that is already assigned to an existing (DA) user. I thought I could tighten the file access from the developer's scripts, but it seems impossible on an NGINX install unless I create a different PHP-FPM pool, which I'm not sure is the way I want to go. If, on the other hand, I can create a new DA user (for the developer and his scripts), and have his application running on the same domain name (example.com/application), then the end result would be the same. He will have his own PHP-FPM socket, because every DA user will get one. Do you think this is not feasible with the instructions in the guide?
 
Important! I did not test it... but the idea of the guide (as far as I can see it) is that you create a separate user with a conditionally the same domain.

But I'd rather not follow it at all. I would let the developer's user to have his own public_html, and would add a separate location in NGINX, the same way as you, but with a separate pool for PHP-FPM.

There might be an issue with permissions though... so you will need to find a working combination.

So:

1. add an user with its own public_html and PHP-FPM with /home/developer/domains/dev.domain.com/public_html/
2. add a custom location into NGINX for /application/ of domain.com and point it to /home/developer/domains/dev.domain.com/public_html/
3. fix possible permission issues.

and you should get:

- domain.com with /home/owner/domains/domain.com/public_html/
- and domain.com/application/ serviced from /home/developer/domains/dev.domain.com/public_html/ with a separate PHP-FPM.

Something like this.
 
Yes, exactly. I thought that was what the guide was suggesting, generally speaking. But I may have to look at it more closely. What you're describing is indeed what I'm after. Two DA users, with one having content served on the other user's domain under /application. Right? There will be permission issues, like you say, but these would not be that hard to solve (I guess). It's exceptional, but it may work.
 
In the guide the both users will have one and the same homedir, see the step #3. This is not what is needed in your case.
 
2. add a custom location into NGINX for /application/ of domain.com and point it to /home/developer/domains/dev.domain.com/public_html/

I haven't had time to try your suggestion, but I'm very interested to see if it works. I was thinking though, where to put the custom location for NGINX: in the developer, or user (domain owner) account. The developer doesn't have a domain name assigned to it, so there will be no config for it. So I guess it will have to be in the user account. That would mean that I'd have to hardcode the socket for the developer in the config:

Code:
fastcgi_pass unix:/usr/local/php71/sockets/developer_username.sock;

Would that actually work?
 
Yes, nginx/apache have sufficient permissions to connect to sockets independently from which virtual host it connects.

You should add a location into user's virtual host, not developer's.
 
Yes, nginx/apache have sufficient permissions to connect to sockets independently from which virtual host it connects.

Had some time this weekend to set this up, and I got it working!

Strangely enough, in the end, there were actually no permissions issues. But because I was expecting them I fiddled with file ownership where it wasn't neccessary. The real missing link was rerooting the webroot to the one in the the developer's home (see 1st line in example config below).

I first changed ownership of all the files and directories in the developer DA account to that of the user and things worked. But later it turned out that scripts could not reach the directory above the the webroot. Even after changing ownership higher up in the tree, it would not work. It wasn't PHP's open_basedir, because I checked the paths, the limit wasn't imposed there.

It turns out that as soon as you pipe a request to another user's PHP-FPM socket, the request becomes that of the socket owner (easily checked in phpinfo()). Makes sense, if you think of it.

So here's the end result that goes into the top HTTPD config for the user's domain. I had to duplicate a bunch of stuff that is normally in the (server) template, but it's not too bad:

Code:
location ~ ^/[B]application[/B]/?.*
{
	root /home/[B]developer[/B]/domains/[B]nodomain.com[/B]/public_html;
	fastcgi_split_path_info ^(.+\.php)(/.+)$;
	include /etc/nginx/fastcgi_params;
	fastcgi_index index.php;
	fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
	include /etc/nginx/nginx_limits.conf;

	#funnel to index.php
	try_files $uri $uri/ /index.php?$args /[B]application[/B]/index.php?$args; 
	
	if (-f $request_filename)
	{
		fastcgi_pass unix:/usr/local/php71/sockets/[B]developer[/B].sock;
	}
}

As you can see, I've used a dummy domain in the developer DA account, because we need a valid webroot, and this fits best within the DA framework. It's interesting to think of which user is getting the webstats and DA tallies for requests to /application, but that'll be for another time...

All in all a lot of time wasted on something that could have been fixed in a simple .htaccess file, if this was Apache...

And while looking at this config, and being able to do this, I can't shake the feeling that there are some larger security issues with this. Can you just pipe traffic through to someone else's PHP-FPM socket without their permission? Does this mean you can enforce PHP_VALUEs on their socket too? That would not be good. Gladly, users cannot change the HTTPD config for NGINX, only admins.

Once again, thanks for your help Alex :)
 
Back
Top