Nginx ↔ Laravel(PHP-FPM) 연동 구조와 SELinux Enforcing 동작 흐름¶
Laravel 운영에서 SELinux 이슈는 대부분 Nginx, PHP-FPM, 파일 컨텍스트, 소켓 접근의 관계를 오해하면서 발생함.
SELinux Enforcing 환경에서 "왜 막히는지"와 "무엇을 허용해야 하는지"를 구조적으로 정리함.
설정 나열이 목적이 아님. 정책의 의미를 이해하고 최소 권한으로 해결하는 것이 목적임.
1. 기본 전제 (이 문서를 읽기 전 상태)¶
다음 조건을 전제로 설명함.
- SELinux: Enforcing
- Nginx 정상 기동 (
httpd_t) - PHP-FPM 정상 기동
- Laravel 실행 경로:
/var/www/[ProjectName]/current - 외부 공개 포트: 80/443
- PHP-FPM 포트/소켓은 외부에 공개하지 않음
2. 전체 요청 흐름 (정상 구조)¶
[ Client ]
↓
[ Nginx (httpd_t) ]
↓ fastcgi_pass
[ PHP-FPM ]
↓
[ Laravel ]
↓
[ DB / Redis ]
핵심은 Nginx가 외부 요청을 받고, Laravel은 내부 런타임으로만 동작한다는 점임.
3. Firewall와 SELinux 역할 차이 (가장 중요한 구분)¶
3.1 Firewall의 역할¶
- 기준: 네트워크 포트
- 질문: "외부에서 이 포트로 들어와도 되는가?"
3.2 SELinux의 역할¶
- 기준: 프로세스/파일/포트 타입
- 질문: "이 프로세스가 이 파일/소켓/포트에 접근해도 되는가?"
핵심 정리:
Firewall는 "들어오게 할지"를 결정하고, SELinux는 "누가 쓰게 할지"를 결정함.
4. Laravel에서 자주 발생하는 SELinux 차단 패턴¶
4.1 storage, bootstrap/cache 쓰기 차단¶
증상:
- 500 오류
- 로그/캐시 파일 생성 실패
- 권한과 소유자가 맞는데도
storage/logs쓰기 실패 setenforce 0상태에서는 동작하지만 Enforcing에서 다시 실패
해결(영구 정책):
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/[ProjectName]/current/storage(/.*)?"
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/[ProjectName]/current/bootstrap/cache(/.*)?"
sudo restorecon -Rv /var/www/[ProjectName]/current/storage
sudo restorecon -Rv /var/www/[ProjectName]/current/bootstrap/cache
이미 규칙이 있으면
-a대신-m사용.chcon은 임시 조치로 끝나기 쉬우므로 운영 기준은semanage fcontext + restorecon임.
4.2 PHP-FPM 소켓 접근 문제¶
권장 구조는 Unix socket 사용임.
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
}
점검 포인트:
- 소켓 경로 일치 여부
- 소켓 권한/소유자
- Nginx와 PHP-FPM 설정의 listen 값 일치
4.3 DB 네트워크 연결 차단 (조건부)¶
일부 환경에서는 PHP 프로세스의 DB 접속이 SELinux에 의해 차단될 수 있음. Audit 로그 근거가 있을 때만 아래를 검토함.
sudo setsebool -P httpd_can_network_connect_db 1
기본은 OFF 유지. "일단 켜보기" 방식으로 사용하지 않음.
5. 포트 정책(semanage port)은 변경 포트일 때만 사용¶
기본 Web 포트(80/443)에 대한 설명은 생략하고, Nginx가 비표준 HTTP/HTTPS 포트를 listen 하는 경우에만 포트 타입 정책을 적용함.
확인:
sudo semanage port -l | grep -w [PORT]
없으면 추가(-a), 있으면 수정(-m).
추가 예시:
sudo semanage port -a -t http_port_t -p tcp [PORT]
수정 예시:
sudo semanage port -m -t http_port_t -p tcp [PORT]
ex)
PORT=8080
sudo semanage port -l | grep -w "$PORT"
sudo semanage port -a -t http_port_t -p tcp "$PORT" 2>/dev/null || sudo semanage port -m -t http_port_t -p tcp "$PORT"
이 명령은 "포트 개방"이 아니라 "포트 타입 분류"임. 외부 접근 허용 여부는 firewall이 결정함.
6. 진단 루틴 (문제 발생 시)¶
sudo ausearch -m AVC,USER_AVC -ts recent
sudo ausearch -m AVC -ts today
sudo tail -n 200 /var/log/nginx/error.log
sudo tail -n 200 /var/log/php-fpm/www-error.log
진단 원칙:
- 증상(502/500) 확인
- audit 로그에서 차단 타입 식별
- 최소 범위 정책만 반영
- 반영 후 재검증
7. 절대 금지 패턴¶
setenforce 0상시 사용/etc/selinux/config에서SELINUX=disabledchcon으로 임시 조치 후 영구 정책 미반영- 프로젝트 전체에
httpd_sys_rw_content_t부여
임시 우회는 즉시 복구 가능한 것처럼 보여도, 다음 배포/재부팅에서 동일 장애를 반복시킴.
8. 운영 기준 요약¶
- SELinux는 끄는 대상이 아니라 정확히 맞춰 쓰는 정책 계층임
- Laravel SELinux 이슈의 대부분은 writable 경로 컨텍스트 문제임
- 해결은
audit 로그 근거 + 최소 권한 정책으로 수행함
9. 최종 결론¶
- Nginx + Laravel + SELinux Enforcing은 완전히 공존 가능함
- 핵심은 "허용 범위를 넓히는 것"이 아니라 "정확히 분류하는 것"임
이 문서 기준을 따르면 SELinux 때문에 운영 구조를 무너뜨릴 일이 크게 줄어듦.