nginx (Русский)

From ArchWiki

Состояние перевода: На этой странице представлен перевод статьи nginx. Дата последней синхронизации: 18 июля 2022. Вы можете помочь синхронизировать перевод, если в английской версии произошли изменения.

nginx (произносится "э́нжин-э́кс" или "э́нжин-и́кс") — это свободный высокопроизводительный HTTP-сервер с открытым исходным кодом, а также обратный прокси и IMAP/POP3 прокси-сервер, написанный Игорем Сысоевым в 2005 году. nginx получил широкое распространение благодаря своей стабильности, богатой функциональности, простой настройке и низкому потреблению ресурсов.

Данная статья описывает установку nginx и интеграцию с PHP через #FastCGI.

Установка

Установите пакет nginx-mainline (основная ветка: новые возможности, обновления и исправления ошибок) или nginx (стабильная ветка; только исправления серьёзных ошибок).

Рекомендуется использовать основную (mainline) ветку. Основная причина для использования стабильной ветки — возможная несовместимость со сторонними модулями или непреднамеренное появление ошибок при реализации новых функций.

Примечание: Все модули nginx, доступные в официальных репозиториях, в качестве зависимости требуют пакет nginx (а не nginx-mainline). Возможно, стоит просмотреть список модулей на наличие тех, которые вам могут понадобиться, прежде чем принимать решение о выборе между nginx и nginx-mainline. Модули для nginx-mainline можно найти в пользовательском репозитории Arch.

Если для обеспечения дополнительной безопасности вы хотите установить nginx в chroot-окружении, смотрите раздел #Установка в chroot.

Запуск

Запустите/включите службу nginx.service.

Страница по умолчанию, доступная по адресу http://127.0.0.1 располагается в /usr/share/nginx/html/index.html.

Настройка

Первые шаги по настройке и использованию nginx описаны в официальном руководстве для начинающих. Вы можете настроить сервер, редактируя файлы в /etc/nginx/; главный файл настроек расположен в /etc/nginx/nginx.conf.

Более подробную информацию можно прочитать на странице Nginx Configuration Examples и в официальной документации.

Приведенные далее примеры покрывают большинство типичных потребностей. Предполагается, что вы используете стандартное место расположения веб-документов (/usr/share/nginx/html). Если это не так, замените путь на свой.

Совет: DigitalOcean предоставляет инструмент для настройки nginx.

Пример настройки

/etc/nginx/nginx.conf
user http;
worker_processes auto;
worker_cpu_affinity auto;

events {
    multi_accept on;
    worker_connections 1024;
}

http {
    charset utf-8;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    server_tokens off;
    log_not_found off;
    types_hash_max_size 4096;
    client_max_body_size 16M;

    # MIME
    include mime.types;
    default_type application/octet-stream;

    # журналы
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log warn;

    # загрузка дополнительных файлов конфигурации
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Основные настройки

Процессы и соединения

Вы должны выбрать подходящее значение для worker_processes. Этот параметр определяет сколько одновременных соединений сможет принимать nginx и сколько процессоров он сможет при этом использовать. Как правило, это значение устанавливают равным количеству аппаратных потоков в системе. Однако, начиная с версий 1.3.8 и 1.2.5, в качестве значения worker_processes вы также можете задать auto, при этом nginx попытается автоматически подобрать оптимальное значение (источник).

Максимальное количество одновременных соединений, которое nginx сможет принимать, вычисляется как max_clients = worker_processes * worker_connections.

Запуск под другим пользователем

По умолчанию запускается мастер-процесс nginx от имени root, а он в свою очередь запускает рабочие процессы от имени пользователя http. Для запуска рабочих процессов от имени другого пользователя измените значение директивы user в файле nginx.conf:

/etc/nginx/nginx.conf
user пользователь [группа];

Если группа не указана, будет использоваться группа, совпадающая с указанным именем пользователя.

Совет: Можно запустить nginx без прав root через systemd. Смотрите #Запуск без привилегий через systemd.

Блоки server

Посредством добавления блоков server в файл настроек возможно обслуживать сразу несколько доменов одновременно. Эти блоки работают аналогично "VirtualHosts" в Apache. Смотрите также примеры в официальной документации.

В этом примере сервер принимает запросы для двух доменов: domainname1.dom и domainname2.dom:

/etc/nginx/nginx.conf
...
server {
    listen 80;
    listen [::]:80;
    server_name domainname1.dom;
    root /usr/share/nginx/domainname1.dom/html;
    location / {
        index index.php index.html index.htm;
    }
}

server {
    listen 80;
    listen [::]:80;
    server_name domainname2.dom;
    root /usr/share/nginx/domainname2.dom/html;
    ...
}

Перезапустите службу nginx.service, чтобы изменения вступили в силу.

Примечание: Убедитесь, что указанные домены существуют и указывают на IP-адрес устройства, на котором запущен nginx. Вы можете настроить DNS-сервер, например BIND или dnsmasq, или посмотрите варианты разрешения имён в локальной сети.
Управление блоками server

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

Создайте следующие каталоги:

# mkdir /etc/nginx/sites-available
# mkdir /etc/nginx/sites-enabled

Внутри каталога sites-available создайте файл, содержащий один или несколько блоков server:

/etc/nginx/sites-available/example.conf
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ...
}

