CentOS

Настройка web сервера nginx, php-fpm, php7 на CentOS 8

В связи с выходом нового релиза популярной операционной системы пришло время актуализировать некоторые статьи. Сегодня я настрою производительный веб сервер CentOS 8 на свежих версиях nginx, php-fpm, где сам php версии 7.2. Сейчас пока нет необходимости использовать сторонние репозитории для php, так как в стандартных присутствует свежая и актуальная версия.


Введение

В этой статье я расскажу, как настроить производительный web сервер на базе популярного стека технологий — nginx и php-fpm. В связи с выходом нового релиза Centos 8, многие статьи на эту тему стали не актуальны, так как версии софта в базовых репозиториях обновились и тот же php нет смысла ставить из стороннего репозитория.

Работать будем на сервере под управлением CentOS 8. Если у вас его еще нет, то читайте мои статьи на тему установки и базовой настройки centos. Не забудьте уделить внимание теме настройки iptables. В данной статье я ее не буду касаться, хотя тема важная для web сервера.

В самом конце я покажу, как настроить SELinux применительно к данному веб серверу. Приведу список правил, которые нужно будет применить конкретно к моей статье. Правила не универсальные. В зависимости от настройки web сервера, они будут отличаться. Если вам не хочется тратить на это свое время, или вам он не нужен, то просто отключите selinux. Если же нужен, то пока только добавляйте конфиги, но ничего не запускайте, пока не дойдете до самого конца. С включенным и не настроенным SELinux все равно ничего не заработает.

В своей тестовой среде я буду использовать следующие сущности.

z.serveradmin.ruимя тестового виртуального хоста и сайта
/web/sitesдиректория для размещения виртуальных хостов
75.37.225.138внешний ip адрес сервера
pma.serveradmin.ruимя виртуального хоста для phpmyadmin

Подопытным сервером будет выступать виртуальная машина от ihor, характеристики следующие:

Процессор4 ядра
Память4 Gb
Диск80 Gb SSD

Это кастомная настройка параметров. Они не оптимальны по цене, но мне были нужны именно такие.

Установка nginx на CentOS 8

Для установки самой свежей стабильной версии nginx на centos подключим официальный репозиторий. Я использую именно родной репозиторий по следующей причине. Мне больше нравится начальное расположение и формат конфигурационных файлов. Устанавливая софт из официальных репозиториев ты помимо того, что имеешь самую свежую и актуальную версию nginx, так и расположение и формат конфигурационных файлов будет одинаковый во всех системах.

Банальный пример. Если ставить nginx из официального репозитория centos 8, настройка дефолтного виртуального хоста будет прямо в основном конфиге nginx.conf. Мне лично это не удобно, так как в этом файле я храню набор настроек без привязки к виртуальным хостам. Если установить nginx из официального репозитория, конфигурация дефолтного хоста будет в отдельном конфиге default.conf.

Подключаем репозиторий nginx в centos 8. Я обычно использую mainline версию. Она имеет все нововведения на борту и достаточно стабильна. По крайней мере у меня никогда проблем с ней не было. Если вы хотите стабильную версию, то используйте репозиторий stable. Рисуем конфиг репозитория /etc/yum.repos.d/nginx.repo.

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

Устанавливаем nginx на сервер.

# dnf install nginx
Установка nginx на centos 8

Запускаем nginx и добавляем в автозагрузку.

# systemctl start nginx
# systemctl enable nginx

Проверяем, запустился ли web сервер. Для этого идем по ссылке http://75.37.225.138/. Вы должны увидеть стандартную страницу заглушку.

Стартовая страница nginx

Если ее не видите, скорее всего у вас включен и не настроен firewalld. В начале статьи я приводил ссылку на статью с настройкой сервера и настройкой iptables. Если вы с ними познакомились, значит либо уже открыли нужные порты, либо сейчас это сделаете. Для тех, кто не хочет со всем этим разбираться, я просто покажу, как открыть порты 80 и 443 на firewalld, который используется в centos 8 по-умолчанию.


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

Проверить, открылись ли порты можно командой.

# firewall-cmd – list-all
Настройка firewalld для веб сервера nginx

Теперь выполним небольшую начальную настройку. Очень подробно вопрос настройки nginx я разбирал отдельно, рекомендую познакомиться со статьей. Там описано все то, что используется далее в конфигах. Здесь же я их просто привожу, без комментариев и объяснений. Начнем с основного конфига /etc/nginx/nginx.conf.

