Drupal и CentOS 7 LEMP (Linux, Nginx, MariaDB, php-fpm)

Об установке CentOS, Dupal и всех.

CentOS 7

Устанавливаем CentOS как таковую, yum update (или yum upgrade).

Для удобства устанавливаем Midnight Commander:

# yum install mc

Обычно я пользуюсь VirtualBox и работаю в два сетевых адаптера - NAT и виртуальный адаптер хоста. В таком варианте второй адаптер не стартует - включаем:

/etc/sysconfig/network-scripts/ifcfg-enp0s8

ONBOOT=yes

firewalld блокирует доступ. Отключить совсем? Либо добавляем сервисы (правила):

# firewall-cmd --zone=public --permanent --add-service=http
# firewall-cmd --zone=public --permanent --add-service=ftp
# firewall-cmd --reload

Проблемы: старые версии MariaDB и PHP. Nginx отсутствует.

Установка Nginx

а) путем добавления "родного" репозитория - файл /etc/yum.repos.d/nginx.repo:

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=1
enabled=1
# rpm --import http://nginx.org/keys/nginx_signing.key

б) из epel:

# yum install epel-release

epel все равно потребуется в дальнейшем.

В любом случае:

# yum install nginx

Сервис выключен - включаем:

# systemctl start nginx
# systemctl enable nginx

Установка MariaDB

Добавляем репозиторий:

# curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash

Или, если не сработает, формируем файл репозитория на основе https://mariadb.org/download/?t=repo-config

Например такой /etc/yum.repos.d/mariadb.repo:

# MariaDB 10.4 CentOS repository list
# https://mariadb.org/download/
[mariadb]
name = MariaDB
# rpm.mariadb.org is a dynamic mirror if your preferred mirror goes offline. See https://mariadb.org/mirrorbits/ for details.
# baseurl = https://rpm.mariadb.org/10.4/centos/$releasever/$basearch
baseurl = https://mirror.docker.ru/mariadb/yum/10.4/centos/$releasever/$basearch
module_hotfixes = 1
# gpgkey = https://rpm.mariadb.org/RPM-GPG-KEY-MariaDB
gpgkey = https://mirror.docker.ru/mariadb/yum/RPM-GPG-KEY-MariaDB
gpgcheck = 1

Устанавливаем:

# yum install MariaDB-server MariaDB-client

Включаем:

# systemctl start mariadb
# systemctl enable mariadb

Защищаем:

# mysql_secure_installation

Первоначально пароль root пустой, Enter, устанавливаем, остальные вопросы - Y.

Необходимо создать БД и пользователя для Drupal. Заходим в командную строку MariaDB - mysql или mysql -p в зависимости от настроек. Серией запросов создаем БД drupal, пользователя drupal (пароль лучше назначить более надежный) и назначаем привилегии:

CREATE DATABASE drupal;
CREATE USER drupal@localhost IDENTIFIED BY 'drupal_password';
GRANT ALL PRIVILEGES ON drupal.* to drupal@localhost;
FLUSH PRIVILEGES;

Выходим - exit или \q

Установка PHP

# yum install http://rpms.remirepo.net/enterprise/remi-release-7.rpm

Если не был установлен epel-release, то подтянется.

# yum install yum-utils
# yum-config-manager --enable remi-php73

(или другую нужную версию)

Внимание! yum install php "тянет" за собой Apache (httpd) - не нужно.

Основа:

# yum install php-fpm

Можно (нужно!) yum install php-cli.

Пример установки дополнительных модулей php:

# yum install php-gd php-mysqlnd php-curl php-mbstring php-xml

Проблема: nginx стартует от пользователя nginx, а php-fpm - apache. "В стиле Ubuntu" добавляем www-data:

# useradd -m -d /var/www -s /sbin/nologin -r -p "*" www-data

GID 33 занят, поэтому просто -r. Возможно без -m, создать /var/www вручную и назначить chown/chmod.

FTP

Из epel:

# yum install proftpd proftpd-utils

Создаем пользователя FTP (подставить uid и gid ранее созданного системного пользователя):

# ftpasswd --passwd --file=/etc/proftpd.d/ftpd.passwd --name=www-data --uid=996 --gid=994 --home=/var/www --shell=/bin/false

Важно! shell именно /bin/false, а не /sbin/nologin. По желанию - на файл паролей chown ftp:ftp и chmod 400.

SELinux "кочевряжится":

# setsebool -P allow_ftpd_full_access=1

Правим /etc/proftpd.conf

"Для красоты":

User ftp
Group ftp

Закомментировать/удалить:

  • AuthPAMConfig proftpd
  • AuthOrder

В конфигурацию сервера:

AuthUserFile /etc/proftpd.d/ftpd.passwd
AuthPAM off
AuthOrder mod_auth_file.c

"Webmin-совместимо" в секцию Global добавить:

RequireValidShell off

Umask закомментировать или убедиться, что стоит 022, чтобы права на новые файлы и каталоги были 644 и 755 соответственно.

Теперь долго и упорно настраиваем сервисы.

