설정은 다 맞는데 403이 난다 — 9할은 SELinux다
httpd 설정을 다 맞췄다. 파일도 /var/www/html에 넣었다. 그런데 브라우저에서 403 Forbidden. Apache 설정만 뒤지다 한참을 헤맨다 — 그런데 진짜 원인은 SELinux인 경우가 압도적으로 많다. 파일을 cp/mv로 옮기면 보안 레이블(컨텍스트)이 잘못되어 SELinux가 접근을 막는 것이다.
리눅스 보안은 다층 방어(defense in depth)다. 전통 권한(DAC) 위에 강제 접근 제어(SELinux), 네트워크 검문(firewalld), 인증(PAM)이 올려져 있고, 각 층이 독립적으로 통과돼야 접근이 허용된다. 이 장을 모르면 "설정 다 맞는데 왜 안 되지?"의 원인을 못 찾고, 방화벽·인증 정책도 못 짠다. 이 글은 그 다층 구조를 다룬다.
접근 제어의 두 층 — DAC와 MAC, 둘 다 통과해야 한다
| 보안 층 | 건물 비유 | 리눅스 |
|---|---|---|
| DAC(권한 rwx) | 출입증 소유자가 자기 사무실 출입 권한 부여 | 전통 UNIX 퍼미션(05장) |
| MAC(SELinux) | 건물 보안 정책이 출입증과 무관하게 추가 검문 | SELinux |
| firewalld | 건물 정문 검문대(외부인 통제) | 네트워크 패킷 필터 |
| PAM | 신분증 확인 절차(로그인·sudo) | 인증 프레임워크 |
| 모델 | 설명 | 예 |
|---|---|---|
| DAC(임의) | 소유자가 권한 결정 | 전통 UNIX 퍼미션 |
| MAC(강제) | 정책이 접근 강제. 소유자 권한 무관 | SELinux, AppArmor |
핵심: DAC가 허용해도 MAC(SELinux)이 거부하면 접근 불가. 둘 다 통과해야 동작한다. 이것이 "서비스 안 될 때 SELinux를 의심해야 하는 이유"다. 권한(chmod)만 보고 "다 됐는데"라고 착각하기 쉽기 때문이다.
SELinux — 모든 것에 레이블을 붙인다
NSA가 기여한 MAC 구현이다. 모든 프로세스와 파일에 레이블(컨텍스트)을 붙이고, 정책이 허용한 관계만 접근을 허용한다. "웹 서버 프로세스가 웹 콘텐츠 디렉토리만 읽게" 같은 세밀한 통제가 가능하다.
| 모드 | 동작 | 용도 |
|---|---|---|
| enforcing | 정책 위반 차단 + 로그 | 운영 기본(Rocky 10 기본) |
| permissive | 위반 허용 + 로그만(감사/디버그) | 문제 원인 파악 |
| disabled | 완전 비활성 | 권장 안 함(재부팅 필요, 보안 약화) |
정책은 RHEL 10에서 targeted(기본, 주요 서비스만 confinement)이 표준이다.
SELinux 컨텍스트(레이블)
user:role:type:mls
예: unconfined_u:object_r:httpd_sys_content_t:s0
│ │ │ │
│ │ │ └ MLS 레벨
│ │ └ 타입(가장 중요, 정책 매칭 단위)
│ └ 롤
└ 사용자
type(_t 접미사)가 정책 적용의 핵심이다. httpd_t 프로세스는 httpd_sys_content_t 파일만 읽도록 허용된다. 파일을 cp/mv로 /var/www/html에 넣으면 이 type 레이블이 잘못되고, SELinux가 차단한다 — 도입부의 403 사태.
firewalld — 네트워크 검문대
동적 방화벽 관리 데몬. RHEL 10 백엔드는 nftables(iptables는 unmaintained, 단계적 제거).
flowchart LR
NIC[네트워크 인터페이스] --> ZONE[Zone<br/>trusted/public/dmz...]
ZONE --> RULE[규칙: 서비스/포트/소스]
RULE --> BACKEND[nftables 백엔드]
BACKEND --> KERNEL[커널 패킷 필터]
- Zone: 신뢰 수준별 규칙 묶음. 인터페이스를 zone에 할당.
public(기본),trusted(모두 허용),block,dmz,home,work. - Service: 미리 정의된 포트/프로토콜 묶음(
http,ssh,dns). - Runtime vs Permanent: 기본은 임시(재시작 시 사라짐).
--permanent로 영구화한 뒤--reload.
PAM과 sudo — 인증과 권한 위임
PAM(Pluggable Authentication Modules)
인증을 모듈화한 프레임워크. 로그인·sudo·ssh 등이 PAM을 통해 인증한다.
flowchart LR
APP[응용: login, sshd, sudo] --> PAM[PAM 라이브러리]
PAM --> CONF[/etc/pam.d/<service>/]
CONF --> MOD[모듈: pam_unix, pam_sss, ...]
MOD --> BACKEND[로컬/LDAP/Kerberos/IdM]
- 설정:
/etc/pam.d/<서비스>. - 관리 도구: authselect(RHEL 10). PAM/NSS 설정을 프로필 단위로 관리. 직접
/etc/pam.d편집 금지(authselect가 덮어씀). - 백엔드 통합: SSSD → IdM/AD/LDAP.
sudo — 권한 위임
일반 사용자가 root 권한 명령을 제한적으로 실행한다.
- 설정:
/etc/sudoers와/etc/sudoers.d/*. 반드시visudo로 편집(문법 검증). - 일반 사용자를
wheel그룹에 넣으면 sudo 사용 가능(RHEL 기본).
/etc/sudoers를 일반 에디터로 고치다 문법을 틀리면 모든 sudo가 막힌다 — root조차 못 된다. 그래서visudo가 저장 시 문법을 검증한다.
시스템 전체 암호화 정책(Crypto Policies)
RHEL은 TLS/SSH/IPsec/DNSSEC/Kerberos 허용 알고리즘을 전역 정책으로 통일 관리한다: DEFAULT(운영 기본, 현대 알고리즘), LEGACY(구형 호환), FUTURE(더 강력), FIPS(FIPS 140 준수).
Rocky 10에서 직접 확인하기
미검증(출처 인용). Rocky 10 VM에서 실행 권장.
# SELinux — 서비스 안 될 때 가장 먼저
getenforce # 현재 모드
sestatus # 상세(모드, 정책)
sudo setenforce 0 # Permissive(임시)
sudo setenforce 1 # Enforcing
ls -Z /var/www/html/index.html # 파일 컨텍스트
ps -eZ | grep httpd # 프로세스 컨텍스트
# AVC 거부 로그(서비스 안 될 때)
sudo ausearch -m AVC -ts recent
sudo journalctl -t setroubleshoot
unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html
SELinux 문제 해결 흐름:
sudo ausearch -m AVC -ts recent | tail # 1. 거부 로그에서 AVC 확인
sudo restorecon -Rv /var/www/html # 2. 컨텍스트 복구(잘못된 레이블 교정)
# 비표준 경로/포트는 semanage로 레이블 추가
sudo semanage fcontext -a -t httpd_sys_content_t "/custom/web(/.*)?"
sudo restorecon -Rv /custom/web
# 반복 거부는 정책 모듈 자동 생성
sudo grep "...denied" /var/log/audit/audit.log | audit2allow -M mypol
sudo semodule -i mypol.pp
# firewalld
sudo systemctl status firewalld
firewall-cmd --get-default-zone
firewall-cmd --list-all # 현재 zone 전체
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload # 영구 → 런타임 반영
sudo firewall-cmd --permanent --change-interface=enp0s3 --zone=public
public (active)
services: cockpit dhcpv6-client ssh
ports: 8080/tcp
# PAM / authselect
authselect current # 현재 프로필
cat /etc/pam.d/system-auth # authselect 관리(직접 편집 금지)
sudo authselect select sssd --update # 프로필 변경
# sudo
sudo -l # 내가 실행 가능한 명령
sudo visudo # 설정 편집(반드시 visudo)
echo "alice ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx" | sudo tee /etc/sudoers.d/alice
# 암호화 정책
update-crypto-policies --show # DEFAULT
sudo update-crypto-policies --set FUTURE
흔히 묻는 것, 흔히 틀리는 것
| 오해 | 정정 |
|---|---|
| "설정 맞는데 안 되면 Apache 설정을 뒤진다" | 9할 SELinux. permissive로 전환 후 AVC 로그 먼저 |
| "SELinux 귀찮으니 disabled로" | 금지. 보안 무력화 + 재부팅 시 레이블 재작성(매우 느림). 차라리 permissive |
"firewall-cmd --add-service=http 영구다" |
아니다. --permanent 없으면 재시작 시 사라짐 |
"/etc/pam.d/system-auth 직접 고친다" |
authselect가 다음 업데이트에 덮어씀. authselect로 |
"/etc/sudoers를 에디터로 고친다" |
문법 틀리면 모든 sudo 막힘(root 못 됨). 반드시 visudo |
| "SELinux는 무작위로 차단" | 아니다. 컨텍스트 매칭 안 되면. restorecon으로 교정 |
| "firewalld 임시 규칙도 부팅 후 유지" | 사라짐. 운영은 --permanent + --reload |
| "포트 열면 서비스 접속된다" | SELinux가 비표준 포트 차단 가능. semanage port로 허용 추가 필요 |
더 깊이
- SELinux boolean: 세밀한 토글 스위치.
setsebool -P httpd_can_network_connect on(웹 서버가 네트워크 연결 허용).getsebool -a로 전체 확인. - auditd와 침입 탐지:
auditd는 시스템 호출·파일 접근을 상세 로깅한다(/var/log/audit/audit.log).ausearch/aureport로 분석. 침해 사후 조사의 핵심. - IdM(Identity Management): RHEL의 중앙 인증(LDAP+Kerberos+DNS+CA 통합). Active Directory와 신뢰 관계 구성 가능.
요약 — 이 글의 결론
- DAC(소유자 결정) + MAC(정책 강제, SELinux). 둘 다 통과해야 접근 — 이게 "권한 줬는데 왜 안 되지?"의 답.
- SELinux: 레이블(컨텍스트
user:role:type:mls) 기반. type(_t)이 핵심. 모드는 enforcing(기본)/permissive/disabled(금지). "설정 맞는데 403"은 9할 컨텍스트 문제 →restorecon. - firewalld: zone(public/trusted/dmz) + service. 백엔드 nftables(Rocky 10).
--permanent+--reload로 영구화. - PAM:
/etc/pam.d/. authselect로 관리(직접 편집 금지). - sudo:
visudo만.wheel그룹이 기본 sudo 권한. 에디터로 고치면 문법 틀렸을 때 전체 마비. - Crypto policies: DEFAULT/LEGACY/FUTURE/FIPS.
- 서비스 안 될 때 3단계: (1) 서비스 상태 (2) firewalld 포트 (3) SELinux 컨텍스트.
생각해 볼 문제
- DAC와 MAC의 차이. 둘 다 통과해야 하는 이유는?
- SELinux의 enforcing/permissive/disabled 차이. 왜 disabled를 피하는가?
- SELinux 컨텍스트에서 가장 중요한 필드는? 왜?
- "httpd 설정 다 맞는데 403"일 때 가장 먼저 확인할 것은?
- firewalld의 runtime과 permanent 차이. 운영에선 어떻게?
/etc/pam.d/system-auth를 직접 고치면 안 되는 이유는?/etc/sudoers를 일반 에디터로 고치면 위험한 이유는? 안전한 도구는?- Rocky 10의 firewalld 백엔드는?
참고
- Using SELinux — RHEL 10 - 접근 2026-07-09
- Configuring firewalls — RHEL 10 - 접근 2026-07-09
- Configuring authentication — RHEL 10 - 접근 2026-07-09
- man page:
man selinux(8),man sestatus,man firewall-cmd,man authselect,man sudoers,man pam(8)
'Tech Artifacts > Linux' 카테고리의 다른 글
| Linux - 11. services (0) | 2026.07.09 |
|---|---|
| Linux - 09. network (0) | 2026.07.09 |
| Linux - 08. package (0) | 2026.07.09 |
| Linux - 07. shell script (0) | 2026.07.09 |
| Linux - 06. io device (0) | 2026.07.09 |