user  nginx;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 30000;
pid /var/run/nginx.pid;
pcre_jit on;

events {
    worker_connections 8192;
    multi_accept on;
}

http {

    # Basic #######################
    
    sendfile			on;
    tcp_nopush			on;
    tcp_nodelay			on;
    reset_timedout_connection 	on;
    keepalive_timeout 		120;
    keepalive_requests		1000;
    types_hash_max_size		2048;
    server_tokens		off;
    send_timeout		30;
    client_body_timeout		30;
    client_header_timeout	30;
    server_names_hash_max_size	4096;

    # Limits ######################
    
    client_max_body_size	10m;
    client_body_buffer_size	128k;
    client_body_temp_path	/var/cache/nginx/client_temp;

    proxy_connect_timeout	60;
    proxy_send_timeout		60;
    proxy_read_timeout		60;
    proxy_buffer_size		4k;
    proxy_buffers		8 16k;
    proxy_busy_buffers_size	64k;
    proxy_temp_file_write_size	64k;
    proxy_temp_path		/var/cache/nginx/proxy_temp;

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
 
    # Logs ########################

    log_format  main	'$remote_addr - $host [$time_local] "$request" '
        '$status $body_bytes_sent "$http_referer" '
        '"$http_user_agent" "$http_x_forwarded_for"'
        'rt=$request_time ut=$upstream_response_time '
        'cs=$upstream_cache_status';
    log_format full	'$remote_addr - $host [$time_local] "$request" '
               'request_length=$request_length '
               'status=$status bytes_sent=$bytes_sent '
               'body_bytes_sent=$body_bytes_sent '
               'referer=$http_referer '
               'user_agent="$http_user_agent" '
               'upstream_status=$upstream_status '
               'request_time=$request_time '
               'upstream_response_time=$upstream_response_time '
               'upstream_connect_time=$upstream_connect_time '
               'upstream_header_time=$upstream_header_time';

    access_log  /var/log/nginx/access.log  main;
    error_log  /var/log/nginx/error.log;

    # Gzip ########################

    gzip on; 
    gzip_static on; 
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/x-icon image/svg+xml application/x-font-ttf;
    gzip_comp_level 9;
    gzip_proxied any;
    gzip_min_length 1000;
    gzip_disable "msie6";
    gzip_vary on;

    etag off;

    # Cache #######################

    #proxy_cache_valid 1m;
    #proxy_cache_key $scheme$proxy_host$request_uri$cookie_US;
    #proxy_cache_path /web/sites/nginx_cache levels=1:2 keys_zone=main:1000m;

    # Zone limits ################

    limit_conn_zone $binary_remote_addr zone=perip:10m;
    limit_req_zone $binary_remote_addr zone=lim_5r:10m rate=5r/s; # lim for dynamic page
    limit_req_zone $binary_remote_addr zone=lim_1r:10m rate=1r/s; # lim for search page
    limit_req_zone $binary_remote_addr zone=lim_10r:10m rate=10r/s;

    # SSL #########################

    ssl_session_cache shared:SSL:50m;
    ssl_session_timeout 1d;
    ssl_session_tickets on;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers 'TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-256-GCM-SHA384:ECDHE:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
    ssl_prefer_server_ciphers on;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    ssl_stapling on;
    ssl_stapling_verify on;
    add_header Strict-Transport-Security max-age=15768000;
    resolver 8.8.8.8;

    include /etc/nginx/conf.d/*.conf;

    # For monitoring ###########
    
    server {
    listen 127.0.0.1:80;
    server_name status.localhost;
    keepalive_timeout	0;
    allow	127.0.0.1;
    deny	all;
    access_log	off;

        location /server-status {
    stub_status	on;
        }

        location /status {
    access_log off;
    allow 127.0.0.1;
    deny all;
    include fastcgi_params;
    fastcgi_pass   unix:/run/php-fpm/www.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        }
    }
}

Сохраните конфиг и проверьте его корректность командой:

# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Если ошибок нет, то можно применить.

# nginx -s reload

Теперь создадим структуру каталогов для сайтов. Я обычно все сайты размещаю отдельно, например в /web/sites. Создадим там два сайта — основной и панель phpmyadmin для него. Привожу просто для примера, вешать на отдельный виртуальный хост phpmyadmin не обязательно, хотя в некоторых случаях это бывает удобно.

# mkdir -p /web/sites/z.serveradmin.ru/{www,log}
# mkdir -p /web/sites/pma.serveradmin.ru/{www,log}
# chown -R nginx. /web/sites/

Создадим конфиги nginx для этих виртуальных хостов. Я сразу буду делать их с учетом https, который мы настроим позже. Так что после создания не надо перезапускать веб сервер и проверять работу — будут ошибки. Виртуальный хост сайта показан на примере wordpress. Конфигурация собрана на основе рекомендаций из официальной документации конкретно для веб сервера nginx.

Конфиг для основного сайта — /etc/nginx/conf.d/z.serveradmin.ru.conf

server {
    listen 443 ssl http2;
    server_name z.serveradmin.ru;
    root /web/sites/z.serveradmin.ru/www/;
    index index.php index.html index.htm;
    access_log /web/sites/z.serveradmin.ru/log/access.log main;
    error_log /web/sites/z.serveradmin.ru/log/error.log;

    ssl_certificate		/etc/letsencrypt/live/z.serveradmin.ru/fullchain.pem;
    ssl_certificate_key		/etc/letsencrypt/live/z.serveradmin.ru/privkey.pem;

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

    location ~* ^.+.(js|css|png|jpg|jpeg|gif|ico|woff)$ {
    access_log off;
    expires max;
    }

    location ~ \.php$ {
    try_files  $uri =404;
    fastcgi_pass   unix:/run/php-fpm/www.sock;
    fastcgi_index index.php;
    fastcgi_param DOCUMENT_ROOT /web/sites/z.serveradmin.ru/www/;
    fastcgi_param SCRIPT_FILENAME /web/sites/z.serveradmin.ru/www$fastcgi_script_name;
    fastcgi_param PATH_TRANSLATED /web/sites/z.serveradmin.ru/www$fastcgi_script_name;
    include fastcgi_params;
    fastcgi_param QUERY_STRING $query_string;
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param CONTENT_TYPE $content_type;
    fastcgi_param CONTENT_LENGTH $content_length;
    fastcgi_param HTTPS on;
    fastcgi_intercept_errors on;
    fastcgi_ignore_client_abort off;
    fastcgi_connect_timeout 60;
    fastcgi_send_timeout 180;
    fastcgi_read_timeout 180;
    fastcgi_buffer_size 128k;
    fastcgi_buffers 4 256k;
    fastcgi_busy_buffers_size 256k;
    fastcgi_temp_file_write_size 256k;
    }

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

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

    location ~ /\.ht {
    deny all;
    }
}

server {
     listen  443 ssl http2;
     server_name  www.z.serveradmin.ru;
     return 301 https://z.serveradmin.ru$request_uri;
}

server {
    listen 80;
    server_name z.serveradmin.ru;
    root /web/sites/z.serveradmin.ru/www/;
    index index.php index.html index.htm;
    access_log /web/sites/z.serveradmin.ru/log/access.log main;
    error_log /web/sites/z.serveradmin.ru/log/error.log;

    location / {
    return 301 https://z.serveradmin.ru$request_uri;
    }
}

server {
     listen  80;
     server_name  www.z.serveradmin.ru;
     return 301 http://z.serveradmin.ru$request_uri;
}

В данной конфигурации настроены все необходимые редиректы, при этом отключен редирект файла robots.txt. Он отдельно отдается по http и https. Для phpmyadmin рисуем конфиг попроще. Я сразу его закрываю отдельным паролем с помощью basic auth.

# mcedit /etc/nginx/conf.d/pma.serveradmin.ru.conf
server {
    listen 443 ssl http2;
    server_name pma.serveradmin.ru;
    root /web/sites/pma.serveradmin.ru/www/;
    index index.php index.html index.htm;
    access_log /web/sites/pma.serveradmin.ru/log/access.log main;
    error_log /web/sites/pma.serveradmin.ru/log/error.log;

    auth_basic "Restricted Content";
    auth_basic_user_file /etc/nginx/.htpasswd;

    ssl_certificate		/etc/letsencrypt/live/pma.serveradmin.ru/fullchain.pem;
    ssl_certificate_key		/etc/letsencrypt/live/pma.serveradmin.ru/privkey.pem;

    location ~ \.php$ {
    fastcgi_pass   unix:/run/php-fpm/www.sock;
    fastcgi_index index.php;
    fastcgi_param DOCUMENT_ROOT /web/sites/pma.serveradmin.ru/www/;
    fastcgi_param SCRIPT_FILENAME /web/sites/pma.serveradmin.ru/www$fastcgi_script_name;
    fastcgi_param PATH_TRANSLATED /web/sites/pma.serveradmin.ru/www$fastcgi_script_name;
    include fastcgi_params;
    fastcgi_param QUERY_STRING $query_string;
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param CONTENT_TYPE $content_type;
    fastcgi_param CONTENT_LENGTH $content_length;
    fastcgi_intercept_errors on;
    fastcgi_ignore_client_abort off;
    fastcgi_connect_timeout 60;
    fastcgi_send_timeout 180;
    fastcgi_read_timeout 180;
    fastcgi_buffer_size 128k;
    fastcgi_buffers 4 256k;
    fastcgi_busy_buffers_size 256k;
    fastcgi_temp_file_write_size 256k;
    }
}

server {
    listen 80;
    server_name pma.serveradmin.ru;
    root /web/sites/pma.serveradmin.ru/www/;
    index index.php index.html index.htm;
    access_log /web/sites/pma.serveradmin.ru/log/access.log main;
    error_log /web/sites/pma.serveradmin.ru/log/error.log;

    location / {
    return 301 https://pma.serveradmin.ru$request_uri;
    }
}

Создаем файл с логином и паролем для доступа.

# sh -c "echo -n 'pma:' >> /etc/nginx/.htpasswd"
# sh -c "openssl passwd -apr1 >> /etc/nginx/.htpasswd"

В данном случае pma — имя пользователя, а пароль будет задан в консоли при выполнении второй команды.

Сохраняем конфиги хостов и движемся дальше. Пока nginx перезапускать не надо. Некоторые сущности, которые упоминются в конфиге, еще не настроены. Одну из них сразу добавим, чтобы не забыть. Нам надо сформировать файл dhparam.pem. Процесс длится долго, до получаса на слабых виртуалках. Этот файл нужен для повышения безопасности и получения максимального рейтинга ssl. Насколько этот параметр критичен в реальности, не берусь судить. Если тороплюсь, то настраиваю без него.

# openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096

Установка php-fpm

Установка php-fpm в Centos 8 сильно упростилась по сравнению с предыдущей версией, потому что в базовом репозитории хранится актуальная версия php 7.2, которой можно пользоваться. Пока нет необходимости подключать сторонние репозитории, так как версия 7.2 вполне свежа и актуальна. Если у вас нет необходимости использовать что-то новее, то можно остановиться на этой версии.

Устанавливаем php и php-fpm в CentOS 8, а так же некоторые популярные модули.

# dnf install php-fpm php-cli php-mysqlnd php-json php-gd php-ldap php-odbc php-pdo php-opcache php-pear php-xml php-xmlrpc php-mbstring php-snmp php-soap php-zip
Установка php 7.2

Проверим конфигурацию php-fpm, которая располагается в файле /etc/php-fpm.d/www.conf. Изменим пользователя, под которым будет работать php-fpm с apache на nginx.

user = nginx
group = nginx

Перезапускаем php-fpm и проверяем, запущен ли сокет.

# systemctl restart php-fpm
# ll /run/php-fpm/www.sock
srw-rw----+ 1 root root 0 Oct  7 16:59 /run/php-fpm/www.sock

Все в порядке, php-fpm запущен и готов к работе. Cделаем еще одну важную настройку. Назначим nginx владельцем директории для хранения сессий php.

# chown -R nginx. /var/lib/php/session

Более безопасно было бы для каждого сайта делать отдельную директорию для сессий и определять ее в настройках php. Но это уже частный случай. В общем случае, можно оставить так.

Возможно, вам захочется поставить более свежу версию php. Как это сделать, читайте в отдельной статье — обновление php-7.2 до 7.4 в CentOS 8.

Для того, чтобы проверить работу нашего веб сервера, нужно установить ssl сертификаты. Без них nginx с текущим конфигом не запустится. Исправляем это.


Настройка бесплатного ssl сертификата Lets Encrypt

Устанавливаем пакет certbot для получения бесплатного ssl сертификата от let’s encrypt. В репозиториях его пока нет, поэтому поставим вручную с сайта разрабочиков.

# cd ~
# wget https://dl.eff.org/certbot-auto
# mv certbot-auto /usr/local/bin/certbot-auto
# chown root /usr/local/bin/certbot-auto
# chmod 0755 /usr/local/bin/certbot-auto

При первом запуске certbot-auto предложит установить зависимости, которые ему нужны для работы.

# certbot-auto
Настройка бесплатного ssl сертификата

После установки зависимостей, certbot ругнется на то, что не смог настроить нам nginx, но и хорошо, мы уже сами все настроили. Наша следующая задача — получить бесплатные сертификаты. Для этого временно остановим nginx, если он вдруг оказался запущен и подтвердим владение доменами с помощью temporary webserver, который certbot поднимет сам на время верификации доменов.

# systemctl stop nginx
# certbot-auto certonly
How would you like to authenticate with the ACME CA?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Nginx Web Server plugin (nginx) [Misconfigured]
2: Spin up a temporary webserver (standalone)
3: Place files in webroot directory (webroot)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-3] then [enter] (press 'c' to cancel): 2
Plugins selected: Authenticator standalone, Installer None
Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c'
to cancel): z.serveradmin.ru
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for z.serveradmin.ru
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/z.serveradmin.ru/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/z.serveradmin.ru/privkey.pem
   Your cert will expire on 2020-01-05. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot-auto
   again. To non-interactively renew *all* of your certificates, run
   "certbot-auto renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

То же самое делаем для второго домена — pma.serveradmin.ru.

После того, как получили сертификаты, можно проверить конфиг nginx и запустить его. Ошибок быть не должно. Если они есть, то разбирайтесь с ними, возможно где-то ошиблись.

# nginx -t
# systemctl start nginx

Настройка nginx на этом завершена. Он должен корректно запуститься и работать в рабочем режиме.

Теперь сделаем так, чтобы сертификаты автоматически обновлялись перед истечением срока действия. Для этого необходимо изменить конфигурации доменов. Они располагаются в директории /etc/letsencrypt/renewal. Так как мы генерировали сертификаты с помощью временного веб сервера, наш текущий конфиг z.serveradmin.ru.conf выглядит вот так:

# renew_before_expiry = 30 days
version = 0.39.0
archive_dir = /etc/letsencrypt/archive/z.serveradmin.ru
cert = /etc/letsencrypt/live/z.serveradmin.ru/cert.pem
privkey = /etc/letsencrypt/live/z.serveradmin.ru/privkey.pem
chain = /etc/letsencrypt/live/z.serveradmin.ru/chain.pem
fullchain = /etc/letsencrypt/live/z.serveradmin.ru/fullchain.pem

# Options used in the renewal process
[renewalparams]
account = a9e5d37f80dafd9f0a2f9a2b019cbc3e
authenticator = standalone
server = https://acme-v02.api.letsencrypt.org/directory

Приводим его к следующему виду:

# renew_before_expiry = 30 days
version = 0.39.0
archive_dir = /etc/letsencrypt/archive/z.serveradmin.ru
cert = /etc/letsencrypt/live/z.serveradmin.ru/cert.pem
privkey = /etc/letsencrypt/live/z.serveradmin.ru/privkey.pem
chain = /etc/letsencrypt/live/z.serveradmin.ru/chain.pem
fullchain = /etc/letsencrypt/live/z.serveradmin.ru/fullchain.pem

# Options used in the renewal process
[renewalparams]
account = a9e5d37f80dafd9f0a2f9a2b019cbc3e
authenticator = webroot
server = https://acme-v02.api.letsencrypt.org/directory
post_hook = nginx -s reload
[[webroot_map]]
z.serveradmin.ru = /web/sites/z.serveradmin.ru/www

По аналогии делаете с остальными виртуальными хостами, для которых используете бесплатные сертификаты let’s encrypt. Осталось дело за малым — настроить автоматический выпуск новых ssl сертификатов, взамен просроченным. Для этого добавляем в /etc/crontab следующую строку:

# Cert Renewal
30 2 * * * root /usr/local/bin/certbot-auto renew – post-hook "nginx -s reload" >> /var/log/le-renew.log

Все, с сертификатами закончили. Двигаемся дальше в настройке web сервера.


Установка mariadb на CentOS 8

Дошла очередь до установки сервера баз данных mysql для web сервера на CentOS 8 — MariaDB. Я буду устанавливать последнюю стабильную версию на момент написания статьи — 10.4 из официального репозитория mariadb.

Для того, чтобы подключить репозиторий MariaDB, можно воспользоваться специальной страницей на официальном сайте, где можно задать параметры системы и получить конфиг репозитория.

В моем случае конфиг получился следующий.

# cat /etc/yum.repos.d/mariadb.repo
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.4/centos8-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

Устанавливаем последнюю версию mariadb на centos.

# dnf install boost-program-options
# dnf install MariaDB-server MariaDB-client – disablerepo=AppStream
Установка и настройка mariadb

Запускаем mariadb и добавляем в автозагрузку.

# systemctl start mariadb
# systemctl enable mariadb

Запускаем скрипт начальной конфигурации mysql и задаем пароль для root. Все остальное можно оставить по-умолчанию.

# /usr/bin/mysql_secure_installation

Сервер баз данных mysql для нашего web сервера готов. Продолжаем настройку. Установим панель управления mysql — phpmyadmin.

Установка phpmyadmin

Для того, чтобы установить phpmyadmin на наш web сервер, достаточно просто распаковать в директроию с виртуальным хостом исходники панели. Все остальные настройки у нас уже готовы, в том числе виртуальный хост с tls сертификатом.

Идем на сайт https://www.phpmyadmin.net и копируем ссылку на последнюю версию панели. Затем загружаем ее через консоль.

# cd ~
# wget https://files.phpmyadmin.net/phpMyAdmin/4.9.1/phpMyAdmin-4.9.1-all-languages.zip

Архив упакован в zip. Если у вас нет на сервере пакета unzip, установите его.

# dnf install unzip

Распаковываем исходники в директорию виртуального хоста.

# unzip phpMyAdmin-4.9.1-all-languages.zip
# cp -R phpMyAdmin-4.9.1-all-languages/* /web/sites/pma.serveradmin.ru/www
# chown -R nginx. /web/sites/pma.serveradmin.ru/www

Можно заходить и проверять работу phpmyadmin, пройдя по адресу pma.serveradmin.ru. Ее установка закончена.

Установка phpmyadmin

Более подробно вопрос установки и настройки phpmyadmin я рассматривал отдельно. Можете зайти в панель и создать базу mysql для тестового сайта, например, wordpress. Затем через консоль загрузить исходники cms и распаковать их.

# cd ~
# wget https://ru.wordpress.org/latest-ru_RU.tar.gz
# tar xzvf latest-ru_RU.tar.gz
# cp -R wordpress/* /web/sites/z.serveradmin.ru/www
# chown -R nginx. /web/sites/z.serveradmin.ru/www

После этого открывайте в браузере страницу z.serveradmin.ru и увидите приветсвие установщика wordpress.

Установка wordpress

На этом основная настройка web сервера завершена. Он уже полностью работоспособен. Если вам достаочно этого функционала, то можете сразу переходить к настройке ротации логов.


Доступ к сайту по sftp

Если вы администратор и единственный пользователь, то больше можно ничего не делать. Вы и так сможете загрузить на сервер все что нужно тем или иным способом. Если же вы будете передавать управление сайтами другим людям, им нужен доступ к директории с исходниками сайта. Раньше для этих целей повсеместно использовали ftp. Если вы хотите так сделать, у меня есть статья по настройке ftp сервера vsftpd.

Я же предлагаю использовать sftp по нескольким причинам:

  1. Он безопаснее.
  2. Его быстрее настроить.
  3. Не надо отдельно настраивать firewall.

Статью по настройке sftp доступа я уже тоже писал, все подробности там. Здесь без комментариев выполним необходимые действия.

Создаем пользователя для подключения к сайту и добавляем его в группу sftp. Я обычно использую имя пользователя пересекающееся с названием сайта. Так удобнее управлять.

# useradd -s /sbin/nologin -d /web/sites/z.serveradmin.ru z.serveradmin.ru
# passwd z.serveradmin.ru
# groupadd sftp
# usermod -a -G sftp z.serveradmin.ru

Открываем конфиг ssh по пути /etc/ssh/sshd_config и комментируем там одну строку, добавляя далее несколько новых.

#Subsystem sftp /usr/libexec/openssh/sftp-server
Subsystem sftp internal-sftp -u 022
Match Group sftp
ChrootDirectory %h
ForceCommand internal-sftp -u 022

Перезапускаем службу sshd.

# systemctl restart sshd

Теперь нужно сделать владельцем каталогов /web/sites и /web/sites/z.serveradmin.ru пользователя root и убрать у остальных права на запись в эти каталоги. Без этого параметр ChrootDirectory работать не будет и при подключении получите ошибку.

# chown root /web/sites && chown root /web/sites/z.serveradmin.ru
# chmod 0755 /web/sites && chmod 0755 /web/sites/z.serveradmin.ru

Этого уже достаточно, чтобы вы могли подключиться к сайту, к примеру, с помощью программы winscp. Если что-то пойдет не так и будут какие-то ошибки, то смотреть подробности нужно в логе /var/log/secure. С помощью такого подключения вы можете загрузить к себе исходники сайта, но не править их. У вас доступ только на чтение. В таком подходе возникает много нюансов с правами к файлам и директориям. Дальше я расскажу, как их аккуратно и грамотно разрулить, чтобы у нас не было проблем с дальнейшей работой сайтов от разных пользователей и были все необходимые права.


Работа с сайтами разных пользователей на одном веб сервере

Самый простой способ решить проблему с правами доступа, это сделать владельцем папки с сайтом пользователя, который подключается по sftp. Тогда он сможет нормально работать с файлами, загружать и удалять их. Если доступ в качестве группы установить для nginx, то в целом все будет работать. Для каких-то сайтов такой вариант может оказаться подходящим. То есть сделать надо вот так:

# chown -R z.serveradmin.ru:nginx /web/sites/z.serveradmin.ru/
# chmod -R 0775 /web/sites/z.serveradmin.ru/

И не забываем обратно вернуть владельца на наш chroot каталог, иначе не подключимся по sftp.

# chown root /web/sites/z.serveradmin.ru/
# chmod 0755 /web/sites/z.serveradmin.ru/

Но при такой схеме будут проблемы с движками сайтов, которые автоматом что-то к себе загружают, потому что php-fpm работает от пользователя nginx, а у нас права nginx только на группу стоят, где нет прав на запись. К примеру, wordpress не сможет автоматически загружать плагины, будет просить доступ к ftp. Это можно исправить просто выставив права 0775 на все каталоги, но это путь костылей. В общем, могут возникнуть некоторые неудобства. Сейчас мы их исправим.

А теперь сделаем все красиво. Назначаем владельцем содержимого нашего сайта только отдельного пользователя.

# chown -R z.serveradmin.ru:z.serveradmin.ru /web/sites/z.serveradmin.ru/

Еще раз обращаю внимание на важный нюанс. Chroot доступ для sftp не будет работать, если владельцем директории, куда чрутимся, будет не root. Будете получать ошибку.

fatal: bad ownership or modes for chroot directory "/web/sites/z.serveradmin.ru" [postauth]

Возвращаем обратно рута владельцем корня chroot.

# chown root /web/sites/z.serveradmin.ru/
# chmod 0755 /web/sites/z.serveradmin.ru/

Обращаю внимание, что сначала мы рекурсивно назначаем права на все содержимое директорий, а потом возвращаем владельца root только на корень домашнего каталога пользователя z.serveradmin.ru.

Добавляем пользователя nginx в группу z.serveradmin.ru, чтобы web сервер имел доступ на чтение всех файлов виртуального хоста.

# usermod -aG z.serveradmin.ru nginx

Создаем отдельный pool для php-fpm, который будет обслуживать сайт z.serveradmin.ru и будет запускаться от этого пользователя. Для этого копируем существующий конфиг /etc/php-fpm.d/www.conf и изменяем в нем несколько строк.

# cd /etc/php-fpm.d && cp www.conf z.serveradmin.ru.conf
[z.serveradmin.ru]
user = z.serveradmin.ru
group = z.serveradmin.ru
listen = /run/php-fpm/z.serveradmin.ru.sock
listen.owner = z.serveradmin.ru
listen.group = z.serveradmin.ru
listen.mode = 0660
;listen.acl_users = apache,nginx # эту строку комментируем

Мы поменяли название пула, запустили его от отдельного пользователя и назначили ему отдельный сокет. Теперь идем в настройки этого виртуального хоста в nginx — /etc/nginx/conf.d/z.serveradmin.ru.conf и везде меняем старое значение сокета

fastcgi_pass unix:/run/php-fpm/www.sock;

на новое

fastcgi_pass unix:/run/php-fpm/z.serveradmin.ru.sock;

Перезапускаем nginx и php-fpm и проверяем работу сайта от отдельного пользователя.

# systemctl restart nginx
# systemctl restart php-fpm

Я рекомендую подключиться по sftp, закинуть еще раз исходники wordpress уже по sftp, установить его и добавить новую тему, чтобы проверить, что все корректно работает.  По аналогии проделанные выше действия повторяются для всех остальных сайтов.


Настройка SELinux для web сервера

Раздел для тех, кто хочет настроить SELinux на своем web сервере. Сначала ставим пакет policycoreutils-python-utils если он еще не установлен. Он нам нужен для утилиты semanage.

# dnf install policycoreutils-python-utils

Дальше нам нужно подготовить набор правил для selinux при нашей текущей конфигурации web сервера. Общий смысл их следующий:

  1. У нас нестандартная директория для сайтов — /web/sites, по-умолчанию запросы будут блокироваться.
  2. Надо разрешить nginx взаимодействовать с сокетом.
  3. Позволить nginx управлять параметром rlimit_nofile.
  4. И еще некоторое количество запретов, которые сможете сами проверить.

Суть описываемой дальше настройки selinux сводится к тому, чтобы следить за ограничениями, которые он накладывает на работу сервиса и добавлять исключения в правила, если вы с ними согласны. Методом нескольких итераций готовится набор правил selinux.

Я для этого пользуюсь следующими командами. Просмотр сработавших блокировок selinux для nginx.

# grep nginx /var/log/audit/audit.log | audit2why

Можете внимательно посмотреть суть блокировок. На первый взгляд выглядит не понятно, но в целом все гуглится. Можно разобраться, если есть желание. Подробно этот процесс описан в статье на сайте nginx.com.

Сформировать список правил для selinux.

# grep nginx /var/log/audit/audit.log | audit2allow -m nginx > ~/nginx.te

Этот файл можно потом вручную дополнять или изменять. После того, как все правила будут собраны в один файл, готовлю модуль для selinux.

# checkmodule -M -m -o nginx.mod nginx.te
# semodule_package -m nginx.mod -o nginx.pp

В конце загружаю этот модуль в selinx.

# semodule -i nginx.pp

Можно не собирать правила самому, а сразу получить готовый модуль для загрузки и установить его.

# grep nginx /var/log/audit/audit.log | audit2allow -M nginx
# semodule -i nginx.pp

Потом повторите все то же самое для php-fpm и можно проверять работу web сервера. Если в процессе проверки окажется, что что-то еще не работает, опять смотрите audit.log и добавляйте новые правила, пересобирайте и загружайте модуль. Так, через несколько итераций, получится рабочий набор правил для selinux.

Убедиться, что модуль загружен, можно командой.

# semodule -l | grep nginx

В целом, по selinux все. Мы просто разрешили все, что веб сервер просил. По идее, надо вдумчиво во всех правилах разбираться и разрешать только то, что считаешь нужным. Я честно скажу, что selinux знаю не очень хорошо. Дальше загрузки готовых модулей и автоматического создания модулей с помощью audit2allow я не двигался. Руками модули никогда не писал. Если есть какой-то более осмысленный и правильный способ настройки selinux на кастомной конфигурации веб сервера, буду рад полезной информации.

Хорошая практическая статья по ручной настройке selinux для web сервера — https://habr.com/ru/post/322904/. Там же есть ссылки на другие статьи автора на тему selinux. Написано содержательно и наглядно, рекомендую для тех, кто будет знакомиться с технологией.


Ротация логов веб сервера nginx

Последний штрих в настройке web сервера — ротация логов виртуальных хостов. Если этого не сделать, то через какое-то, обычно продолжительное, время возникает проблема в связи с огромным размером лог файла.

У нас уже будет файл конфигурации logrotate для nginx, который был создан во время установки — /etc/logrotate.d/nginx. Приведем его к следующему виду:

/var/log/nginx/*log
/web/sites/pma.serveradmin.ru/log/*log {

    create 0644 nginx nginx
    size=10M
    rotate 10
    missingok
    notifempty
    compress
    sharedscripts
    postrotate
        /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
    endscript
}

/web/sites/z.serveradmin.ru/log/*log {

    create 0644 z.serveradmin.ru z.serveradmin.ru
    size=10M
    rotate 10
    missingok
    notifempty
    compress
    sharedscripts
    postrotate
        /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
    endscript
}

Я предлагаю ротировать файлы логов по достижению ими размера в 10Мб, сжимать после ротации и хранить 10 архивов с логом. Для виртуальных хостов, работающих от отдельного пользователя, новые логи создаются сразу с соответствующими правами, чтобы у пользователя был доступ к ним. Для всех остальных хостов можно использовать самое первое правило, просто добавляя туда новые пути для логов.

Обращаю внимание на важный нюанс при ротации логов по размеру. Скорее всего в общем случае она будет работать не так, как вы ожидаете. Подробности читайте по ссылке. Я привел пример простой конфигурации. Все параметры вы можете поменять по своему усмотрению. Примеров конфигурации logrotate в интернете много.

На этом все. Я рассмотрел все основные моменты, которые необходимы для установки и настройки производительного web сервера на основе nginx и php-fpm последних версий. При этом рассказал о некоторых вещах, которые повышают удобство и гибкость эксплуатации сервера.


Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *