Я не могу писать файлы в моем каталоге веб-сервера apache. Я использую CentOS 7. Я пытаюсь выполнить команды git из файла PHP, используя exec:
exec("/usr/local/bin/git -C ../myRepo fetch 2>&1", $output, $exec_return_value);
Распечатывая вывод, я вижу, что ошибка проявляется как:
error: cannot open .git/FETCH_HEAD: Permission denied
Я обнаружил (с помощью этого блога: http://jondavidjohn.com/git-pull-from-a-php-script-not-so-simple/) что это «ожидаемо», потому что Apache будет выполняться с использованием другого пользователя, который я использую через ssh. Поэтому я должен убедиться, что мои разрешения позволяют этому пользователю иметь доступ для записи.
Я определил, что моя установка Apache выполняется как пользователь с именем «apache», напечатав вывод этого в моем php-файле:
exec("whoami", $debugOutput, $debugRetVal);
Я также настроил SSH, как рекомендовано в той же ссылке. Но, к сожалению, я все еще получаю ту же ошибку в файле PHP:
error: cannot open .git/FETCH_HEAD: Permission denied
Что меня действительно поразило, так это то, что у меня нет проблем с разрешениями, когда я запускаю apache из моей сессии ssh с помощью:
sudo -u apache /usr/local/bin/git -C ../myRepo fetch
Пользователь «apache», по-видимому, имеет различные разрешения при выполнении из PHP. Может кто-нибудь помочь мне понять это?
Поведение, которое я наблюдал, когда пользователь «apache» имел разные разрешения при выполнении из PHP по сравнению с выполнением из моего SSH, было результатом SELinux. Когда SELinux находится в своем «принудительном» режиме (который по умолчанию в CentOS 7), он по существу «накладывает» схему защиты доступа поверх стандартных пользовательских разрешений linux. В упрощенном смысле пользователь «apache» действительно имел разные разрешения, когда он выполнялся вне процесса httpd.
Чтобы получить более подробную информацию, каталог html, который apache «обслуживает», имеет тип контекста SELinux «httpd_sys_content_t». Мой репозиторий git наследовал тот же контекст. Но политика безопасности SELinux работает так, что httpd имеет доступ только для чтения к типу контекста «httpd_sys_content_t». Чтобы httpd имел доступ для записи в файлы, мне нужно было изменить контекст на «httpd_sys_rw_content_t». Я сделал это с помощью:
sudo chcon -R -t httpd_sys_rw_content_t ../myRepo
Это наконец решило мою первоначальную ошибку! К сожалению, всплыла новая ошибка, о которой также упоминалась ссылка из вопроса:
array(5) { [0]=> string(68) "ssh: connect to host PRIVATEGITHOST: Permission denied" [1]=> string(45) "fatal: Could not read from remote repository." [2]=> string(0) "" [3]=> string(51) "Please make sure you have the correct access rights" [4]=> string(26) "and the repository exists." }
Но, как я уже упоминал в своем вопросе, я уже настроил ssh-ключ для пользователя «apache», как было предложено в блоге. Так что на самом деле это была другая ошибка. Ошибка, снова происходящая из SELinux.
По умолчанию существует логическое значение SELinux, называемое httdp_can_network_connect, которое отключено. Это не позволяет httpd создавать собственные внешние подключения. Что вам нужно, если все, что вы делаете, — это обслуживание контента, существующего на вашем сервере. Для моих целей мне нужно было установить это логическое значение при использовании:
sudo setsebool httpd_can_network_connect on
И через 2 дня выдернул мои волосы. Я могу сделать «git fetch» из php-файла.
Я отладил последнюю ошибку с помощью логического значения с помощью инструмента «audit2allow», который просматривает некоторые журналы SELinux и говорит вам на более или менее простом английском языке, как решить эту проблему.
sudo audit2allow -a
OUTPUT:
#============= httpd_t ==============
#!!!! This avc can be allowed using one of the these booleans:
# nis_enabled, httpd_can_network_connect
allow httpd_t unreserved_port_t:tcp_socket name_connect;
Если у кого-то возникают проблемы с SELinux, связанные с apache / httpd, я настоятельно рекомендую взглянуть на этот инструмент.
Для полноты картины я хотел бы отметить, что изменение контекста файла моего репозитория, как я делал выше, является «временным изменением» и не будет сохраняться после сброса системы. Чтобы навсегда изменить тип контекста, я использовал:
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/myRepo(/.*)?"
Точно так же я использовал это, чтобы навсегда изменить логическое значение:
sudo setsebool -P httpd_can_network_connect on
Других решений пока нет …