Перейти к содержанию

cgi: Поддержка CGI для NGINX

Установка на Debian/Ubuntu

Эти документы относятся к APT-пакету nginx-module-cgi, предоставляемому репозиторией GetPageSpeed Extras.

  1. Настройте APT-репозиторий, как описано в настройке APT-репозитория.
  2. Установите модуль:
sudo apt-get update
sudo apt-get install nginx-module-cgi
Показать дистрибутивы и архитектуры
| Дистрибутив | Версия             | Компонент   | Архитектуры   |
|-------------|--------------------|-------------|----------------|
| debian      | bookworm           | main        | amd64, arm64   |
| debian      | bookworm-mainline  | main        | amd64, arm64   |
| debian      | trixie             | main        | amd64, arm64   |
| debian      | trixie-mainline    | main        | amd64, arm64   |
| ubuntu      | focal              | main        | amd64, arm64   |
| ubuntu      | focal-mainline     | main        | amd64, arm64   |
| ubuntu      | jammy              | main        | amd64, arm64   |
| ubuntu      | jammy-mainline     | main        | amd64, arm64   |
| ubuntu      | noble              | main        | amd64, arm64   |
| ubuntu      | noble-mainline     | main        | amd64, arm64   |

Добавляет поддержку CGI для Nginx и Angie веб-сервера.

ОС Тестирование с Nginx Angie
Linux AlmaLinux 9, Debian 12 и Ubuntu 24.04/20.04 нормально нормально
Darwin MacOS 15.1 нормально нормально
BSD FreeBSD 14.2 и OpenBSD 7.6 нормально нормально
Solaris OmniOS r1510521 нормально нормально
Windows Нет плана, nginx слабо поддерживает Windows

Перед всем

CGI — это ни демон, ни ангел. Это просто инструмент. Так же, как поварской нож в руках повара или меч в руках воина, вы не будете использовать меч для готовки, и не возьмете нож повара на поле битвы. То же самое с CGI: у него есть свои подходящие сценарии, и его нельзя использовать неправильно или демонизировать.

CGI хорошо для:

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

CGI плохо для:

  • Высокой QPS
  • Высокого трафика
  • Высокой конкурентности

Я создал канал в Discord. Если вы:

  • Также фанат CGI
  • Имеете какие-либо проблемы с nginx-cgi
  • Хотите получать обновления о nginx-cgi
  • Хотите знать больше друзей

Пожалуйста, присоединяйтесь к нам: https://discord.gg/EJSfqHHmaR.

Быстрый старт (с Debian 12+, Ubuntu 24.04+)

Соберите и установите:

## клонирование исходного кода
git clone https://github.com/pjincz/nginx-cgi
cd nginx-cgi

#!/bin/bash

echo "Content-Type: text/plain"
echo

echo Hello CGI

Добавьте права на исполнение к cgi-скрипту:

chmod +x /var/www/html/cgi-bin/hello.sh

Теперь попробуйте это:

curl http://127.0.0.1/cgi-bin/hello.sh

Если вы ничего не сделали неправильно, вы получите вывод Hello CGI.

Использование

Загрузка плагина

Если этот плагин установлен в стандартный путь модуля nginx (например, /usr/lib/nginx/modules), плагин будет загружен автоматически. В противном случае вам нужно вручную загрузить плагин с помощью load_module.

Добавьте следующее заявление в верхний уровень контекста nginx для загрузки плагина:

load_module <путь-к-плагину>/ngx_http_cgi_module.so;

Включение cgi

После загрузки плагина вы можете добавить cgi on в контексты location для включения cgi. Пример:

location /cgi-bin {
    cgi on;
}

Как только cgi включен в location, все вложенные locations также будут иметь включенный cgi. Если вы хотите отключить cgi для дочернего location, просто используйте cgi off.

Когда location запрашивается, nginx-cgi найдет скрипт в корневом каталоге документа (который указывается оператором root). Например, если вы указали корневой каталог документа как /var/www/html, тогда, когда вы получите доступ к /cgi-bin/hello.sh, будет выполнен /var/www/html/cgi-bin/hello.sh.

