How I set up Nginx on CentOS 7 with PHP 7.2

I’ve just finished setting up Nginx on my brand new PC running on CentOS 7 with PHP7.2.

I used to use Ubuntu 16 with PHP7.0 and was quite familiar with the combo. I thought that setting up Nginx on CentOS wouldn’t be that different from doing so on Ubuntu, which wasn’t really true.

It took me MUCH LONGER to have Nginx run on CentOS, so I wrote this article to record stuff I’ve learned in the process.

Why I switched from Ubuntu to CentOS

In the last year, I upgraded Ubuntu 16 on my computer into Ubuntu 18, but it turned out to be unbelievably slow.

After that, I bought a new PC and installed Ubuntu 18 to it. Unfortunately, Ubuntu 18 was still painfully slow on the machine too (again, this is a brand new computer), which made me decide to try a new distro.

Step 1: The www-data user doesn’t exist on CentOS 7

On Ubuntu, you’ll see a user/group called www-data, which Nginx uses. As far as I’ve learned, www-data is used for security reasons. However, this user doesn’t exist in CentOS.

(I don’t see anything when I search the “/etc/group” file with “www-“. At the bottom of the image, you see “‘www-‘ not found”)

By the way, what is www-data and why was it created?

Some web servers run as www-data. Web content should not be owned by this user, or a compromised web server would be able to rewrite a web site.

www-data is the user that web servers on Ubuntu (Apache, nginx, for example) use by default for normal operation. The web server process can access any file that www-data can access. It has no other importance.

(source)

On many (not all) distributions, www-data is the user under which the Apache web server runs. This also means that everything done by Apache (especially including PHP scripts) will be done with the permissions of user www-data (and also group www-data) by default.

(source)

 

It took me a while to learn that the two distros are using different package mechanisms (apt and yum), which results in…

  1. different packages
  2. different maintainers with different approaches to software

So, here is a question.

Question: Which user do I use for Nginx?

Answer: refer to /etc/nginx/nginx.conf.

Just open /etc/nginx/nginx.conf and see the value of “user”. It’ll be “nginx”.

Then, make sure that the current user and the web user (“nginx”) are allowed to access your app by giving them permissions. For Laravel 5 apps, you can achieve this by something like…

sudo chown -R nginx:nginx /your_laravel_app/storage

Step 2: Setting up PHP-FPM

Why does Nginx need PHP-FPM in the first place?

As far as I know, Nginx can serve static files with the return directive, but Nginx can’t return dynamic contents to the client by itself, because it doesn’t embed a server-side language to itself. This is why we need to configure a standalone PHP service, PHP-FPM.

Here is an overly simplified process about how Nginx works in conjunction with PHP-FPM.

  • First, the client makes requests for dynamic content.
  • Then, Nginx passes the requests to PHP-FPM.
  • PHP-FPM gets the dynamic content from the server (i.e. PHP app) and passes it back to Nginx.
  • Upon receiving the response from PHP-FPM (usually in HTML), Nginx will return that to the client.

However, on CentOS 7, it wasn’t clear to me where and how PHP-FPM was installed.

What is the actual name of PHP-FPM in my CentOS?

On Ubuntu, I was able to install PHP-FPM by the following command.

apt-get install php-fpm

Assuming that the version is 7.0, the command below could be used to see whether PHP-FPM had already been installed.

systemctl status php7.0-fpm

On CentOS, I installed php7.2-fpm with the following command

yum install php-fpm

Ok, now that I’ve installed it, let’s run the following command to see the status

systemctl status php7.2-fpm

However…

Apparently, it’s not php7.2.-fpm. If I run “ps aux | grep php-fpm”…

PHP-FPM itself is working, but it seems that the name is php-fpm, not php7.2-fpm.

Where is PHP-FPM’s socket file?

It is required to specify the path to the socket file in Nginx config files, so that Nginx can work with PHP.

e.g.

location ~ \.(hh|php)$ {
    fastcgi_keep_conn on;
    fastcgi_pass unix:/run/php/php7.0-fpm.sock;  #This is what I'm talking about!
    fastcgi_index index.php;
    fastcgi_param ENVIRONMENT production;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    include /etc/nginx/fastcgi_params;
}

On Ubuntu with php-fpm7.0, if I run the find command (i.e.  find / -type f -name “.sock” ), the sock file was found at…

/run/php/php7.0-fpm.sock

…but I couldn’t find this sock file with the find command. In fact, there is no /run/php directory.

I saw some posts on StackOverflow regarding where the socket file is located (like this one), but none of them were dealing with php7.2.

On top of that, some people claim that the socket file will be generated after PHP-FPM started to run, but I still didn’t see the socket file after I confirmed PHP-FPM was running with systemctl status php-fpm.

What is the socket for PHP-FPM in the first place?

I’ve spent a very long time in Google, but haven’t found what the socket is for PHP-FPM.

So, I decided to learn “socket” as a general term in programming.

According to tutorials point

Sockets allow communication between two different processes on the same or different machines.

To be more precise, it’s a way to talk to other computers using standard Unix file descriptors.

In Unix, every I/O action is done by writing or reading a file descriptor. A file descriptor is just an integer associated with an open file and it can be a network connection, a text file, a terminal, or something else.

So, I assume that the PHP-FPM’s socket is meant to either…

  • allowing Nginx and PHP-FPM to work together, or
  • allowing Nginx and PHP to work together

If you don’t have the socket file, you can use the default port for PHP-FPM

