목차

Issue with httpd, php-fpm process failing to write files

🗓️

Apache랑 PHP-FPM 사이에서 /home이 read-only로 보이던 문제를 한 번 정리해봤다. 결론부터 말하면, 커널이나 LVM, ext4가 문제가 아니라 systemd httpd 서비스의 Protect 옵션들이 /home을 읽기 전용으로 바꿔놓고 있었다.


상황 정리 – CLI에선 되는데, 웹에서만 안 된다

워드프레스 마이그레이션 후 플러그인 업데이트를 누르니 FTP 자격 증명을 물어본다.
권한 문제 같아서 퍼미션을 열어도 해결이 안 돼서, 직접 PHP로 쓰기 테스트를 했다.

<?php
$file = __DIR__ . '/' . time() . '.txt';
var_dump($file);
var_dump(@file_put_contents($file, "test\n"));

웹에서 호출하면 이렇게 나온다.

PHP Warning: file_put_contents(/home/abc/pub/text.txt): Failed to open stream: Read-only file system …

CLI에서는 같은 디렉터리에서 잘 써진다.
sudo -u httpd touch …, php -r 'file_put_contents(...)' 둘 다 정상.
findmnt /home으로 보면 /home은 rw, ext4, relatime 딱 보기 좋은 상태다.

그런데 httpd 프로세스 입장에서 /home을 다시 보니까 느낌이 확 온다.

ps aux | grep httpd | head
cat /proc/1912/mounts | grep ' /home '
/dev/mapper/home_vg-home_lv /home ext4 ro,relatime 0 0

커널 전체에서는 rw인데, Apache 프로세스가 붙어 있는 mount namespace에서만 /home이 ro로 마운트되어 있었다.


systemd Protect 옵션 확인

httpd 서비스가 어떻게 띄워져 있는지부터 본다.

systemctl show httpd | egrep 'PrivateTmp|PrivateMounts|ProtectSystem|ProtectHome'
PrivateTmp=yes
PrivateTmpEx=connected
PrivateMounts=no
ProtectHome=read-only
ProtectSystem=yes
  • PrivateTmp=yes 는 /tmp 분리라 괜찮다.
  • PrivateMounts=no 인데도 /proc/<httpd-pid>/mounts 에서 /home이 ro로 잡혀 있다.
  • 결정적으로 ProtectHome=read-only, ProtectSystem=yes 가 걸려 있다.

즉, httpd는 자기 전용 mount 네임스페이스 안에서 /home을 읽기 전용으로 다시 붙이고 있었고, 그 안에서 돌아가는 PHP는 자연스럽게 “Read-only file system”을 맞고 있었다.

재미있는 건 php-fpm 쪽이다.

ps aux | grep 'php-fpm: pool' | head
nsenter -t 997 -m -u -i -n -p mount | grep ' /home '
/dev/mapper/home_vg-home_lv on /home type ext4 (rw,relatime)

php-fpm 프로세스가 바라보는 /home은 rw다.
그러니까 커널/FS/LVM은 멀쩡하고, httpd 서비스만 따로 보호 설정에 걸려 있었던 것.


fix – httpd override에서 보호 해제

원인은 알았으니, httpd 쪽에서 /home에 대한 read-only 보호를 일단 풀어버렸다.

먼저 /home을 한 번 전체적으로 rw로 remount:

mount -o remount,rw /home

그 다음 httpd 서비스 override를 수정한다.

vi /etc/systemd/system/httpd.service.d/override.conf

내용은 최소한 이렇게 맞췄다.

[Service]
PrivateMounts=no
ProtectSystem=off
ProtectHome=off

그리고 reload + restart:

systemctl daemon-reload
systemctl restart httpd

다시 Apache 워커 하나 골라서 확인:

cat /proc/$(pgrep -n httpd)/mounts | grep ' /home '
/dev/mapper/home_vg-home_lv /home ext4 rw,relatime 0 0

이제 웹에서 다시 호출하면, 파일 작성이 가능해진다.
워드프레스도 FTP 자격 증명 팝업 없이 플러그인/코어 업데이트가 잘 올라간다.


정리

  • CLI/ssh에서는 잘 되는데 웹에서만 Read-only file system이면, 퍼미션보다 먼저 서비스별 mount 네임스페이스를 의심해 볼 만하다.
  • /home이 rw로 보이는데도 PHP가 ro라고 우길 때는 /proc/<pid>/mounts를 직접 보는 게 제일 빠르다.
  • systemd 기반 서비스라면 ProtectSystem, ProtectHome, PrivateMounts 같은 옵션이 의도치 않게 데이터 디렉터리를 read-only로 만들 수 있다.
  • 보호 수준을 낮출 땐 최소한 필요한 범위에서만 풀고, 왜 처음에 그렇게 설정되어 있었는지(보안 정책, 호스팅 템플릿 등)도 같이 한 번 돌아봐야 한다.

이번 건은 httpd만 따로 ro 네임스페이스를 타고 있었던 게 원인이라, override 하나로 깔끔하게 정리됐다.