Nginx-cgi также поддерживает alias, это как оператор root в nginx, единственное отличие заключается в том, что префикс location будет удален из uri. Например, если вы хотите, чтобы /cgi/hello.sh также ссылался на тот же скрипт, вы можете сделать это:

location /cgi {
    alias /var/www/html/cgi-bin;
    cgi on;
}

Скрипт Hello

Скрипт cgi можно писать на любом языке. Вот пример на shell. Вы можете сохранить его в /var/www/html/cgi-bin/hello.sh для тестирования (если вы не изменяли корневой каталог документа):

#!/bin/sh

echo "Status: 200 OK"
echo "Content-Type: text/plain"
echo

echo "Hello world"

Первая строка скрипта — это шебанг. Если вы явно укажете cgi_interpreter, можно удалить эту строку, в противном случае отсутствие шебанга вызовет ошибку 500. Некоторые оболочки позволяют скрипту быть исполняемым даже без шебанга, но здесь это не разрешено. Если скрипт запускается оболочкой, но возвращает ошибку 500, проверьте шебанг.

Вывод cgi-скрипта состоит из 2-х секций: секции заголовка и секции тела. Первые 2 оператора echo выводят секцию заголовка, а последний оператор echo выводит секцию тела. Оператор echo в середине выводит разделитель. Оба раздела заголовка и тела могут быть пустыми, но разделитель обязателен. Отсутствие разделителя приведет к ошибке 500.

Все строки в секции заголовка будут обработаны как обычные строки заголовка http-ответа и переданы клиентской стороне. Существует один специальный заголовок Status, который будет передан в строке состояния ответа. Если cgi_strict включен, nginx-cgi проверит все заголовки вывода cgi, и будет возвращена ошибка 500, если обнаружен недопустимый заголовок. В противном случае недопустимые заголовки также будут переданы клиентской стороне. Настоятельно рекомендуется оставлять cgi_strict включенным.

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

x разрешение

В конце концов, вам нужно добавить права на исполнение к файлу:

chmod +x /var/www/html/cgi-bin/hello.sh

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

Заголовок запроса

Заголовки запроса будут разобраны и затем переведены в переменные окружения, которые затем передаются в cgi-скрипт.

Например, вы можете найти строку запроса в переменной окружения QUERY_STRING. И получить доступ к Http-Accept с помощью HTTP_ACCEPT.

Вот пример:

#!/bin/sh
echo ""

echo "строка запроса: $QUERY_STRING"
echo "http accept: $HTTP_ACCEPT"

Для полного списка переменных окружения смотрите раздел о переменных окружения.

Тело запроса

Тело запроса будет передано через stdin. Вот пример, чтобы прочитать все тело запроса и вывести его:

#!/bin/sh
echo ""

body=$(cat)

echo "тело запроса: $body"

Потоковая передача

Nginx-cgi поддерживает потоковую передачу как для тела запроса, так и для тела ответа. Например, мы можем реализовать самый простой онлайн-калькулятор при помощи bc:

#!/bin/sh
echo ""

bc 2>&1

Затем мы можем протестировать наш калькулятор с помощью curl:

curl 127.0.0.1/cgi-bin/bc.sh --no-progress-meter -T .

Плагин nginx-cgi достаточно умен, чтобы выбрать правильный способ передачи тела запроса. Если он получит весь вывод достаточно быстро, он выведет тело сразу. Если вывод задерживается, он выведет тело по частям (HTTP 1.1) или потоково (HTTP 1.0).

Заголовки http hop-by-hop

Заголовки http hop-by-hop не разрешены в выводе cgi-скрипта. Если они появляются в ответе, клиенту будет возвращена ошибка 500.

Для получения дополнительной информации: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#hop-by-hop_headers

Хитрости и часто задаваемые вопросы

Я хочу перечислить все переменные окружения

Поместите следующий скрипт в свой каталог cgi, и curl его из вашего терминала:

#!/bin/sh

echo 'Content-Type: text/plain'
echo

printenv

Я хочу права root

Поместите файл sudo в /etc/sudoers.d и выполните sudo в своем скрипте или задайте cgi_interpreter как /usr/bin/sudo.

Вот пример конфигурационного файла sudo:

## разрешить www-data запускать /var/www/bin/my-danger-script с аккаунтом root
www-data ALL=(root) NOPASSWD: /var/www/bin/my-danger-script

