nginx (Русский) - ArchWiki

Jump to content
From ArchWiki

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

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

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

## Установка

Установите один из пакетов:

- nginx-mainline - основная ветка: новые возможности, обновления и исправления ошибок.
- nginx - стабильная ветка; только исправления серьёзных ошибок.
- angieAUR - форк, предполагает подмену nginx на месте без переработки конфигурации с расширением возможностей.

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

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

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

## Запуск

Запустите/включите службу `nginx.service` или `angie.service`, если вы используете Angie.

Страница по умолчанию, доступная по адресу 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`.

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

- Использование WebDAV с nginx
- nginx configuration pitfalls
- Very good in-depth 2014 look at nginx security and Reverse Proxying
- Installing LEMP (nginx, PHP, MySQL with MariaDB engine and PhpMyAdmin) in Arch Linux
- Certbot (Русский)#Nginx

Category:

- Web server (Русский)