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, then the default behavior is as follows.
Passenger will run applications as the owner of their "startup file", which depends on the application's language. You can learn more about that at How Passenger autodetects applications. Below you will find a table of startup files.
|Application type||Startup file|
|Ruby, Ruby on Rails||config.ru|
|Node.js or Meteor JS in bundled/packaged mode||app.js|
|Meteor JS in non-bundled/packaged mode||.meteor|
So suppose that you have a Ruby app, and
config.ru is owned by user 'joe'. Passenger will run the corresponding application under the OS user account
joe. The exact rules are a little bit more complicated, and they're explained further down in this guide.
User account sandboxing is the reason why, in all official Passenger Library documentation, we recommend that you create a new user account for each application.
When is user account sandboxing enabled?
User account sandboxing is only enabled when all of the following conditions are met:
- When not using the Flying Passenger mode (this is probably the case):
- The passenger_user_switching option must be enabled. It is enabled by default.
- The Nginx's control process must have root privileges. This is usually the case.
- When using the Flying Passenger mode:
- The Flying Passenger daemon must be run with root privileges.
This means that on most installations, user account sandboxing is already enabled.
When not using Flying Passenger, the following table illustrates the effect for different combinations of the requirements.
|passenger_user_switching on||passenger_user_switching off|
|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.|
When using Flying Passenger, the effect is as follows:
|Daemon run with root privileges||User switching enabled.|
|Daemon run without root privileges||User switching disabled. Apps are run as the daemon's user.|
When user switching is enabled, the following rules are followed to determine what user an application should be run as. The first matching rule is the rule that will be followed.
- If passenger_user or passenger_group are set, then the application will be run as the specified user/group. Thus, these options are a good way to override user switching settings.
- If the startup file is owned by root or an unknown user, then the application will run as the user specified by passenger_default_user and passenger_default_group. This way, we discourage accidentally running applications as root, which is insecure.
- Otherwise, the application is run as the owner of the startup file.
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