Почему программа Perl CGI не записывает в файлы на Fedora 40 с Apache?

Вопрос или проблема

На прошлой неделе я обновил свой файловый сервер на Linux с Fedora 39 до Fedora 40, и несколько CGI-приложений, написанных на Perl, перестали работать. Я впервые заметил это, когда Foswiki не смог показать никакие страницы, потому что не смог открыть свой файл журнала.

После неудачных попыток выяснить, что обновление системы привело к какой-то несовместимости между (обновленными) библиотеками perl и (старым) приложением Foswiki, я обнаружил, что приложение, которое я написал сам, имело ту же проблему.

Я теперь свел это к очень маленькой программе, ядро которой состоит всего из нескольких строк:

my $file_to_write = "/tmp/writetest.txt";    
unless (open(OUTFILE, ">>", $file_to_write)) {
    print "Не удалось открыть (для добавления) $file_to_write.<BR>\n";
}
printf "%s %s Запись в тест в $file_to_write\n", ljpDate(), ljpTime();
printf OUTFILE "%s %s Запись в тест\n", ljpDate(), ljpTime();
close OUTFILE;
print "Запись завершена<BR>\n";

Похоже, что открытие проходит успешно (я не получаю сообщение “Не удалось…”), но ничего не записывается в файл, хотя у него режим 666 (-rw-rw-rw-) и он принадлежит apache:apache. Если файл существует, он остается нетронутым, а если он не существует, он не создается.

Если я запускаю скрипт из командной строки (./writetest.cgi), все работает как ожидалось.

Это работало на прошлой неделе до обновления. Есть ли какая-то новая функция песочницы, которая убивает мои приложения?

Это похоже на головную боль с SE Linux.

Проверьте, включен ли SE Linux, выполнив это:

sestatus

Вывод должен выглядеть примерно так:

Статус SELinux:                 включен
Монтирование SELinuxfs:         /sys/fs/selinux
Корневая директория SELinux:     /etc/selinux
Загруженное имя политики:         targeted
Текущий режим:                   enforcing
Режим из конфигурационного файла: enforcing
Статус политики MLS:             включен
Статус политики deny_unknown:    разрешен
Проверка защиты памяти:          фактическая (защищенная)
Максимальная версия политики ядра: 33

Если да, вы можете включить доступ CGI для HTTPD так:

sudo setsebool -P httpd_enable_cgi on;

Затем измените файловую систему для SE Linux так:

sudo semanage fcontext -a -t httpd_sys_script_exec_t /var/www/cgi-bin/script.cgi
sudo restorecon -vR /var/www/cgi-bin/script.cgi

И все должно работать нормально.


Заметки: Просто имейте в виду, что вам может потребоваться установить policycoreutils-python, чтобы выполнить эти команды. Я делаю это, чтобы установить этот пакет на Red Hat 7:

sudo yum install policycoreutils-python

В противном случае вы можете отключить SE Linux так, чтобы запустить тесты на скрипте:

sudo setenforce permissive

Чтобы навсегда отключить SE Linux, отредактируйте конфигурацию SE Linux (/etc/selinux/config) и измените значение:

SELINUX=enforcing

На это:

SELINUX=permissive

И перезагрузите машину, чтобы это значение вступило в силу.

Программы могут иметь разный обзор файловой системы, настроенной для них менеджером служб (или, редко, PAM для входа пользователей).

Например, распространено настраивать .service Apache2 в systemd так, чтобы у него был изолированный /tmp, который виден только процессам внутри .service – вы увидите PrivateTmp= включенным в systemctl cat apache2, и если вы выполните findmnt в вашем CGI-скрипте, он выдаст другой вывод, чем findmnt из интерактивной сессии.

Когда PrivateTmp включен, /tmp службы фактически сопоставляется с /tmp/systemd-private-foo/ на хосте (используя привязочное монтирование, которое настраивает systemd), поэтому создание файла успешно¹ – в отличие от SELinux, который вернул бы ошибку доступа (и записал бы это в dmesg) – но файл находится не там, где вы думаете.

¹ То есть это мне кажется совершенно другой ситуацией, чем ваша первоначальная проблема “Foswiki не может открыть файл журнала”. Если функция open не удалась, то это, скорее всего, была какая-то проблема с разрешениями или SELinux. (Конечно, первый шаг должен заключаться в проверке, какой код ошибки она вернула, а не просто “не удалось” – в идеале wiki должен записывать сбой в журнал systemd с использованием функции syslog(), которая всегда доступна службам и даже CGI-скриптам.)

use Sys::Syslog;
if (open(my $fh, ">>", "/path/to/log")) {
    ...
} else {
    syslog("err", "Не удалось открыть /path/to/log: %m");
    die();
}

Часто сетевые службы настраиваются с множеством параметров пространственного именования файловой системы, включая установку каталога /home в режим только для чтения (или невидимый), чтобы они не выходили туда, куда им обычно не следует.

Ответ или решение

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

1. Проблемы с SELinux

Одной из наиболее вероятных причин является политика безопасности SELinux (Security-Enhanced Linux), которая может блокировать доступ к файловой системе для CGI-скриптов, выполняемых под управлением веб-сервера Apache.

Проверка статуса SELinux:
Для начала убедитесь, что SELinux действительно активен на вашем сервере. Вы можете сделать это, выполнив команду:

sestatus

Если статус SELinux показывает, что он включен и работает в режиме "enforcing", это может ограничивать доступ ваших скриптов к файловой системе.

Настройка SELinux для CGI:
Если SELinux активен, вам необходимо разрешить доступ для выполнения CGI-скриптов. Используйте следующие команды, чтобы включить поддержку CGI в Apache:

sudo setsebool -P httpd_enable_cgi on

Затем вы можете изменить контекст файловой системы для вашего CGI-скрипта:

sudo semanage fcontext -a -t httpd_sys_script_exec_t '/var/www/cgi-bin/script.cgi'
sudo restorecon -vR /var/www/cgi-bin/script.cgi

2. Разрешения на файлы и каталоги

Хотя вы упомянули, что файл имеет права доступа 666 и принадлежит пользователю apache:apache, важно также проверить, имеет ли веб-сервер соответствующие разрешения на запись в каталог /tmp.

Проверьте, что сам каталог /tmp имеет правильные разрешения. Обычно разрешения для /tmp устанавливаются на 1777:

ls -ld /tmp

Если права доступа неправильные, исправьте их с помощью команды:

sudo chmod 1777 /tmp

3. Изоляция ресурсов с помощью systemd

Существует возможность, что зависимости от конфигурации Apache могут ограничивать доступ к файловой системе. Проверьте, использует ли ваш Apache опцию PrivateTmp, которая изолирует временные файлы. Это можно сделать с помощью команды:

systemctl cat httpd

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

4. Логирование ошибок

Очень важно правильно обработать ошибки в вашем скрипте, чтобы понять, что конкретно происходит. Вы можете использовать модуль Sys::Syslog, чтобы записывать ошибки в системный журнал:

use Sys::Syslog;
if (open(my $fh, ">>", "/path/to/log")) {
    # Запись в файл
} else {
    syslog("err", "Не удалось открыть /path/to/log: %m");
    die();
}

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

Заключение

На основании вышеизложенного, причина, по которой ваш Perl CGI-программа не может записывать файлы на Fedora 40 с Apache, скорее всего связана с SELinux, разрешениями на файлы и каталоги или настройками изоляции ресурсов web-сервиса.

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

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

Капча загружается...