대상 OS: Rocky Linux 9

장애 대응에서 제일 시간을 잡아먹는 순간은 “막혔는데, 왜 막혔는지 모를 때”다.
SELinux는 그 ‘왜’를 로그로 남겨주지만, 익숙하지 않으면 로그가 그냥 소음처럼 보인다.
그 결과로 운영자가 SELinux를 끄고(perm=0), 공격자는 그 순간부터 서버를 ‘일반 리눅스’로 취급한다.
오늘은 Rocky Linux 9에서 SELinux를 끄지 않고도, 차단 로그를 빠르게 해석하고 안전하게 풀어내는 절차를 정리한다.
특히 커스텀 포트/커스텀 경로를 쓰는 웹/에이전트 배포에서 자주 터지는 케이스를 기준으로 간다.

---

이 글의 전제(무엇을 다루고/무엇을 안 다루나)

- SELinux 모드: Enforcing을 유지한다.
- 목표: “정상 동작에 필요한 최소 권한”만 열고, 임시 허용은 최대한 짧게 가져간다.
- 다루지 않는 것: DB(MariaDB/PostgreSQL) 하드닝, 컨테이너 오케스트레이션(K8s) 심화.

---

1. SELinux가 실제로 무엇을 막고 있는지 10초 안에 확인

1) 현재 모드와 정책을 확인.

getenforce
sestatus

2) 차단(AVC) 로그가 정말 발생하는지 확인.

sudo ausearch -m AVC,USER_AVC -ts recent | tail -n 20

로그가 비어 있다면 “SELinux 문제”가 아니라 애플리케이션/권한/방화벽 문제일 가능성이 높다.

---

2. 가장 흔한 원인 3개: (경로 라벨) (포트 라벨) (불린)

SELinux 트러블슈팅에서 대부분은 아래 셋으로 귀결된다.

- 파일/디렉터리 컨텍스트(label)가 서비스 타입과 안 맞는다.
- 데몬이 쓰는 포트가 기본 라벨에 없어서 차단된다.
- 정책은 맞는데 boolean(토글)이 꺼져 있다.

아래부터는 “실무에서 자주 보이는 형태”로 풀어본다.

---

3. 케이스 A: Nginx/Apache가 새 경로(/srv/app) 접근을 차단당할 때

예: `/srv/app/current/public`에 정적 파일을 두었는데 403/permission denied가 발생.
리눅스 파일 권한이 맞아도 SELinux 라벨이 틀리면 막힌다.

1) 문제 파일/디렉터리의 현재 라벨을 본다.

ls -ldZ /srv/app /srv/app/current /srv/app/current/public

2) 웹서버가 읽을 정적 경로라면 보통 `httpd_sys_content_t`가 필요하다.

(업로드/쓰기까지 필요하면 별도 타입을 쓴다. 무턱대고 write 라벨을 주면 위험해진다.)

sudo dnf -y install policycoreutils-python-utils
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/app(/.*)?"
sudo restorecon -Rv /srv/app

3) 업로드 폴더처럼 “웹이 써야 하는” 디렉터리는 `httpd_sys_rw_content_t`를 좁게 적용.

sudo semanage fcontext -a -t httpd_sys_rw_content_t "/srv/app/shared/uploads(/.*)?"
sudo restorecon -Rv /srv/app/shared/uploads

4) 그래도 막히면, 차단 메시지를 원인 형태로 번역.

sudo ausearch -m AVC -ts recent | audit2why

운영 팁: `/var/www` 아래로 억지로 옮기는 건 “구동”은 되지만, 배포 구조가 커질수록 사고가 난다. 경로는 경로대로 두고 라벨을 맞추는 쪽이 장기적으로 안전하다.

---

4. 케이스 B: 서비스가 8081/8443 같은 커스텀 포트를 열었는데 접속이 안 될 때

SELinux는 “포트도 정책의 일부”로 본다. 프로세스 타입이 특정 포트 라벨에 바인딩할 수 있어야 한다.

1) 해당 포트가 어떤 SELinux 포트 타입에 묶여 있는지 확인.

sudo semanage port -l | grep -E "\b(8081|8443)\b" || true

2) 예를 들어 웹(HTTP) 성격의 포트를 추가하려면 `http_port_t`에 할당한다.

sudo semanage port -a -t http_port_t -p tcp 8081
sudo semanage port -a -t http_port_t -p tcp 8443

