리눅스 시스템을 관리하다 보면 chmod나 chown 같은 전통적인 권한 관리 방식을 통해 파일과 디렉토리를 보호하는 것에 익숙해집니다. 하지만 소유자, 그룹, 나머지 사용자로 구분되는 전통적인 임의 접근 제어(DAC, Discretionary Access Control) 방식은 한 가지 치명적인 약점을 가지고 있습니다. 바로 시스템 최고 관리자인 root 계정이 탈취되거나, root 권한으로 실행 중인 웹 서버 프로세스가 해킹당하면 시스템 전체가 무방비로 노출된다는 점입니다.
예를 들어 Apache나 Nginx 같은 웹 서버 데몬이 보안 취약점으로 인해 공격자에게 장악되었다고 가정해 보겠습니다. 이 프로세스가 root 권한이거나 상당히 높은 수준의 권한으로 구동 중이었다면, 공격자는 웹 서버 프로세스의 권한을 이용해 /etc/shadow 파일을 읽거나 시스템 설정을 임의로 변경할 수 있습니다.
이러한 전통적 보안의 한계를 극복하고 커널 수준에서 프로세스의 행위를 강제적으로 격리하는 강력한 방패가 바로 강제 접근 제어(MAC, Mandatory Access Control) 시스템입니다. 리눅스 진영의 양대 산맥인 SELinux와 AppArmor의 개념과 작동 원리, 그리고 실무 예제를 상세히 살펴보겠습니다.
전통적인 DAC 구조에서는 파일의 주인이 "이 파일은 읽고 쓸 수 있어"라고 선언하면 시스템은 이를 제지하지 않습니다. 반면 MAC 구조에서는 시스템 관리자가 설정한 엄격한 보안 정책(Policy)이 최우선시됩니다.
파일 소유자가 권한을 아무리 777로 열어두었거나 프로세스가 root 권한으로 실행 중이라 할지라도, 커널 내부의 MAC 정책이 "웹 서버 프로세스는 오직 /var/www/html 디렉토리만 읽을 수 있고, 그 외의 영역은 접근할 수 없다"고 규정하고 있다면 프로세스의 모든 접근 시도는 차단됩니다. 즉, 프로세스가 가질 수 있는 권한의 '최대 한계선'을 커널이 강제로 그어버리는 것입니다.
리눅스 커널은 이를 구현하기 위해 LSM(Linux Security Module)이라는 프레임워크를 제공하며, 이를 기반으로 구현된 대표적인 솔루션이 Red Hat 계열(RHEL, CentOS, Rocky Linux)의 SELinux와 Debian 계열(Ubuntu, openSUSE)의 AppArmor입니다.
SELinux(Security-Enhanced Linux)는 미 국방안보국(NSA)이 개발하여 커널에 기증한 오픈소스 프로젝트입니다. 매우 강력하고 정교한 제어가 가능하지만, 구조가 다소 복잡하여 많은 엔지니어들이 트러블슈팅 중 가장 먼저 끄는 대상이 되기도 합니다. 하지만 원리를 이해하면 이보다 안전한 방패는 없습니다.
SELinux는 시스템의 모든 주체(프로세스)와 객체(파일, 디렉토리, 네트워크 포트 등)에 '보안 문맥'이라는 라벨을 붙여 관리합니다. ls -Z 또는 ps -eZ 명령어를 사용하면 이 라벨을 확인할 수 있습니다.
보안 문맥은 사용자:역할:타입:민감도 구조로 이루어져 있으며, 가장 핵심적인 부분은 세 번째에 위치한 타입(Type)입니다. 이를 타입 강제(Type Enforcement)라고 부릅니다.
프로세스의 타입: 도메인(Domain)이라고 부르며, 해당 프로세스가 어떤 행동을 할 수 있는지 결정합니다. (예: Apache 웹 서버 프로세스는 httpd_t 도메인을 가집니다.)
파일의 타입: 객체 타입이라고 부르며, 어떤 도메인이 이 파일에 접근할 수 있는지 규정합니다. (예: 웹 문서 디렉토리의 파일들은 httpd_sys_content_t 타입을 가집니다.)
커널 내부의 SELinux 정책에 "도메인 httpd_t는 타입 httpd_sys_content_t를 읽을 수 있다"라는 규칙이 명시되어 있어야만 정상적인 웹 서비스가 가능해집니다. 만약 웹 서버 프로세스가 사용자의 비밀번호가 저장된 /etc/shadow(shadow_t 타입)에 접근하려고 하면, 프로세스의 권한이 root일지라도 SELinux가 이를 단호히 차단합니다.
Enforcing: 정책을 강제합니다. 규칙에 위배되는 모든 행위를 차단하고 로그에 기록합니다. (운영 환경 권장)
Permissive: 규칙에 위배되는 행위를 차단하지는 않지만, 경고 메시지를 로그에 남깁니다. 보안 정책을 테스트하거나 문제를 진단할 때 사용합니다.
Disabled: SELinux 기능을 완전히 끕니다. 커널 수준에서 비활성화되므로 재부팅이 필요합니다.
기본 웹 디렉토리인 /var/www/html 대신 관리자가 새로운 디렉토리 /webdata를 생성하여 웹 서비스를 제공하려고 할 때 흔히 발생하는 오류와 해결 방법입니다.
Bash
# 1. 새로운 웹 디렉토리 생성 및 파일 배치
mkdir /webdata
echo "Hello SELinux" > /webdata/index.html
# 2. 웹 서버 설정 변경 후 아파치 재시작 (생략)
# 3. 브라우저 접속 시 '403 Forbidden' 에러 발생
# 4. 원인 분석을 위해 파일의 SELinux 라벨 확인
ls -Zd /webdata
# 출력 예시: drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 /webdata
새로 만든 디렉토리는 기본값인 default_t 타입을 가집니다. 아파치 프로세스(httpd_t)는 default_t 타입에 접근할 권한이 없으므로 전통적 권한이 완벽해도 403 에러가 납니다.
Bash
# 5. /webdata 디렉토리와 하위 파일들의 라벨 정책을 웹 콘텐츠 타입으로 등록
semanage fcontext -a -t httpd_sys_content_t "/webdata(/.*)?"
# 6. 등록된 정책을 실제 파일 시스템에 적용 (새로고침)
restorecon -R -v /webdata
# 출력 예시: Relabeled /webdata from unconfined_u:object_r:default_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0
# 7. 브라우저 재접속 시 정상적으로 웹 페이지가 출력됨을 확인
AppArmor(Application Armor)는 수세 리눅스에서 개발되어 현재는 우분투의 기본 보안 모듈로 자리 잡은 MAC 시스템입니다. 복잡한 라벨링 시스템을 사용하는 SELinux와 달리, AppArmor는 오직 파일의 절대 경로를 기준으로 보안 정책을 수립합니다. 상대적으로 직관적이고 학습 곡선이 낮아 다루기 쉽다는 장점이 있습니다.
AppArmor는 특정 애플리케이션의 행동을 정의한 텍스트 파일을 가집니다. 이를 프로필이라고 하며, 주로 /etc/apparmor.d/ 디렉토리에 저장됩니다. 프로필 파일의 이름은 해당 실행 파일의 경로 이름을 따서 짓습니다. (예: /usr/sbin/tcpdump 프로필의 파일명은 usr.sbin.tcpdump가 됩니다.)
프로필 내부에는 해당 프로그램이 어떤 파일에 접근할 수 있는지 권한 문자열과 함께 명시됩니다.
r: 읽기 권한
w: 쓰기 권한
px: 고유 프로필을 가진 실행 권한
Enforce: 프로필에 정의되지 않은 제한된 행위를 엄격히 차단하고 로그를 남깁니다.
Complain: 제한된 행위를 차단하지는 않고 시스템 로그에 기록만 남깁니다. (SELinux의 Permissive와 유사)
보안이 중요한 특정 파일 공유 데몬 프로그램이 지정된 경로 외에는 절대 한 발짝도 나가지 못하도록 가두는 프로필 관리 방법입니다.
Bash
# 1. 현재 시스템의 AppArmor 프로필 상태 확인
aa-status
# 출력 예시: 수많은 프로필이 enforce 모드로 작동 중임을 확인 가능
# 2. 전용 도구를 이용하여 특정 프로그램의 프로필 템플릿 자동 생성
# (예시로 핑 명령어를 가두는 프로필 생성 테스트)
aa-genprof /bin/ping
# 3. 생성된 프로필 파일 내부 확인 (/etc/apparmor.d/bin.ping)
# 파일 내용을 열어보면 /bin/ping이 실행되기 위해 필요한 라이브러리 읽기 권한들이 경로 형태로 기재되어 있습니다.
# 만약 허용되지 않은 디렉토리의 파일을 읽으려고 하면 AppArmor가 즉시 차단합니다.
# 4. 프로필을 완화(Complain) 모드로 변경하여 먼저 오작동 여부 테스트
aa-complain /bin/ping
# 5. 테스트 완료 후 실환경 적용을 위해 Enforce 모드로 강제 전환
aa-enforce /bin/ping
두 솔루션은 시스템을 보호한다는 궁극적인 목적은 같지만, 설계 철학과 구현 방식에서 명확한 차이를 보입니다.
| 비교 항목 | SELinux (Red Hat 계열 기본) | AppArmor (Debian/Ubuntu 계열 기본) |
| 제어 기준 | 주체와 객체의 보안 라벨(타입) 중심 | 파일 시스템의 절대 경로 중심 |
| 정교함 | 매우 세밀함 (시스템 전체 오브젝트 제어 가능) | 상대적으로 단순함 (개별 프로그램 단위 격리) |
| 설정 난이도 | 높음 (정책 언어와 컨텍스트 개념 이해 필수) | 낮음 (일반적인 파일 경로와 문자로 구성) |
| 파일 이동 시 | 파일 이동 시 라벨이 유실되면 수동 재설정 필요 | 경로 기준이므로 파일 내용만 맞으면 유지됨 |
MAC 시스템을 운영하다 보면 정상적인 서비스 설정임에도 불구하고 차단 벽에 막혀 프로세스가 시동되지 않는 경우가 허다합니다. 이때 무작정 보안 기능을 꺼버리기보다는 안전하게 진단하는 절차를 숙지해야 합니다.
보안 모듈이 차단한 모든 행위는 감사 로그에 고스란히 기록됩니다.
SELinux 감사 로그: /var/log/audit/audit.log 또는 /var/log/messages 파일에서 AVC (Access Vector Cache) 혹은 denied라는 단어를 검색합니다.
AppArmor 감사 로그: /var/log/syslog 또는 /var/log/kern.log 파일에서 apparmor="DENIED"라는 단어를 검색합니다.
문제가 보안 모듈 때문인지, 아니면 순수한 소스코드나 네트워크 오류인지 헷갈릴 때는 임시로 모드를 풀어서 확인합니다.
Bash
# SELinux 임시 완화 모드 전환 (재부팅 시 원복됨)
setenforce 0
# 서비스 테스트 수행 후 다시 방어벽 가동
setenforce 1
Bash
# AppArmor 특정 프로필 임시 완화 모드 전환
aa-complain /usr/sbin/nginx
# 서비스 테스트 수행 후 다시 방어벽 가동
aa-enforce /usr/sbin/nginx
만약 완화 모드에서 서비스가 정상 작동한다면, 그것은 100% 보안 모듈의 정책 누락이나 설정 오류이므로 앞서 다룬 실무 예제처럼 적절한 라벨을 부여하거나 프로필에 경로를 추가해 주면 됩니다.
오늘날의 보안 위협은 지능적이고 다각화되어 있습니다. 소스코드의 작은 취약점 하나로 인해 웹 서버의 통제권이 허무하게 해커에게 넘어가는 사고는 지금 이 순간에도 발생하고 있습니다.
SELinux와 AppArmor는 공격자가 시스템의 약점을 파고들어 최고 권한을 획득하더라도, 커널이라는 가장 밑바닥 단계에서 "네가 탈취한 프로세스는 이 방 밖으로 단 한 발짝도 나갈 수 없다"고 잠가버리는 최후의 방어선입니다. 초기 설정의 번거로움을 조금만 감수하고 실무 예제들을 바탕으로 정책을 다듬어 나간다면, 리눅스 서버의 보안성은 비교할 수 없을 정도로 단단해질 것입니다.