How I configured Nginx on CentOS 7 with PHP 7.2 For Laravel apps

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 the 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.

Overview

  1. Install PHP and all the extentions required for your Laravel app
  2. Install MySQL
  3. Set the user information for MySQL
  4. Create a DB shema for you app
  5. Install Nginx
  6. Install Composer
  7. Install Laravel
  8. Set the database and server name into .env in your Laravel app
  9. Set the server name into /etc/hosts
  10. Configure Nginx and PHP-FPM
  11. Test it on the browser

In this post, I’m NOT going to explain details of them other than 10, because…

  • When I started to write this post, I had already installed all the required software (so I can’t recall every one of them)
  • There are already plenty of online articles about which software to install
  • You might be using different versions of PHP or PHP extentions ( and when I review this article, I might be usinig different versions of them)

I’d like to focus on 10 (configuring Nginx and PHP-FPM), because I didn’t find a lot of information for the Laravel + CentOS7 + Nginx combo. 

Here are the 4 steps I’d like to record for myself and anyone who is stuck in this process.

(I’ll show you how my Nginx config file looks like later.)

Step 1: Create sites-available/enabled

Installing Nginx into Cent OS doesn’t create the “/etc/nginx/sites-available” and “/etc/nginx/sites-enabled” directies. If you want to use the directory rather than cond.d, you have to make them manually.

Also, don’t forget to include the “enabled” directry to the root configuration. In other words, you’ll need the following line in the “http” block in “/etc/nginx/nginx.conf”.

include /etc/nginx/sites-enabled/*;

NOTE: 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.

Step 2: Update the user/group

One thing you have to do after installing all the software (including your Laravel app) is to make sure that your app resides in the directory with the right user and group.

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.confand see the value of “user”. It’ll be “nginx”. In other words, your /etc/nginx/nginx.conf should contain…

user 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:$USER /your_laravel_app/storage

(If you put “nginx” for both user and group, you won’t be able to edit the file manually)

NOTE: Be careful of the user/group of the folder where you put the application

If you’re putting your app outside /var/www/, make sure that the both Nginx and PHP-FPM are able to access the directory, not just your app. (I’ll cover PHP-FPM in a minute)

I once created a directory called /home/user/app/ and put my apps into it, which PHP-FPM wasn’t be able to access, even though the owner of the app was nginx, which was the user of PHP-FPM.

If your apps are in /var/www/, this won’t be the case (unless you’ve changed the owner of the directory).

Step 3: 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;

In addition, you’ll have to set the owner and group to nginx and mode to 0660. In other words, in your /etc/php-fpm.d/www.conf, you should have the following lines.

user = nginx
group = nginx

listen.owner = nginx
listen.group = nginx
listen.mode = 0660

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.

At this point, my Nginx config looked like…

server {
        listen 80;
        listen [::]:80 ipv6only=on;

        access_log /var/log/nginx/MY-APP-access.log;
        error_log /var/log/nginx/MY-APP-error.log;

        root /ROOT_OF_APP/public;
        index index.php index.html index.htm;

        server_name HOST_NAME;

        location / {
            try_files $uri $uri/ /index.php?$args;
        }

        location ~* \.php$ {
            include /etc/nginx/fastcgi_params;
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_index index.php;
        }

        location = /favicon.ico { log_not_found off; access_log off; }
        location = /robots.txt  { log_not_found off; access_log off; }
}

Step 4: 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

(The “sentenceforce” command will be ineffective when you turn off your computer. If you want to disable it after turning off the computer, run the following command instead.)

sudo chcon -Rt httpd_sys_content_t /path/to/www/

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

(source)

Summary

  • If you want to use /etc/nginx/sites-avaiable, you have to make it by yourself.
  • 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.

Comments 5

Leave a Reply

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