이미 등록된 포트라면 `-m`(modify)로 바꾼다.

sudo semanage port -m -t http_port_t -p tcp 8081

3) 방화벽(firewalld)도 같이 확인해야 한다(SELinux만 맞춰도 외부는 막힐 수 있음).

sudo firewall-cmd --list-all
sudo firewall-cmd --add-port=8081/tcp --permanent
sudo firewall-cmd --add-port=8443/tcp --permanent
sudo firewall-cmd --reload

---

5. 케이스 C: 원격 리소스 접근(NFS/홈 디렉터리) 때문에 막힐 때(불린 점검)

정책은 맞는데 boolean이 꺼져 있는 경우가 있다. 대표적으로 웹이 사용자 홈 디렉터리 콘텐츠를 읽는 케이스.

1) 관련 boolean을 찾아본다.

sudo getsebool -a | grep -E "httpd_.*home|httpd_.*network|httpd_.*nfs" || true

2) 예: 웹이 사용자 홈을 읽어야 한다면(필요한 경우에만) `httpd_enable_homedirs`를 켠다.

sudo setsebool -P httpd_enable_homedirs on

3) 네트워크로 아웃바운드 연결이 필요한 웹(예: 내부 API 호출)은 `httpd_can_network_connect`가 힌트가 된다.

sudo setsebool -P httpd_can_network_connect on

주의: boolean은 편리하지만 “범위를 넓게 열어주는 스위치”일 때가 많다. 정말 필요한지부터 확인하자.

---

6. ‘절대 금지에 가까운’ 나쁜 해결법: permissive/disable

1) 일시적으로 permissive로 돌려 “SELinux가 원인인지”만 확인하는 건 가능.

sudo setenforce 0
# 테스트 후 즉시 복귀
sudo setenforce 1

2) 하지만 아래는 운영 보안 관점에서 거의 금지다.

# 하지 말 것: 영구 비활성화
# sudo sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config

SELinux를 꺼서 해결되는 문제는 대개 “라벨/포트/불린” 중 하나로 해결된다. 꺼버리는 순간, 앞으로 발생할 취약점/오탐/침해 대응 비용이 폭발한다.

---

7. 마지막 카드: audit2allow로 ‘최소’ 커스텀 정책 만들기(검증 포함)

라벨/포트/불린으로도 해결이 안 되면 커스텀 정책이 필요할 수 있다. 단, 무작정 허용하면 위험하다.

1) 최근 AVC를 모아서 정책 초안을 만든다.

sudo ausearch -m AVC -ts recent | audit2allow -M localfix

2) 만들어진 `.te` 파일을 꼭 열어서 “무슨 권한을 여는지” 확인한다.

sed -n '1,200p' localfix.te

3) 납득 가능한 범위라면 모듈을 설치한다.

sudo semodule -i localfix.pp

4) 설치 후 같은 동작을 재현해서 AVC가 사라졌는지 확인한다.

sudo ausearch -m AVC -ts recent | tail -n 50

운영 팁: `audit2allow`는 빠르지만 “허용을 쉽게 만들어주는 도구”다. 라벨로 해결할 수 있는 문제를 정책으로 해결하지 말자(나중에 원인 추적이 더 어려워진다).

---

8. 점검 체크리스트(배포 파이프라인에 넣기 좋게)

1) SELinux 모드가 Enforcing인지 확인.

getenforce

2) 서비스 경로의 라벨이 기대값인지 확인.

ls -lZ /srv/app | head

3) 커스텀 포트가 필요한 타입에 속해 있는지 확인.

sudo semanage port -l | grep -E "http_port_t|ssh_port_t" | head

4) 최근 AVC가 쌓이고 있지 않은지 확인.

sudo ausearch -m AVC -ts today | tail -n 20

---

마무리: SELinux는 ‘끄는 기능’이 아니라 ‘설명 가능한 방어선’이다

SELinux를 유지하는 팀은 보안이 강한 것뿐 아니라, 장애 대응이 빨라진다.
차단 로그가 남기 때문에 “뭐가 막혔는지”가 명확해지고, 라벨/포트/불린으로 구조적으로 해결할 수 있다.
Rocky Linux 9를 운영한다면, SELinux는 기본값 그대로 가져가고(Enforcing), 필요할 때만 최소한으로 조정하는 습관을 팀 표준으로 만드는 것을 권한다.