리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.
리눅스 시스템 기초 47 / 50 · Part 5. 네트워크·보안·SOC
실습 환경: Rocky Linux 9 (10.0.0.200, SELinux enforcing) / Ubuntu 22.04 (AppArmor) · 이 글의 출력은 모두 형식 설명용 예시
이전 글: 46. Firewalld 이해
15·17편에서 파일 권한(rwx)과 소유자를 배웠다. 이 방식을 DAC(Discretionary Access Control, 임의 접근 제어) 라고 한다. 파일 주인이 권한을 정하고, root는 그 권한을 무시할 수 있다.
DAC에는 두 가지 한계가 있다.
SELinux 는 여기에 두 번째 관문을 추가한다. "이 종류의 프로세스가 이 종류의 파일에 이 동작을 해도 되는가"를 시스템 정책으로 강제한다. 이를 MAC(Mandatory Access Control, 강제 접근 제어) 라고 한다.
이번 글에서 주의할 점: SELinux(RHEL 계열 기본)와 AppArmor(Ubuntu 기본)는 목적은 같지만 동작 방식이 다르다. 둘을 혼동하지 않도록 마지막에 따로 비교한다.
SELinux는 모든 프로세스, 파일, 포트에 라벨(컨텍스트) 을 붙인다.
system_u : object_r : httpd_sys_content_t : s0
사용자 역할 타입(type) 보안 수준
실무에서 거의 모든 판단은 세 번째 필드인 타입 으로 한다. 프로세스의 타입은 도메인 이라고도 부른다.
| 대상 | 확인 명령 | 예 |
|---|---|---|
| 프로세스 | ps -eZ, id -Z | httpd_t, sshd_t, 로그인 사용자는 unconfined_t |
| 파일 | ls -Z | /var/www/html → httpd_sys_content_t, /etc/shadow → shadow_t |
| 포트 | semanage port -l | http_port_t → 80, 443, 8080 등 |
정책은 "도메인 A가 타입 B에 대해 어떤 동작을 허용하는가"의 목록이다.
| 규칙 (개념) | 결과 |
|---|---|
httpd_t → httpd_sys_content_t : read | 허용 — 웹 페이지 제공 |
httpd_t → shadow_t : read | 규칙 없음 → 거부 |
httpd_t → http_port_t : bind | 허용 — 80/443에서 대기 |
httpd_t → 임의 포트 : connect | 부울 httpd_can_network_connect가 켜져야 허용 |
허용 규칙이 없으면 거부 가 기본이다. 그래서 웹 서버가 root 권한으로 탈취되어도 httpd_t 도메인에 갇혀 있는 한 /etc/shadow를 읽거나 임의 외부로 연결하기 어렵다.
| 모드 | 동작 | 용도 |
|---|---|---|
| enforcing | 정책 위반을 차단 하고 기록 | 운영 기본값 |
| permissive | 차단하지 않고 기록만 | 문제 분석, 정책 개발 |
| disabled | 동작 안 함 | 권장하지 않음. 켜고 끌 때 재부팅·재라벨링 필요 |
setenforce 0/1은 enforcing ↔ permissive를 즉시(재부팅 전까지) 바꾸고, 영구 설정은 /etc/selinux/config의 SELINUX=다.
정책을 다시 작성하지 않고 켜고 끌 수 있는 스위치다.
| 부울 | 켜면 |
|---|---|
httpd_can_network_connect | 웹 서버가 외부로 네트워크 연결 가능 |
httpd_can_network_connect_db | 웹 서버가 DB 포트로 연결 가능 |
httpd_enable_homedirs | 사용자 홈 디렉터리 웹 제공 |