After spending so much time in googling “php-fpm 7.2 socket” (or something similar to it), I stumbled on this post on SO.

The solution I took was not to use the socket, but to use PHP-FPM’s default port, 9000.

Open /etc/php-fpm.d/www.conf, find “listen”, and set the value in the following way.

listen = 127.0.0.1:9000

In each config file, use the same port, like…

fastcgi_pass 127.0.0.1:9000;

To be 100% honest, I still don’t understand why I don’t have the socket file, which I did have on Ubuntu 16 with PHP 7.0. If I find out why, I should probably update this article.

The 3rd step: SELinux

At this point, I had learned that…

  1. The “www-data” user didn’t exist in CentOS 7, so I have to set “nginx” group for folders in apps where Nginx has to access.
  2. For some reasons, the socket file wasn’t generated in my CentOS 7 machine, even though I had this file in my previous Ubuntu 16 by just installing PHP-FPM.
  3. If the socket file wasn’t available, PHP-FPM could use port 9000 as the “pastcgi_pass” parameter in the config file (and/etc/php-fpm.d/www.conf file).

Still, I was unable to run my app due to a permission error. Even when I made a mock file with “chmod 777“, Nginx couldn’t access it, resulting in the “File not found” error (the HTTP status was 404).

I tried chmod 777 and confirmed that both Nginx and PHP-FPM were working too. Despite all that, my app couldn’t be accessed via Nginx.

After spending very long hours, it turned out that the final piece of the puzzle was a Linux kernal module, called SELinux.

Here is the definition from Wikipedia.

Security-Enhanced Linux (SELinux) is a Linux kernel security module that provides a mechanism for supporting access control security policies, including mandatory access controls (MAC).

SELinux is a set of kernel modifications and user-space tools that have been added to various Linux distributions. Its architecture strives to separate enforcement of security decisions from the security policy itself, and streamlines the amount of software involved with security policy enforcement.

SELinux is available for Ubuntu, but it seems that it’s not installed by default, but CentOS seems to have it by default.

So, why SELinux restricts access to some files from users, programmers adn programs (e.g. Nginx)?

As far as I’ve learned…

Without SELinux enabled, only traditional discretionary access control (DAC) methods such as file permissions or access control lists (ACLs) are used to control the file access of users.

Users and programs alike are allowed to grant insecure file permissions to others or, conversely, to gain access to parts of the system that should not otherwise be necessary for normal operation.

(source)

So what’s the problem of allowing users and programmers to give permissions of file accesses? I haven’t worked as a server admin, so I can’t really imagine what type of risks are associated with disabling SELinux.

Here are some potential risks you may face without SELinux.

  • Administrators have no way to control users: A user could set world readable permissions on sensitive files such as ssh keys and the directory containing such keys, customarily: ~/.ssh/

  • Processes can change security properties: A user’s mail files should be readable only by that user, but the mail client software has the ability to change them to be world readable
  • Processes inherit user’s rights: Firefox, if compromised by a trojaned version, could read a user’s private ssh keys even though it has no reason to do so.

(The source is the same as the previous quote)

Reading all those possible risks, I’m not really sure how I should use Nginx with SELinux in the live server. All those sentences certainly make sense, but then how do we allow Nginx to work?

Nonetheless, I need to prevent SELinux from interrupting Nginx on my local machine, so SELinux has to be disabled.

Here is the solution.

Firstly, run this command to check if SELinux is running.

# getenforce

Then, disable it with…

# setenforce Permissive

After that, reload Nginx and see if the problem persists.

(source)

Summary

  • Be aware that www-data doesn’t exist in CentOS, so you’ll typically use the “nginx” user (or group) to directories which Nginx has to access.
  • With PHP7.2, PHP-FPM’s name isn’t php7.2-fpm.
  • If PHP-FPM’s socket isn’t available, use port 9000.
  • If Nginx still can’t access your app (permission denied) after setting the permission and config file, disable SELinux.

Extra: What is the difference between conf.d and sites-available/enabled?

To tell the truth, I didn’t really remember what conf.d was meant to and why I was using sites-available.enabled for storing config files.

Answer:

The sites-available folder is for storing all of your vhost configurations, whether or not they’re currently enabled.

The sites-enabled folder contains symlinks to files in the sites-available folder. This allows you to selectively disable vhosts by removing the symlink.

Typically, the sites-enabled folder is used for virtual host definitions, while conf.d is used for global server configuration.

conf.d does the job, but you have to move something out of the folder, delete it, or make changes to it when you need to disable something.

So, use conf.d for things like module loading, log files, and other things that are not specific to a single virtual host.

The sites-* folder abstraction makes things a little more organized and allows you to manage them with separate support scripts.

If you’re supporting multiple web sites — i.e., virtual hosts — then each one gets its own file, so you can enable and disable them very easily by moving files in and out of sites-enabled (or creating and removing symlinks, which is probably a better idea).

(source)

Virtual hosting is meant to host multiple domain names on a single server and I do this with Nginx almost all the time. So, “the sites-availbale/enabled approach” seems to be the way to go.

Comments 1

  • Maybe LEMP stack installation requires more configuration than the very well known LAMP setup, but you can be assured that you will be using the latest technology from both worlds fast PHP-FPMprocessor package with modern Nginx web service. That is all for now, feel free to check other VPS tutorials on our VPS tutorial page .

Leave a Reply

Your email address will not be published. Required fields are marked *