커널 보안 (Kernel Security)

커널 보안을 공격 표면 축소와 권한 경계 강제 관점에서 종합적으로 심층 분석합니다. LSM 프레임워크와 SELinux/AppArmor/Landlock 정책 모델, seccomp-bpf 시스템 콜 필터링, credentials·capabilities 권한 체계, namespace/cgroup와 결합한 격리 강화, KASLR/SMEP/SMAP/CFI 등 하드닝 기법, Audit·trace 기반 침해 추적, 컨테이너·서버 운영에서의 정책 배포와 예외 처리, 성능과 보안의 균형점을 찾는 실무 전략까지 다룹니다.

전제 조건: 동기화 기법메모리 배리어 문서를 먼저 읽으세요. 보안 취약점 분석은 동시성/수명주기 오류와 강하게 결합되므로, race/UAF 경로를 먼저 식별하는 관점이 필요합니다.
일상 비유: 이 주제는 시설 보안 점검과 침투 시뮬레이션과 비슷합니다. 출입 통제, 취약 구간, 경보 체계를 함께 점검하듯이, 커널 보안도 정책과 실행 경로를 동시에 검증해야 합니다.

핵심 요약

  • 전제 결합 — 보안, 성능, 아키텍처 지식을 함께 적용합니다.
  • 경계 명확화 — API 경계와 ABI 영향 범위를 먼저 확인합니다.
  • 위험 관리 — UAF, race, side-effect 가능성을 우선 점검합니다.
  • 계측 기반 판단 — 추측 대신 데이터로 개선 여부를 판단합니다.
  • 점진 적용 — 실험 범위를 작게 시작해 단계적으로 확장합니다.

단계별 이해

  1. 가설 수립
    문제와 개선 목표를 수치로 정의합니다.
  2. 제약 분석
    호환성, 안정성, 보안 제약을 먼저 확인합니다.
  3. 실험 적용
    최소 변경으로 효과와 부작용을 측정합니다.
  4. 정식 반영
    검증된 변경만 문서화해 반영합니다.
관련 표준: ISO/IEC 15408 (Common Criteria, IT 보안 평가), POSIX.1-2017 (capabilities, DAC), X.509 (인증서 기반 검증) — 커널 보안 프레임워크가 참조하는 보안 평가 및 인증 규격입니다. 종합 목록은 참고자료 — 표준 & 규격 섹션을 참고하세요.
예제 읽기 가이드: 이 문서는 개념 설명용 의사코드를 우선 사용하되, 실무 재현을 위해 실행 가능한 실습 예제도 함께 제공합니다. 코드 주석에 개념 예시가 표시된 블록은 구조 이해 목적이며, 실습 예제가 표시된 블록은 사용자 공간에서 컴파일/실행 검증을 고려한 형태입니다. 배포판별 보안 정책, 인증 상태, 기본 활성화 여부는 참고자료 — FIPS 지원/인증 상태참고자료 — 보안 & 암호화 표준을 단일 기준으로 확인하세요.

커널 보안 아키텍처 개요

Linux 커널의 보안 모델은 전통적인 DAC(Discretionary Access Control)에서 출발하여, MAC(Mandatory Access Control), 그리고 이를 유연하게 지원하는 LSM(Linux Security Modules) 프레임워크로 발전해왔습니다.

계층메커니즘설명
DACUNIX 퍼미션 (rwx), ACL파일 소유자가 접근 권한 결정. 전통적 UNIX 모델
CapabilitiesCAP_* 비트마스크root 권한을 세분화하여 최소 권한 원칙 적용
MAC (LSM)SELinux, AppArmor, SMACK, Landlock시스템 정책이 접근 권한 강제. 사용자가 변경 불가
seccompBPF 필터프로세스별 시스템 콜 제한 (샌드박싱)
Auditkauditd, auditctl보안 관련 이벤트 감사 로깅
하드닝KASLR, KPTI, CFI, SMEP/SMAP커널 익스플로잇 완화 기술

security/ 소스 트리 구조

security/ selinux/ apparmor/ smack/ landlock/ tomoyo/ yama/ security.c lsm_hooks.h commoncap.c LSM 프레임워크 코어 훅 정의 (~230개) POSIX Capabilities integrity/ keys/ lockdown/ safesetid/ IMA/EVM 키링 서브시스템 Lockdown LSM UID/GID 매핑

LSM (Linux Security Modules) 프레임워크

LSM은 커널에 보안 정책을 플러그인 형태로 삽입할 수 있는 프레임워크입니다. 시스템 콜 경로의 핵심 지점에 보안 훅(security hook)을 배치하여, 접근 제어 결정을 LSM 모듈에 위임합니다. security_hook_heads 구조체에 약 230개의 훅 포인트(파일·프로세스·소켓·IPC 등)가 정의되며, SELinux·AppArmor·Landlock 등이 훅을 등록합니다. 여러 LSM을 동시에 활성화하는 LSM 스태킹도 지원하며, 모든 LSM이 허용해야 접근이 승인됩니다(AND 로직).

LSM 프레임워크의 내부 구조·훅 디스패치 메커니즘·스태킹 설정에 대한 상세 내용은 LSM 프레임워크 심화를 참고하세요.

SELinux

SELinux(Security-Enhanced Linux)는 NSA가 개발한 MAC 시스템으로, Type Enforcement(TE), RBAC(Role-Based Access Control), MLS/MCS(Multi-Level/Multi-Category Security) 모델을 결합한 가장 포괄적인 리눅스 보안 프레임워크입니다. RHEL, Fedora, CentOS 등에서 기본 활성화되며, 커널 소스 security/selinux/ 디렉토리에 약 30,000줄 이상의 코드로 구현됩니다.

TE 모델은 모든 프로세스(subject)와 자원(object)에 보안 컨텍스트(security context: user:role:type:level)를 부여하고, 정책 규칙(allow, neverallow, type_transition 등)에 따라 접근을 허용/거부합니다. 프로세스가 다른 프로그램을 execve()하면 도메인 전이(domain transition)가 발생하여 새로운 보안 도메인으로 전환됩니다. AVC(Access Vector Cache)가 정책 결정을 캐싱하여 성능을 최적화합니다.

SELinux의 Type Enforcement, RBAC, MLS/MCS 모델, 커널 내부 구현(AVC, SID 테이블, policydb), 정책 컴파일 파이프라인, 도메인 전이 메커니즘에 대한 프레임워크 수준 상세 내용은 LSM 프레임워크 심화 -- SELinux 문서를 참고하세요.

SELinux 관리 및 상태 확인

# SELinux 기본 관리 명령
getenforce                   # Enforcing / Permissive / Disabled
setenforce 0                 # Permissive로 전환 (런타임)
sestatus                     # 상세 상태 정보
ls -Z /etc/passwd            # 파일 보안 컨텍스트 확인
ps -eZ | grep sshd           # 프로세스 보안 컨텍스트 확인

# 보안 컨텍스트 확인 및 변경
chcon -t httpd_content_t /var/www/html/newpage.html          # 일시적 변경
semanage fcontext -a -t httpd_content_t "/srv/web(/.*)?"     # 영구적 등록
restorecon -Rv /srv/web                                       # 정책 기반 적용

# 포트 타입 라벨링 (httpd가 8080 포트 사용 허용)
semanage port -a -t http_port_t -p tcp 8080

SELinux Boolean 시스템

Boolean은 재컴파일 없이 정책의 특정 기능을 런타임에 켜고 끌 수 있는 스위치입니다. 약 300~400개의 boolean이 사전 정의되어 있으며, 가장 빈번한 SELinux 관련 문제를 boolean 토글만으로 해결할 수 있습니다.

# boolean 확인 및 변경
getsebool -a                                  # 모든 boolean 목록
getsebool httpd_can_network_connect           # 특정 boolean 확인
setsebool -P httpd_can_network_connect on     # 영구적 변경 (-P 플래그)
semanage boolean -l | grep httpd              # boolean 설명 확인
Boolean기본값설명
httpd_can_network_connectoffhttpd가 네트워크 포트에 연결 허용 (프록시, DB 연결)
httpd_can_network_connect_dboffhttpd가 데이터베이스 포트에 연결 허용
httpd_enable_homedirsoffhttpd가 사용자 홈 디렉토리 접근 허용
samba_enable_home_dirsoffSamba가 홈 디렉토리 공유 허용
container_manage_cgroupoff컨테이너가 cgroup 직접 관리 허용

SELinux 트러블슈팅

SELinux에서 접근이 거부되면 AVC 거부 메시지/var/log/audit/audit.log에 기록됩니다. audit2allowaudit2why 도구로 거부 원인을 분석하고 정책 모듈을 생성할 수 있습니다.

# AVC 거부 원인 분석
ausearch -m avc --start today | audit2why

# 정책 모듈 자동 생성 및 설치
ausearch -m avc --start today | audit2allow -M my_httpd_policy
semodule -i my_httpd_policy.pp
거부 패턴원인해결 방법
파일 타입 불일치파일이 잘못된 SELinux 타입을 가짐restorecon -Rv /path 또는 semanage fcontext
네트워크 접근 거부서비스가 비표준 포트 사용semanage port -a -t type -p proto port
Boolean 비활성화필요한 기능이 boolean으로 비활성화됨setsebool -P boolean_name on
도메인 전이 실패실행 파일 타입 미지정chcon -t xxx_exec_t /path/to/binary
audit2allow 남용 경고:

audit2allow가 생성하는 정책을 무분별하게 적용하면 보안 수준이 크게 저하됩니다. 항상 먼저 파일 타입 복원(restorecon)이나 boolean 변경으로 해결 가능한지 확인하세요. audit2allow는 최후의 수단으로 사용하며, 생성된 규칙을 반드시 검토한 후 적용하세요.

MCS (Multi-Category Security) -- 컨테이너 격리

MCS는 MLS의 하위 집합으로, 동일 타입의 프로세스/파일을 카테고리로 분리합니다. 컨테이너 런타임(Docker, Podman)에서 각 컨테이너에 고유 카테고리를 할당하여 컨테이너 간 격리를 강화합니다. RHEL/Fedora의 기본 정책(targeted)은 MCS를 사용합니다.

# 컨테이너별 MCS 카테고리 확인
podman inspect --format '{{.ProcessLabel}}' my-container
# system_u:system_r:container_t:s0:c42,c128

# 동일 타입(container_t)이지만 카테고리가 다르면 상호 접근 불가
# Container A: s0:c42,c128  /  Container B: s0:c256,c789

AppArmor

AppArmor는 경로(path) 기반 MAC 시스템으로, /etc/apparmor.d/에 프로그램별 프로파일을 정의하여 파일, 네트워크, capability 접근을 제어합니다. SELinux의 라벨(xattr) 기반 모델과 달리 파일 경로를 기준으로 정책을 적용하므로 설정이 간단하며, Ubuntu, SUSE, Debian 등에서 기본 사용됩니다. 프로파일은 enforce(위반 차단) 또는 complain(위반 로깅만) 모드로 동작합니다.

AppArmor의 프레임워크 내부 구현(aa_profile, aa_label, DFA 규칙 매칭), 프로파일 문법 상세, SELinux와의 비교, hat(서브 프로파일) 메커니즘에 대한 상세 내용은 LSM 프레임워크 심화 -- AppArmor 문서를 참고하세요.

프로파일 예제: nginx

# /etc/apparmor.d/usr.sbin.nginx (요약)
#include <tunables/global>

