Вопрос или проблема
На прошлой неделе я обновил свой файловый сервер на 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-сервиса.
Рекомендуется внимательно проверить указанные аспекты и применять предложенные решения для устранения проблемы. Если после выполнения всех указанных проверок проблема не решается, вам может понадобиться подробное логирование и дополнительное исследование конфигураций системы.