В файле nginx.conf конце блока http добавьте строку include sites-enabled/*;:

/etc/nginx/nginx.conf
http {
    ...
    include sites-enabled/*;
}

Чтобы включить сайт, в каталоге sites-enabled создайте символическую ссылку на связанный с ним файл:

# ln -s /etc/nginx/sites-available/example.conf /etc/nginx/sites-enabled/example.conf

А чтобы отключить сайт, удалите её:

# unlink /etc/nginx/sites-enabled/example.conf

Перезапустите службу nginx.service или перезагрузите конфигурацию (reload), чтобы изменения вступили в силу.

TLS

openssl предоставляет поддержку TLS/SSL и установлен по умолчанию на установленных Arch.

Совет:
  • Перед тем как настраивать SSL, вы можете почитать документацию ngx_http_ssl_module
  • Let's Encrypt — это бесплатный, автоматизированный и открытый центр сертификации. Есть плагин для получения доверенных SSL-сертификатов прямо из командной строки и автоматической настройки.
  • На сайте Mozilla есть полезная статья про TLS, а также инструмент, помогающий составить безопасную конфигурацию.
Важно: Если вы планируете развернуть SSL/TLS, вы должны знать, что некоторые вариации и реализации всё ещё подвержены атакам. За дополнительной информацией о текущих подверженных версиях этих реализаций SSL/TLS и как применить нужные настройки к nginx посетите https://disablessl3.com/ и https://weakdh.org/sysadmin.html

Создайте секретный ключ и самоподписанный сертификат. Это подходит для большинства случаев, в которых не требуется CSR:

# cd /etc/nginx/
# openssl req -new -x509 -nodes -newkey rsa:4096 -keyout nginx.key -out nginx.crt -days 1095
# chmod 400 nginx.key
# chmod 444 nginx.crt
Примечание: Опция -days является необязательной, а RSA keysize можно уменьшить до 2048 (по умолчанию).

Если же вам нужно создать CSR, то следуйте данным инструкциям по созданию ключа, вместо приведённых выше:

# openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out nginx.key
# chmod 400 nginx.key
# openssl req -new -sha256 -key nginx.key -out nginx.csr
# openssl x509 -req -days 1095 -in nginx.csr -signkey nginx.key -out nginx.crt
Примечание: Подробнее о доступных опциях openssl можно узнать в man-странице openssl(1ssl) или в документации на официальном сайте.

В качестве отправкой точки для создания конфигурации TLS в /etc/nginx/nginx.conf можно использовать генератор настроек SSL от Mozilla.

Перезапустите службу nginx, чтобы изменения вступили в силу.

Пользовательские каталоги

Чтобы сделать Apache-подобные адреса вида ~пользователь, указывающие на пользовательские каталоги ~/public_html, используйте подобную конфигурацию. (Примечание: если вы планируете использовать PHP, то связанный с ним location должен стоять первым.)

/etc/nginx/nginx.conf
...
server {
    ...
    # Обработка файлов PHP в пользовательских каталогах, например http://example.com/~user/test.php
    location ~ ^/~(.+?)(/.+\.php)$ {
        alias          /home/$1/public_html$2;
        fastcgi_pass   unix:/run/php-fpm/php-fpm.sock;
        fastcgi_index  index.php;
        include        fastcgi.conf;
    }

    # Пользовательские каталоги, например http://example.com/~user/
    location ~ ^/~(.+?)(/.*)?$ {
        alias     /home/$1/public_html$2;
        index     index.html index.htm;
        autoindex on;
    }
    ...
}
...

Подробнее о настройке PHP в nginx смотрите в разделе #Реализация PHP.

Перезапустите службу nginx.service, чтобы изменения вступили в силу.

FastCGI

FastCGI или просто FCGI — это протокол, являющийся интерфейсом между веб-сервером и интерактивными программами. Это модифицированный CGI (Common Gateway Interface), главная цель которого — снизить накладные расходы, связанные со взаимодействием веб сервера и CGI программ, тем самым позволяя серверу обрабатывать большее количество запросов одновременно.

Технология FastCGI встроена в nginx для работы со многими внешними инструментами, например, Perl, PHP и Python.

Реализация PHP

В качестве FastCGI-сервера для PHP рекомендуется использовать PHP-FPM.

Установите пакет php-fpm и проверьте корректность настроек PHP. Основным конфигурационным файлом PHP-FPM является /etc/php/php-fpm.conf. Включите и запустите systemd службу php-fpm.

Примечание:
  • Если вы запускаете nginx под другим пользователем, убедитесь, что Unix-сокет PHP-FPM доступен этому пользователю, или используйте TCP-сокет.
  • Если вы запускаете nginx в изолированном окружении (к примеру, chroot находится в /srv/nginx-jail, веб-документы расположены в /srv/nginx-jail/www), то вы должны в /etc/php/php-fpm.conf добавить опции chroot /srv/nginx-jail и listen = /srv/nginx-jail/run/php-fpm/php-fpm.sock внутри секции пула (по умолчанию это [www]). Создайте каталог для файла сокета, если его нет. Более того, для модулей, которые динамически связаны с зависимостями, вам нужно будет скопировать эти зависимости в chroot (например, для php-imagick вам нужно будет скопировать в chroot библиотеки ImageMagick, но не сам imagick.so).
Настройка nginx

Внутри каждого блока server, который обслуживает веб-приложение PHP, должен находиться блок location [1], например:

/etc/nginx/sites-available/example.conf
server {
    root /usr/share/nginx/html;

    location / {
        index index.html index.htm index.php;
    }

    location ~ \.php$ {
        # 404
        try_files $fastcgi_script_name =404;

        # default fastcgi_params
        include fastcgi_params;

        # fastcgi settings
        fastcgi_pass			unix:/run/php-fpm/php-fpm.sock;
        fastcgi_index			index.php;
        fastcgi_buffers			8 16k;
        fastcgi_buffer_size		32k;

        # fastcgi params
        fastcgi_param DOCUMENT_ROOT	$realpath_root;
        fastcgi_param SCRIPT_FILENAME	$realpath_root$fastcgi_script_name;
        #fastcgi_param PHP_ADMIN_VALUE	"open_basedir=$base/:/usr/lib/php/:/tmp/";
    }
}

Если требуется обрабатывать другие расширения наряду с PHP (например .html и .htm):

location ~ [^/]\.(php|html|htm)(/|$) {
    ...
}

Все расширения, обрабатываемые в php-fpm должны быть также явно добавлены в /etc/php/php-fpm.d/www.conf:

security.limit_extensions = .php .html .htm
Примечание: Аргумент fastcgi_pass должен быть определен как TCP-сокет или сокет Unix выбранным FastCGI сервером в его конфигурационном файле. По умолчанию для php-fpm используется сокет
fastcgi_pass unix:/run/php-fpm/php-fpm.sock;

Вы можете использовать также общий TCP-сокет:

fastcgi_pass 127.0.0.1:9000;
Однако, доменные сокеты Unix должны работать быстрее.
Совет: Если несколько блоков server используют одну и ту же конфигурацию PHP-FPM, можно вынести общие настройки в отдельный файл для удобства, например php_fastcgi.conf:
/etc/nginx/php_fastcgi.conf
location ~ \.php$ {
    # 404
    try_files $fastcgi_script_name =404;

    # default fastcgi_params
    include fastcgi_params;

    # fastcgi settings
    ...
}

И затем подключать этот файл в тех блоках server, в которых нужна обработка PHP:

/etc/nginx/sites-available/example.conf
server {
    server_name example.com;
    ...

    include /etc/nginx/php_fastcgi.conf;
}
Проверка конфигурации

Перезапустите службы php-fpm и nginx после изменения настроек, чтобы изменения вступили в силу.

Чтобы проверить работу FastCGI, создайте новый файл .php внутри каталога веб-документов, содержащий:

<?php
  phpinfo();
?>

При открытии файла в браузере должна отобразиться информационная страница с текущими настройками PHP.

Реализация CGI

Эта реализация нужна для CGI-приложений.

fcgiwrap

Установите fcgiwrap. Настроить его можно путём редактирования юнита fcgiwrap.socket. Включите и запустите fcgiwrap.socket.

Несколько рабочих потоков

Если вы хотите породить несколько рабочих потоков, вам рекомендуется использовать multiwatchAUR, который умеет отслеживать упавшие подпроцессы и перезапускать их. Вам нужно использовать spawn-fcgi, чтобы создать доменный сокет Unix, так как multiwatch не может обрабатывать сокеты, созданные systemd, однако, fcgiwrap сама по себе не вызывает никаких проблем, если вызывается непосредственно из юнит-файла.

Сделайте замещение файла юнита fcgiwrap.service (и юнита fcgiwrap.socket, если он есть), и отредактируйте строку ExecStart в соответствии с вашими нуждами. В примере показан юнит файл, который использует multiwatchAUR. Убедитесь, что fcgiwrap.socket не включен и не запущен, потому что он будет конфликтовать с этим юнитом:

/etc/systemd/system/fcgiwrap.service
[Unit]
Description=Simple CGI Server
After=nss-user-lookup.target

[Service]
ExecStartPre=/bin/rm -f /run/fcgiwrap.socket
ExecStart=/usr/bin/spawn-fcgi -u http -g http -s /run/fcgiwrap.sock -n -- /usr/bin/multiwatch -f 10 -- /usr/sbin/fcgiwrap
ExecStartPost=/usr/bin/chmod 660 /run/fcgiwrap.sock
PrivateTmp=true
Restart=on-failure

[Install]
WantedBy=multi-user.target

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

Важно: Строка ExecStartPost требуется из-за странного поведения, которое я наблюдаю при использовании опции -M 660 для spawn-fcgi. Устанавливается неправильный режим. Может это баг?
Настройка nginx

В каталоге /etc/nginx скопируйте файл fastcgi_params в fcgiwrap_params. В файле fcgiwrap_params удалите строки, которые устанавливают SCRIPT_NAME и DOCUMENT_ROOT.

Внутри каждого блока server CGI-приложения должен находиться вложенный блок location:

location ~ \.cgi$ {
     include       fcgiwrap_params;
     fastcgi_param DOCUMENT_ROOT /srv/www/cgi-bin/;
     fastcgi_param SCRIPT_NAME   myscript.cgi;
     fastcgi_pass  unix:/run/fcgiwrap.sock;
}

Сокетом по умолчанию для fcgiwrap является /run/fcgiwrap.sock.

Вместо параметров DOCUMENT_ROOT и SCRIPT_NAME можно использовать более короткую альтернативу fastcgi_param SCRIPT_FILENAME /srv/www/cgi-bin/myscript.cgi. При её использовании не понадобится копировать fastcgi_params в fcgiwrap_params и удалять строки DOCUMENT_ROOT and SCRIPT_NAME.

Важно: Если используются SCRIPT_NAME и DOCUMENT_ROOT, fcgiwrap будет отклонять любые другие fastcgi_param, установленные в nginx. Вы должны использовать SCRIPT_FILENAME для того, чтобы другие параметры (например, PATH_INFO) могли быть установлены через конфигурацию Nginx. Подробнее на GitHub.

Если вы продолжаете получать ошибку 502 - bad Gateway, проверьте, передаёт ли ли ваше CGI-приложение mime-тип содержимого. Для html это должно быть Content-type: text/html.

Если вы получаете ошибки 403, убедитесь, что CGI-файл доступен для чтения и выполнения пользователю http и что каждая родительская папка доступна ему для чтения.

Установка в chroot

Установка nginx в chroot добавляет дополнительный уровень безопасности. Для максимальной безопасности chroot должен включать только файлы, необходимые для запуска сервера nginx, при этом все файлы должны иметь по возможности максимально ограниченные права доступа. Например, как можно больше файлов должно принадлежать пользователю root, а таким каталогам, как /usr/bin должен быть установлен запрет на чтение и запись.

Arch поставляется с пользователем http и группой по умолчанию, от имени которых запускается сервер. Измененный корневой каталог будет находиться в /srv/http.

Существует perl-скрипт для создания chroot-окружения, который доступен в jail.pl gist. Вы можете либо использовать его, либо следовать дальнейшим инструкциям из этой статьи. Скрипт требует прав суперпользователя для работы. Вам нужно будет раскомментировать строку, перед тем, как он сможет выполнять какие-либо изменения.

Создание необходимых устройств

Для nginx нужны /dev/null, /dev/random и /dev/urandom. Чтобы установить их в chroot мы создадим каталог /dev и добавим устройства с помощью mknod. Избегайте монтирования всех устройств в /dev: тогда, даже если chroot будет скомпрометирован, атакующий должен будет выбраться из chroot-окружения чтобы добраться до важных устройств, например /dev/sda1.

Совет: Убедитесь, что /src/http примонтирован без опции no-dev
Совет: Смотрите mknod(1) и ls -l /dev/{null,random,urandom}, чтобы лучше понять опции mknod.
# export JAIL=/srv/http
# mkdir $JAIL/dev
# mknod -m 0666 $JAIL/dev/null c 1 3
# mknod -m 0666 $JAIL/dev/random c 1 8
# mknod -m 0444 $JAIL/dev/urandom c 1 9

Создание необходимых каталогов

Для работы nginx требует определенный набор файлов. Перед тем, как их копировать, создайте для них соответствующие каталоги. Предполагается, что ваш корневой каталог веб-документов nginx находится в /srv/http/www.

# mkdir -p $JAIL/etc/nginx/logs
# mkdir -p $JAIL/usr/{lib,bin}
# mkdir -p $JAIL/usr/share/nginx
# mkdir -p $JAIL/var/{log,lib}/nginx
# mkdir -p $JAIL/www/cgi-bin
# mkdir -p $JAIL/{run,tmp}
# cd $JAIL; ln -s usr/lib lib
# cd $JAIL; ln -s usr/lib lib64
# cd $JAIL/usr; ln -s lib lib64

Затем смонтируйте $JAIL/tmp и $JAIL/run как tmpfs-ы. Размер должен быть ограничен, чтобы быть уверенным, что атакующий не сможет занять всю доступную RAM.

# mount -t tmpfs none $JAIL/run -o 'noexec,size=1M'
# mount -t tmpfs none $JAIL/tmp -o 'noexec,size=100M'

Для того, чтобы монтирование выполнялось автоматически при загрузке системы, добавьте следующие записи в /etc/fstab:

/etc/fstab
 tmpfs   /srv/http/run   tmpfs   rw,noexec,relatime,size=1024k   0       0
 tmpfs   /srv/http/tmp   tmpfs   rw,noexec,relatime,size=102400k 0       0

Заполнение chroot

Сначала скопируйте простые файлы.

# cp -r /usr/share/nginx/* $JAIL/usr/share/nginx
# cp -r /usr/share/nginx/html/* $JAIL/www
# cp /usr/bin/nginx $JAIL/usr/bin/
# cp -r /var/lib/nginx $JAIL/var/lib/nginx

Теперь скопируйте нужные библиотеки. Используйте ldd, чтобы отобразить их и скопируйте все файлы в правильное место. Копирование предпочтительнее, чем создание жестких ссылок, потому, что даже если атакующий получит права записи в файлы, они не смогут уничтожить или изменить системные файлы вне chroot-окружения.

$ ldd /usr/bin/nginx
linux-vdso.so.1 (0x00007fffc41fe000)
libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007f57ec3e8000)
libcrypt.so.1 => /usr/lib/libcrypt.so.1 (0x00007f57ec1b1000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f57ebead000)
libm.so.6 => /usr/lib/libm.so.6 (0x00007f57ebbaf000)
libpcre.so.1 => /usr/lib/libpcre.so.1 (0x00007f57eb94c000)
libssl.so.1.0.0 => /usr/lib/libssl.so.1.0.0 (0x00007f57eb6e0000)
libcrypto.so.1.0.0 => /usr/lib/libcrypto.so.1.0.0 (0x00007f57eb2d6000)
libdl.so.2 => /usr/lib/libdl.so.2 (0x00007f57eb0d2000)
libz.so.1 => /usr/lib/libz.so.1 (0x00007f57eaebc000)
libGeoIP.so.1 => /usr/lib/libGeoIP.so.1 (0x00007f57eac8d000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f57eaa77000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f57ea6ca000)
/lib64/ld-linux-x86-64.so.2 (0x00007f57ec604000)

Для файлов, находящихся в /usr/lib, вы можете воспользоваться следующей командой:

# cp $(ldd /usr/bin/nginx | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib

А для ld-linux-x86-64.so — следующей командой:

# cp /lib64/ld-linux-x86-64.so.2 $JAIL/lib
Примечание: Не пытайтесь скопировать linux-vdso.so — это не настоящая библиотека и ее не существует в /usr/lib.

Копируйте другие необходимые библиотеки и системные файлы.

# cp /usr/lib/libnss_* $JAIL/usr/lib
# cp -rfvL /etc/{services,localtime,nsswitch.conf,nscd.conf,protocols,hosts,ld.so.cache,ld.so.conf,resolv.conf,host.conf,nginx} $JAIL/etc

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

$JAIL/etc/group
http:x:33:
nobody:x:99:
$JAIL/etc/passwd
http:x:33:33:http:/:/bin/false
nobody:x:99:99:nobody:/:/bin/false
$JAIL/etc/shadow
http:x:14871::::::
nobody:x:14871::::::
$JAIL/etc/gshadow
http:::
nobody:::
# touch $JAIL/etc/shells
# touch $JAIL/run/nginx.pid

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

# chown -R root:root $JAIL/

# chown -R http:http $JAIL/www
# chown -R http:http $JAIL/etc/nginx
# chown -R http:http $JAIL/var/{log,lib}/nginx
# chown http:http $JAIL/run/nginx.pid

# find $JAIL/ -gid 0 -uid 0 -type d -print | xargs chmod -rw
# find $JAIL/ -gid 0 -uid 0 -type d -print | xargs chmod +x
# find $JAIL/etc -gid 0 -uid 0 -type f -print | xargs chmod -x
# find $JAIL/usr/bin -type f -print | xargs chmod ug+rx
# find $JAIL/ -group http -user http -print | xargs chmod o-rwx
# chmod +rw $JAIL/tmp
# chmod +rw $JAIL/run

Если ваш сервер будет принимать входящие соединения на 80 порту (или любому другому порту в диапазоне [1-1023]), дайте исполняемому файлу внутри chroot права на использование этих портов без прав суперпользователя.

# setcap 'cap_net_bind_service=+ep' $JAIL/usr/bin/nginx

Отредактируйте nginx.service для запуска chroot

Сделайте замещение файла юнита nginx.service — тогда обновление nginx не изменит ваш файл .service.

Юнит systemd должен быть настроен так, чтобы запускать nginx в chroot от имени пользователя http и хранить pid-файл в chroot.

Примечание: Я не уверен, нужно ли хранить pid-файл в chroot.
/etc/systemd/system/nginx.service
[Unit]
Description=A high performance web server and a reverse proxy server
After=syslog.target network.target

[Service]
Type=forking
PIDFile=/srv/http/run/nginx.pid
ExecStartPre=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -t -q -g 'pid /run/nginx.pid; daemon on; master_process on;'
ExecStart=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid; daemon on; master_process on;'
ExecReload=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid; daemon on; master_process on;' -s reload
ExecStop=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid;' -s quit

[Install]
WantedBy=multi-user.target
Примечание: Обновление nginx с помощью pacman не обновит установленную в chroot копию. Вы должны вручную выполнять обновления, повторяя указанные выше шаги по переносу файлов. Не забудьте также обновить библиотеки, которые использует nginx.

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

# pacman -Rsc nginx

Если вы не удалили nginx, установленный вне chroot, проверьте, что работающий процесс nginx — это действительно именно тот, что в находится chroot. Для этого посмотрите, куда указывает символическая ссылка /proc/PID/root: она должна указывать на /srv/http, а не на /.

# ps -C nginx | awk '{print $1}' | sed 1d | while read -r PID; do ls -l /proc/$PID/root; done

Советы и рекомендации

Запуск без привилегий через systemd

Создайте drop-in файл для службы nginx.service и пропишите в нём секцию [Service] с опцими User и (опционально) Group, чтобы запустить службу от имени указанного вами непривилегированного пользователя:

/etc/systemd/system/nginx.service.d/user.conf
[Service]
User=пользователь
Group=группа

Также можно запретить повышение привилегий:

/etc/systemd/system/nginx.service.d/user.conf
[Service]
...
NoNewPrivileges=yes
Совет: Подробнее о повышающих безопасность опциях можно почитать в systemd.exec(5).

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

Совет: Аналогичные настройки можно применить и для сервера FastCGI.

Порт

По умолчанию Linux запрещает не-root процессам слушать порты ниже 1024. Можно использовать порт выше 1024:

/etc/nginx/nginx.conf
server {
        listen 8080;
}
Совет: Если вы хотите, чтобы nginx всё равно был доступен на порту 80 или 443, можно настроить межсетевой экран для перенаправления запросов с порта 80 или 444 на порт, который использует nginx.

Или можно выдать процессу nginx привилегию CAP_NET_BIND_SERVICE, которая позволит ему использовать порты ниже 1024:

/etc/systemd/system/nginx.service.d/user.conf
[Service]
...
CapabilityBoundingSet=
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=
AmbientCapabilities=CAP_NET_BIND_SERVICE

Или можно использовать активацию сокетов systemd. В этом случае systemd будет прослушивать порты и, когда будет установлено соединение, запустит nginx, передав сокет в качестве дескриптора файла. Это означает, что процессу nginx не нужны особые привилегии, так как сокет уже существует на момент запуска. Этот подход опирается на использование внутренней переменной окружения, которую nginx использует для передачи сокетов [2], и поэтому официально не поддерживается. Вместо установки CapabilityBoundingSet и AmbientCapabilities переопределите переменную окружения NGINX, чтобы сообщить процессу nginx, какие файловые дескрипторы будут передаваться сокетам:

/etc/systemd/system/nginx.service.d/user.conf
[Service]
...
Environment=NGINX=3:4;

На каждый прослушиваемый порт будет приходиться один сокет, начиная с файлового дескриптора 3, поэтому в данном примере мы говорим nginx ожидать два сокета. Теперь создайте юнит nginx.socket, указав, какие порты прослушивать:

/etc/systemd/system/nginx.socket
[Socket]
ListenStream=0.0.0.0:80
ListenStream=0.0.0.0:443
After=network.target
Requires=network.target

[Install]
WantedBy=sockets.target

Сокеты будут передаваться в порядке, определённом в этом юните, поэтому порт 80 будет файловым дескриптором 3, а порт 443 — файловым дескриптором 4. Если вы ранее включали или запускали службу nginx.service, вам нужно остановить её и включить вместо неё nginx.socket. При запуске системы nginx не будет запущен, но запустится, когда вы зайдете на сайт в браузере. Благодаря этому можно дополнительно защитить службу; например, во многих случаях вы можете установить PrivateNetwork=True в файле службы, блокируя nginx от внешней сети, поскольку сокета, созданного systemd, достаточно для обслуживания веб-сайта. Обратите внимание, что при этом в журналы службы nginx будет выведено предупреждение: 2020/08/29 19:33:20 [notice] 254#254: using inherited sockets from "3:4;"

PID-файл

По умолчанию nginx использует /run/nginx.pid. Нужно будет создать каталог, в котором пользователь будет иметь право записи, и перенастроить запись PID-файла туда. Например, можно использовать systemd-tmpfiles:

/etc/tmpfiles.d/nginx.conf
d /run/nginx 0775 root группа - -

Примените изменения:

# systemd-tmpfiles --create

Отредактируйте параметры службы, связанные с PID-файлом:

/etc/systemd/system/nginx.service.d/user.conf
[Service]
...
PIDFile=/run/nginx/nginx.pid
ExecStart=
ExecStart=/usr/bin/nginx -g 'pid /run/nginx/nginx.pid; error_log stderr;'
ExecReload=
ExecReload=/usr/bin/nginx -s reload -g 'pid /run/nginx/nginx.pid;'

/var/lib/nginx/*

Некоторые каталоги в каталоге /var/lib/nginx должны быть инициализированы путём запуска nginx от имени root. Для этого не обязательно запускать весь сервер, nginx сделает это при проверке конфигурации. Так что просто запустите её — и готово.

Права каталогов и файлов журналов

После запуска проверки конфигурации будет создан журнал, принадлежащий root. Удалите журналы в /var/log/nginx.

Пользователю, от имени которого будет работать служба, nginx нужно выдать разрешение на запись в /var/log/nginx. Для этого может понадобиться изменить права и/или владельца каталога.

Альтернативный скрипт для systemd

В systemd есть встроенная возможность запуска через chroot. [3]. Для примера используем такие настройки пользователя, группы и pid:

/etc/nginx/nginx.conf
user http;
pid /run/nginx.pid;

Абсолютный путь к файлу настроек будет /srv/http/etc/nginx/nginx.conf.

/etc/systemd/system/nginx.service
[Unit]
Description=nginx (Chroot)
After=syslog.target network.target

[Service]
Type=forking
PIDFile=/srv/http/run/nginx.pid
RootDirectory=/srv/http
ExecStartPre=/usr/bin/nginx -t -c /etc/nginx/nginx.conf
ExecStart=/usr/bin/nginx -c /etc/nginx/nginx.conf
ExecReload=/usr/bin/nginx -c /etc/nginx/nginx.conf -s reload
ExecStop=/usr/bin/nginx -c /etc/nginx/nginx.conf -s stop

[Install]
WantedBy=multi-user.target

Указывать стандартный путь к файлу настроек необязательно, nginx по умолчанию использует -c /etc/nginx/nginx.conf, но, возможно, явное прописывание является хорошей идеей.

Также можно запускать только ExecStart внутри chroot с параметром RootDirectoryStartOnly заданным как yes (смотрите systemd.service(5)) или запустить его до того, как точка монтирования станет эффективной или будет доступен путь systemd (смотрите systemd.path(5)).

/etc/systemd/system/nginx.path
[Unit]
Description=nginx (Chroot) path
[Path]
PathExists=/srv/http/site/Public_html
[Install]
WantedBy=default.target

Включите созданный юнит nginx.path и в юните nginx.service измените строку WantedBy=default.target на WantedBy=nginx.path.

Параметр PIDFile в файле юнита позволяет systemd следить за процессом (требуется абсолютный путь). Если это нежелательно, вы можете изменить тип на oneshot и удалить упоминание pid-файла из файла юнита.

Nginx beautifier

nginxbeautifierAUR — это инструмент командной строки, используемый для улучшения и форматирования конфигурационных файлов nginx.

Более удобное управление заголовками

Nginx имеет довольно неинтуитивную систему управления HTTP-заголовками: они могут быть определены только в одном контексте, любые другие заголовки игнорируются. Чтобы исправить это, можно установить модуль headers-more-nginx.

Установите пакет nginx-mod-headers-more. Модуль будет установлен в каталог /usr/lib/nginx/modules.

Чтобы загрузить модуль, добавьте следующее в начало основного конфигурационного файла nginx.

/etc/nginx/nginx.conf
load_module "/usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so";
...

Решение проблем

Валидация конфигурации

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

Ошибка: Страница, которую вы ищете, временно недоступна. Пожалуйста, попробуйте позже. (502 Bad Gateway)

Это из-за того, что сервер FastCGI не запущен или используемый сокет имеет неправильные права доступа.

Попробуйте этот ответ, чтобы исправить 502 ошибку.

В Arch Linux, файлом настройки, упомянутом по ссылке выше, является /etc/php/php-fpm.conf.

Ошибка: No input file specified

1. Убедитесь, что переменная open_basedir в /etc/php/php.ini содержит путь, который соответствует аргументу root в nginx.conf (обычно /usr/share/nginx/). Если в качестве FastCGI-сервера используется PHP-FPM, можно попробовать добавить fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/"; в тот блок location, который используется для обработки php-файлов.

2. Другой причиной может быть то, что задан неправильный аргумент root в секции location ~ \.php$ в nginx.conf. Убедитесь, что root указывает на ту же директорию, что и в location / на том же сервере. Либо вы можете просто задать параметр root глобально, не переопределяя его в каких-либо location секциях.

3. Проверьте права доступа: например, пользователь/группа http, биты разрешений 755 для каталогов и 644 для файлов. Имейте в виду, что все родительские каталоги тоже должны иметь корректные права доступа. Как массово изменить права всего дерева каталогов, описано в разделе Разрешения и атрибуты файлов#Массовое изменение разрешений.

4. Возможно, у вас не установлена переменная SCRIPT_FILENAME, содержащая полный путь до ваших скриптов. Если конфигурация nginx (fastcgi_param SCRIPT_FILENAME) правильная, то эта ошибка означает, что php не смог загрузить запрашиваемый скрипт. Часто это просто оказывается ошибкой прав доступа, и вы можете запустить php-cgi с правами root:

# spawn-fcgi -a 127.0.0.1 -p 9000 -f /usr/bin/php-cgi

или вам следует создать группу и пользователя для запуска php-cgi:

# groupadd www
# useradd -g www www
# chmod +w /srv/www/nginx/html
# chown -R www:www /srv/www/nginx/html
# spawn-fcgi -a 127.0.0.1 -p 9000 -u www -g www -f /usr/bin/php-cgi

5. Если вы используете chroot, убедитесь, что опция chroot в файле /etc/php-fpm/php-fpm.d/www.conf имеет корректное значение.

Warning: Could not build optimal types_hash

Если при запуске nginx.service в журнале появляется такое сообщение:

[warn] 18872#18872: could not build optimal types_hash, you should increase either types_hash_max_size: 1024 or types_hash_bucket_size: 64; ignoring types_hash_bucket_size

то попробуйте увеличить значения этих параметров в блоке http [4] [5]:

/etc/nginx/nginx.conf
http {
    types_hash_max_size 4096;
    server_names_hash_bucket_size 128;
    ...
}

Cannot assign requested address

Полный текст ошибки в статусе юнита nginx.service:

[emerg] 460#460: bind() to A.B.C.D:443 failed (99: Cannot assign requested address)

Даже если файл юнита nginx настроен на запуск после network.target с помощью systemd, nginx может попытаться прослушивать адрес, который настроен, но ещё не добавлен ни к одному интерфейсу. Убедиться, что проблема именно в этом, можно, попытавшись запустить nginx вручную (тем самым показав, что IP-адрес настроен правильно). Настройка nginx на прослушивание всех адресов решит эту проблему. Если же в вашем случае обязательно требуется прослушивание конкретного адреса, одним из возможных решений является перенастройка systemd.

Чтобы запустить nginx только после того, как все настроенные сетевые устройства будут запущены и получат IP-адреса, добавьте network-online.target к строке After= в файле nginx.service и запустите/включите systemd-networkd-wait-online.service.

Смотрите также