/usr/sbin/nginx {
  #include <abstractions/base>
  #include <abstractions/nameservice>

  capability net_bind_service,
  capability setuid,
  capability setgid,

  network inet stream,
  network inet6 stream,

  /usr/sbin/nginx         mr,
  /etc/nginx/**           r,
  /etc/ssl/certs/**       r,
  /var/www/**             r,
  /var/log/nginx/*.log    a,
  /run/nginx.pid          rw,

  deny /etc/shadow        r,
  deny /root/**           rwlx,
}

프로파일 관리 명령

# 프로파일 상태 확인
aa-status

# 프로파일 모드 전환
aa-enforce /etc/apparmor.d/usr.sbin.nginx    # enforce 모드로
aa-complain /etc/apparmor.d/usr.sbin.nginx   # complain 모드로
aa-disable /etc/apparmor.d/usr.sbin.nginx    # 비활성화

# 자동 프로파일 생성 (학습 모드)
aa-genprof /usr/sbin/nginx

# 위반 로그 기반 프로파일 업데이트
aa-logprof

# 프로파일 수동 로드/언로드/검증
apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx   # 리로드
apparmor_parser -R /etc/apparmor.d/usr.sbin.nginx   # 언로드
apparmor_parser -p /etc/apparmor.d/usr.sbin.nginx   # 문법 검증
선택 가이드:

규정 준수(PCI-DSS, HIPAA, Common Criteria 등)가 요구되면 SELinux를, 빠른 배포와 간단한 서비스 격리가 목적이면 AppArmor를 선택하세요. 두 LSM은 동시에 사용할 수 없으며(major LSM은 하나만 활성화), 컴파일 시 CONFIG_DEFAULT_SECURITY와 부팅 시 lsm= 파라미터로 선택합니다.

SMACK (Simplified Mandatory Access Control Kernel)

SMACK은 라벨 기반 MAC으로, SELinux보다 훨씬 단순한 규칙 모델을 제공합니다. IoT 디바이스, 임베디드 시스템(Tizen 등)에서 주로 사용됩니다.

# SMACK 라벨 확인
attr -S -g SMACK64 /path/to/file
# 또는
getfattr -n security.SMACK64 /path/to/file

# SMACK 규칙 형식: subject object access
# access: r(read) w(write) x(execute) a(append) t(transmute) l(lock)
echo "WebApp DataFiles rwx" > /sys/fs/smackfs/load2
echo "System WebApp rx"     > /sys/fs/smackfs/load2

# 특수 라벨
# _     : 최소 권한 (floor label)
# ^     : 최대 권한 (hat/ceiling)
# *     : 범용 접근 (star label - 모든 읽기 허용)
💡

SMACK은 정책 규칙이 단순한 subject object access 3-tuple이므로, SELinux의 복잡한 Type Enforcement 정책을 사용하기 어려운 임베디드 환경에 적합합니다. 상세 내용은 LSM 프레임워크 심화 — SMACK 문서를 참고하세요.

Landlock

Landlock은 커널 5.13에서 도입된 비특권(unprivileged) 샌드박싱 LSM입니다. 일반 사용자가 root 권한 없이 프로세스의 파일시스템/네트워크 접근을 제한할 수 있습니다. 3단계 API(landlock_create_ruleset()landlock_add_rule()landlock_restrict_self())로 동작하며, 적용 후 되돌릴 수 없습니다.

ABI 버전이 커널마다 확장되어, v1(5.13)에서는 파일시스템만, v4(6.7)부터는 네트워크 포트 제어까지 지원합니다. seccomp과 유사하게 PR_SET_NO_NEW_PRIVS가 필요합니다.

Landlock 3단계 API 사용법, ABI 버전별 기능, 코드 예제에 대한 상세 내용은 LSM 프레임워크 심화 — Landlock 문서를 참고하세요.

seccomp-bpf

seccomp(Secure Computing)은 프로세스가 호출할 수 있는 시스템 콜을 제한하는 커널 보안 메커니즘입니다. Strict 모드(read/write/exit/sigreturn만 허용)와 Filter 모드(cBPF 프로그램으로 시스템 콜별 세밀한 정책 적용)를 지원합니다. 필터 체인은 누적(additive)되어 점점 더 제한적으로만 변경되며, SECCOMP_RET_KILL_PROCESS부터 SECCOMP_RET_ALLOW까지 8단계의 반환 액션 우선순위를 갖습니다.

커널 5.0+에서는 SECCOMP_RET_USER_NOTIF로 시스템 콜을 사용자 공간 감독자에게 위임할 수 있으며, 컨테이너 런타임(runc, containerd)에서 mount, mknod 등을 안전하게 에뮬레이션하는 데 사용됩니다. Bitmap 캐시 최적화 덕분에 ALLOW 확정 시스템 콜은 BPF 실행을 생략합니다.

# seccomp 상태 확인
grep Seccomp /proc/self/status
# Seccomp:        2    (filter)
# Seccomp_filters: 3   (필터 체인 길이, 5.8+)

# audit 로그에서 seccomp 이벤트 검색
ausearch -m SECCOMP --start today
seccomp의 커널 내부 구현(seccomp_filter, seccomp_data), 필터 체인 메커니즘, SECCOMP_RET_USER_NOTIF 감독자 구현, cBPF 프로그램 작성, libseccomp API, Bitmap 최적화에 대한 상세 내용은 LSM 프레임워크 심화 — Seccomp 문서를 참고하세요.

Credentials & Capabilities

Linux 커널에서 모든 보안 결정의 기반이 되는 것은 struct cred입니다. 프로세스의 UID/GID, capability 세트(5종: inheritable, permitted, effective, bounding, ambient), LSM 보안 데이터가 모두 이 구조체에 집중됩니다. Credential은 COW(Copy-on-Write) 방식으로 관리되며, prepare_creds()로 복사 후 수정하고 commit_creds()로 원자적 교체합니다.

struct cred의 내부 구조, COW 생명주기(prepare_creds/commit_creds), real_cred vs cred 구분, ambient capability 계산식에 대한 커널 구현 상세는 LSM 프레임워크 심화 -- Credentials 문서를 참고하세요.

Capability 시스템

Linux capability는 전통적인 root 특권을 약 41개의 독립 비트로 분리합니다. 기초 개념은 Linux Containers 페이지를 참조하세요.

Capability설명
CAP_CHOWN파일 소유자 변경
CAP_DAC_OVERRIDE파일 DAC 권한 무시
CAP_NET_ADMIN네트워크 관리 (인터페이스, 라우팅, 방화벽 등)
CAP_NET_BIND_SERVICE1024 이하 포트 바인드
CAP_NET_RAWRAW/PACKET 소켓 사용
CAP_SYS_ADMIN시스템 관리 (mount, swapon, ioctl 등) -- 가장 넓은 범위
CAP_SYS_PTRACE임의 프로세스 ptrace
CAP_SYS_MODULE커널 모듈 로드/언로드
CAP_SETUID / CAP_SETGIDUID/GID 변경
CAP_BPFBPF 프로그램 로드 (5.8+)
CAP_PERFMON성능 모니터링 (perf_event_open) (5.8+)
CAP_CHECKPOINT_RESTORECRIU 체크포인트/복원 (5.9+)

파일 Capability 관리

# 파일에 capability 부여 (setuid 대체)
sudo setcap cap_net_bind_service=ep /usr/bin/myserver
# e = effective, p = permitted, i = inheritable

# 파일 capability 확인
getcap /usr/bin/myserver

# 프로세스의 현재 capability 확인
cat /proc/self/status | grep Cap
# CapInh / CapPrm / CapEff / CapBnd / CapAmb

# capsh로 디코딩
capsh --decode=000001ffffffffff

커널 하드닝

KASLR (Kernel Address Space Layout Randomization)

커널 코드, 모듈, 물리 메모리 매핑의 베이스 주소를 부팅 시 랜덤으로 배치하여, 메모리 레이아웃을 예측할 수 없게 합니다. 공격자가 커널 심볼의 정확한 주소를 알아야 하는 ROP/JOP 공격을 근본적으로 어렵게 만드는 1차 방어선입니다.

ℹ️

KASLR은 x86_64에서 커널 3.14, arm64에서 4.6부터 지원됩니다. CONFIG_RANDOMIZE_BASE=y로 활성화하며, 배포판별 기본 활성화 정책은 참고자료에서 최신 상태를 확인하세요.

KASLR의 랜덤화 대상 영역

영역CONFIG 옵션랜덤화 범위 (x86_64)설명
커널 텍스트CONFIG_RANDOMIZE_BASE1GB 범위 내vmlinux 로드 주소를 2MB 정렬로 랜덤 배치
모듈 영역CONFIG_RANDOMIZE_BASE1GB 범위 내모듈 로드 베이스 주소 랜덤화
물리 메모리 매핑CONFIG_RANDOMIZE_MEMORY1TB 범위 내direct map, vmalloc, vmemmap 영역 랜덤화
스택CONFIG_RANDOMIZE_KSTACK_OFFSET엔트리마다syscall 진입 시 스택 오프셋을 랜덤 조정

KASLR 부팅 시 랜덤화 과정

/* arch/x86/boot/compressed/kaslr.c */
/* 부트로더 → 압축 해제기(decompressor)에서 KASLR 처리 */

/* 1단계: 엔트로피 소스 수집 */
static unsigned long get_boot_seed(void)
{
    /* RDTSC (CPU 타임스탬프 카운터) */
    /* RDRAND/RDSEED (하드웨어 RNG, 지원 시) */
    /* i8254 타이머 카운터 */
    /* 부트로더가 전달한 시드 (EFI RNG Protocol 등) */
}

/* 2단계: 사용 가능한 물리 메모리 슬롯 탐색 */
/* e820 메모리 맵에서 커널 크기를 수용할 수 있는 영역 목록 생성 */
/* 이미 사용된 영역(initrd, 부트 파라미터 등)은 제외 */

/* 3단계: 랜덤 슬롯 선택 → 커널 재배치 */
static unsigned long find_random_virt_addr(
    unsigned long minimum,
    unsigned long image_size)
{
    /* slots = 사용 가능 슬롯 수 */
    /* random_addr = minimum + (kaslr_get_random_long() % slots) * alignment */
    /* x86_64: 2MB 정렬, arm64: 64KB 또는 2MB 정렬 */
}

FKASLR (Function-Granular KASLR)

기본 KASLR은 커널 전체를 하나의 블록으로 이동하므로, 하나의 주소만 유출되면 나머지 모든 심볼의 오프셋을 계산할 수 있습니다. FKASLR은 함수 단위로 재배치하여 이 한계를 극복합니다.

/* FKASLR: CONFIG_FG_KASLR (x86_64, 실험적) */
/* 컴파일 시 -ffunction-sections 플래그로 함수별 별도 섹션 생성 */
/* 부팅 시 각 함수 섹션을 독립적으로 랜덤 재배치 */

/* 장점: 단일 주소 유출로 다른 함수 위치 추론 불가 */
/* 단점: 부팅 시간 증가, 캐시 지역성 저하, 코드 크기 증가 */
/*        /proc/kallsyms 심볼 순서가 매 부팅마다 달라짐 */

KASLR 우회 공격과 대응

우회 기법원리대응 기술
커널 정보 누출초기화 안 된 메모리에서 커널 포인터 유출CONFIG_INIT_ON_ALLOC_DEFAULT_ON, HARDENED_USERCOPY
/proc/kallsyms심볼 주소 직접 읽기kptr_restrict=1/2로 비루트 사용자 차단
dmesg 주소 유출%pK 대신 %p로 포인터 출력dmesg_restrict=1, %pK 포맷 사용 강제
타이밍 사이드채널캐시/TLB 타이밍으로 주소 추론KPTI, 사이트 격리
Spectre 변종투기적 실행으로 커널 메모리 읽기Retpoline, IBRS, STIBP
하드웨어 DMAIOMMU 없이 DMA로 커널 메모리 접근IOMMU(Intel VT-d, AMD-Vi) 필수 활성화

KASLR 상태 확인 및 관련 sysctl

# KASLR 상태 확인
cat /proc/cmdline | grep -o 'nokaslr\|kaslr'
dmesg | grep "KASLR"
# KASLR enabled (커널 기본 활성화)

# 커널 포인터 보호 수준
cat /proc/sys/kernel/kptr_restrict
# 0: 모든 사용자에게 심볼 주소 노출 (위험)
# 1: 비루트 사용자에게 0x0000000000000000으로 마스킹
# 2: 루트 포함 모든 사용자에게 마스킹 (권장)

# dmesg 접근 제한
sysctl kernel.dmesg_restrict=1

# 커널 텍스트 오프셋 확인 (디버깅 용도, 루트 필요)
# /proc/kallsyms에서 _text 심볼 주소 확인
grep " _text$" /proc/kallsyms
# 매 부팅마다 다른 주소가 출력됨

# 스택 오프셋 랜덤화 확인
cat /proc/sys/kernel/randomize_kstack_offset
# 1 = 활성화 (syscall 진입 시 스택 오프셋 랜덤)
nokaslr 커널 파라미터:

디버깅 시 KASLR을 비활성화하면 커널 주소가 고정되어 심볼 디버깅이 쉬워집니다. 하지만 프로덕션 환경에서는 절대 사용하지 마십시오. QEMU/GDB 커널 디버깅 시에만 nokaslr을 사용하는 것이 일반적입니다.

KPTI (Kernel Page Table Isolation)

Meltdown (CVE-2017-5754) 취약점 완화 기술입니다. 사용자 공간에서 커널 페이지 테이블을 분리하여, 사용자 모드에서 커널 메모리를 투기적 실행으로 읽을 수 없게 합니다.

설명 요약:
  • arch/x86/mm/pti.c */
  • 사용자 공간용과 커널 공간용 두 벌의 페이지 테이블 유지 */
  • 사용자 PGD: 최소한의 커널 매핑 (entry/exit 코드만) */
  • 커널 PGD: 전체 커널 메모리 매핑 */
  • syscall/인터럽트 진입 시 PGD 전환 → CR3 레지스터 교체 */
  • KPTI 성능 영향: 시스템 콜 집중 워크로드에서 1~5% 오버헤드 */
  • PCID (Process Context Identifier) 사용 시 TLB 플러시 감소 */
# KPTI 상태 확인
dmesg | grep "page tables isolation"
# Kernel/User page tables isolation: enabled

cat /sys/devices/system/cpu/vulnerabilities/meltdown
# Mitigation: PTI

SMEP/SMAP

기술설명방어 대상
SMEP (Supervisor Mode Execution Prevention)Ring 0에서 사용자 공간 코드 실행 차단ret2usr 공격
SMAP (Supervisor Mode Access Prevention)Ring 0에서 사용자 공간 메모리 접근 차단커널이 의도치 않게 사용자 데이터 읽기
/* SMAP 일시 비활성화 (사용자 데이터 복사 시) */
stac();  /* Set AC flag → SMAP 일시 해제 */
copy_from_user(kbuf, ubuf, len);
clac();  /* Clear AC flag → SMAP 재활성화 */

/* copy_from_user/copy_to_user 내부에서 자동 처리 */

Stack Protector

설명 요약:
  • 스택 버퍼 오버플로 감지
  • CONFIG_STACKPROTECTOR=y (기본 보호 - 취약한 함수만)
  • CONFIG_STACKPROTECTOR_STRONG=y (강한 보호 - 배열/주소 사용 함수)
  • 함수 프롤로그에 canary 값 삽입
  • 함수 에필로그에서 canary 검증 → 변조 시 panic
  • canary는 per-CPU 변수로 스레드마다 다름

CFI (Control Flow Integrity)

간접 호출(함수 포인터)의 목적지를 검증하여 ROP/JOP 공격을 차단합니다. 리눅스 커널은 수천 개의 함수 포인터(file_operations, vm_operations_struct 등)를 사용하므로, 함수 포인터 오염을 통한 제어 흐름 탈취는 커널 익스플로잇의 핵심 기법입니다. CFI는 이러한 공격을 런타임에 탐지하여 차단합니다.

ℹ️

Forward-edge vs Backward-edge CFI: Forward-edge CFI는 간접 호출(call *%rax)과 간접 점프의 목적지를 검증합니다 (JOP/COP 방어). Backward-edge CFI는 함수 리턴 주소를 보호합니다 (ROP 방어). 완전한 제어 흐름 보호를 위해 둘 다 필요합니다.

CFI 기술 비교

기술커널 버전아키텍처유형설명
Clang CFI (kCFI)6.1+x86_64, arm64Forward-edge (SW)간접 호출 시 함수 시그니처 해시 검증. CONFIG_CFI_CLANG
FineIBT6.2+x86_64 (Intel)Forward-edge (HW+SW)Intel CET IBT + kCFI 결합. 하드웨어 기반 CFI. CONFIG_X86_KERNEL_IBT
Shadow Call Stack5.8+ (arm64)arm64Backward-edge별도 스택에 리턴 주소 보관. CONFIG_SHADOW_CALL_STACK
Intel CET Shadow Stack6.6+x86_64 (Intel)Backward-edge (HW)하드웨어 Shadow Stack으로 리턴 주소 보호. CONFIG_X86_USER_SHADOW_STACK

kCFI (Kernel Control Flow Integrity) 동작 원리

kCFI는 Clang 컴파일러가 모든 간접 호출 대상 함수에 타입 해시를 프리픽스로 삽입하고, 호출 직전에 해시를 검증하는 소프트웨어 기반 CFI입니다.

/* kCFI 컴파일러 계측 예시 (개념적) */

/* 원본 코드 */
struct file_operations fops = {
    .read = my_read,
    .write = my_write,
};
filp->f_op->read(filp, buf, count, pos);

/* kCFI 계측 후 (어셈블리 수준에서 발생하는 동작) */
/*
 * 1. 컴파일러가 각 함수 앞에 4바이트 타입 해시 삽입:
 *    my_read:
 *      .long 0xDEAD1234   ← 함수 시그니처 해시 (ssize_t(*)(struct file*, char*, size_t, loff_t*))
 *      push  %rbp          ← 실제 함수 시작
 *      ...
 *
 * 2. 간접 호출 직전에 해시 검증 코드 삽입:
 *      mov   f_op(%rdi), %rax     ← 함수 포인터 로드
 *      movl  -4(%rax), %ecx       ← 대상 함수 앞의 해시 로드
 *      cmpl  $0xDEAD1234, %ecx    ← 기대 해시와 비교
 *      jne   __cfi_failure         ← 불일치 시 → BUG()/panic
 *      call  *%rax                 ← 일치 시 정상 호출
 */
kCFI vs 이전 Clang CFI:

커널 5.13에서 도입된 초기 Clang CFI는 간접 호출을 점프 테이블로 치환하는 방식이었으나, 크로스 모듈 호출 처리가 복잡하고 LTO(Link-Time Optimization)가 필수였습니다. 커널 6.1의 kCFI는 해시 기반 검증으로 전환하여 LTO 없이도 동작하고, 모듈과의 호환성이 크게 개선되었습니다.

FineIBT (Forward-Edge CFI with Intel IBT)

FineIBT는 Intel CET(Control-flow Enforcement Technology)의 IBT(Indirect Branch Tracking)와 kCFI를 결합한 하드웨어 가속 CFI입니다. IBT만 단독 사용 시 모든 ENDBR64 명령어가 유효한 분기 대상이 되어 보호가 약하지만, kCFI의 타입 해시 검증을 결합하면 강력한 forward-edge 보호가 가능합니다.

설명 요약:
  • FineIBT 동작 메커니즘 (x86_64 어셈블리 수준) */
  • [함수 프롤로그] (IBT + kCFI 결합)
  • my_read:
  • endbr64 ← IBT: 유효한 간접 분기 대상 표시
  • subl $0xDEAD1234, %r10d ← kCFI: 전달된 해시에서 기대값을 뺌
  • jz .Lok ← 0이면(일치) 정상 진행
  • ud2 ← 불일치 시 #UD 예외 → BUG()
  • .Lok:
  • push %rbp
  • ...
  • [간접 호출 사이트]
  • movl $0xDEAD1234, %r10d ← 기대 해시를 r10에 로드
  • call *%rax ← 간접 호출 (IBT가 endbr64 존재 확인)
  • FineIBT 활성화 조건 */
  • CONFIG_X86_KERNEL_IBT=y ← IBT 활성화
  • CONFIG_CFI_CLANG=y ← kCFI 활성화
  • CPU가 CET-IBT 지원 (Intel 12세대+, AMD Zen 4+)
  • 커널이 Clang으로 빌드됨
ℹ️

FineIBT는 커널 부팅 시 CPU의 CET-IBT 지원 여부를 자동 감지합니다. 지원하는 CPU에서는 FineIBT(HW+SW), 미지원 CPU에서는 순수 kCFI(SW only)로 자동 폴백합니다. cfi= 커널 파라미터로 동작을 제어할 수 있습니다: cfi=kcfi (소프트웨어만), cfi=fineibt (하드웨어+소프트웨어), cfi=off (비활성화).

Shadow Call Stack (arm64)

Shadow Call Stack(SCS)은 리턴 주소를 일반 스택과 분리된 별도의 그림자 스택에 저장하여, 스택 버퍼 오버플로로 인한 리턴 주소 변조를 방지하는 backward-edge CFI 기법입니다.

/* Shadow Call Stack 동작 (arm64) */
/*
 * arm64에서 x18 레지스터를 Shadow Call Stack 포인터로 예약
 * (x18은 플랫폼 레지스터, 일반 코드에서 사용 금지)
 *
 * [함수 프롤로그]
 *   str  x30, [x18], #8     ← 리턴 주소(LR)를 SCS에 push
 *   stp  x29, x30, [sp, #-16]!  ← 일반 스택에도 저장 (프레임 포인터)
 *
 * [함수 에필로그]
 *   ldr  x30, [x18, #-8]!   ← SCS에서 리턴 주소 복원
 *   ldp  x29, x30, [sp], #16
 *   ret                      ← SCS의 주소로 리턴
 *
 * 공격자가 일반 스택의 리턴 주소를 변조해도,
 * 실제 리턴은 SCS의 원본 주소를 사용하므로 ROP 공격 실패
 */

/* SCS 메모리 할당 (per-task) */
/* arch/arm64/kernel/scs.c */
#define SCS_SIZE       (1 << 10)  /* 1KB (128개 리턴 주소) */
#define SCS_GFP        (GFP_KERNEL | __GFP_ZERO)

/* 태스크 생성 시 SCS 할당, 종료 시 해제 */
/* vmalloc 영역에 할당하여 guard page로 오버플로 감지 */

AArch64 GCS — Guarded Control Stack (커널 6.13+)

커널 6.13에서 AArch64 GCS(Guarded Control Stack) 유저스페이스 지원이 추가되었습니다. GCS는 Armv9.4-A의 하드웨어 기능으로, Shadow Call Stack의 소프트웨어 구현을 하드웨어로 대체합니다.

구분Shadow Call Stack (기존)GCS (v6.13+)
구현컴파일러 삽입 코드 (x18 레지스터)CPU 하드웨어 (별도 GCS 메모리)
보호 대상커널 코드만유저스페이스 + 커널 모두
변조 방지소프트웨어 기반 (우회 가능)하드웨어 강제 (일반 store 명령으로 GCS 메모리 쓰기 불가)
오버헤드<1%~0% (하드웨어 지원)
GCS 활성화: prctl(PR_SET_SHADOW_STACK_STATUS)로 스레드별 GCS를 활성화합니다. GCS는 x86의 Intel CET Shadow Stack과 유사한 개념이며, 함수 호출 시 리턴 주소가 별도의 하드웨어 보호 스택에 자동 저장됩니다.

CFI 위반 처리와 디버깅

# CFI 위반 시 커널 로그 예시
# CFI failure at some_function+0x42/0x100 (target: 0xffffffff81234567)
# kernel BUG at arch/x86/kernel/cfi.c:NN!

# CFI 모드 확인
dmesg | grep -i "cfi\|fineibt\|IBT"
# x86/cfi: Switching to FineIBT CFI
# 또는: x86/cfi: Using kCFI

# 모듈에서 CFI 지원 확인
modinfo some_module | grep cfi
# CFI가 활성화된 커널에서 비-CFI 모듈 로드 시
# 간접 호출이 검증 실패할 수 있음

# CONFIG_CFI_PERMISSIVE=y 사용 시 위반을 경고만 출력 (디버깅용)
# 프로덕션에서는 반드시 permissive 비활성화
CFI와 외부 모듈 호환성:

kCFI가 활성화된 커널에서 서드파티/out-of-tree 모듈을 사용하려면 해당 모듈도 동일한 Clang 버전과 CFI 옵션으로 빌드해야 합니다. GCC로 빌드된 모듈은 kCFI 해시가 없으므로 간접 호출 시 CFI 위반이 발생합니다. DKMS 모듈이나 NVIDIA 드라이버 등은 CFI 환경에서 호환성 문제가 있을 수 있습니다.

FORTIFY_SOURCE

설명 요약:
  • CONFIG_FORTIFY_SOURCE=y */
  • 컴파일 타임 + 런타임에 버퍼 오버플로 감지 */
  • memcpy, strcpy, sprintf 등의 안전한 래퍼 제공 */
  • 예: memcpy 크기가 대상 버퍼를 초과하면 */
  • 컴파일 타임: 경고/에러 */
  • 런타임: fortify_panic() 호출 → BUG() */

하드닝 CONFIG 옵션 요약

CONFIG 옵션기능오버헤드
CONFIG_RANDOMIZE_BASEKASLR무시 가능
CONFIG_MITIGATION_PAGE_TABLE_ISOLATIONKPTI (Meltdown)1~5%
CONFIG_MITIGATION_RETPOLINESpectre v2 완화~2%
CONFIG_STACKPROTECTOR_STRONG스택 canary<1%
CONFIG_CFI_CLANG간접 호출 CFI~1%
CONFIG_X86_KERNEL_IBTFineIBT (CET)<1%
CONFIG_SHADOW_CALL_STACK리턴 주소 보호 (arm64)<1%
CONFIG_FORTIFY_SOURCE버퍼 오버플로 감지무시 가능
CONFIG_HARDENED_USERCOPY사용자-커널 복사 검증<1%
CONFIG_INIT_ON_ALLOC_DEFAULT_ON할당 시 0 초기화1~3%
CONFIG_INIT_ON_FREE_DEFAULT_ON해제 시 0 초기화1~5%
CONFIG_SLAB_FREELIST_RANDOMSlab freelist 랜덤화무시 가능
CONFIG_SLAB_FREELIST_HARDENEDSlab freelist 포인터 보호무시 가능
CONFIG_STRICT_KERNEL_RWX커널 코드 W^X 강제무시 가능
CONFIG_STRICT_MODULE_RWX모듈 코드 W^X 강제무시 가능

io_uring 보안 훅 (v6.15+)

커널 6.15에서 io_uring에 대한 LSM 보안 훅이 추가되었습니다. 기존에는 io_uring이 LSM(SELinux, AppArmor 등)의 시스템 콜 훅을 우회할 수 있는 구조적 문제가 있었으나, 전용 보안 훅을 통해 io_uring 연산에도 MAC 정책을 적용할 수 있게 되었습니다.

검사 시점LSM 훅설명
SQE 제출 시security_uring_sqe()각 io_uring 연산의 권한 검사
링 생성 시security_uring_setup()io_uring 인스턴스 생성 허용 여부
커맨드 실행 시security_uring_cmd()IORING_OP_URING_CMD 패스스루 검사
보안 정책 주의: io_uring LSM 훅이 없는 커널(6.15 미만)에서는 seccomp으로 io_uring_setup / io_uring_enter 시스템 콜 자체를 차단하는 것이 권장됩니다. Chrome, Docker 등은 이미 seccomp 프로파일에서 io_uring을 기본 차단하고 있습니다.

Audit 서브시스템

Linux Audit 서브시스템은 커널 수준에서 보안 관련 이벤트를 기록하는 프레임워크입니다. 시스템 콜, 파일 접근, 네트워크 연결, 사용자 인증 등 거의 모든 커널 활동을 감사할 수 있으며, PCI-DSS, HIPAA, SOX, CAPP(Controlled Access Protection Profile) 등 보안 컴플라이언스 인증의 필수 요구사항을 충족합니다.

ℹ️

Audit 서브시스템은 CONFIG_AUDIT (기본 프레임워크)와 CONFIG_AUDITSYSCALL (시스템 콜 감사)로 활성화됩니다. 배포판별 기본 활성화 정책은 참고자료에서 최신 상태를 확인하세요. 부팅 시 audit=1 커널 파라미터로 조기 부팅 단계부터 감사를 시작할 수 있습니다.

아키텍처

Audit 서브시스템은 커널 공간의 감사 후크(hook)와 사용자 공간의 auditd 데몬으로 구성됩니다. 커널이 이벤트를 수집하고, NETLINK_AUDIT 소켓을 통해 사용자 공간으로 전달합니다.

커널 공간 사용자 공간 시스템 콜 진입 audit_syscall_entry() audit_context 이벤트 데이터 수집 시스템 콜 종료 audit_syscall_exit() audit_buffer 메시지 직렬화 필터 엔진 규칙 매칭 (5개 리스트) kauditd 커널 스레드 audit_skb_queue netlink 전송 큐 NETLINK_AUDIT AF_NETLINK 소켓 auditd 감사 데몬 audisp 플러그인 이벤트 디스패처 /var/log/audit/ ausearch aureport

감사 규칙 평가 체인

감사 이벤트 발생 시, 커널은 5개의 필터 리스트를 정해진 순서로 평가합니다. 각 리스트의 규칙은 순차적으로 매칭되며, 첫 매칭 규칙의 action(AUDIT_ALWAYS/AUDIT_NEVER)이 적용됩니다. 규칙 배치 순서가 성능에 직접적 영향을 미치므로, 빈도 높은 제외 규칙을 앞에 배치하는 것이 중요합니다.

이벤트 발생 시스템 콜/fork 1. task fork/clone 시점 uid, gid, pid loginuid, sessionid 2. exit syscall 종료 시 모든 필드 사용 가능 (가장 많이 사용) 3. user 사용자 공간 메시지 uid, pid, loginuid msgtype 4. exclude 전송 직전 제외 msgtype 기반 노이즈 필터링 5. filesystem fstype 기반 감시 규칙 매칭 요소 필드 비교 (Field) -F uid=0 -F auid>=1000 -F exit=-EACCES -F arch=b64 AUDIT_EQUAL / NOT_EQUAL Syscall 매칭 -S execve -S openat -S open -S connect -S all (모든 syscall) 비트마스크 O(1) 검사 파일 감시 (Watch) -w /etc/passwd -p wa -w /etc/sudoers -p wa audit_watch + fsnotify audit_tree (디렉토리) inode 기반 추적 action = AUDIT_ALWAYS → audit_context.state = RECORD → 로그 생성 action = AUDIT_NEVER → audit_context.state = DISABLED → 로그 생략 성능 최적화: never(제외) 규칙을 always 규칙보다 앞에 배치하여 불필요한 평가 방지

감사 이벤트 레코드 계층 구조

하나의 감사 이벤트는 여러 레코드 타입으로 구성됩니다. SYSCALL 레코드가 부모 역할을 하며, 관련 정보가 자식 레코드(PATH, CWD, EXECVE, SOCKADDR, PROCTITLE)로 분리됩니다. 모든 레코드는 동일한 시리얼 번호(EPOCH:SERIAL)로 연결됩니다.

이벤트 그룹: msg=audit(1706000000.123:789) — 동일 시리얼로 연결 SYSCALL (type=1300) 부모 레코드 — syscall NR, 인자, UID, PID, exe, key arch=c000003e syscall=257 success=yes exit=3 auid=1000 PATH (1302) name, inode, dev mode, ouid, ogid item=0,1,... (다중) CWD (1307) cwd="/home/user" 현재 작업 디렉토리 EXECVE (1309) argc, a0, a1, a2 실행 인자 전체 긴 인자는 분할 SOCKADDR (1306) saddr (hex 인코딩) 소켓 주소 정보 PROCTITLE (1327) proctitle (hex) 프로세스 커맨드라인 EOE (End of Event) — type=1320 동일 시리얼의 모든 레코드 전송 완료 표시
레코드 재조립:

ausearch는 동일 시리얼(EPOCH:SERIAL)을 가진 모든 레코드를 자동으로 그룹화하여 표시합니다. --raw 옵션으로 원시 형식을 확인하면 각 레코드가 별도 줄로 출력되며, --format text로 사람이 읽기 쉬운 형태로 재조립됩니다. 프로그래밍 방식으로 로그를 파싱할 때는 auparse 라이브러리(C API)를 사용하면 이벤트 그룹화를 자동 처리합니다.

audit_context 생명주기

모든 감사 가능한 태스크는 task_struct->audit_context를 통해 audit_context 구조체를 갖습니다. 이 구조체는 시스템 콜 진입/종료 시 이벤트 데이터를 수집하고, 규칙 매칭 결과에 따라 로그 생성 여부를 결정합니다.

/* kernel/audit.h — audit_context 핵심 필드 */
struct audit_context {
    int                    in_syscall;     /* 시스템 콜 실행 중 여부 */
    enum audit_state       state;          /* AUDIT_STATE_{DISABLED,BUILD,RECORD} */
    enum audit_state       current_state;  /* 현재 감사 상태 */
    unsigned int           serial;         /* 이벤트 시리얼 번호 */
    int                    major;          /* 시스템 콜 번호 (NR) */
    int                    uring_op;       /* io_uring 연산 코드 */
    unsigned long          argv[4];        /* 시스템 콜 인자 (a0~a3) */
    long                   return_code;    /* 시스템 콜 반환값 */
    u64                    prio;           /* 우선순위 (에러 시 증가) */
    int                    return_valid;   /* 반환값 유효 여부 */

    /* 이름/경로 정보 */
    struct audit_names     *names;         /* 파일 이름 연결 리스트 */
    int                    name_count;     /* 이름 항목 수 */
    struct path            pwd;            /* 현재 작업 디렉토리 */

    /* IPC/소켓 감사 정보 */
    struct audit_aux_data  *aux;           /* 보조 감사 데이터 체인 */
    struct audit_aux_data  *aux_pids;      /* PID 추적 보조 데이터 */
    struct sockaddr_storage *sockaddr;     /* 소켓 주소 */
    size_t                 sockaddr_len;

    /* 필터링 */
    pid_t                  target_pid;     /* 대상 프로세스 PID */
    kuid_t                 target_auid;    /* 대상 audit UID */
    kuid_t                 target_uid;     /* 대상 실제 UID */

    /* execve 인자 (EXECVE 레코드용) */
    struct audit_tree_refs *trees;         /* 감시 트리 참조 */
    struct audit_proctitle  proctitle;      /* PROCTITLE 레코드 데이터 */
    /* ... */
};
/* kernel/auditsc.c — 시스템 콜 감사 흐름 */

/* 1. 시스템 콜 진입 시 — entry 후크 */
void __audit_syscall_entry(int major, unsigned long a1,
                           unsigned long a2, unsigned long a3,
                           unsigned long a4)
{
    struct audit_context *context = audit_context();
    if (!context)
        return;

    context->serial = 0;          /* 새 이벤트마다 리셋 */
    context->in_syscall = 1;       /* 시스템 콜 내부 마크 */
    context->current_state = state;
    context->major = major;        /* syscall NR 저장 */
    context->argv[0] = a1;         /* 인자 저장 */
    context->argv[1] = a2;
    context->argv[2] = a3;
    context->argv[3] = a4;
}

/* 2. 시스템 콜 종료 시 — exit 후크 */
void __audit_syscall_exit(int success, long return_code)
{
    struct audit_context *context = audit_context();

    context->return_valid = AUDITSC_SUCCESS;
    context->return_code  = return_code;

    /* exit 필터 리스트에서 규칙 매칭 */
    if (context->current_state == AUDIT_STATE_BUILD)
        state = audit_filter_syscall(context);

    /* RECORD 상태면 감사 레코드 생성 및 전송 */
    if (context->current_state >= AUDIT_STATE_RECORD)
        audit_log_exit();        /* → audit_log_start() + audit_log_format() */

    context->in_syscall = 0;       /* 시스템 콜 완료 */
    audit_free_names(context);     /* 이름 목록 해제 */
    audit_free_aux(context);       /* 보조 데이터 해제 */
}
audit_state 상태 머신:

AUDIT_STATE_DISABLED(감사 안 함) → AUDIT_STATE_BUILD(데이터 수집 중, 아직 로그 기록 미확정) → AUDIT_STATE_RECORD(규칙 매칭 성공, 로그 기록 확정). entry 필터에서 BUILD로 전환하고, exit 필터에서 최종 RECORD 여부를 결정합니다. 이 2단계 평가 방식으로 시스템 콜 반환값 기반 필터링이 가능합니다.

커널 감사 후크

Audit 서브시스템은 커널 전반에 후크를 삽입하여 보안 관련 이벤트를 포착합니다. 주요 후크 지점:

후크 함수호출 위치수집 정보
__audit_syscall_entry()시스템 콜 진입점syscall NR, 인자 4개
__audit_syscall_exit()시스템 콜 반환점반환값, 성공/실패, 규칙 매칭 → 레코드 생성
__audit_inode()VFS 경로 해석inode, 디바이스, 파일 모드, UID/GID
__audit_inode_child()디렉토리 내 파일 생성/삭제부모 inode, 자식 이름
audit_log_task_info()레코드 기록 시PID, UID, GID, comm, exe, sessionid
__audit_mq_open()POSIX 메시지 큐 열기큐 이름, 모드, 속성
__audit_socket_*소켓 연산소켓 주소, bind/connect/accept 정보
__audit_fd_pair()pipe/socketpair파일 디스크립터 쌍
__audit_ptrace()ptrace 호출대상 PID, 작업 코드
__audit_log_kern_module()모듈 로드모듈 이름
__audit_fanotify()fanotify 응답응답 유형, 대상 파일
__audit_tk_injoffset()시간 조정timekeeping 오프셋 변경
/* fs/namei.c — VFS 경로 해석 중 감사 후크 호출 예 */
static struct dentry *__lookup_hash(
    const struct qstr *name,
    struct dentry *base, unsigned int flags)
{
    struct dentry *dentry = lookup_dcache(name, base, flags);
    if (dentry)
        return dentry;

    dentry = __lookup_slow(name, base, flags);

    /* 감사 후크: 경로 해석 결과를 audit_context에 기록 */
    if (!IS_ERR(dentry) && unlikely(audit_context()))
        __audit_inode(name, dentry, flags);

    return dentry;
}

감사 메시지 형식

감사 레코드는 type=TYPE msg=audit(EPOCH:SERIAL): FIELDS 형식을 따릅니다. 동일 이벤트의 여러 레코드는 같은 EPOCH:SERIAL 쌍으로 연결됩니다.

# 하나의 이벤트는 여러 레코드 타입으로 구성됨 (같은 serial 공유)

# SYSCALL — 시스템 콜 기본 정보
type=SYSCALL msg=audit(1706000000.123:789): arch=c000003e syscall=257
  success=yes exit=3 a0=ffffff9c a1=7ffd1234 a2=0 a3=0
  items=1 ppid=4521 pid=4523 auid=1000 uid=0 gid=0 euid=0
  suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0
  ses=3 comm="cat" exe="/usr/bin/cat"
  subj=unconfined_u:unconfined_r:unconfined_t:s0
  key="sensitive_files"

# CWD — 현재 작업 디렉토리
type=CWD msg=audit(1706000000.123:789): cwd="/home/user"

# PATH — 접근한 파일 경로 (item 번호로 순서 지정)
type=PATH msg=audit(1706000000.123:789): item=0
  name="/etc/shadow" inode=131074 dev=08:01 mode=0100640
  ouid=0 ogid=42 rdev=00:00 nametype=NORMAL
  cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0

# PROCTITLE — 프로세스 제목 (hex 인코딩)
type=PROCTITLE msg=audit(1706000000.123:789):
  proctitle=636174002F6574632F736861646F77

주요 레코드 타입

타입번호설명주요 필드
SYSCALL1300시스템 콜 이벤트arch, syscall, success, exit, auid, uid, exe, key
PATH1302파일 경로 정보name, inode, dev, mode, ouid, ogid, nametype
CWD1307현재 작업 디렉토리cwd
EXECVE1309exec 인자argc, a0, a1, a2...
PROCTITLE1327프로세스 커맨드라인proctitle (hex 인코딩)
SOCKADDR1306소켓 주소saddr (hex 인코딩 sockaddr)
USER_AUTH1100사용자 인증pid, uid, auid, msg (PAM 정보)
USER_LOGIN1112사용자 로그인pid, uid, auid, ses, msg
USER_CMD1123sudo 명령 실행pid, uid, auid, cmd
AVC1400SELinux 접근 제어pid, comm, scontext, tcontext, tclass, perm
NETFILTER_PKT1325netfilter 패킷 감사mark, saddr, daddr, sport, dport, proto
KERN_MODULE1326커널 모듈 로드name
ANOM_ABEND1701비정상 프로세스 종료auid, uid, gid, ses, pid, comm, sig
ℹ️

auid (Audit UID)는 사용자가 최초 로그인할 때 설정되며, susudo로 UID가 변경되어도 원래 로그인 사용자를 추적할 수 있습니다. loginuid가 한 번 설정되면 변경 불가하도록 CONFIG_AUDIT_LOGINUID_IMMUTABLE을 설정할 수 있습니다. auid가 4294967295(-1)이면 로그인 과정을 거치지 않은 데몬 프로세스입니다.

kauditd 커널 스레드

kauditd는 커널 감사 메시지를 사용자 공간 auditd로 전달하는 커널 스레드입니다. 3개의 skb 큐를 관리하며, auditd 연결 상태에 따라 메시지 처리 전략을 결정합니다.

/* kernel/audit.c — kauditd 큐 관리 */
static struct sk_buff_head audit_queue;       /* 기본 전송 큐 */
static struct sk_buff_head audit_retry_queue; /* 전송 실패 재시도 큐 */
static struct sk_buff_head audit_hold_queue;  /* auditd 부재 시 보관 큐 */

static int kauditd_thread(void *dummy)
{
    while (!kthread_should_stop()) {
        /* 1. retry 큐: 이전 전송 실패 메시지 재전송 시도 */
        kauditd_send_queue(sk, &audit_retry_queue,
                           UNICAST_RETRIES, kauditd_rehold_skb);

        /* 2. hold 큐: auditd 복귀 시 보관 메시지 전송 */
        kauditd_send_queue(sk, &audit_hold_queue,
                           UNICAST_RETRIES, kauditd_hold_skb);

        /* 3. main 큐: 새 감사 메시지 전송 */
        kauditd_send_queue(sk, &audit_queue,
                           1, kauditd_rehold_skb);

        /* 메시지 없으면 대기 (wake_up으로 깨움) */
        wait_event_freezable(kauditd_wait,
                             skb_queue_len(&audit_queue));
    }
    return 0;
}

필터 엔진

감사 규칙은 5개의 필터 리스트에 배치되며, 이벤트 발생 시 해당 리스트의 규칙을 순차 매칭합니다. 첫 매칭 규칙의 action(always/never)이 적용됩니다.

필터 리스트평가 시점사용 가능 필드
taskfork/clone 시 (자식에 적용)uid, gid, pid, loginuid, sessionid
exit시스템 콜 종료 시 (가장 많이 사용)모든 필드 (syscall NR, 반환값, 경로, uid 등)
user사용자 공간 메시지 도착 시uid, pid, loginuid, msgtype
exclude레코드 전송 직전msgtype (특정 메시지 타입 제외)
filesystem파일시스템 감시 규칙fstype
/* kernel/auditfilter.c — 필터 규칙 구조체 */
struct audit_krule {
    u32                    pflags;      /* 규칙 플래그 */
    u32                    flags;       /* AUDIT_FILTER_{TASK,EXIT,...} */
    u32                    listnr;      /* 필터 리스트 번호 */
    u32                    action;      /* AUDIT_ALWAYS 또는 AUDIT_NEVER */
    u32                    mask[AUDIT_BITMASK_SIZE]; /* syscall 비트마스크 */
    u32                    buflen;
    u32                    field_count; /* 조건 필드 수 */
    struct audit_field     *fields;     /* 필드 조건 배열 */
    struct audit_watch     *watch;      /* 파일 감시 (-w 규칙) */
    struct audit_tree      *tree;       /* 디렉토리 트리 감시 */
    struct audit_fsnotify_mark *exe;   /* 실행 파일 감시 */
    char                   *filterkey;  /* -k 키 문자열 */
    struct list_head       list;        /* 리스트 연결 */
    struct list_head       rlist;       /* 규칙 인덱스 */
};

struct audit_field {
    u32   type;    /* AUDIT_PID, AUDIT_UID, AUDIT_ARCH, ... */
    u32   val;     /* 비교 값 */
    u32   op;      /* AUDIT_EQUAL, AUDIT_NOT_EQUAL, ... */
    union {
        kuid_t uid;
        kgid_t gid;
        struct {
            char   *lsm_str;
            void   *lsm_rule;  /* LSM 규칙 (SELinux 컨텍스트 등) */
        };
    };
};

감사 규칙 유형

제어 규칙 (Control Rules)

감사 서브시스템 자체의 동작을 제어합니다.

# 감사 활성화/비활성화
auditctl -e 0     # 비활성화
auditctl -e 1     # 활성화
auditctl -e 2     # 잠금 (재부팅 전까지 규칙 변경 불가)

# 백로그 제한 (기본 64, 프로덕션에서 증가 필요)
auditctl -b 8192  # 백로그 버퍼 크기

# 실패 모드 (백로그 초과 시 동작)
auditctl -f 0     # silent — 초과분 버림
auditctl -f 1     # printk — 경고 메시지 출력 (기본값)
auditctl -f 2     # panic — 커널 패닉 (고보안 환경)

# 초당 최대 메시지 수 제한
auditctl -r 100   # 초당 100개 (0 = 무제한)

# 모든 규칙 삭제
auditctl -D

# 현재 상태 확인
auditctl -s
# enabled 1
# failure 1
# pid 1234     (auditd PID)
# backlog_limit 8192
# lost 0       (유실된 메시지 수)
# backlog 0    (현재 큐 대기 수)
-e 2 (잠금 모드):

프로덕션 환경에서는 규칙 설정 완료 후 -e 2로 잠금하여 공격자가 감사 규칙을 비활성화하지 못하도록 방지합니다. 잠금은 재부팅으로만 해제됩니다. 규칙 파일의 마지막 줄에 -e 2를 배치하세요.

파일시스템 감시 규칙 (Watch Rules)

# -w (감시 대상) -p (권한 필터) -k (검색 키)
# 권한: r=읽기, w=쓰기, x=실행, a=속성변경

# 인증 관련 파일 감시
auditctl -w /etc/passwd -p wa -k auth_files
auditctl -w /etc/shadow -p wa -k auth_files
auditctl -w /etc/group -p wa -k auth_files
auditctl -w /etc/gshadow -p wa -k auth_files
auditctl -w /etc/sudoers -p wa -k auth_files
auditctl -w /etc/sudoers.d/ -p wa -k auth_files

# PAM 설정 감시
auditctl -w /etc/pam.d/ -p wa -k pam_config

# SSH 설정 감시
auditctl -w /etc/ssh/sshd_config -p wa -k sshd_config
auditctl -w /root/.ssh/ -p wa -k root_ssh

# 시간 변경 감시 (규정 준수 필수)
auditctl -w /etc/localtime -p wa -k time_change

# 커널 모듈 관련
auditctl -w /sbin/insmod -p x -k module_load
auditctl -w /sbin/modprobe -p x -k module_load
auditctl -w /etc/modprobe.d/ -p wa -k module_config

# cron 설정 감시
auditctl -w /etc/cron.d/ -p wa -k cron_config
auditctl -w /etc/crontab -p wa -k cron_config
auditctl -w /var/spool/cron/ -p wa -k cron_config
감시 규칙 내부 구현:

-w 규칙은 내부적으로 audit_watch 구조체로 변환되어 fsnotify 프레임워크와 연동됩니다. 감시 대상 inode에 대한 FS_MODIFY, FS_ATTRIB 등의 이벤트를 수신하여 감사 레코드를 생성합니다. 디렉토리 감시(-w /etc/sudoers.d/)는 audit_tree를 사용하여 하위 파일까지 재귀적으로 추적합니다.

시스템 콜 규칙 (Syscall Rules)

# 형식: -a action,filter -F field=value -S syscall -k key
# action: always (기록) / never (무시)
# filter: task, exit, user, exclude

# 프로세스 실행 추적 (모든 execve)
auditctl -a always,exit -F arch=b64 -S execve -k exec_log

# 접근 거부 감사 (EACCES, EPERM)
auditctl -a always,exit -F arch=b64 -S open -S openat -S openat2 \
  -F exit=-EACCES -k access_denied
auditctl -a always,exit -F arch=b64 -S open -S openat -S openat2 \
  -F exit=-EPERM -k access_denied

# 파일 삭제 추적
auditctl -a always,exit -F arch=b64 -S unlink -S unlinkat -S rename \
  -S renameat -k file_delete

# 시간 변경 시스템 콜 감사
auditctl -a always,exit -F arch=b64 -S adjtimex -S settimeofday \
  -S clock_settime -k time_change

# 네트워크 연결 감사 (connect만)
auditctl -a always,exit -F arch=b64 -S connect -F a2!=110 -k network_connect

# 특정 UID 범위 감사 (서비스 계정 제외)
auditctl -a always,exit -F arch=b64 -S execve -F auid>=1000 \
  -F auid!=4294967295 -k user_exec

# 커널 모듈 로드/언로드
auditctl -a always,exit -F arch=b64 -S init_module -S finit_module \
  -S delete_module -k kernel_modules

# mount/umount 감사
auditctl -a always,exit -F arch=b64 -S mount -S umount2 -k mount_ops

# ptrace 감사 (디버거 연결 탐지)
auditctl -a always,exit -F arch=b64 -S ptrace -k ptrace_attach

# 네트워크 설정 변경 (sethostname, setdomainname)
auditctl -a always,exit -F arch=b64 -S sethostname -S setdomainname -k hostname

# 32비트 시스템 콜도 함께 감사 (호환성 계층)
auditctl -a always,exit -F arch=b32 -S execve -k exec_log_32bit

제외 규칙 (Exclude Rules)

# 노이즈가 많은 메시지 타입 제외
auditctl -a always,exclude -F msgtype=CWD          # CWD 레코드 제외
auditctl -a always,exclude -F msgtype=EOE          # 이벤트 종료 마커 제외
auditctl -a always,exclude -F msgtype=CRYPTO_KEY_USER # SSH 키 이벤트 제외

# 특정 프로그램의 감사 제외 (성능 최적화)
auditctl -a never,exit -F arch=b64 -S all -F exe=/usr/sbin/chronyd
auditctl -a never,exit -F arch=b64 -S all -F exe=/usr/bin/vmtoolsd

감사 도구

auditctl — 규칙 관리

# 현재 규칙 목록
auditctl -l

# 현재 상태
auditctl -s

# 규칙 삭제 (개별)
auditctl -d always,exit -F arch=b64 -S execve -k exec_log

# 모든 규칙 삭제
auditctl -D

# 규칙 파일에서 로드
auditctl -R /etc/audit/rules.d/custom.rules

ausearch — 로그 검색

# 키로 검색
ausearch -k passwd_changes --start today

# 메시지 타입으로 검색
ausearch -m SYSCALL -sc execve --start recent
ausearch -m USER_AUTH -sv no   # 인증 실패만

# auid (원래 로그인 사용자)로 검색
ausearch -ua 1000 --start today

# 특정 PID로 검색
ausearch -p 4523

# 시간 범위 검색
ausearch --start 01/15/2024 12:00:00 --end 01/15/2024 18:00:00

# 실행 파일로 검색
ausearch -x /usr/bin/passwd

# 파일 이름으로 검색
ausearch -f /etc/shadow

# 해석된 형식 출력 (-i: UID→이름, syscall→이름 변환)
ausearch -k exec_log -i --start today

# 이벤트 단위 구분자와 함께 출력
ausearch -k exec_log --format text

# CSV 형식 출력 (외부 도구 연동)
ausearch -k exec_log --format csv

# 특정 이벤트 ID로 검색
ausearch -a 789  # serial 789인 모든 레코드

# 여러 조건 AND 결합
ausearch -m SYSCALL -sc openat -k sensitive_files -sv no --start today

aureport — 보고서 생성

# 종합 요약 보고서
aureport --summary

# 인증 이벤트 보고서
aureport --auth --start this-week

# 파일 접근 보고서
aureport --file --summary

# 시스템 콜 통계
aureport --syscall --summary

# 이상 이벤트
aureport --anomaly

# 실패한 이벤트만
aureport --failed

# 실행 보고서
aureport -x --summary

# 키별 이벤트 수
aureport -k --summary

# 로그인 보고서
aureport --login --summary

# 사용자별 이벤트 수
aureport --user --summary

# 터미널별 이벤트
aureport --tty

autrace — 프로세스 추적

# autrace: strace와 유사하지만 audit 프레임워크 사용
# 모든 감사 규칙을 일시 삭제 후 대상 프로세스만 추적

autrace /bin/ls /tmp

# 추적 결과 분석
ausearch --start recent -p 12345 --raw | aureport --file --summary
ausearch --start recent -p 12345 --raw | aureport --syscall

auditd 데몬 설정

# /etc/audit/auditd.conf — 핵심 설정 항목

# 로그 파일 위치
log_file = /var/log/audit/audit.log

# 로그 형식 (RAW: 원시 형식, ENRICHED: UID→이름 등 해석 포함)
log_format = ENRICHED

# 로그 그룹 (adm 그룹에 읽기 권한 부여)
log_group = adm

# 로그 파일당 최대 크기 (MB)
max_log_file = 50

# 최대 로그 파일 수 (순환)
num_logs = 10

# 최대 크기 도달 시 동작
max_log_file_action = ROTATE     # ROTATE, SYSLOG, SUSPEND, IGNORE, KEEP_LOGS

# 디스크 공간 부족 시 동작
space_left = 75                  # 잔여 공간 경고 임계값 (MB)
space_left_action = SYSLOG       # SYSLOG, EMAIL, EXEC, SUSPEND, SINGLE, HALT
admin_space_left = 50            # 관리자 경고 임계값 (MB)
admin_space_left_action = SUSPEND
disk_full_action = SUSPEND       # 디스크 꽉 찬 경우
disk_error_action = SUSPEND      # 디스크 오류 시

# 네트워크 전달 (원격 감사 서버)
# name_format = HOSTNAME
# name = myserver.example.com

# 디스패처 (audisp 플러그인)
dispatcher = /sbin/audispd

# 플러시 방식
flush = INCREMENTAL_ASYNC        # 성능과 안정성 균형
freq = 50                        # 50개 레코드마다 플러시

# TCP 리스너 (원격 수집 서버로 사용 시)
# tcp_listen_port = 60
# tcp_max_per_addr = 1

영구 규칙 설정

auditctl로 설정한 규칙은 재부팅 시 사라집니다. 영구 규칙은 /etc/audit/rules.d/ 디렉토리에 .rules 파일로 저장합니다.

# /etc/audit/rules.d/audit.rules — 영구 규칙 파일 구조

# === 1단계: 기존 규칙 초기화 ===
-D
-b 8192
-f 1
--backlog_wait_time 60000

# === 2단계: 파일 감시 규칙 ===
-w /etc/passwd -p wa -k auth_files
-w /etc/shadow -p wa -k auth_files
-w /etc/group -p wa -k auth_files
-w /etc/sudoers -p wa -k auth_files
-w /etc/sudoers.d/ -p wa -k auth_files
-w /etc/ssh/sshd_config -p wa -k sshd_config

# === 3단계: 시스템 콜 규칙 ===
-a always,exit -F arch=b64 -S execve -F auid>=1000 -F auid!=4294967295 -k user_exec
-a always,exit -F arch=b32 -S execve -F auid>=1000 -F auid!=4294967295 -k user_exec
-a always,exit -F arch=b64 -S init_module -S finit_module -S delete_module -k kernel_modules
-a always,exit -F arch=b64 -S mount -S umount2 -k mount_ops
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -S clock_settime -k time_change

# === 4단계: 고성능 제외 규칙 ===
-a never,exit -F arch=b64 -S all -F exe=/usr/sbin/chronyd
-a always,exclude -F msgtype=CWD

# === 마지막: 규칙 잠금 (프로덕션 필수) ===
-e 2

# 규칙 적용: augenrules --load 또는 service auditd restart
ℹ️

augenrules/etc/audit/rules.d/의 모든 .rules 파일을 알파벳 순으로 병합하여 /etc/audit/audit.rules를 생성합니다. 규칙 파일에 번호 접두사를 사용하면 순서를 제어할 수 있습니다: 10-base.rules, 20-watch.rules, 30-syscall.rules, 99-finalize.rules.

시스템 콜 감사 예제

# 특정 사용자(UID 1000)의 모든 파일 삭제 감사
auditctl -a always,exit -F arch=b64 -S unlinkat -F uid=1000 -k user_deletes

# 감사 로그 확인
ausearch -k user_deletes
# ----
# type=SYSCALL msg=audit(1706000000.123:456): arch=c000003e
#   syscall=263 success=yes exit=0 a0=ffffff9c a1=55a8b... 
#   uid=1000 gid=1000 comm="rm" exe="/usr/bin/rm"
# type=PATH msg=audit(1706000000.123:456): item=1
#   name="secret.txt" inode=12345 nametype=DELETE

로그 분석 기법

# 1. 의심스러운 실행 파일 탐지
ausearch -m EXECVE --start today -i | \
  aureport -x --summary | sort -rn -k1 | head -20

# 2. 실패한 파일 접근 상위 사용자
ausearch -sv no -m SYSCALL --start this-week | \
  aureport --user --summary | sort -rn -k1

# 3. 시간대별 이벤트 분포 (DDoS/brute-force 탐지)
aureport --start today --summary

# 4. 특정 파일에 접근한 모든 프로세스 추적
ausearch -f /etc/shadow -i --start this-month

# 5. 권한 상승 이벤트 추적 (setuid/setgid 호출)
ausearch -m SYSCALL -sc setuid --start today -i
ausearch -m SYSCALL -sc setgid --start today -i

# 6. 특정 사용자의 전체 세션 활동 재구성
ausearch --session 42 -i | aureport --file
ausearch --session 42 -i | aureport --syscall
ausearch --session 42 -i | aureport --host

# 7. PROCTITLE 디코딩 (hex → 텍스트)
ausearch -k exec_log --start today --raw | \
  awk '/proctitle=/{gsub(/proctitle=/,""); cmd=$NF; gsub(/../," 0x&",cmd); print cmd}' | \
  xargs printf '%b\n'

audisp 플러그인

audisp(Audit Dispatcher)은 감사 이벤트를 외부 시스템으로 전달하는 플러그인 프레임워크입니다. /etc/audit/plugins.d/에 플러그인 설정 파일을 배치합니다.

# /etc/audit/plugins.d/syslog.conf — syslog 전달 플러그인
active = yes
direction = out
path = /sbin/audisp-syslog
type = always
args = LOG_INFO
format = string

# /etc/audit/plugins.d/af_unix.conf — 유닉스 소켓 전달
active = yes
direction = out
path = builtin_af_unix
type = always
args = 0640 /var/run/audispd_events
format = string
플러그인용도설정 파일
audisp-syslogsyslog/rsyslog로 감사 이벤트 전달syslog.conf
audisp-remote원격 감사 서버로 TCP 전달au-remote.conf
sedispatchSELinux setroubleshoot 연동sedispatch.conf
audisp-af_unix유닉스 소켓으로 이벤트 전달af_unix.conf
audisp-ids침입 탐지 시스템 연동ids.conf

성능 최적화

감사의 성능 영향:

Audit 서브시스템은 시스템 콜 경로에 후크를 삽입하므로 성능 오버헤드가 발생합니다. 규칙 수가 많을수록, 매칭 빈도가 높을수록 오버헤드가 증가합니다. 일반적으로 1~5% 수준이지만, -S all 규칙이나 과도한 파일 감시 시 더 커질 수 있습니다.

최적화 기법설명
규칙 순서 최적화never(제외) 규칙을 always 규칙보다 앞에 배치. 고빈도 제외 대상을 먼저 매칭하여 불필요한 평가 방지
노이즈 프로세스 제외-a never,exit -F exe=/path/to/noisy로 감사 불필요한 데몬 제외
auid 필터 활용-F auid>=1000 -F auid!=4294967295로 데몬 프로세스 자동 제외
백로그 크기 조정-b 8192 이상으로 설정. 백로그 초과 시 이벤트 유실 발생
backlog_wait_time백로그 가득 찬 경우 태스크 대기 시간(ms). 0이면 대기 없이 유실
로그 플러시 방식flush = INCREMENTAL_ASYNC로 비동기 플러시 (성능↑, 약간의 유실 위험)
ENRICHED 비활성화log_format = RAW로 auditd CPU 부하 절감 (UID→이름 해석 생략)
exclude 규칙-a always,exclude -F msgtype=CWD로 불필요 레코드 타입 제외
/* kernel/auditsc.c — 규칙 매칭 오버헤드 최소화를 위한 최적화 */

/* syscall 비트마스크로 해당 syscall에 규칙이 있는지 O(1) 확인 */
static inline int audit_n_rules;  /* 전체 규칙 수 — 0이면 감사 완전 스킵 */

/* 감사 비활성화 시 빠른 경로 */
static inline void audit_syscall_entry(int major, ...)
{
    /* audit_n_rules == 0 이면 즉시 반환 (오버헤드 거의 0) */
    if (unlikely(audit_n_rules))
        __audit_syscall_entry(major, ...);
}

보안 컴플라이언스 규칙 세트

주요 보안 표준에서 요구하는 감사 규칙 예시:

표준요구사항대응 규칙
PCI-DSS 10.2.1관리자 활동 감사-a always,exit -F arch=b64 -S execve -F uid=0 -k admin_cmd
PCI-DSS 10.2.2root 활동 감사-w /etc/sudoers -p wa -k sudo_changes
PCI-DSS 10.2.4접근 실패 기록-a always,exit -S openat -F exit=-EACCES -k access_fail
PCI-DSS 10.2.6감사 로그 초기화/삭제-w /var/log/audit/ -p wa -k audit_log_tamper
PCI-DSS 10.2.7객체 생성/삭제-a always,exit -S unlink -S rmdir -k object_delete
CIS 4.1.4시간 변경-a always,exit -S adjtimex -S clock_settime -k time-change
CIS 4.1.6MAC 정책 변경-w /etc/selinux/ -p wa -k MAC-policy
CIS 4.1.8로그인/로그아웃-w /var/log/lastlog -p wa -k logins
CIS 4.1.9세션 정보-w /var/run/utmp -p wa -k session
사전 구성된 규칙 세트:

audit 패키지에는 CIS, PCI-DSS, STIG 등에 맞춘 사전 규칙 세트가 포함되어 있습니다. /usr/share/audit/sample-rules/ 디렉토리에서 30-pci-dss-v31.rules, 30-stig.rules, 30-nispom.rules 등을 참조하세요. augenrules로 필요한 규칙 파일을 /etc/audit/rules.d/에 복사하여 활성화합니다.

LSM 연동

Audit 서브시스템은 SELinux, AppArmor 등 LSM과 긴밀하게 연동됩니다. LSM이 접근을 거부하면 자동으로 감사 레코드가 생성됩니다.

# SELinux AVC 거부 → 자동 감사 레코드 생성
type=AVC msg=audit(1706000000.456:321): avc:  denied  { read } for
  pid=3456 comm="httpd" name="index.html" dev="sda1" ino=67890
  scontext=system_u:system_r:httpd_t:s0
  tcontext=system_u:object_r:user_home_t:s0
  tclass=file permissive=0

# AppArmor DENIED → 자동 감사 레코드 생성
type=AVC msg=audit(1706000000.789:654):
  apparmor="DENIED" operation="open"
  profile="/usr/sbin/nginx" name="/etc/shadow"
  pid=5678 comm="nginx" requested_mask="r" denied_mask="r"
  fsuid=33 ouid=0

# SELinux 컨텍스트 기반 감사 규칙
# 특정 SELinux 타입이 접근하는 이벤트만 감사
auditctl -a always,exit -F arch=b64 -S openat \
  -F subj_type=httpd_t -k httpd_access
/* security/selinux/avc.c — SELinux AVC에서 감사 호출 */
static void avc_audit_post_callback(
    struct audit_buffer *ab,
    void *a)
{
    struct common_audit_data *ad = a;
    audit_log_format(ab, " ");
    audit_log_untrustedstring(ab, ad->selinux_audit_data->scontext);
    audit_log_format(ab, " tcontext=");
    audit_log_untrustedstring(ab, ad->selinux_audit_data->tcontext);
    audit_log_format(ab, " tclass=%s",
                     secclass_map[ad->selinux_audit_data->tclass - 1].name);
}

/* 감사 결정: AVC denied 또는 auditallow 규칙 매칭 시 로그 */
static inline int avc_audit_required(
    u32 requested, struct av_decision *avd,
    int result, u32 auditdeny, u32 *deniedp)
{
    u32 denied = requested & ~avd->allowed;

    /* 거부된 경우: auditdeny 마스크와 교차 확인 */
    if (denied && (denied & auditdeny))
        return 1;   /* 감사 필요 */

    /* 허용된 경우: auditallow 마스크 확인 */
    if (!denied && (requested & avd->auditallow))
        return 1;   /* auditallow로 허용 이벤트도 감사 */

    return 0;       /* 감사 불필요 */
}

IMA/EVM 및 Lockdown

IMA(Integrity Measurement Architecture)와 EVM(Extended Verification Module)은 파일 무결성을 보장합니다. Lockdown LSM은 커널의 자체 변경을 방지합니다. 이 세 기술은 부팅부터 런타임까지의 신뢰 체인(chain of trust)을 구성하는 핵심 요소입니다.

ℹ️

IMA/EVM과 Lockdown의 상세 구현은 Secure Boot 심화 — IMA/EVMSecure Boot 심화 — Lockdown LSM을 참조하세요.

기술역할핵심 CONFIG
IMA파일 해시 측정 및 검증. TPM PCR에 기록CONFIG_IMA
IMA Appraisalxattr 서명 검증. 변조 파일 실행 차단CONFIG_IMA_APPRAISE
EVM보안 xattr(SELinux 라벨, IMA 해시) 무결성 보호CONFIG_EVM
Lockdown커널 자체 수정 방지 (kexec, /dev/mem, eBPF 쓰기 등)CONFIG_SECURITY_LOCKDOWN_LSM

IMA (Integrity Measurement Architecture)

IMA는 파일이 열릴 때 해시를 계산하여 무결성을 검증하는 커널 서브시스템입니다. 세 가지 핵심 기능을 제공합니다:

기능동작목적
Measure (측정)파일 해시를 계산하여 IMA 측정 리스트에 추가하고, TPM PCR 10을 확장(extend)원격 검증(Remote Attestation) — 시스템 상태 증명
Appraise (평가)파일 해시를 security.ima xattr에 저장된 기준값과 비교. 불일치 시 접근 거부로컬 무결성 보호 — 변조 파일 실행/로드 차단
Audit (감사)파일 해시를 감사 로그에 기록사후 분석 — 어떤 파일이 접근되었는지 추적
File Open open()/exec() IMA 훅 ima_file_check() IMA 정책 매칭 measure? appraise? audit? 규칙 확인 func=FILE_CHECK 해시 계산 SHA-256/512 Measure (측정) IMA 측정 리스트에 추가 TPM PCR 10 extend Appraise (평가) security.ima xattr와 비교 일치 → 접근 허용 불일치 → 접근 거부 (EACCES) Audit (감사) 해시를 audit 로그에 기록 security.ima 기준 해시/서명 저장 TPM (PCR 10) PCR_new = SHA(PCR_old || 파일해시) 단방향 확장 — 롤백 불가 원격 검증 (Remote Attestation) TPM PCR + IMA 측정 리스트 → 원격 서버에 시스템 상태 증명

IMA 정책

IMA 정책은 어떤 파일에 대해 measure/appraise/audit 동작을 수행할지 정의합니다. 부트 파라미터 ima_policy=로 기본 정책을 선택하거나, securityfs를 통해 커스텀 정책을 로드합니다.

# 부트 파라미터로 IMA 정책 설정
# ima_policy=tcb          — 기본 TCB (Trusted Computing Base) 정책
# ima_policy=appraise_tcb — TCB + 평가 활성화
# ima_policy=secure_boot  — Secure Boot 연동 정책
# ima_appraise=enforce    — 평가 실패 시 접근 거부 (기본: fix 모드)
# ima_appraise=fix        — 평가 실패 시 xattr 자동 갱신

# 커널 명령줄 예:
# ima_policy=appraise_tcb ima_appraise=enforce ima_hash=sha256

IMA 정책 규칙 형식과 주요 예:

# IMA 정책 규칙 형식:
# action  condition [condition ...] [template=name]
# action: measure | dont_measure | appraise | dont_appraise | audit

# === 모든 실행 파일 측정 ===
measure func=BPRM_CHECK mask=MAY_EXEC template=ima-ng

# === 모든 mmap 실행 측정 (공유 라이브러리) ===
measure func=FILE_MMAP mask=MAY_EXEC template=ima-ng

# === 커널 모듈 측정 및 평가 ===
measure func=MODULE_CHECK template=ima-ng
appraise func=MODULE_CHECK appraise_type=imasig

# === 서명된 파일만 실행 허용 ===
appraise func=BPRM_CHECK appraise_type=imasig

# === 펌웨어 로드 측정 ===
measure func=FIRMWARE_CHECK template=ima-ng

# === kexec 커널 이미지 평가 ===
appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig

# === 특정 경로 측정 제외 ===
dont_measure fsmagic=0x9fa0   # procfs 제외
dont_measure fsmagic=0x62656572  # sysfs 제외
dont_measure fsmagic=0x64626720  # debugfs 제외

# === UID 기반 감사 ===
audit func=FILE_CHECK mask=MAY_READ uid=0

# === 커스텀 정책 로드 ===
# cat /etc/ima/ima-policy > /sys/kernel/security/ima/policy
# IMA 상태 및 측정 확인
cat /sys/kernel/security/ima/runtime_measurements_count
# 1847

# IMA 측정 리스트 확인 (PCR, 템플릿 해시, 파일 해시, 파일 경로)
head -5 /sys/kernel/security/ima/ascii_runtime_measurements
# 10 abc123... ima-ng sha256:def456... /usr/bin/bash
# 10 789abc... ima-ng sha256:012def... /usr/lib64/libc.so.6
# 10 345678... ima-ng sha256:9abcde... /usr/sbin/sshd

# 바이너리 측정 리스트 (TPM 검증용)
hexdump -C /sys/kernel/security/ima/binary_runtime_measurements | head

# IMA 위반 카운트
cat /sys/kernel/security/ima/violations

EVM (Extended Verification Module)

EVM은 보안 관련 확장 속성(xattr)의 무결성을 보호합니다. security.selinux, security.ima, security.capability 등의 xattr을 HMAC 또는 디지털 서명으로 보호하여, 공격자가 보안 라벨이나 IMA 해시를 변조하는 것을 탐지합니다.

보호 대상 확장 속성 (xattr) security.selinux SELinux 보안 컨텍스트 security.ima IMA 파일 해시/서명 security.capability 파일 capability security.apparmor AppArmor 라벨 EVM 계산 HMAC-SHA1(key, selinux || ima || capability || inode) 또는 RSA/EC 디지털 서명 security.evm HMAC 또는 서명 저장 xattr 변조 시 검증 실패 → 접근 거부 xattr 변조 탐지 공격자가 security.selinux를 변조 → EVM HMAC/서명 재계산 시 불일치 탐지 공격자가 security.ima를 변조 → EVM 검증 실패 → 파일 접근 거부 EVM 키 없이는 security.evm을 올바르게 재생성할 수 없음 (HMAC: 커널 키링, 서명: 개인 키 필요)

EVM 모드

모드보호 방식키 요구특성
HMAC대칭 키 기반 HMAC-SHA1커널 키링에 로드된 마스터 키로컬 보호. 같은 키로 검증/생성 가능. 오프라인 변조 불가 (키 미보유 시)
디지털 서명RSA/EC 비대칭 키 서명빌드 시 개인 키로 서명, 커널에 공개 키 내장불변 보호. 커널이 서명 검증만 수행. 런타임 재생성 불가. Secure Boot 연계
# === EVM HMAC 키 설정 ===

# 1. 마스터 키 생성 (trusted key — TPM 보호)
keyctl add trusted evm-key "new 32" @u
# 또는 user key (TPM 없이)
keyctl add user evm-key "$(head -c 32 /dev/urandom | xxd -p)" @u

# 2. EVM 초기화 (HMAC 모드)
echo 1 > /sys/kernel/security/evm

# 3. 기존 파일에 EVM HMAC 생성
evmctl hmac /usr/bin/bash
evmctl hmac --recursive /usr/

# === EVM 디지털 서명 ===

# 1. 키 쌍 생성
openssl genrsa -out privkey.pem 2048
openssl rsa -in privkey.pem -pubout -out pubkey.pem

# 2. 파일에 EVM 서명 적용
evmctl sign --key privkey.pem /usr/bin/bash

# 3. 서명 검증
evmctl verify /usr/bin/bash

# === evmctl 주요 명령 ===
evmctl ima_hash /path/to/file     # IMA 해시 계산 및 security.ima 설정
evmctl ima_sign --key priv.pem /path  # IMA 서명 적용
evmctl verify /path                # EVM/IMA 서명 검증
evmctl ima_measurement /path       # 측정값 확인

IMA + EVM + TPM 신뢰 체인

Secure Boot, IMA, EVM, TPM은 부팅부터 런타임까지 연속적인 신뢰 체인을 구성합니다. 각 단계에서 다음 단계의 무결성을 검증하여, 변조된 컴포넌트가 실행되는 것을 방지합니다.

# 신뢰 체인 (Chain of Trust)
#
# [UEFI Secure Boot]           — 부트로더/커널 서명 검증
#        ↓
# [TPM PCR 확장]               — 부팅 과정의 각 단계를 PCR에 기록
#   PCR 0: BIOS/UEFI 코드
#   PCR 1: BIOS/UEFI 설정
#   PCR 4: 부트 매니저 (GRUB)
#   PCR 5: 부트 옵션 (커널 명령줄)
#   PCR 7: Secure Boot 정책
#   PCR 8-9: GRUB 설정/커널
#        ↓
# [IMA 측정]                   — 런타임 파일 해시를 PCR 10에 확장
#   PCR 10: IMA 측정 집계 값
#        ↓
# [IMA Appraisal]              — 파일 해시/서명 검증, 변조 시 접근 거부
#        ↓
# [EVM]                        — 보안 xattr 무결성 보호
#        ↓
# [Remote Attestation]         — TPM PCR + IMA 리스트로 원격 검증
# TPM PCR 읽기 (tpm2-tools)
tpm2_pcrread sha256:10
# sha256:
#   10: 0xABC123...  (IMA 측정 집계값)

# TPM Quote 생성 (원격 검증용)
tpm2_createak --ek-context ek.ctx --ak-context ak.ctx --ak-name ak.name
tpm2_quote --ak-context ak.ctx --pcr-list sha256:10 --message quote.msg \
  --signature quote.sig --qualification nonce.dat

# PCR 10을 IMA 측정 리스트로부터 재계산 (검증)
# 원격 서버에서 IMA 리스트의 각 항목을 순차적으로 SHA-256 extend하여
# 결과가 TPM Quote의 PCR 10 값과 일치하는지 확인

dm-verity — 블록 수준 무결성 검증

dm-verity는 Device Mapper 기반의 블록 수준 무결성 검증 메커니즘입니다. 읽기 전용 파일시스템의 모든 블록에 대한 해시를 Merkle 트리 형태로 저장하고, 블록 읽기 시 실시간으로 해시를 검증합니다. Android, ChromeOS, 임베디드 시스템에서 루트 파일시스템 보호에 널리 사용됩니다.

# dm-verity 설정 (veritysetup)

# 1. 해시 트리 생성
veritysetup format /dev/sda2 /dev/sda3
# → 데이터 장치: /dev/sda2 (읽기 전용 루트 파일시스템)
# → 해시 장치: /dev/sda3 (Merkle 트리 저장)
# Root hash: abc123def456...  (부트 파라미터에 지정)

# 2. verity 장치 활성화
veritysetup open /dev/sda2 verified-root /dev/sda3 \
  abc123def456...

# 3. 마운트
mount -o ro /dev/mapper/verified-root /mnt/root

# 커널 명령줄에서 dm-verity 사용 (initramfs에서)
# root=/dev/dm-0 dm-mod.create="verified-root,,,ro,
#   0 SIZE verity 1 DATA_DEV HASH_DEV 4096 4096 BLOCKS HASH_OFFSET
#   sha256 ROOT_HASH SALT"
dm-verity vs IMA:

dm-verity는 블록 수준에서 전체 파일시스템을 보호하며, 읽기 전용 파티션에 적합합니다 (루트 FS 보호). IMA는 파일 수준에서 개별 파일을 보호하며, 읽기/쓰기 파일시스템에서도 동작합니다. Android는 /system 파티션에 dm-verity를, /data 파티션의 앱에 IMA를 적용하여 계층적 보호를 구현합니다.

Lockdown LSM

Lockdown LSM은 커널 자체의 무결성과 기밀성을 보호하기 위해 특정 커널 기능의 사용을 제한합니다. 두 가지 수준을 제공합니다:

모드제한 대상목적
integrity커널 코드/데이터 수정 방지: kexec (미서명 커널), /dev/mem 쓰기, /dev/kmem, ACPI 테이블 주입, unsigned 모듈 로드, hibernation 이미지 변조커널 무결성 보호
confidentialityintegrity 제한 + 커널 정보 유출 방지: /proc/kcore, /dev/mem 읽기, eBPF 사용, perf 접근, kprobes 사용, ACPI 커스텀 메서드커널 기밀성 보호 (KASLR 우회 방지)
# Lockdown 상태 확인
cat /sys/kernel/security/lockdown
# [none] integrity confidentiality

# 부트 파라미터로 Lockdown 활성화
# lockdown=integrity       — 무결성 모드
# lockdown=confidentiality — 기밀성 모드 (더 제한적)

# Secure Boot 연동: UEFI Secure Boot가 활성화되면
# 커널이 자동으로 integrity 모드 적용 (CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT)

# sysrq를 통한 런타임 전환 (integrity → none은 불가, 단방향)
# echo integrity > /sys/kernel/security/lockdown  (escalation만 가능)
/* security/lockdown/lockdown.c — Lockdown LSM 핵심 */
static enum lockdown_reason kernel_locked_down;

static const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
    [LOCKDOWN_NONE]                  = "none",
    [LOCKDOWN_MODULE_SIGNATURE]      = "unsigned module loading",
    [LOCKDOWN_DEV_MEM]               = "/dev/mem,kmem,port",
    [LOCKDOWN_KEXEC]                 = "kexec of unsigned images",
    [LOCKDOWN_HIBERNATION]           = "hibernation",
    [LOCKDOWN_PCI_ACCESS]            = "direct PCI access",
    [LOCKDOWN_IOPORT]                = "raw io port access",
    [LOCKDOWN_ACPI_TABLES]           = "custom ACPI table",
    /* ... integrity 경계 ... */
    [LOCKDOWN_CONFIDENTIALITY_MAX]   = "confidentiality",
    [LOCKDOWN_KCORE]                 = "/proc/kcore access",
    [LOCKDOWN_KPROBES]               = "use of kprobes",
    [LOCKDOWN_BPF_READ_KERNEL]       = "use of bpf to read kernel RAM",
    [LOCKDOWN_PERF]                  = "unsafe use of perf",
};