PHP-FPM

/etc/php-fpm.d/www.conf

user = www-data
group = www-data
listen = /run/php-fpm/www.sock
listen.owner = www-data
listen.group = www-data

chown -R на /var/lib/php и /var/log/php-fpm

Внимание! На логи ругается, если у папки запись не у группы root.

/etc/php.ini

cgi.fix_pathinfo=0

По желанию:

expose_php = Off
memory_limit
display_errors
display_startup_errors
post_max_size
upload_max_filesize
max_file_uploads
allow_url_fopen = Off

Включаем и запускаем сервис.

Nginx

/etc/nginx/nginx.conf

user www-data;

https://www.nginx.com/resources/wiki/start/topics/recipes/drupal/
ВЗЯТЬ ИСПРАВЛЕННЫЙ

Создаем конфигурацию, например /etc/nginx/conf.d/drupal.conf:

server {
    server_name drupal.example.com;
    listen 80;
    root /var/www/drupal; ## <-- Your only path reference.

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

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # Very rarely should these ever be accessed outside of your lan
    location ~* \.(txt|log)$ {
        allow 192.168.0.0/16;
        deny all;
    }

    location ~ \..*/.*\.php$ {
        return 403;
    }

    location ~ ^/sites/.*/private/ {
        return 403;
    }

    # Block access to scripts in site files directory
    location ~ ^/sites/[^/]+/files/.*\.php$ {
        deny all;
    }

    # Allow "Well-Known URIs" as per RFC 5785
    location ~* ^/.well-known/ {
        allow all;
    }

    # Block access to "hidden" files and directories whose names begin with a
    # period. This includes directories used by version control systems such
    # as Subversion or Git to store control files.
    location ~ (^|/)\. {
        return 403;
    }

    location / {
        # try_files $uri @rewrite; # For Drupal <= 6
        try_files $uri /index.php?$query_string; # For Drupal >= 7
    }

    location @rewrite {
        rewrite ^/(.*)$ /index.php?q=$1;
    }

    # Don't allow direct access to PHP files in the vendor directory.
    location ~ /vendor/.*\.php$ {
        deny all;
        return 404;
    }

    # In Drupal 8, we must also match new paths where the '.php' appears in
    # the middle, such as update.php/selection. The rule we use is strict,
    # and only allows this pattern with the update.php front controller.
    # This allows legacy path aliases in the form of
    # blog/index.php/legacy-path to continue to route to Drupal nodes. If
    # you do not have any paths like that, then you might prefer to use a
    # laxer rule, such as:
    #   location ~ \.php(/|$) {
    # The laxer rule will continue to work if Drupal uses this new URL
    # pattern with front controllers other than update.php in a future
    # release.
    location ~ '\.php$|^/update.php' {
        # Security note: If you're running a version of PHP older than the
        # latest 5.3, you should have "cgi.fix_pathinfo = 0;" in php.ini.
        # See http://serverfault.com/q/627903/94922 for details.
        # regex to split $uri to $fastcgi_script_name and $fastcgi_path
        fastcgi_split_path_info ^(.+\.php)(/.+)$;

        # Check that the PHP script exists before passing it
        try_files $fastcgi_script_name =404;

        # Bypass the fact that try_files resets $fastcgi_path_info
        # see: http://trac.nginx.org/nginx/ticket/321
        set $path_info $fastcgi_path_info;
        fastcgi_param PATH_INFO $path_info;

        fastcgi_index index.php;
        fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
        include fastcgi_params;

        # Block httpoxy attacks. See https://httpoxy.org/.
        fastcgi_param HTTP_PROXY "";
        fastcgi_intercept_errors on;

        # PHP socket location.
        fastcgi_pass unix:/run/php-fpm/www.sock;
    }

    # Fighting with Styles? This little gem is amazing.
    # location ~ ^/sites/.*/files/imagecache/ { # For Drupal <= 6
    location ~ ^/sites/.*/files/styles/ { # For Drupal >= 7
        try_files $uri @rewrite;
    }

    # Handle private files through Drupal. Private file's path can come
    # with a language prefix.
    location ~ ^(/[a-z\-]+)?/system/files/ { # For Drupal >= 7
        try_files $uri /index.php?$query_string;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        try_files $uri @rewrite;
        expires max;
        log_not_found off;
    }
}

Соответственно "исходники" находятся в /var/www/drupal.

Поскольку меняли пользователя, скорее всего нужно перезапустить сервис.

# systemctl restart nginx

SELinux

Опять! (Снова...)

Устанавливаем утилиты:

# yum install policycoreutils-python

Разрешаем запись:

# semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/drupal/sites/all(/.*)?"    
# semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/drupal/sites/default/files(/.*)?"

Применяем:

# restorecon -r /var/www/drupal/sites

Проверяем, что установился правильный контекст:

# ls -Z /var/www/drupal/sites/all
# ls -Z /var/www/drupal/sites/default/files

Заключение

В общих чертах на этом настройка стека окончена - сайт должен заработать. Далее наступает творческий этап, на который моя инструкция уже не распространяется.