## разрешить всем CGI-скриптам запускаться с помощью sudo непосредственно через nginx-cgi
www-data ALL=(root) NOPASSWD: SETENV: /var/www/html/cgi-bin/*

Как я могу запустить CGI-скрипты с chroot

Запуск CGI-скрипта с chroot крайне не рекомендуется. Потому что chroot не предназначен для обеспечения безопасности. Он все равно делит много пространств ядра с хост-системой. Например, запустив ps -ef в процессе chroot, все процессы в хост-системе будут возвращены. Это не должно быть слишком ужасно? Нет, это действительно ужасно, потому что вы также можете делать kill в скрипте chroot по той же причине. И люди обычно запускают программы с правами root в окружении chroot. Это крайне плохо. Это ставит систему под высокий риск по сравнению с запуском скрипта с www-data.

Если вам нужна песочница, lxc, docker и jails гораздо лучше для этой цели.

Если вы все еще хотите chroot, хорошо, позвольте мне показать вам, как это сделать.

В этом примере я предполагаю, что вы используете /var/www/html как корневой каталог документа.

Сначала подготовьте CGI-скрипт:

mkdir -p /var/www/html/cgi-bin
cat > /var/www/html/cgi-bin/ls.sh <<EOF
#!/bin/sh
echo "Status: 200"
echo "Content-Type: text-plain"
echo
echo "файлы в /:"
ls /
EOF
chmod +x /var/www/html/cgi-bin/ls.sh

## попробуйте это
/var/www/html/cgi-bin/ls.sh

Шаг 1: подготовьте каталог chroot.

Существует много способов сделать этот шаг. debootstrap — популярный способ на системах, основанных на Debian. busybox — самый легкий способ. docker — современный способ.

Давайте создадим самый легкий каталог с помощью busybox здесь:

## В этом примере я помещаю все в /var/www/chroot
## Будьте осторожны, я загружаю версию busybox для x86_64 здесь, вы можете поменять ее
## Вам нужны права root для выполнения всех следующих команд, мне слишком лень
## добавлять sudo к каждой команде здесь.

root_dir=/var/www/chroot

mkdir -p "$root_dir/bin" && cd "$root_dir/bin"
wget https://www.busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox
chmod +x busybox

cd "$root_dir"
mkdir -p $(dirname $(./bin/busybox --list-full) | sort -u)
./bin/busybox --list-full | while read line; do ln -sf /bin/busybox $line; done

## попробуйте это
chroot "$root_dir" ls

Шаг 2: смонтировать корневой каталог в каталог chroot

mkdir -p /var/www/chroot/var/www/html
mount --bind /var/www/html /var/www/chroot/var/www/html

## попробуйте это
ls /var/www/chroot/var/www/html

Обратите внимание:

  • Я использую здесь трюк, после chroot корневой каталог документа остается тем же. Таким образом, мы можем сэкономить время на сопоставлении путей.

  • Монтирование не будет сохраняться после перезагрузки. Возможно, вам придется добавить запись в /etc/fstab. Или переместить /var/www/html в chroot и создать символьную ссылку снаружи.

Шаг 3: разрешить www-data запускать chroot с правами root.

cat >/etc/sudoers.d/www-run-with-chroot <<EOF
## разрешить и только разрешить www-data запускать chroot с /var/www/chroot
www-data ALL=(root) NOPASSWD: /usr/sbin/chroot /var/www/chroot *
EOF

Теперь все готово, добавьте следующий раздел в ваш nginx/angie:

location /cgi-bin {
    cgi on;
    cgi_interpreter /usr/bin/sudo /usr/sbin/chroot /var/www/chroot;
}

попробуйте это:

curl 127.0.0.1/cgi-bin/ls.sh

Как я могу запустить CGI-скрипты с помощью docker

В этом примере я предполагаю, что вы используете /var/www/html как корневой каталог документа.

Сначала подготовьте CGI-скрипт:

mkdir -p /var/www/html/cgi-bin
cat > /var/www/html/cgi-bin/ls.sh <<EOF
#!/bin/sh
echo "Status: 200"
echo "Content-Type: text-plain"
echo
echo "файлы в /:"
ls /
EOF
chmod +x /var/www/html/cgi-bin/ls.sh

## попробуйте это
/var/www/html/cgi-bin/ls.sh

Создайте контейнер и запустите его в фоновом режиме:

## Измените -v, если необходимо
## -d: работает в фоновом режиме
## -i -t: сохранить терминал
## --restart always: поддерживать контейнер в активном состоянии
docker run -dit --restart always --name my_cgi_docker -v /var/www:/var/www busybox sh

## попробуйте это
docker exec my_cgi_docker /var/www/html/cgi-bin/ls.sh

Разрешите www-data запускать команды docker:

sudo usermod -aG docker www-data

## попробуйте это
sudo -u www-data docker exec my_cgi_docker /var/www/html/cgi-bin/ls.sh

Теперь все готово, добавьте следующий раздел в ваш nginx/angie:

location /cgi-bin {
    cgi on;
    cgi_interpreter /usr/bin/docker exec my_cgi_docker;
}

Как я могу запустить CGI-скрипты с помощью jails

Окей, вы фанат FreeBSD? Я тоже.

Это действительно похоже на запуск скриптов с помощью chroot.

Здесь я предполагаю, что вы также используете /var/www/html как корневой каталог документа.

Сначала подготовьте CGI-скрипт:

mkdir -p /var/www/html/cgi-bin
cat > /var/www/html/cgi-bin/ls.sh <<EOF
#!/bin/sh
echo "Status: 200"
echo "Content-Type: text-plain"
echo
echo "файлы в /:"
ls /
EOF
chmod +x /var/www/html/cgi-bin/ls.sh

## попробуйте это
/var/www/html/cgi-bin/ls.sh

Шаг 1: создайте тюрьму

Давайте положим тюрьму в /var/www/jail.

mkdir -p /var/www/jail && cd /var/www/jail
fetch https://download.freebsd.org/ftp/releases/$(uname -m)/$(uname -m)/$(uname -r)/base.txz
tar -xvf base.txz -C .

## создайте точки монтирования
mkdir -p /var/www/jail/var/www/html
touch /var/www/jail/etc/resolv.conf

Поместите следующий конфиг в /etc/jail.conf:

www-jail {
    path = "/var/www/jail";
    host.hostname = "www-jail.local";

    exec.clean;
    exec.start = "/bin/sh /etc/rc";
    exec.stop = "/bin/sh /etc/rc.shutdown";

    # монтирование /var/www/html => /var/www/jail/var/www/html
    exec.prestart += "mount_nullfs /var/www/html /var/www/jail/var/www/html || true";
    mount.devfs;

    # раскомментируйте следующие строки, если хотите разрешить сетевой доступ в jail
    # ip4 = inherit;
    # ip6 = inherit;
    # exec.prestart += "mount_nullfs /etc/resolv.conf /var/www/jail/etc/resolv.conf || true";

    # раскомментируйте следующие строки, если вы также хотите, чтобы в jail был доступен `ping`
    # allow.raw_sockets = 1;

    persist; # сохранить jail, если ни один процесс не работает
}

И убедитесь, что следующая строка появилась в /etc/rc.conf:

jail_enable="YES"

И запустите тюрьму:

service jail start www-jail

## попробуйте это
jexec www-jail ls /
jexec www-jail /var/www/html/cgi-bin/ls.sh

Шаг 2: разрешите www запускать jexec с правами root.

Я использую sudo здесь. Я не знаком с doas, если вы предпочитаете doas, вы можете попробовать это сами. В любом случае, ни sudo, ни doas не предустановлены в FreeBSD. Вам нужно вручную установить один из них.

cat >/usr/local/etc/sudoers.d/www-jexec <<EOF
## разрешить и только разрешить `www` запускать `jexec` с `www-jail`
www ALL=(root) NOPASSWD: /usr/sbin/jexec www-jail *
EOF

## попробуйте это
sudo -u www sudo jexec www-jail /var/www/html/cgi-bin/ls.sh

Теперь все готово, добавьте следующий раздел в ваш nginx/angie:

location /cgi-bin {
    cgi on;
    cgi_interpreter /usr/local/bin/sudo /usr/sbin/jexec www-jail;
}

попробуйте это:

curl 127.0.0.1/cgi-bin/ls.sh

Я хочу создать долгосрочный фоновый процесс

Просто убедитесь, что не унаследовали stdout при создании процесса (в идеале, избегайте наследования stdin и stderr). Вот пример, написанный на shell.

taskid=1234
logfile="/var/lib/my-project/$taskid"
./long-run-task.sh "$taskid" </dev/null >"$logfile" 2>&1 &

Или, если вам знакома операция с пайпами, просто закройте stdout (также лучше закрыть stdin и stderr), http-запрос будет завершен мгновенно. И вы можете использовать процесс как фоновый процесс.

exec </dev/null >somewhere 2>&1

## теперь http-ответ готов, делайте что хотите
sleep 9999

Мой http-запрос завис

Как вы видите выше. В мире CGI жизненный цикл http-запроса зависит от жизненного цикла пайпа (stdout).

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

Это может вызывать путаницу, когда вы хотите длительный фон или убийство CGI процесса.

Для создания длительного процесса смотрите вышеуказанную тему.

Для убийства CGI процесса убивайте всю группу процессов, а не сам CGI процесс.

cgi_pid=...

## не делайте это
## kill "$cgi_pid"

## сделайте это
kill -- "-$cgi_pid"

Я хочу убить мой cgi-скрипт

Смотрите вышеуказанную тему.

Я хочу генерировать контент динамически

Традиционно люди используют перезапись для достижения этого. Но здесь это гораздо проще. Вы можете сделать это с помощью cgi pass. Вот пример для динамической отрисовки markdown:

{
    location ~ ^.*\.md$ {
        cgi_pass /var/www/bin/cgi/render-markdown.sh;
    }
}
#!/bin/sh

set -e

if [ ! -f "${DOCUMENT_ROOT}${PATH_INFO}" ]; then
    echo "Status: 404"
    echo
    exit
fi

echo "Status: 200"
echo "Content-Type: text/html"
echo

echo "<html><body>"
markdown "${DOCUMENT_ROOT}${PATH_INFO}"
echo "</body></html>"

Мне не нравятся суффиксы в url

Способ 1: Удалить суффикс CGI-скрипта

Способ 2: использовать перезапись

Способ 3: cgi pass

Как я могу ответить статусом, отличным от 200

#!/bin/sh

echo "Status: 404"
echo "Content-Type: text/plain"
echo

echo "Добро пожаловать в пустоту"

Как я могу ответить перенаправлением

#!/bin/sh

echo "Status: 302"
echo "Location: https://theuselessweb.com"
echo

Как я могу получить тело http-запроса

Вы можете прочитать тело запроса из stdin. Если вы используете shell, cat может быстро сохранить тело запроса в файл.

Как я могу отправить файл клиенту

Для маленьких файлов вы можете напрямую записывать файл в stdout.

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

Я хочу написать CGI на python, ruby, perl, C, C++...

Делайте это. Nginx-cgi не заботится о том, на каком языке вы пишете. Просто получайте информацию из переменных окружения, читайте тело запроса из stdin и записывайте вывод в stdout.

Руководство

Опции

cgi <on|off> или cgi pass <script_path> [script_args...]

Включить или отключить модуль cgi в заданном блоке location.

Если вы укажете on, плагин будет работать в традиционном режиме. Он сначала разбирает uri запроса, а затем находит скрипт в каталоге корневого документа с uri запроса. В конце концов он разделяет uri запроса на SCRIPT_NAME и PATH_INFO. Это хорошо, если у вас есть старый CGI проект или вы хотите строго следовать rfc3875.

Я также предоставил синтаксис в стиле nginx здесь. Если вы укажете cgi pass, плагин пропустит этап поиска CGI скрипта. Он использует переданное вами значение напрямую. Вы можете ссылаться на переменные nginx во втором аргументе, например: cgi pass $document_root$uri. Указанный выше пример делает что-то похожее на rfc3875, но не равное. В этой форме uri запроса будет назначена переменной PATH_INFO непосредственно. А SCRIPT_NAME будет пустым. Эта форма действительно хороша для динамической генерации контента. Это избавляет от сложного и ненужного переписывания uri.

Кроме того, вторая форма также предоставляет вам возможность передавать дополнительные аргументы скрипту, например: cgi pass my_script.sh $uri. С этим вы можете полностью избежать запутанных переменных окружения rfc3875.

Если вы укажете off, плагин будет отключен.

По умолчанию: off

cgi_pass <script_path>

Псевдоним для cgi pass <script_path>.

cgi_interpreter [интерпретатор] [аргументы...]

Установить интерпретатор и аргументы интерпретатора для cgi-скрипта.

Когда этот параметр не пуст, cgi-скрипт будет запущен с указанным интерпретатором. В противном случае скрипт будет выполняться напрямую.

Этот параметр может содержать переменные nginx, смотрите https://nginx.org/en/docs/varindex.html для получения дополнительной информации.

Этот параметр особенно полезен во многих сценариях, например:

  • запуск скриптов CGI без прав x
  • выполнение sudo перед выполнением CGI-скрипта
  • обертывание общего бинарного файла в качестве CGI-скрипта
  • фильтрация вывода CGI-скрипта
  • ...

По умолчанию: пусто

cgi_working_dir <dir>

Установить рабочий каталог для CGI-скрипта.

Если это значение установлено в пустое, CGI-скрипты унаследуют рабочий каталог nginx.

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

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

Этот параметр не изменяет способ нахождения интерпретатора или скрипта (если они указаны с соответствующим путем, они всегда относятся к рабочему каталогу nginx).

Этот параметр может содержать переменные nginx. Хотя я не знаю, какая в этом польза. Возможно, вы можете настроить разные рабочие каталоги для разных server_name с помощью этого.

По умолчанию: пусто

cgi_body_only <on|off>

Стандартный CGI-скрипт должен выводить две части: заголовок и тело. И пустая строка, чтобы разделить эти две части.

Если вы хотите просто запустить обычную программу как CGI-программу. Вы можете включить это.

Как только эта опция включена, весь вывод будет рассматриваться как тело ответа и отправлен клиенту.

По умолчанию: off

cgi_path <PATH>

Изменить cgi переменную окружения PATH.

По умолчанию: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

cgi_strict <on|off>

Включить или отключить строгий режим.

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

По умолчанию: on

cgi_set_var <имя> <значение>

Добавить и передать дополнительные переменные окружения в CGI-скрипт. Первый аргумент этой команды — это имя переменной окружения. Оно должно содержать только буквы, цифры и символы подчеркивания и не должно начинаться с цифры. Второй аргумент этой команды — это значение переменной. Он может содержать переменные nginx, смотрите https://nginx.org/en/docs/varindex.html для получения дополнительной информации.

Эта опция может появляться более одного раза для установки нескольких переменных. Если более одной опции устанавливает одну и ту же переменную, будет работать последняя. Эти директивы унаследованы от предыдущего уровня конфигурации, если и только если не определены директивы cgi_set_var на текущем уровне.

Эта опция также может использоваться для переопределения стандартных переменных CGI. Это может быть полезно в некоторых случаях, например, для изменения старого CGI-скрипта или симулирования стандартных переменных, которые в настоящее время не поддерживаются этим плагином (таких как PATH_TRANSLATED, REMOTE_IDENT). Но это не рекомендуется, это может вызвать путаницу в вашей системе.

cgi_stderr <path>

Перенаправить stderr cgi в указанный файл.

По умолчанию, nginx-cgi захватывает вывод stderr CGI-скрипта и выводит его в журнал nginx. Но это действие довольно дорого, потому что оно требует создания дополнительного подключения для прослушивания вывода stderr. Если вы хотите избежать этого, вы можете использовать эту опцию для перенаправления вывода stderr CGI-скрипта в файл. Или вы можете даже отказаться от всего вывода stderr, перенаправив его на /dev/null. Также вы можете использовать это для перенаправления всего вывода stderr в stderr nginx, установив его как /dev/stderr.

cgi_rdns <on|off|double> [required]

Включить или выключить обратное dns.

off: отключить функцию rdns.

on: Выполнить обратное dns перед запуском CGI-скрипта и передать результат rdns в CGI-скрипт через переменную окружения REMOTE_HOST.

double: После обратного dns снова выполнить прямое dns, чтобы проверить результат rdns. Если результат совпадает, передать результат как REMOTE_HOST.

required: Если rdns не сработает, клиенту будет возвращен ответ 403, 503 или 500. Зависит от причины сбоя rdns.

Если вы включите эту опцию, вам также нужно настроить resolver в nginx. В противном случае вы получите ошибку no resolver defined to resolve.

замечание автора: не включайте эту опцию, она замедлит каждый запрос. Эта функция может быть легко реализована с помощью dig -x или nslookup в скрипте. Единственная причина, по которой я реализовал это, — сделать модуль полностью совместимым со стандартом rfc3875.

cgi_timeout <t1> [t2]

Отправляет сигналы TERM/KILL процессу CGI, если он работает слишком долго.

Если и t1, и t2 равны 0. Функция тайм-аута отключена.

Если t1 или t2 не равны 0. Сигнал TERM или KILL будет отправлен процессу после тайм-аута.

Если оба t1 и t2 не равны нулю. Сначала на временной метке t1 будет отправлен TERM. А затем снова будет отправлен KILL на временной метке t1+t2 (если процесс все еще жив на этот момент).

Если t2 не указан, он считается равным 0.

По умолчанию: 0 0

Стандартные переменные окружения

Nginx-cgi реализует почти все стандартные переменные rfc3875. Если они не покрывают все ваши потребности, вы можете добавить свою переменную с помощью cgi_set_var. Также эти переменные можно переопределить с помощью cgi_set_var, если вы действительно хотите.

  • AUTH_TYPE, REMOTE_USER (стандарт rfc3875)

Если cgi-скрипт находится за модулем авторизации (например, ngx_http_auth_basic_module), и авторизация успешна, значение устанавливается в тип авторизации (например, Basic) и авторизованного пользователя.

Если модуль авторизации не включен, не имеет значения, передает ли клиент заголовок авторизации или нет. Эти 2 поля отсутствуют.

Заголовок Authorization не виден в cgi-скрипте по соображениям безопасности. Если вы действительно хотите выполнить авторизацию в скрипте CGI, попробуйте cgi_set_var.

  • CONTENT_LENGTH, CONTENT_TYPE (стандарт rfc3875)

Точно так же, как заголовки запроса Content-Length и Content-Type.

  • GATEWAY_INTERFACE (стандарт rfc3875)

Всегда будет CGI/1.1 в этом плагине.

  • PATH_INFO (стандарт rfc3875)

Предположим, у вас есть скрипт под /cgi-bin/hello.sh, и вы получаете доступ к http://127.0.0.1/cgi-bin/hello.sh/somewhat.

Тогда PATH_INFO будет содержать строку /somewhat.

В комбинации с rewrite или cgi pass, эта переменная может использоваться для динамической генерации контента.

  • PATH_TRANSLATED (стандарт rfc3875)

Примечание: эта опция не реализована строго в соответствии со стандартом rfc3875. Пожалуйста, избегайте этого, если вы пишете новый CGI-скрипт.

Это связано с PATH_INFO.

Предположим, у вас есть скрипт под /cgi-bin/hello.sh, и вы получаете доступ к http://127.0.0.1/cgi-bin/hello.sh/somewhat.

Стандарт говорит, что сервер должен попробовать снова с http://127.0.0.1/somewhat, и выяснить, куда следует сопоставить uri.

По техническим причинам я просто конструирую эту переменную из корневого каталога документа и переменной PATH_INFO.

Поведение может измениться в будущих версиях.

  • QUERY_STRING (стандарт rfc3875)

Содержит строку запроса запроса. Например, если вы получаете доступ к http://127.0.0.1/cgi-bin/hello.sh?a=1&b=2, QUERY_STRING будет содержать a=1&b=2.

  • REMOTE_ADDR, (стандарт rfc3875)

IP-адрес клиента.

  • REMOTE_HOST (стандарт rfc3875)

Имя хоста клиента. Доступно только если включен cgi_rdns.

Если cgi_rdns включен, nginx-cgi выполнит обратное DNS и найдет домен, соответствующий REMOTE_ADDR. Если найдено любое, оно будет установлено в REMOTE_HOST.

Если cgi_rdns двойной, после RDNS nginx-cgi снова выполнит прямое DNS. REMOTE_HOST будет установлен только в том случае, если результат прямого DNS совпадает с исходным адресом.

Смотрите cgi_rdns для получения дополнительной информации.

  • REMOTE_IDENT (стандарт rfc3875)

Плагин nginx-cgi не поддерживает это по соображениям безопасности.

  • REQUEST_METHOD (стандарт rfc3875)

Метод запроса, например: GET, POST...

  • SCRIPT_NAME (стандарт rfc3875)

Путь к текущему скрипту. Обычно вам это не нужно. Это не содержит полного пути. Смотрите SCRIPT_FILENAME.

Единственная причина использовать это — это конструирование URI после переписывания. Вы можете использовать SCRIPT_NAME + PATH_INFO, чтобы получить URI после переписывания.

  • SERVER_NAME (стандарт rfc3875)

Имя сервера, обычно оно равно заголовку Host без части порта. Если заголовок Host не появляется в запросе (HTTP/1.0) или содержит недопустимое значение, то это значение устанавливается в отраженный IP-адрес сервера. Если IP-адрес является адресом ipv6, он будет заключен в скобки, например, [::1].

  • SERVER_PORT (стандарт rfc3875)

Порт, на котором слушает сервер, например, 80, 443...

  • SERVER_PROTOCOL (стандарт rfc3875)

Протокол, используемый между клиентом и сервером. Например, HTTP/1.0, HTTP/1.1...

  • SERVER_SOFTWARE (стандарт rfc3875)

Содержит строку nginx и версию, например, nginx/1.27.4.

  • X_ (стандарт rfc3875)

Все заголовки http-запросов, начинающиеся с префикса X-, будут преобразованы в переменные X_. Например:

Если в заголовке появляется X-a: 123, X_A будет установлено в 123.

  • HTTP_ (стандарт rfc3875)

Все остальные заголовки http-запросов будут преобразованы в переменные HTTP_, например:

Если в заголовке появляется Accept: */*, HTTP_ACCEPT будет установлено в */*.

  • DOCUMENT_ROOT (не стандартное, реализовано apache2)

Корневой каталог текущего блока location, смотрите оператор root в nginx.

  • REMOTE_PORT (не стандартное, реализовано apache2)

Номер порта клиента.

  • REQUEST_SCHEME (не стандартное, реализовано apache2)

http или https.

  • REQUEST_URI (не стандартное, реализовано apache2)

Сырой uri до переписывания. Если вы хотите URL после переписывания, попробуйте SCRIPT_NAME + PATH_INFO.

Примечание: эта переменная не совпадает с переменной nginx $request_uri. Вы можете найти документ по адресу https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html.

  • SCRIPT_FILENAME (не стандартное, реализовано apache2)

Полный путь к CGI-скрипту.

  • SERVER_ADDR (не стандартное, реализовано apache2)

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

Известные проблемы

Неправильная реализация PATH_TRANSLATED

Согласно rfc3875, PATH_TRANSLATED должен указывать на файл, на который будет доступен $PATH_INFO как uri. Но выполнить это в nginx очень сложно, это требует повторного запуска процесса поиска nginx на уровне location. И эти функции являются частными и не могут быть доступны непосредственно плагином. Другой способ реализовать это — это запуск подзапроса, но это слишком затратно, и эта переменная действительно редко используется. Это не стоит делать. Поэтому я просто конструирую эту переменную из корневого каталога документа и переменных path_info.

Реализация RDNS не обращается к /etc/hosts

Реализация resolver nginx не обращается к /etc/hosts. Я не хочу реализовать дополнительный resolver в плагине. Поэтому я просто проигнорировал эту проблему.

Ссылки

rfc3875

https://datatracker.ietf.org/doc/html/rfc3875

nginx

https://nginx.org/en/docs/dev/development_guide.html https://hg.nginx.org/nginx-tests

Заголовки hop-by-hop

https://datatracker.ietf.org/doc/html/rfc2616#section-13.5.1

CGI окружения

https://datatracker.ietf.org/doc/html/rfc3875#section-4.1

Apache CGI

https://httpd.apache.org/docs/2.4/howto/cgi.html

Lighttpd CGI

https://redmine.lighttpd.net/projects/lighttpd/wiki/Mod_cgi