- Bypassing a Slow Composer Repository
- Slow Composer Followup
- Getting Started with PHP Composer and Magento
- PHP: The Right Way; A Review
- PSR and Code Style Guidelines
- Sellvana: Developer’s Alpha Review
- PHP Primer: Type Hints
- PHP Primer: Namespaces
- PHP Primer: Interfaces
- Design Patterns, PHP, and Magento
- PHP-FPM and File Permissions
- Why did the Elephant join the Guild?
First, let’s state the problem we’re going to examine
In a secure unix enviornment, the web server’s user unix account and the unix user account created for command line access (for deploying code and running command line tasks) should (for security reasons) be different. This, in turn, means PHP code that runs in one context (web) will creates files or directories that may not be readable or writable in other directories
This is a persistent problem for web developers of all stripes, but PHP developers feel it harder than most. Because of PHP’s success, there’s a massive enviornment of inexpensive web hosting available. As a result of its low-cost, these web environments are inflexible in the user account and permissions configurations they provide. While it’s easy to ignore this hosting ecosystem when you’re using PHP to build a service, things are a little different if you’re building PHP things for other PHP users to use. Not considering these sorts of enviornments means you’re either on the cusp of being negligent, or making a conscious decision to leave a majority (market share wise) of your PHP users behind.
What are FastCGI and PHP-FPM?
Running PHP in a FastCGI enviornment is often seen as a solution to these sorts of permissions problems. This is sort-of-true, but also sort-of-false. To understand why you need to understand what FastCGI and PHP-FPM are.
A web request in a traditional PHP enviornment goes something like this
Browser: Hey Apache, give me this URL
Apache: (to self) OK, this is a PHP url, let me run this PHP program to get the HTML page.
Apache: OK browser, here’s the HTML page
With FastCGI, the imaginary conversion goes something like this
Browser: Hey Apache, give me this URL
Apache: (to self) OK, this is a FastCGI URL. Hey FastCGI server, run this command with these parameters for me
FastCGI Server: OK, let me pass the command off to one of those processes I’ve already started and get an HTML page
Fast CGI Server: OK Apache, here’s the HTML page
Apache: Thanks FastCGI server!
Apache: OK browser, here’s the HTML
In a FastCGI setup, your web server is still exposed to the world, but there’s a second daemon process running. That second process is a FastCGI server that handles any URL that requires running a program. In other words, the web server still handles serving javascript files, CSS files, images, static HTML pages, etc., but the FastCGI server runs programs.
This has a few advantages. First, the FastCGI server is less exposed to direct world wide assault via network requests which means (or so says one line of security thinking) you can be more liberal as to which unix user account runs this server and its child processes.
The second advantage is you no longer need to embed your programming language in a web server module (MOD_PHP
, MOD_PERL
, etc.). So long as your web server can talk to a FastCGI server (both apache and nginx can), and your programming language has a FastCGI server, you’re good to go. Not tying your language to one web server is a Good Thing™
The third advantage, unrelated to permissions, is a FastCGI server doesn’t run into the expensive startup problem that plagues regular old CGI.
PHP-FPM stands for PHP FastCGI Process Management. PHP-FPM is PHP’s FastCGI server. Most PHP distributions will ship with a php-fpm
binary and configuration files. For example, here’s where you can find the liip PHP-FPM binary and configuration files
/usr/local/php5/sbin/php-fpm
/usr/local/php5/etc/php-fpm.conf.default
/usr/local/php5/etc/php-fpm.d/www.conf.default
While most PHP distributions ship with PHP-FPM, many distributions will not be setup to automatically run your php-fpm
server-daemon process. In other words, you may not have a launchctl
, init.d
, systemctl
, or service
script to run it. The default configuration files may not have smart defaults. All this, however, is another topic for another time.
PHP-FPM and Permissions
When folks try to get help with permissions problems online, you’ll often hear FastCGI or PHP-FPM brought up as a solution. There is some truth to this. As a developer, PHP-FPM can make your permissions problems go away, but only during development.
By running the FastCGI server (i.e. the PHP-FPM daemon) as your own user, or running it via the root user and configuring your www.conf
file to run processes as your own user, most of your permissions problems will evaporate. With the two contexts (web, CLI) unified under a single user, files and directories created by the PHP application won’t have ownership by/from multiple user accounts. Developer bliss.
However — PHP-FPM doesn’t solve the permission problems for your deployment server. Tying the production PHP-FPM process to a specific user account is not a security best-practice. Also, with many PHP systems, there’s a second command line process (the cron user) to consider. In production we’re back to the problem of many user accounts reading and writing the same directories and we need to carefully orchestrate users, groups, umasks, and permissions in our environments.
What to Do
Whenever this topic comes up w/r/t an open source platform I’m — surprised? — how often it’s met by a shoulder shrug or the false-elitism of only losers use those environments. Sometimes this is couched in more politically sensitive language like
It wasn’t a priority but the team’s working hard on it and it’s a hard problem
or
Our user analysis shows those users aren’t adopting our platform and so we can’t allocate resources to it
or
Those users are not a market we’re targeting
What’s most frustrating about these attitudes is — this is a solved problem. PHP Frameworks solved it a long time ago with a few simple best practices
- Limit the file systems writes your PHP application needs to do when running in a web context
- Keep those writes under a limited number of directories (ideally one)
- Abstract any file system read/writes to a single system which allows you to
- Control the PHP
umask
when making file system read/writes - Provide user-friendly error-messaging/experience when you run into a permission problem
- Swap in a different system for reads and writes (everything from transfers/writes via SFTP, or replacing the whole thing with redis)
- Control the PHP
Off the top of my head — Joomla, WordPress, Drupal, Laravel — they all have (intentionally or not) created platforms that hew to the above. This means folks can use those platforms whether they’re running via a hand rolled FastCGI enviornment, or using good old mod_php from a WAMP/MAMP setup.
Wrap Up
There’s one last parting bit of advice for anyone running a dev team that’s shipping PHP code for other PHP developers to use: Give your team an enviornment (vagrant, docker, etc) where they feel the pain of these permissions problems. Maybe that’s MOD_PHP
, maybe that’s PHP-FPM with proper user segregation.
When development teams just spin up a FastCGI enviornment with their own user accounts permissions problems will start to slip into your system, but no one will notice. By the time you ship to end users you’ll have hundreds of little paper cuts that you’ll probably never get around to fixing, but that your end-developer-users will nonetheless need to handle themselves.
When development teams feel this pain themselves they’ll seek out the best practices above on their own. You won’t need an endless round of post-launch sprints to clean these problems up — your team will have already solved the problem because it was in their interest to solve it.