/* LSM 훅: 제한된 작업 수행 시도 시 호출 */
static int lockdown_is_locked_down(enum lockdown_reason what)
{
    if (kernel_locked_down >= what) {
        pr_notice("Lockdown: %s is restricted; see man kernel_lockdown.7\n",
                  lockdown_reasons[what]);
        return -EPERM;
    }
    return 0;
}
Lockdown과 디버깅의 충돌:

Lockdown이 활성화되면 kprobes, eBPF, perf 등 커널 디버깅/트레이싱 도구가 제한됩니다(confidentiality 모드). 개발 환경에서는 lockdown=none을 사용하고, 프로덕션에서는 lockdown=integrity를 기본으로 적용하세요. Secure Boot가 활성화된 시스템에서 디버깅이 필요하면, 서명된 MOK(Machine Owner Key)으로 모듈을 서명하거나, UEFI 설정에서 Secure Boot를 일시적으로 비활성화해야 합니다.

보안 관련 커널 설정 종합

카테고리CONFIG 옵션설명
LSMCONFIG_SECURITYLSM 프레임워크 활성화
CONFIG_SECURITY_SELINUXSELinux
CONFIG_SECURITY_APPARMORAppArmor
CONFIG_SECURITY_SMACKSMACK
CONFIG_SECURITY_LANDLOCKLandlock
CONFIG_SECURITY_YAMAYama (ptrace 제한)
seccompCONFIG_SECCOMPseccomp 기본
CONFIG_SECCOMP_FILTERseccomp-bpf 필터
CapabilitiesCONFIG_SECURITY_CAPABILITIESPOSIX capabilities 지원
CONFIG_DEFAULT_SECURITY기본 major LSM 선택
CONFIG_LSMLSM 활성화 순서 문자열
하드닝CONFIG_RANDOMIZE_BASEKASLR
CONFIG_STACKPROTECTOR_STRONG스택 보호 (강함)
CONFIG_FORTIFY_SOURCE버퍼 오버플로 감지
CONFIG_HARDENED_USERCOPYuser-kernel copy 검증
CONFIG_STRICT_KERNEL_RWX커널 코드 W^X
무결성CONFIG_IMAIntegrity Measurement
CONFIG_EVMExtended Verification
CONFIG_SECURITY_LOCKDOWN_LSMLockdown LSM
감사CONFIG_AUDIT감사 프레임워크
CONFIG_AUDITSYSCALL시스템 콜 감사
메모리CONFIG_INIT_ON_ALLOC_DEFAULT_ON할당 시 0 초기화
CONFIG_SLAB_FREELIST_RANDOMSlab 랜덤화
CONFIG_SLAB_FREELIST_HARDENEDSlab 포인터 보호
보안과 성능의 트레이드오프:

보안 하드닝 옵션은 성능 오버헤드를 수반합니다. CONFIG_INIT_ON_FREE_DEFAULT_ON은 1~5%, KPTI는 1~5% (시스템 콜 집중 워크로드), Retpoline은 ~2% 정도입니다. 프로덕션 환경에서는 보안 요구사항과 성능 영향을 벤치마크하여 선택하세요.

커널 보안과 관련된 다른 주제를 더 깊이 이해하고 싶다면 다음 문서를 참고하세요.