Sandboxing apps with Unix user accounts (user switching)
on Passenger + Nginx
When running web applications with Passenger, we recommend that you make use of the user account sandboxing feature, also known as user switching. This means (if you run multiple applications on a server) running each application under its own operating system user account, instead of running them all as the same user. Doing so improves the overall security of the system.
To better understand the problem, let us consider the situation with PHP. There is a problem that plagues most PHP web hosts, namely the fact that all PHP applications are run in the same user context as the web server. So for example, Joe's PHP application will be able to read Jane's PHP application's passwords. This is obviously undesirable on many servers.
Passenger's user account sandboxing feature solves this problem. This feature makes it very easy to run each application as its own operating system user. Assuming that you have correctly secured your files with the right filesystem permissions, user account sandboxing stops or limits the impact of certain classes of vulnerabilities. Going back to our example, if Joe's application has been hijacked through a vulnerability (or is intentionally malicious), then it won't be able to access Jane's passwords, assuming that Jane has secured her password files with the right permissions.
Table of contents
How it works
If the user account sandboxing feature is enabled, Passenger will need to know what user / group to run the application as. This is determined as follows:
If the options above are not specified, Passenger tries to autodetect by looking at the owner of the application's "startup file".
If autodetection fails, or if the owner of the startup file is the
rootuser, Passenger will set the user and group name specified in passenger_default_user and passenger_default_group instead (default:
When is user account sandboxing enabled?
Not using Flying Passenger
In most installations user account sandboxing is already enabled. Assuming you are not using the Flying Passenger mode (this is probably the case), the following table shows the exceptions:
|passenger_user_switching on (default)
|Web server has root privileges||User account sandboxing enabled.||User account sandboxing disabled. Apps are run as passenger_default_user and passenger_default_group.|
|Web server has no root privileges||User account sandboxing disabled. Apps are run as Nginx's user.||User account sandboxing disabled. Apps are run as Nginx's user.|
Using Flying Passenger
When using Flying Passenger, the effect is as follows:
|Daemon run with root privileges||User account sandboxing enabled.|
|Daemon run without root privileges||User account sandboxing disabled. Apps are run as the daemon's user.|
Caveats & troubleshooting
If your application regularly encounters permission errors or fails to find certain files, then this is an indication that your application is started as a user that you did not intent it to be run as. Other symptoms include:
- The application fails to start because Bundler complains that it cannot find gems. This probably indicates that Bundler does not have read access to the directory that contains Bundler-installed gems.
- The application fails to start and its error message mentions the path
/nonexistent. This probably indicates that your application is started as the
nobodyuser. This is because on many systems, the
nobodyuser's home directory is
To check whether it is indeed the case that your application is started as a different user than you intended to, see Finding out what user an application is running as.
The most likely reason why your application is started as
nobody is probably because your startup file is owned by
nobody or by an unknown user. To fix this, change the owner of the startup file to the owner that you want to run the application as.
Whatever user your application runs as, it must have read access to the application root, and read/write access to the application's
Red Hat and CentOS caveats
This is because our RPMs configure the default instance registry directory to
/var/run/passenger-instreg, which is only writable by root. If you disable user switching, then the Passenger processes will run as passenger_default_user which (as long as it's not root) won't be able to write to that directory.
Note that any alternative instance registry directory must have the proper SELinux context, allowing the web server to read and write to it. We recommend that you create a directory
/var/lib/passenger-instreg and give it the label
$ sudo mkdir /var/lib/passenger-instreg $ sudo chcon -t var_run_t /var/lib/passenger-instreg
Then, in your Nginx config file:
Finding out what user an application is running as
To find our what user an application is started as, first access its URL in your browser so that Passenger starts the application. For example:
$ curl http://www.example.local/
The application will now either successfully start or fail to start. If it fails to start then you will see an error page that tells you what user the application was being started as. If you do not see the error page in the browser then set passenger_friendly_error_pages on.
If the application successfully started, then run
passenger-status to find the process's PID:
---------- General information ----------- Max pool size : 6 Processes : 1 Requests in top-level queue : 0 ---------- Application groups ----------- /webapps/example.local#default: App root: /webapps/example.local Requests in queue: 0 * PID: 16915 Sessions: 0 Processed: 1 Uptime: 2s CPU: 0% Memory : 9M Last used: 2s ago
In the above example we see that the PID is 16915. Next, use 'ps' to find out the user that it is running as:
$ ps -o pid,user,comm -p 16915 PID USER COMM 16915 phusion Passenger RackApp: /webapps/example.local
As you can see, the application in this example is being run as user