접근 요청은 커널에서 다음 순서로 검사된다.
type=AVC msg=audit(1758562387.412:913): avc: denied { read } for pid=7900 comm="cat"
name="shadow" dev="dm-0" ino=16797
scontext=system_u:system_r:httpd_t:s0
tcontext=system_u:object_r:shadow_t:s0 tclass=file permissive=0
| 필드 | 의미 |
|---|---|
denied { read } | 거부된 동작 |
pid, comm | 누가 (프로세스 ID, 명령 이름) |
name, ino | 무엇을 |
scontext | 주체(프로세스) 컨텍스트 — httpd_t |
tcontext | 대상(파일) 컨텍스트 — shadow_t |
tclass | 대상 종류 (file, dir, tcp_socket …) |
permissive=0 | enforcing 상태에서 실제로 차단됨 (1이면 기록만) |
이 한 줄을 사람이 읽으면: "웹 서버 도메인의 프로세스가 cat으로 /etc/shadow를 읽으려다 차단되었다." 웹 서버가 cat /etc/shadow를 실행할 이유는 없으므로, 웹셸이나 RCE로 명령을 실행한 흔적 이다. SELinux는 막았을 뿐 아니라 공격 시도를 기록 했다.
# 1) 상태
getenforce
sestatus
grep ^SELINUX= /etc/selinux/config
# 2) 컨텍스트 확인
id -Z
ps -eZ | grep -E 'httpd|sshd'
ls -Z /var/www/html /etc/shadow
sudo semanage port -l | grep http_port_t
# 3) AVC 거부 로그
sudo ausearch -m AVC -ts recent
sudo ausearch -m AVC -ts today -i | grep -E 'scontext|tcontext|comm' | head
sudo sealert -a /var/log/audit/audit.log # setroubleshoot 설치 시, 원인과 해결책 제안
# 4) 흔한 문제: 다른 곳에서 옮긴 파일의 라벨이 틀림
sudo mv /root/index.html /var/www/html/ # mv 는 원래 라벨(admin_home_t 등) 유지
ls -Z /var/www/html/index.html
sudo restorecon -v /var/www/html/index.html # 정책에 맞는 라벨로 복원
# 5) 사용자 지정 경로를 웹 콘텐츠로 등록
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/web(/.*)?'
sudo restorecon -Rv /srv/web
# 6) 부울
getsebool -a | grep httpd_can_network
sudo setsebool -P httpd_can_network_connect_db on # -P : 영구
# 7) 비표준 포트 허용
sudo semanage port -a -t http_port_t -p tcp 8088
# Ubuntu AppArmor
sudo aa-status
ls /etc/apparmor.d/
sudo journalctl -k | grep 'apparmor="DENIED"' | tail
아래 출력은 형식 설명용 예시다.
① 운영 장애: 옮긴 파일의 라벨
$ ls -Z /var/www/html/index.html
unconfined_u:object_r:admin_home_t:s0 /var/www/html/index.html
type=AVC ... avc: denied { read } for pid=946 comm="httpd" name="index.html"
scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file permissive=0
권한(rwx)은 맞는데 웹 페이지가 403이다. mv는 원래 위치(/root)의 라벨을 그대로 가져오므로 httpd_t가 읽을 수 없다. 해결은 setenforce 0이 아니라 restorecon 이다.
② 보안 사건: 웹 서버의 외부 연결 차단
type=AVC ... avc: denied { name_connect } for pid=7811 comm="sysd" dest=3333
scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=0
httpd_t 도메인에서 실행된 sysd가 3333번 포트(채굴 풀, 32·33편 시나리오)로 연결하려다 막혔다. SELinux 덕분에 채굴은 실패했지만, 코드 실행 자체는 이미 일어났다 는 뜻이다. 즉시 침해 조사 대상이다.
| 상황 | 의미 | 대응 |
|---|---|---|
웹·DB 도메인의 AVC 거부 (shadow_t, name_connect, execute) | 탈취된 서비스가 권한 밖 행동 시도 | 침해 지표 — 조사 |
setenforce 0 실행 기록, permissive 전환 | 공격자 또는 관리자의 방어 해제 (T1562.001) | audit 로그 MAC_STATUS, sudo 로그 |
/etc/selinux/config의 SELINUX=disabled 변경 | 재부팅 후 해제 | 파일 무결성 감시 |
부울 변경 (httpd_can_network_connect on) | 통로 개방 | getsebool 기준값 비교 |
| "SELinux 끄면 된다"는 운영 관행 | 방어층 하나를 통째로 제거 | 라벨·부울·포트로 해결 |
getenforce; sestatus | grep -E 'mode|Policy'
sudo ausearch -m AVC -ts today -i 2>/dev/null | grep -oE 'scontext=[^ ]+|tcontext=[^ ]+|comm="[^"]+"' | sort | uniq -c | sort -rn | head
sudo ausearch -m MAC_STATUS,MAC_CONFIG_CHANGE -ts this-week -i # 모드·부울 변경
[Alert] SIEM: audit AVC denied — scontext=httpd_t tcontext=shadow_t comm=cat (02:31:12)
↓
[의미] 웹 서버 도메인에서 cat /etc/shadow 시도 → 웹셸 명령 실행
↓
[트리] pid → httpd → sh → cat (35편), access_log 02:31 요청 (25편)
↓
[결과] permissive=0 → 차단 성공. 하지만 코드 실행은 발생
↓
[Response] 웹셸 제거, 취약점 조치, 같은 시각 다른 AVC·네트워크 연결 확인
user:role:type:level 중 type 으로 판단한다. ps -eZ, ls -Z, semanage port -l./var/log/audit/audit.log의 type=AVC. scontext(누가)·tcontext(무엇을)·permissive=0(실제 차단).restorecon → setsebool -P → semanage port 순으로 해결한다.다음 글 「48. Linux 로그와 journalctl」 에서는 지금까지 여러 글에 흩어져 나온 로그를 한곳에 정리한다. rsyslog와 systemd-journald의 관계, RHEL과 Ubuntu의 로그 파일 차이, journalctl의 필터(유닛·시간·우선순위·PID), audit 로그, 그리고 로그를 SIEM으로 보내는 구조를 다룬다.