커널 보안 (Kernel Security)

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

전제 조건: 동기화 기법메모리 배리어(Memory Barrier) 문서를 먼저 읽으세요. 보안 취약점(Vulnerability) 분석은 동시성/수명주기 오류와 강하게 결합되므로, 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 (인증서 기반 검증) — 커널 보안 프레임워크가 참조하는 보안 평가 및 인증 규격입니다. 종합 목록은 참고자료 — 표준 & 규격 섹션을 참고하세요.
예제 읽기 가이드: 이 문서는 개념 설명용 의사코드를 우선 사용하되, 실무 재현을 위해 실행 가능한 실습 예제도 함께 제공합니다. 코드 주석에 개념 예시가 표시된 블록은 구조 이해 목적이며, 실습 예제가 표시된 블록은 사용자 공간(User Space)에서 컴파일/실행 검증을 고려한 형태입니다. 배포판별 보안 정책, 인증 상태, 기본 활성화 여부는 참고자료 — FIPS 지원/인증 상태참고자료 — 보안 & 암호화(Encryption) 표준을 단일 기준으로 확인하세요.

커널 보안 아키텍처 개요

Linux 커널의 보안 모델은 전통적인 DAC(Discretionary Access Control)에서 출발하여, MAC(Mandatory Access Control), 그리고 이를 유연하게 지원하는 LSM(Linux Security Modules) 프레임워크로 발전해왔습니다. 커널 보안은 하드웨어, 커널, 사용자 공간의 3계층 구조로 이루어져 있으며, 각 계층이 독립적이면서도 상호 보완적인 방어선을 형성합니다.

보안 계층 구조

리눅스 보안은 종심 방어(Defense in Depth) 원칙에 따라 여러 계층의 보안 메커니즘이 중첩되어 동작합니다. 한 계층이 우회되더라도 다음 계층에서 공격을 차단할 수 있도록 설계되어 있습니다.

리눅스 보안 계층 구조 (Defense in Depth) 하드웨어 보안 계층 SMEP / SMAP NX/XD bit Intel CET / ARM BTI TPM / TEE AES-NI / SHA-NI 커널 메모리 보호 계층 KASLR / FKASLR KPTI Stack Canary kCFI / FineIBT KFENCE W^X 커널 접근 제어 계층 DAC (rwx/ACL) Capabilities LSM (MAC) seccomp-BPF Namespaces Cgroups 커널 무결성 및 감사 계층 IMA / EVM dm-verity Audit Lockdown LSM Keyring fs-verity 암호화 및 네트워크 보안 계층 Crypto API kTLS netfilter / nftables IPsec / WireGuard SELinux netlabel

커널 보안 모델의 핵심 원칙

리눅스 커널 보안은 다음 세 가지 핵심 원칙을 기반으로 합니다:

원칙설명구현 메커니즘
최소 권한(Least Privilege)프로세스가 작업에 필요한 최소한의 권한만 보유Capabilities, seccomp, Namespaces
종심 방어(Defense in Depth)다수의 독립적 보안 계층이 중첩 방어DAC + MAC + 하드닝 + Audit
실패 시 안전(Fail-Safe Defaults)기본적으로 접근 거부, 명시적 허용만 승인LSM AND 로직, seccomp 화이트리스트

보안 메커니즘 종합

계층메커니즘설명도입 버전
DACUNIX 퍼미션 (rwx), ACL파일 소유자가 접근 권한 결정. 전통적 UNIX 모델초기
CapabilitiesCAP_* 비트마스크root 권한을 세분화하여 최소 권한 원칙 적용2.2
MAC (LSM)SELinux, AppArmor, SMACK, Landlock시스템 정책이 접근 권한 강제. 사용자가 변경 불가2.6
seccompBPF 필터프로세스별 시스템 콜 제한 (샌드박싱)2.6.12 / 3.5
Auditkauditd, auditctl보안 관련 이벤트 감사 로깅2.6
하드닝KASLR, KPTI, CFI, SMEP/SMAP커널 익스플로잇 완화 기술3.14+
무결성IMA, EVM, dm-verity, fs-verity파일 및 메타데이터 무결성 검증2.6.30+
암호화Crypto API, kTLS, Keyring커널 내 암호화 연산 및 키 관리2.6+
네트워크netfilter, nftables, IPsec네트워크 패킷 필터링 및 암호화2.4+
격리Namespaces, Cgroups, Landlock프로세스 격리 및 자원 제한2.6.24+

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 매핑

접근 제어 메커니즘 상세

리눅스 커널의 접근 제어는 DAC → Capabilities → MAC → seccomp의 4단계로 구성됩니다. 각 단계는 이전 단계를 보완하며, 시스템 콜 처리 경로에서 순차적으로 검사됩니다.

DAC (임의적 접근 제어)

DAC(Discretionary Access Control)는 UNIX의 전통적인 접근 제어 모델로, 파일 소유자가 접근 권한을 결정합니다. 모든 파일과 디렉터리에 rwx(읽기/쓰기/실행) 권한이 소유자(owner), 그룹(group), 기타(others) 3범주에 대해 설정됩니다.

메커니즘설명한계
UNIX 퍼미션(Permission)소유자/그룹/기타에 대한 rwx 9비트 + setuid/setgid/sticky 3비트세밀한 사용자별 제어 불가
ACL (Access Control List)POSIX ACL — 개별 사용자/그룹에 대한 세밀한 권한 설정관리 복잡성 증가
umask새 파일/디렉터리 생성 시 기본 권한 마스크생성 후 변경은 별도 필요
# 실습 예제: DAC 권한 확인 및 ACL 설정

# 파일 권한 상세 확인
ls -la /etc/shadow
# -rw-r----- 1 root shadow ... /etc/shadow

# POSIX ACL 확인
getfacl /etc/passwd

# 특정 사용자에 ACL 추가
setfacl -m u:webadmin:rx /var/www/html

# ACL이 설정된 파일은 ls -l에서 + 표시
ls -la /var/www/html
# drwxr-xr-x+ 2 root root ... /var/www/html

# umask 확인 (보안 권장값: 0027)
umask
# 0022 (기본) → 새 파일: 644, 새 디렉터리: 755
DAC의 근본적 한계:

DAC 모델에서는 파일 소유자가 자유롭게 권한을 변경할 수 있으므로, 악의적이거나 침해된 프로세스가 중요 파일의 권한을 열어버릴 수 있습니다. 이 한계를 보완하기 위해 MAC(강제적 접근 제어)가 도입되었습니다. MAC에서는 시스템 관리자가 정의한 정책을 사용자가 변경할 수 없습니다.

DAC vs MAC 비교

특성DACMAC
권한 결정자자원 소유자 (사용자)시스템 정책 (관리자)
정책 변경소유자가 자유롭게 변경 가능정책 관리자만 변경 가능
root 제한root는 모든 제한 우회 가능root도 정책에 의해 제한됨
공격 시나리오침해된 프로세스가 권한 확대 가능정책으로 피해 범위 제한
구현 복잡도단순 (비트 연산)복잡 (정책 언어, 라벨 관리)
리눅스 구현VFS inode_permission()LSM security_inode_permission()

seccomp-bpf 시스템 콜 필터링

seccomp(Secure Computing)은 프로세스의 공격 표면(Attack Surface)을 줄이는 가장 직접적인 메커니즘입니다. 불필요한 시스템 콜을 차단함으로써, 커널 취약점의 도달 가능 경로를 물리적으로 제거합니다.

/* 실습 예제: seccomp-bpf 필터 설치 (libseccomp 사용) */

#include <seccomp.h>
#include <stdio.h>
#include <unistd.h>

int main(void)
{
    /* 기본 동작: 모든 시스템 콜 거부 (KILL) */
    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);

    /* 필요한 시스템 콜만 화이트리스트 */
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);

    /* write() 시스템 콜에 인자 기반 조건 추가 */
    /* fd == 1 (stdout)인 경우만 허용, stderr는 차단 */
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1,
                     SCMP_A0(SCMP_CMP_EQ, STDOUT_FILENO));

    /* 필터 로드 (이후 해제 불가) */
    seccomp_load(ctx);
    seccomp_release(ctx);

    /* 이 시점부터 허용된 시스템 콜만 사용 가능 */
    write(STDOUT_FILENO, "Hello, sandboxed world!\n", 24);

    /* open() 시도 시 SIGSYS로 프로세스 종료됨 */
    /* open("/etc/passwd", O_RDONLY);  ← SIGKILL */

    return 0;
}

컨테이너 런타임에서 기본 적용되는 seccomp 프로파일은 약 300개 이상의 시스템 콜 중 위험한 약 50개를 차단합니다:

차단 시스템 콜위험 이유대안
mount, umount2파일시스템 변경으로 탈출 가능바인드 마운트(Bind Mount)는 런타임이 사전 설정
ptrace다른 프로세스 메모리/레지스터 조작디버깅 시에만 명시적 허용
reboot호스트 재부팅필요 없음
init_module, finit_module커널 모듈 로드로 커널 코드 주입호스트에서만 관리
kexec_load새 커널로 교체호스트에서만 관리
unshare (일부 플래그)새 namespace 생성으로 격리 우회런타임이 사전 설정
keyctl커널 키링 조작제한된 키링만 허용

LSM (Linux Security Modules) 프레임워크

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

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

LSM 프레임워크 아키텍처 사용자 공간: open(), connect(), exec() 시스템 콜 진입 (syscall entry) DAC 검사 (inode_permission) LSM 훅 포인트 (security_*) security_hook_heads → call_int_hook() SELinux Type Enforcement AppArmor 경로 기반 MAC Landlock 비특권 샌드박스 SMACK 라벨 기반 MAC BPF LSM eBPF 런타임 정책 AND 로직: 모든 LSM이 허용해야 접근 승인 실제 자원 접근 수행 또는 -EACCES 반환

주요 LSM 모듈 비교

특성SELinuxAppArmorSMACKLandlockTOMOYO
접근 제어 모델Type Enforcement + RBAC + MLS경로 기반 MAC라벨 기반 MAC자체 제한(Self-restrict)경로 기반 MAC
정책 결정 기준보안 컨텍스트(라벨)파일 경로Smack 라벨파일시스템/네트워크 규칙파일 경로
root 제한정책에 의해 완전 제한프로파일로 제한라벨로 제한자발적 제한학습 후 제한
기본 배포판RHEL, Fedora, CentOSUbuntu, SUSE, DebianTizen(모든 배포판, 5.13+)TOMOYO Linux
훅 등록 수~200개~50개~30개~10개~20개
학습 모드permissivecomplain미지원미지원learning
비특권 사용불가 (root 필요)불가 (root 필요)불가 (root 필요)가능 (핵심 차별점)불가
복잡도높음중간낮음낮음중간
도입 커널 버전2.6.02.6.362.6.255.132.6.30

SELinux

SELinux(Security-Enhanced Linux)는 NSA가 개발한 MAC 시스템으로, Type Enforcement(TE), RBAC, MLS/MCS 모델을 결합한 가장 포괄적인 리눅스 보안 프레임워크입니다. RHEL, Fedora, CentOS 등에서 기본 활성화되며, 보안 컨텍스트(user:role:type:level)를 기반으로 모든 프로세스와 자원의 접근을 제어합니다.

SELinux 보안 컨텍스트

모든 프로세스, 파일, 소켓, IPC 객체에 보안 컨텍스트(Security Context)가 할당됩니다. 형식은 user:role:type:level이며, 접근 제어 결정에서 type이 가장 중요합니다.

# 실습 예제: SELinux 보안 컨텍스트 확인

# 프로세스의 SELinux 컨텍스트 확인
ps -eZ | grep httpd
# system_u:system_r:httpd_t:s0  1234 ?  00:00:01 httpd

# 파일의 SELinux 컨텍스트 확인
ls -Z /var/www/html/index.html
# system_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html

# SELinux 모드 확인
getenforce
# Enforcing (차단) / Permissive (로깅만) / Disabled (비활성)

# AVC 거부 로그 확인
sudo ausearch -m avc -ts recent

# 거부된 접근을 허용하는 정책 모듈 생성
sudo ausearch -m avc -ts recent | audit2allow -M mypolicy
sudo semodule -i mypolicy.pp
컨텍스트 요소설명예시
userSELinux 사용자 (UNIX 사용자와 별도)system_u, unconfined_u
roleRBAC 역할 (사용자가 전환 가능한 도메인 제한)system_r, sysadm_r
typeTE 도메인(프로세스)/타입(파일). 접근 제어의 핵심httpd_t, httpd_sys_content_t
levelMLS/MCS 보안 수준 (민감도:카테고리)s0, s0:c0.c1023
참고: SELinux의 Type Enforcement, RBAC, MLS/MCS 모델, 커널 내부 구현(AVC, SID 테이블, policydb), 정책 컴파일 파이프라인(Pipeline), 도메인 전이 메커니즘, Boolean 시스템, MCS 컨테이너 격리, 트러블슈팅 기법에 대한 상세 내용은 LSM / Seccomp 전용 페이지(Page)를 참고하세요.

AppArmor

AppArmor는 경로(path) 기반 MAC 시스템으로, SELinux보다 설정이 간단하며 Ubuntu, SUSE, Debian 등에서 기본 사용됩니다. 프로그램별 프로파일을 정의하여 파일, 네트워크, capability 접근을 제어하고, enforce(차단) 또는 complain(로깅) 모드로 동작합니다.

# 실습 예제: AppArmor 프로파일 관리

# AppArmor 상태 확인
sudo aa-status
# 출력: 로드된 프로파일 수, enforce/complain 모드 구분

# 프로파일 모드 전환
sudo aa-enforce /usr/sbin/nginx     # enforce 모드 (위반 차단)
sudo aa-complain /usr/sbin/nginx    # complain 모드 (위반 로깅만)

# 프로파일 자동 생성 (학습 모드)
sudo aa-genprof /usr/bin/myapp
# 프로그램 실행 후 F를 눌러 학습 종료, S로 저장

# 프로파일 예시 (/etc/apparmor.d/usr.sbin.nginx)
# /usr/sbin/nginx {
#   /var/www/html/** r,          # 웹 콘텐츠 읽기
#   /var/log/nginx/** w,         # 로그 쓰기
#   /run/nginx.pid rw,           # PID 파일
#   capability net_bind_service,  # 80/443 포트 바인드
#   network inet stream,          # TCP 소켓
#   deny /etc/shadow r,           # 패스워드 파일 읽기 차단
# }
참고: AppArmor의 프레임워크 내부 구현(aa_profile, aa_label, DFA 규칙 매칭), 프로파일 문법 상세, SELinux와의 비교, hat(서브 프로파일) 메커니즘, 프로파일 관리 명령에 대한 상세 내용은 LSM / Seccomp 전용 페이지를 참고하세요.

SMACK (Simplified Mandatory Access Control Kernel)

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

# SMACK 규칙 예시 (Tizen 플랫폼)

# 규칙 형식: subject_label object_label access
# /etc/smack/accesses 파일에 정의

# WebApp이 MediaFiles를 읽기만 허용
WebApp MediaFiles r

# System이 모든 대상에 읽기/쓰기/실행 허용
System _ rwx

# 프로세스 라벨 확인
cat /proc/self/attr/current
# System

# 파일 라벨 확인
getfattr -n security.SMACK64 /path/to/file
참고: SMACK의 라벨 체계, 규칙 문법, 특수 라벨(floor/hat/star), 임베디드 환경 활용 사례에 대한 상세 내용은 LSM / Seccomp 전용 페이지를 참고하세요.

Landlock

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

Landlock 3단계 API

/* 실습 예제: Landlock 샌드박스 설정 (비특권 사용자) */

#include <linux/landlock.h>
#include <sys/syscall.h>
#include <fcntl.h>

int main(void)
{
    /* 1단계: 규칙셋 생성 — 제한할 접근 유형 선언 */
    struct landlock_ruleset_attr attr = {
        .handled_access_fs =
            LANDLOCK_ACCESS_FS_READ_FILE |
            LANDLOCK_ACCESS_FS_WRITE_FILE |
            LANDLOCK_ACCESS_FS_EXECUTE,
    };
    int ruleset_fd = syscall(SYS_landlock_create_ruleset,
                            &attr, sizeof(attr), 0);

    /* 2단계: 허용 규칙 추가 — /usr과 /tmp만 접근 허용 */
    struct landlock_path_beneath_attr path_attr;
    path_attr.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
                               LANDLOCK_ACCESS_FS_EXECUTE;
    path_attr.parent_fd = open("/usr", O_PATH | O_CLOEXEC);
    syscall(SYS_landlock_add_rule, ruleset_fd,
            LANDLOCK_RULE_PATH_BENEATH, &path_attr, 0);
    close(path_attr.parent_fd);

    /* 3단계: 규칙셋 적용 — 이후 되돌릴 수 없음 */
    prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);  /* 필수 */
    syscall(SYS_landlock_restrict_self, ruleset_fd, 0);
    close(ruleset_fd);

    /* 이후 /etc/shadow 접근 시도 → -EACCES */
    open("/etc/shadow", O_RDONLY);  /* 실패 */

    return 0;
}
ABI 버전커널 버전추가 기능
v15.13파일시스템 접근 제어 기본
v25.19파일 참조(Refer) 제어
v36.2파일 절단(Truncate) 제어
v46.7네트워크(TCP) 접근 제어
v56.10ioctl 제어
참고: Landlock 3단계 API 사용법, ABI 버전별 기능 확장(v1~v5), 코드 예제, seccomp과의 비교에 대한 상세 내용은 LSM / Seccomp 전용 페이지를 참고하세요.

seccomp-bpf

seccomp(Secure Computing)은 프로세스가 호출할 수 있는 시스템 콜을 제한하는 커널 보안 메커니즘입니다. Strict 모드와 Filter 모드(cBPF)를 지원하며, 필터 체인은 누적되어 점점 더 제한적으로만 변경됩니다. 컨테이너 런타임에서 시스템 콜 격리의 핵심 기술로 사용됩니다.

seccomp 동작 모드

모드API동작유연성
Strictprctl(PR_SET_SECCOMP, 1)read, write, exit, sigreturn만 허용매우 제한적
Filterprctl(PR_SET_SECCOMP, 2, &prog)cBPF 프로그램으로 필터링 규칙 정의유연함

seccomp 반환값과 우선순위

반환값우선순위(높은 순)동작용도
SECCOMP_RET_KILL_PROCESS1 (가장 엄격)프로세스 전체 종료 (SIGSYS)치명적 위반
SECCOMP_RET_KILL_THREAD2현재 스레드만 종료스레드별 격리
SECCOMP_RET_TRAP3SIGSYS 시그널 전달 (핸들러 호출)에뮬레이션
SECCOMP_RET_ERRNO4지정 errno 반환graceful 거부
SECCOMP_RET_USER_NOTIF5감독자 프로세스에 위임컨테이너 런타임
SECCOMP_RET_TRACE6ptrace 트레이서에 통지디버깅
SECCOMP_RET_LOG7허용 + 로그 기록모니터링
SECCOMP_RET_ALLOW8 (가장 관대)정상 실행허용
다중 필터 평가: 프로세스에 여러 필터가 설치된 경우, 모든 필터가 평가되고 가장 엄격한(우선순위가 높은) 결과가 채택됩니다. 예를 들어 하나가 ALLOW, 다른 하나가 KILL을 반환하면 최종 결과는 KILL입니다.
참고: 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()로 원자적(Atomic) 교체합니다.

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네트워크 관리 (인터페이스, 라우팅(Routing), 방화벽(Firewall) 등)
CAP_NET_BIND_SERVICE1024 이하 포트 바인드
CAP_NET_RAWRAW/PACKET 소켓 사용
CAP_SYS_ADMIN시스템 관리 (mount, swapon, ioctl 등) -- 가장 넓은 범위
CAP_SYS_PTRACE임의 프로세스 ptrace
CAP_SYS_MODULE커널 모듈(Kernel Module) 로드/언로드
CAP_SETUID / CAP_SETGIDUID/GID 변경
CAP_BPFBPF 프로그램 로드 (5.8+)
CAP_PERFMON성능 모니터링 (perf_event_open) (5.8+)
CAP_CHECKPOINT_RESTORECRIU 체크포인트(Checkpoint)/복원 (5.9+)

Capability 5종 세트 관계

Capability 5종 세트 관계와 계산 Bounding (cap_bset) permitted 상한 제한 삭제만 가능, 추가 불가 Permitted (cap_permitted) 보유 가능한 최대 집합 effective의 상한 Effective (cap_effective) 현재 실제 적용 집합 capable() 검사 대상 상한 상한 Inheritable (cap_inheritable) exec 시 자식에 전달 가능 파일 inheritable과 교집합 Ambient (cap_ambient) 비특권 exec에서 자동 상속 커널 4.3+ (setuid 대체) execve() 시 계산 공식 P'(permitted) = (P(inh) & F(inh)) | (F(perm) & bset) | P'(amb) P'(effective) = F(eff) ? P'(perm) : P'(amb) 실전 예: nginx에 CAP_NET_BIND_SERVICE만 부여 방법 1 (파일 cap): setcap cap_net_bind_service=ep /usr/sbin/nginx 방법 2 (ambient): systemd AmbientCapabilities=CAP_NET_BIND_SERVICE 결과: root 없이 80/443 포트 바인드 가능, 나머지 root 권한은 없음

파일 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, KPTI, kCFI, SMEP/SMAP, Stack Protector, FORTIFY_SOURCE, Lockdown LSM, Spectre/Meltdown 방어 등 다양한 하드닝 기술을 제공합니다. 이러한 기술들은 4개 계층(하드웨어 → 컴파일러 → 메모리 → 접근 제어)의 종심 방어(defense in depth) 구조를 형성하여, 취약점이 존재하더라도 익스플로잇 성공 확률을 크게 낮춥니다.

하드닝 4계층 구조

계층기술방어 대상활성화 방법
1. 하드웨어SMEP커널→사용자 코드 실행CPU 지원 시 자동 (CR4.SMEP)
SMAP커널→사용자 데이터 접근CPU 지원 시 자동 (CR4.SMAP)
Intel CET / ARM BTI간접 분기 대상 검증CPU + 커널 설정
2. 컴파일러Stack Canary스택 버퍼 오버플로CONFIG_STACKPROTECTOR_STRONG
FORTIFY_SOURCE문자열/메모리 함수 오버플로CONFIG_FORTIFY_SOURCE
kCFI함수 포인터 변조CONFIG_CFI_CLANG (Clang 필요)
3. 메모리KASLR주소 예측 공격CONFIG_RANDOMIZE_BASE
KPTI커널 데이터 유출 (Meltdown)CONFIG_PAGE_TABLE_ISOLATION
W^X코드 주입CONFIG_STRICT_KERNEL_RWX
4. 접근 제어Lockdown LSM커널 자체 변경CONFIG_SECURITY_LOCKDOWN_LSM
Module Signature비인가 커널 모듈 로드CONFIG_MODULE_SIG_FORCE
# 실습 예제: 현재 시스템의 하드닝 상태 점검

# CPU 보안 기능 확인
cat /proc/cpuinfo | grep -oE '(smep|smap|nx|ibrs|stibp|ssbd|cet)' | sort -u

# Spectre/Meltdown 완화 상태
grep . /sys/devices/system/cpu/vulnerabilities/*

# 출력 예시:
# /sys/devices/system/cpu/vulnerabilities/meltdown:Mitigation: PTI
# /sys/devices/system/cpu/vulnerabilities/spectre_v1:Mitigation: usercopy/swapgs ...
# /sys/devices/system/cpu/vulnerabilities/spectre_v2:Mitigation: Retpoline ...

# Lockdown 모드 확인
cat /sys/kernel/security/lockdown
# [none] integrity confidentiality

# 모듈 서명 검증 상태
cat /proc/sys/kernel/tainted
# 0이면 서명되지 않은 모듈 없음
참고:

KASLR/FKASLR, KPTI, kCFI/FineIBT, Shadow Call Stack, SMEP/SMAP, Stack Protector, FORTIFY_SOURCE, Lockdown LSM, Spectre/Meltdown 방어, 하드닝 체크리스트 등의 상세 구현과 실전 설정은 커널 하드닝 문서를 참조하세요.

Audit 서브시스템

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

Audit 구성 요소

구성 요소위치역할
kauditd커널 스레드감사 이벤트 수집 및 사용자 공간 전달
auditd사용자 공간 데몬이벤트 수신, 로그 파일 기록, 로그 회전
auditctlCLI 도구감사 규칙 추가/삭제/조회
ausearchCLI 도구감사 로그 검색
aureportCLI 도구감사 로그 요약 보고서 생성
audisp사용자 공간 디스패처이벤트를 외부 시스템(SIEM 등)에 전달
# 실습 예제: Audit 규칙 설정

# 현재 감사 규칙 확인
sudo auditctl -l

# /etc/passwd 파일 변경 감시
sudo auditctl -w /etc/passwd -p wa -k passwd_changes
# -w: 감시 경로, -p wa: 쓰기+속성변경 감시, -k: 검색 키

# 특정 시스템 콜 감시 (execve — 프로그램 실행 추적)
sudo auditctl -a always,exit -F arch=b64 -S execve -k exec_tracking

# 특정 사용자의 파일 접근 감시
sudo auditctl -a always,exit -F arch=b64 -S open,openat -F auid=1000 -k user1000_access

# 감사 로그 검색
sudo ausearch -k passwd_changes -ts recent

# 감사 보고서 생성
sudo aureport --summary
sudo aureport --auth   # 인증 이벤트 보고서
참고:

audit_context/audit_buffer 구조체, kauditd 커널 스레드(Kernel Thread), 필터 엔진과 규칙 작성, auditd/audisp/ausearch/aureport 도구, 컴플라이언스 매핑(Mapping), 성능 최적화 등의 상세 내용은 Audit 서브시스템 문서를 참조하세요.

IMA/EVM 및 Lockdown

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

기술역할핵심 CONFIG
IMA파일 해시(Hash) 측정(Measure) · 검증(Appraise) · 감사(Audit). TPM PCR 10에 기록CONFIG_IMA
EVM보안 xattr(security.selinux, security.ima 등) HMAC/서명 보호CONFIG_EVM
Lockdown커널 자체 수정 방지 — integrity/confidentiality 2단계CONFIG_SECURITY_LOCKDOWN_LSM
💡

참고: IMA 정책 문법, 템플릿(ima-ng/ima-sig), 다이제스트 리스트, EVM HMAC/디지털서명, 이식가능 서명, 원격증명(keylime), dm-verity/fs-verity 연동, 컴플라이언스 매핑 등의 상세 내용은 IMA/EVM 문서를 참조하세요. Lockdown LSM 모드와 제한 항목은 커널 하드닝 — Lockdown을 참조하세요.

신뢰 체인 (Chain of Trust)

부팅부터 런타임까지의 신뢰 체인은 다음과 같이 구성됩니다:

UEFI Secure Boot (펌웨어)
    │ 서명 검증
    ▼
GRUB/systemd-boot (부트로더)
    │ 서명 검증
    ▼
vmlinuz (커널 이미지)
    │ 모듈 서명 검증 (CONFIG_MODULE_SIG)
    ├─ 커널 모듈 (.ko)
    │
    │ IMA/EVM 검증
    ├─ 시스템 바이너리 (/usr/bin/*)
    ├─ 공유 라이브러리 (/usr/lib/*)
    ├─ 설정 파일 (/etc/*)
    │
    │ dm-verity
    ├─ 읽기 전용 파티션 (/, /usr)
    │
    │ fs-verity
    └─ 개별 파일 (APK, 정책 파일)
TPM 연동: IMA는 측정(measure) 결과를 TPM PCR 10에 누적 확장(extend)합니다. 원격 증명(Remote Attestation) 시 TPM의 PCR 값과 IMA 측정 로그를 함께 검증하여, 시스템이 변조되지 않은 소프트웨어만 실행했는지 암호학적으로 증명합니다. 기밀 컴퓨팅ARM TrustZone 문서도 참고하세요.

보안 관련 커널 설정 종합

카테고리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버퍼 오버플로(Buffer Overflow) 감지
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 포인터 보호
보안과 성능의 트레이드오프:

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

보안 기능별 성능 영향 요약

보안 기능성능 영향워크로드 의존성비활성화 위험
KASLR<0.1%없음주소 예측 공격 가능
Stack Canary<1%함수 호출 빈도스택 오버플로 무방비
KPTI1~5%시스템 콜 집중 시 높음Meltdown 취약
Retpoline~2%간접 분기 빈도Spectre v2 취약
INIT_ON_ALLOC1~3%메모리 할당 빈도정보 유출 가능
INIT_ON_FREE3~5%메모리 해제 빈도UAF 정보 유출
kCFI1~2%간접 호출 빈도ROP/JOP 공격 가능
FORTIFY_SOURCE<1%문자열/메모리 함수버퍼 오버플로 무감지
seccomp-BPF<1%시스템 콜 빈도 (Bitmap 최적화)공격 표면 확대
SELinux1~3%파일/네트워크 I/OMAC 보호 없음
AppArmor<1%파일 경로 매칭MAC 보호 없음
Audit1~5%규칙 수, 이벤트 빈도감사 추적 불가
권장 전략:

대부분의 보안 기능은 <3% 수준의 오버헤드이므로, 기본적으로 모두 활성화하는 것이 권장됩니다. 성능이 극도로 중요한 워크로드(HPC, 실시간 트레이딩)에서만 선택적으로 비활성화를 검토하되, 반드시 대체 방어 수단을 확보하세요. "보안 vs 성능" 이분법보다 "허용 가능한 오버헤드 내에서 최대 보안"을 목표로 설정하세요.

KASLR/KPTI/CFI 통합 방어

KASLR(주소 랜덤화), KPTI(페이지 테이블(Page Table) 격리), kCFI(제어 흐름 무결성)는 각각 독립된 공격 벡터를 차단하며, 세 기술이 동시에 활성화될 때 종심 방어 구조를 형성합니다.

참고:

KASLR/KPTI/CFI 통합 방어, Stack Protector 구현 세부, FORTIFY_SOURCE 메커니즘, Lockdown LSM 모드, Spectre/Meltdown 방어, 하드닝 체크리스트 등의 상세 내용은 커널 하드닝 문서를 참조하세요.

LSM 훅 호출 체인: sys_open → security_file_open

사용자 공간에서 open() 시스템 콜을 호출하면, 커널 내부에서 여러 단계를 거쳐 최종적으로 LSM 보안 훅이 실행됩니다. 이 경로를 정확히 이해하면 LSM 정책이 어느 시점에서, 어떤 정보를 기반으로 접근 제어 결정을 내리는지 파악할 수 있습니다.

호출 경로 개요

파일 열기 시스템 콜의 보안 훅 호출 경로는 다음과 같습니다:

사용자 공간: open("/etc/passwd", O_RDONLY)
    │
    ▼
sys_openat()                     /* fs/open.c */
    │
    ▼
do_sys_openat2()                 /* fs/open.c */
    │
    ▼
do_filp_open()                   /* fs/namei.c */
    │
    ▼
path_openat()                    /* fs/namei.c - 경로 해석 + 파일 열기 */
    │
    ├─ link_path_walk()          /* 디렉터리 순회: security_inode_permission() */
    │
    ├─ do_open()                 /* 최종 열기 단계 */
    │     │
    │     ▼
    │   vfs_open()               /* fs/open.c */
    │     │
    │     ▼
    │   security_file_open()     /* security/security.c - LSM 훅 디스패치 */
    │     │
    │     ▼
    │   call_int_hook(file_open, file)
    │     │
    │     ├─ selinux_file_open()
    │     ├─ apparmor_file_open()
    │     └─ (기타 등록된 LSM)
    │
    └─ 파일 디스크립터 반환
LSM 훅 디스패치: security_file_open() 내부 sys_openat() [사용자 콜] path_openat() → vfs_open() security_file_open(file) call_int_hook(file_open, file) selinux_file_open() apparmor_file_open() landlock_file_open() 0 (허용) 0 (허용) -EACCES (거부) 최종 결과: -EACCES (하나라도 거부 → 거부)

핵심 포인트는 AND 로직입니다. 등록된 모든 LSM 모듈이 0을 반환해야 접근이 허용되며, 하나라도 음수 에러 코드를 반환하면 즉시 해당 에러가 호출자에게 전파됩니다.

security_file_open() 구현 분석

security/security.c에 정의된 security_file_open()의 핵심 구현입니다:

/* security/security.c — 파일 열기 LSM 훅 디스패치 */

int security_file_open(struct file *file)
{
    int ret;

    /* 1. LSM 훅 체인 순회 — 모든 등록 모듈의 file_open 호출 */
    ret = call_int_hook(file_open, file);

    /* 2. 하나라도 거부하면 즉시 에러 반환 */
    if (ret)
        return ret;

    /* 3. fsnotify — 파일 열기 이벤트 통지 (inotify, fanotify) */
    return fsnotify_perm(file, MAY_OPEN);
}
코드 설명
  • 3행security_file_open()struct file 포인터를 받습니다. 이 시점에서 경로 해석(path resolution)은 이미 완료되어 있으며, file->f_path에 dentry/vfsmount가 설정된 상태입니다.
  • 7행call_int_hook() 매크로는 security_hook_heads.file_open 리스트에 등록된 모든 LSM 콜백을 순회합니다. 첫 번째 비-0 반환값이 발견되면 순회를 중단합니다.
  • 10행LSM 훅이 음수 에러(-EACCES, -EPERM 등)를 반환하면, 파일 열기가 거부됩니다. DAC 검사를 통과해도 MAC에서 차단될 수 있습니다.
  • 14행보안 검사 통과 후 fsnotify_perm()을 호출합니다. fanotify의 FAN_OPEN_PERM 이벤트 처리로, 사용자 공간 데몬이 열기를 허용/거부할 수 있는 추가 검사점입니다.

call_int_hook 매크로 내부

call_int_hook()은 LSM 프레임워크의 핵심 디스패치 매크로로, 등록된 모든 보안 모듈의 훅을 순차 호출합니다:

/* security/security.c — 정수 반환 훅 디스패치 매크로 (간소화) */

#define call_int_hook(FUNC, ...) ({                  \
    int RC = LSM_RET_DEFAULT(FUNC);                  \
    struct security_hook_list *P;                     \
                                                         \
    hlist_for_each_entry(P,                            \
            &security_hook_heads.FUNC, list) {           \
        RC = P->hook.FUNC(__VA_ARGS__);                  \
        if (RC != 0)                                   \
            break;                                      \
    }                                                    \
    RC;                                                  \
})
코드 설명
  • 4행LSM_RET_DEFAULT(FUNC)는 훅별 기본 반환값입니다. 대부분의 접근 제어 훅은 0(허용)이 기본값이며, 등록된 LSM이 없으면 모든 접근이 허용됩니다.
  • 5행security_hook_list는 개별 LSM 모듈이 등록한 훅 엔트리입니다. hook 유니온에 실제 함수 포인터가 저장됩니다.
  • 7-8행hlist_for_each_entry()security_hook_heads.FUNC 해시 리스트를 순회합니다. LSM 등록 순서대로 호출되며, 순서는 커널 부팅 파라미터 lsm=으로 결정됩니다.
  • 9행각 LSM의 훅 함수를 가변 인자와 함께 호출합니다. 예를 들어 file_open이면 selinux_file_open(file)이 호출됩니다.
  • 10-11행비-0 반환값(거부)이 나오면 즉시 순회를 중단합니다. 이것이 AND 로직의 구현부입니다 — 앞선 LSM이 거부하면 뒤쪽 LSM은 호출조차 되지 않습니다.

SELinux의 file_open 훅 구현

SELinux가 file_open 훅에서 수행하는 접근 검사의 핵심 흐름입니다:

/* security/selinux/hooks.c — SELinux file_open 훅 (간소화) */

static int selinux_file_open(struct file *file)
{
    struct file_security_struct *fsec = selinux_file(file);
    struct inode_security_struct *isec;
    struct inode *inode = file_inode(file);
    u32 sid = current_sid();           /* 현재 프로세스의 SID */
    u32 av = file_to_av(file);         /* 파일 모드 → AV 비트 변환 */

    isec = inode_security(inode);

    /* AVC(Access Vector Cache)에서 결정 조회 또는 계산 */
    return avc_has_perm(sid, isec->sid, isec->sclass, av, NULL);
}
코드 설명
  • 5행selinux_file()file->f_security에서 SELinux 전용 보안 데이터를 추출합니다. LSM 블롭(blob) 공유 방식으로 관리됩니다.
  • 8행current_sid()는 현재 태스크(Task)의 SELinux SID(Security Identifier)를 반환합니다. 이것이 접근 주체(subject)입니다.
  • 9행file_to_av()는 파일 열기 모드(O_RDONLY, O_WRONLY 등)를 SELinux 접근 벡터(Access Vector) 비트로 변환합니다. 예: O_RDONLY → FILE__READ.
  • 14행avc_has_perm()은 AVC 캐시에서 (subject_SID, object_SID, class, permission) 튜플을 조회합니다. 캐시 미스 시 정책 DB에서 계산하고 결과를 캐싱합니다.
경로 중 호출되는 다른 보안 훅: path_openat() 내에서 파일 열기 전에도 여러 보안 훅이 호출됩니다. security_inode_permission()(디렉터리 탐색 권한), security_inode_follow_link()(심볼릭 링크 추적), security_file_alloc()(파일 구조체 할당 시 보안 데이터 초기화) 등이 사전 검사됩니다.

struct cred 필드 심층 분석

struct cred는 Linux 커널에서 프로세스의 모든 보안 자격증명을 담는 핵심 구조체입니다. UID/GID, capability 세트, 네임스페이스 정보, LSM 보안 블롭이 모두 이 하나의 구조체에 집중되어 있으며, 커널의 모든 접근 제어 결정이 이 구조체를 기반으로 이루어집니다.

필드별 상세 분석

다음은 include/linux/cred.h에 정의된 struct cred의 주요 필드입니다:

/* include/linux/cred.h — struct cred 정의 (간소화) */

struct cred {
    atomic_long_t   usage;          /* 참조 카운트 */

    /* ── UID/GID 세트 ── */
    kuid_t          uid;            /* 실제 UID (real) */
    kgid_t          gid;            /* 실제 GID (real) */
    kuid_t          suid;           /* 저장된 set-user-ID (saved) */
    kgid_t          sgid;           /* 저장된 set-group-ID (saved) */
    kuid_t          euid;           /* 유효 UID (effective) — 접근 결정 기준 */
    kgid_t          egid;           /* 유효 GID (effective) */
    kuid_t          fsuid;          /* 파일시스템 UID — VFS 접근 검사용 */
    kgid_t          fsgid;          /* 파일시스템 GID */

    /* ── 보안 비트 ── */
    unsigned        securebits;     /* SECBIT_* 플래그 (NOROOT, NO_SETUID_FIXUP 등) */

    /* ── Capability 세트 (5종) ── */
    kernel_cap_t    cap_inheritable; /* exec 시 자식에게 전달 가능 */
    kernel_cap_t    cap_permitted;   /* 프로세스가 보유 가능한 최대 집합 */
    kernel_cap_t    cap_effective;   /* 현재 실제로 적용되는 집합 */
    kernel_cap_t    cap_bset;        /* Bounding set — cap_permitted 상한 */
    kernel_cap_t    cap_ambient;     /* exec 시 자동 상속 (4.3+) */

    /* ── 사용자/네임스페이스 참조 ── */
    struct user_struct    *user;      /* 사용자별 자원 추적 (프로세스 수 제한 등) */
    struct user_namespace *user_ns;   /* 자격증명이 속한 user namespace */
    struct group_info    *group_info; /* 보조 그룹 목록 */

    /* ── LSM 보안 블롭 ── */
    struct lsm_blob_sizes *security;  /* LSM별 보안 데이터 (SELinux SID 등) */

    /* ── RCU 보호 ── */
    struct rcu_head       rcu;        /* RCU grace period 후 해제 */
} __randomize_layout;               /* 구조체 레이아웃 랜덤화 */
필드 그룹필드용도검사 시점
UID/GIDuid / gid실제 사용자/그룹 ID. getuid() 반환값프로세스 소유권
euid / egid유효 ID. DAC 접근 제어의 기준파일/IPC 권한 검사
suid / sgid저장 ID. setuid 프로그램의 권한 복귀용seteuid() 복귀
fsuid / fsgid파일시스템 전용 ID. NFS 서버 등에서 사용VFS inode 접근
보안 비트securebitsSECBIT_NOROOT: euid=0이어도 cap 자동 부여 금지execve()
Capabilitycap_permitted보유 가능 최대 집합. effective의 상한capable()
cap_effective현재 실제 적용 집합. 커널이 실제 검사하는 대상모든 권한 검사
cap_inheritableexec 시 자식에게 전달 가능 집합execve()
cap_bsetBounding set. cap_permitted에 추가될 수 있는 상한execve()
cap_ambient비특권 exec에서도 자동 상속. 4.3+ 추가execve()
네임스페이스user_ns자격증명의 해석 컨텍스트. UID 매핑 기준네임스페이스 경계
LSMsecuritySELinux SID, AppArmor 프로파일 등 LSM별 데이터LSM 훅 호출
struct cred — 보안 자격증명 관계도 struct task_struct real_cred / cred (2개 포인터) real_cred cred (override) real_cred (객관적) 시그널 전송, kill() 검사 다른 프로세스가 이 태스크를 볼 때 cred (주관적/override) 파일 접근, 자원 생성 이 태스크가 다른 객체에 접근할 때 struct cred 내부 uid/gid/euid/egid/fsuid/fsgid suid/sgid + securebits cap_inheritable / cap_permitted / cap_effective / cap_bset / cap_ambient user_ns / user / group_info security (LSM 블롭) usage (atomic refcount) / rcu (RCU 해제용) 보통 real_cred == cred (override_creds() 호출 시에만 분리)

Credential COW 생명주기: prepare_creds → commit_creds

커널은 credential을 COW(Copy-on-Write) 방식으로 관리합니다. 보안 속성을 변경하려면 반드시 새 복사본을 만들고, 수정 후 원자적으로 교체해야 합니다:

/* kernel/cred.c — credential 변경 패턴 */

/* 1단계: 현재 cred의 복사본 생성 */
struct cred *new = prepare_creds();
if (!new)
    return -ENOMEM;

/* 2단계: 복사본에서 원하는 필드 수정 */
new->euid = make_kuid(new->user_ns, 1000);
new->cap_effective = cap_drop(new->cap_effective, CAP_SYS_ADMIN);

/* 3단계: 보안 검사 (LSM이 변경을 허용하는지) */
int ret = security_prepare_creds(new, current_cred(), GFP_KERNEL);
if (ret) {
    abort_creds(new);    /* 실패 시 복사본 해제 */
    return ret;
}

/* 4단계: 원자적 교체 — rcu_assign_pointer(task->cred, new) */
commit_creds(new);
/* 이 시점 이후 현재 태스크의 보안 속성이 변경됨 */
/* 이전 cred는 RCU grace period 후 해제 */
코드 설명
  • 4행prepare_creds()는 현재 프로세스의 credkmemdup()으로 복사합니다. 참조 카운트가 1인 새 구조체를 반환하며, user/group_info/user_ns 등의 참조 카운트도 증가시킵니다.
  • 9-10행복사본에서만 수정하므로, 현재 실행 중인 다른 코드 경로가 보는 credential에는 영향이 없습니다. 이것이 COW의 핵심입니다.
  • 13행security_prepare_creds()는 LSM에게 credential 변경을 사전 검증할 기회를 줍니다. SELinux는 도메인 전이 규칙을 검사합니다.
  • 15행abort_creds()는 LSM 검사 실패 시 복사본을 안전하게 해제합니다. 참조 카운트를 감소시키고 관련 자원을 정리합니다.
  • 20행commit_creds()rcu_assign_pointer()로 태스크의 cred/real_cred 포인터를 원자적으로 교체합니다. 이전 cred는 RCU grace period가 지난 후 안전하게 해제됩니다.

override_creds / revert_creds 패턴

커널 내부에서 일시적으로 다른 자격증명으로 작업해야 할 때 사용하는 패턴입니다. NFS 서버, 파일시스템 데몬 등에서 활용됩니다:

/* 일시적 credential 변경 패턴 */

const struct cred *old_cred;
struct cred *new_cred;

new_cred = prepare_creds();
new_cred->fsuid = make_kuid(new_cred->user_ns, 0);  /* root로 파일 접근 */

/* 현재 태스크의 주관적 cred만 교체 (real_cred는 유지) */
old_cred = override_creds(new_cred);

/* 이 구간에서는 root 권한으로 파일 접근 수행 */
vfs_open(...);

/* 원래 credential로 복귀 */
revert_creds(old_cred);
put_cred(new_cred);
코드 설명
  • 10행override_creds()는 태스크의 cred 포인터만 교체하고 real_cred는 그대로 유지합니다. 이로써 "이 태스크가 다른 객체에 접근할 때"만 변경된 자격증명이 사용됩니다.
  • 16행revert_creds()cred 포인터를 저장해둔 이전 값으로 복원합니다. override/revert는 반드시 같은 함수 내에서 쌍을 이루어야 합니다.
  • 17행put_cred()로 임시 credential의 참조 카운트를 감소시킵니다. 더 이상 참조하는 곳이 없으면 RCU를 통해 해제됩니다.
real_cred vs cred 구분:

태스크에는 두 개의 credential 포인터가 있습니다. real_cred객관적 자격증명으로, 다른 프로세스가 이 태스크를 식별할 때(시그널 전송, ptrace 등) 사용됩니다. cred주관적 자격증명으로, 이 태스크가 파일·소켓 등 자원에 접근할 때 사용됩니다. 보통 두 포인터는 동일한 구조체를 가리키며, override_creds() 호출 시에만 분리됩니다.

seccomp-BPF 필터 내부 경로

seccomp-BPF는 시스템 콜 진입점에서 BPF 필터를 실행하여 호출 허용/거부를 결정합니다. 이 섹션에서는 커널 내부에서 필터가 실행되는 정확한 경로와 struct seccomp_filter의 구조를 분석합니다.

필터 실행 경로

사용자 공간: write(fd, buf, len)
    │
    ▼
syscall_enter_from_user_mode()   /* arch/x86/entry/common.c */
    │
    ├─ syscall_trace_enter()
    │     │
    │     ▼
    │   __secure_computing()     /* kernel/seccomp.c — 진입점 */
    │     │
    │     ├─ (strict 모드) → 허용 목록 검사 (read/write/exit/sigreturn만)
    │     │
    │     └─ (filter 모드) → seccomp_run_filters()
    │           │
    │           ▼
    │         BPF_PROG_RUN(filter→prog, &sd)
    │           │
    │           ├─ SECCOMP_RET_ALLOW  → 시스템 콜 계속 진행
    │           ├─ SECCOMP_RET_KILL   → SIGSYS로 프로세스 종료
    │           ├─ SECCOMP_RET_TRAP   → SIGSYS 시그널 전달
    │           ├─ SECCOMP_RET_ERRNO  → 지정 errno 반환
    │           ├─ SECCOMP_RET_TRACE  → ptrace 트레이서에 통지
    │           ├─ SECCOMP_RET_LOG    → 허용 + 로그 기록
    │           └─ SECCOMP_RET_USER_NOTIF → 감독자 프로세스에 위임
    │
    ▼
do_syscall_64()                  /* 실제 시스템 콜 핸들러 실행 */
seccomp-BPF 필터 실행 흐름 syscall_enter_from_user_mode() __secure_computing(sd) STRICT FILTER read/write/exit/sigreturn만 seccomp_run_filters() BPF_PROG_RUN() seccomp_data 입력 필터 체인 순회 (자식 → 부모) 가장 엄격한 결과 채택 RET_ALLOW RET_KILL → SIGSYS RET_USER_NOTIF do_syscall_64() 실행 프로세스 종료 감독자 프로세스 응답 대기 Bitmap 최적화: 허용된 syscall은 BPF 실행 없이 즉시 통과 (6.0+)

struct seccomp_filter 분석

seccomp 필터는 BPF 프로그램과 메타데이터를 담은 연결 리스트 구조입니다:

/* kernel/seccomp.c — seccomp 필터 구조체 (간소화) */

struct seccomp_filter {
    refcount_t          refs;       /* 참조 카운트 (fork 시 공유) */
    refcount_t          users;      /* task 연결 수 */
    bool                log;        /* 로깅 활성화 여부 */
    struct action_cache cache;      /* Bitmap 캐시 — syscall별 빠른 판정 */
    struct seccomp_filter *prev;    /* 부모 필터 (연결 리스트) */
    struct bpf_prog     *prog;      /* 컴파일된 cBPF 프로그램 */
    struct notification  *notif;     /* USER_NOTIF 데이터 (NULL이면 미사용) */
};
코드 설명
  • 4행refs는 필터의 참조 카운트입니다. fork() 시 자식 프로세스가 부모의 필터를 공유하므로, 참조 카운트가 증가합니다. 모든 참조가 해제되면 필터가 메모리에서 제거됩니다.
  • 7행action_cache는 Bitmap 최적화(커널 6.0+)의 핵심입니다. 시스템 콜 번호별로 "항상 ALLOW"인 경우를 비트맵에 기록하여, BPF 프로그램 실행 없이 즉시 허용 판정합니다. 이로써 허용된 시스템 콜의 오버헤드가 거의 제거됩니다.
  • 8행prev 포인터로 필터 체인을 형성합니다. prctl(PR_SET_SECCOMP)을 여러 번 호출하면 필터가 스택처럼 쌓입니다. 새 필터가 head가 되고, 기존 필터가 prev를 통해 연결됩니다.
  • 9행bpf_prog는 사용자가 제출한 cBPF 프로그램이 eBPF로 변환(JIT 또는 인터프리터)된 결과입니다. seccomp_data 구조체(시스템 콜 번호, 아키텍처, 인자 6개)를 입력으로 받습니다.
  • 10행notifSECCOMP_RET_USER_NOTIF 사용 시 감독자 프로세스와 통신하기 위한 데이터입니다. 컨테이너 런타임(runc, crun 등)에서 시스템 콜 에뮬레이션에 활용됩니다.

필터 평가 코드 분석

seccomp_run_filters()는 필터 체인을 순회하며 가장 엄격한 결과를 채택합니다:

/* kernel/seccomp.c — 필터 체인 실행 (간소화) */

static u32 seccomp_run_filters(const struct seccomp_data *sd,
                                struct seccomp_filter **match)
{
    u32 ret = SECCOMP_RET_ALLOW;
    struct seccomp_filter *f;

    /* 현재 태스크의 필터 체인을 자식→부모 순으로 순회 */
    for (f = READ_ONCE(current->seccomp.filter); f; f = f->prev) {
        u32 cur_ret = bpf_prog_run_pin_on_cpu(f->prog, sd);

        /* 더 엄격한(낮은 우선순위) 결과를 채택 */
        if ((cur_ret & SECCOMP_RET_ACTION_FULL) <
            (ret & SECCOMP_RET_ACTION_FULL)) {
            ret = cur_ret;
            *match = f;
        }
    }

    return ret;
}
코드 설명
  • 6행기본 반환값은 SECCOMP_RET_ALLOW(가장 관대)입니다. 필터가 없으면 모든 시스템 콜이 허용됩니다.
  • 10행current->seccomp.filter에서 가장 최근에 설치된 필터(head)부터 시작하여 prev 포인터를 따라 부모 필터까지 순회합니다. READ_ONCE()로 컴파일러 최적화에 의한 재로딩을 방지합니다.
  • 11행bpf_prog_run_pin_on_cpu()는 BPF 프로그램을 현재 CPU에 고정하여 실행합니다. 입력은 seccomp_data(시스템 콜 번호, 아키텍처, args[0..5])입니다.
  • 14-15행반환값의 ACTION 부분을 비교하여 더 엄격한(숫자가 작은) 결과를 채택합니다. 우선순위: KILL_PROCESS < KILL_THREAD < TRAP < ERRNO < USER_NOTIF < TRACE < LOG < ALLOW.
  • 17행*match에 결정을 내린 필터를 기록합니다. 이 정보는 감사 로그와 SECCOMP_RET_USER_NOTIF 처리에 사용됩니다.

seccomp_data: BPF 필터 입력

BPF 필터가 검사할 수 있는 데이터는 struct seccomp_data로 제한됩니다:

/* include/uapi/linux/seccomp.h */

struct seccomp_data {
    int   nr;                  /* 시스템 콜 번호 */
    __u32 arch;                /* AUDIT_ARCH_* 아키텍처 식별자 */
    __u64 instruction_pointer; /* 시스템 콜 진입 시 IP */
    __u64 args[6];             /* 시스템 콜 인자 6개 */
};
보안 주의: args[]는 레지스터 값의 스냅샷이므로 포인터가 가리키는 사용자 공간 메모리 내용을 직접 검사할 수 없습니다. 포인터 인자의 내용물 검사가 필요하면 SECCOMP_RET_USER_NOTIF를 통해 감독자 프로세스에서 처리해야 합니다 (TOCTOU 경쟁 조건 방지를 위해 SECCOMP_IOCTL_NOTIF_ADDFD 등 전용 API 사용).

LSM 프레임워크 내부 구조

LSM 프레임워크의 핵심은 security_hook_heads 전역 구조체와, 각 LSM 모듈이 훅을 등록하는 security_add_hooks() 함수입니다. 이 섹션에서는 훅 등록과 디스패치의 내부 메커니즘을 분석합니다.

security_hook_heads 구조

security_hook_heads는 약 230개의 보안 훅 포인트를 정의하는 전역 구조체입니다. 각 훅 포인트는 hlist_head(해시 리스트 헤드)로, 여러 LSM 모듈의 콜백이 연결 리스트로 등록됩니다:

/* security/security.c — 보안 훅 헤드 전역 구조체 */

struct security_hook_heads {
    struct hlist_head binder_set_context_mgr;
    struct hlist_head binder_transaction;
    /* ... */
    struct hlist_head file_permission;
    struct hlist_head file_alloc_security;
    struct hlist_head file_open;          /* ← security_file_open()이 순회하는 리스트 */
    /* ... */
    struct hlist_head task_alloc;
    struct hlist_head cred_prepare;
    struct hlist_head cred_free;
    /* ... ~230개 훅 포인트 */
} __lsm_ro_after_init;               /* 초기화 후 읽기 전용 (보안 강화) */
코드 설명
  • 3행security_hook_heads는 각 보안 검사 지점에 대응하는 hlist_head를 모은 구조체입니다. 파일, 프로세스, 소켓, IPC, 네트워크 등 커널의 모든 보안 관련 경로에 훅 포인트가 배치됩니다.
  • 9행file_open 훅 헤드에는 SELinux의 selinux_file_open, AppArmor의 apparmor_file_open 등이 연결 리스트로 등록됩니다. security_file_open() 호출 시 이 리스트를 순회합니다.
  • 15행__lsm_ro_after_init 어트리뷰트는 부팅 초기화 완료 후 이 구조체를 읽기 전용 메모리로 전환합니다. 공격자가 훅 포인터를 변조하여 보안 검사를 우회하는 것을 방지하는 하드닝 기법입니다.

security_hook_list 구조체

각 LSM 모듈이 등록하는 개별 훅 엔트리는 security_hook_list 구조체입니다:

/* include/linux/lsm_hooks.h — 개별 훅 엔트리 */

struct security_hook_list {
    struct hlist_node     list;   /* 연결 리스트 노드 */
    struct hlist_head     *head;  /* 소속 훅 헤드 포인터 */
    union security_list_options hook; /* 실제 콜백 함수 포인터 (유니온) */
    const struct lsm_id  *lsmid; /* LSM 식별자 (이름, ID) */
} __randomize_layout;

security_add_hooks() 등록 메커니즘

LSM 모듈이 부팅 시 자신의 훅들을 프레임워크에 등록하는 함수입니다:

/* security/security.c — LSM 훅 등록 */

void __init security_add_hooks(struct security_hook_list *hooks,
                              int count,
                              const struct lsm_id *lsmid)
{
    int i;

    for (i = 0; i < count; i++) {
        hooks[i].lsmid = lsmid;
        hlist_add_tail_rcu(&hooks[i].list, hooks[i].head);
    }
}
코드 설명
  • 3행__init 어트리뷰트는 이 함수가 부팅 초기화 시에만 호출됨을 표시합니다. 초기화 완료 후 이 함수의 코드 메모리는 해제됩니다.
  • 4행count는 등록할 훅 배열의 크기입니다. 예를 들어 SELinux는 약 200개, AppArmor는 약 50개의 훅을 등록합니다.
  • 10행각 훅 엔트리에 LSM 식별자를 저장합니다. 이 정보는 디버깅과 감사 로그에서 어떤 LSM이 결정을 내렸는지 추적하는 데 사용됩니다.
  • 11행hlist_add_tail_rcu()로 훅을 해당 포인트의 리스트 끝에 추가합니다. RCU 보호 삽입이므로, 이미 실행 중인 훅 순회에 영향을 주지 않습니다. 리스트 순서가 곧 LSM 호출 순서입니다.
security_hook_heads — 훅 연결 리스트 구조 security_hook_heads .file_open .file_permission .task_alloc .cred_prepare ... (~230개) .socket_connect security_hook_list selinux_file_open() security_hook_list apparmor_file_open() hook_list landlock_*() selinux_file_perm() apparmor_file_perm() selinux_task_alloc() selinux_cred_prepare() apparmor_cred_prepare() 부팅 시 security_add_hooks()로 등록. lsm= 파라미터 순서대로 배치. __lsm_ro_after_init: 초기화 완료 후 읽기 전용 전환 (변조 방지)

SELinux 훅 등록 예시

SELinux가 부팅 시 약 200개의 훅을 등록하는 코드 패턴입니다:

/* security/selinux/hooks.c — SELinux 훅 배열 및 등록 */

static struct security_hook_list selinux_hooks[] __ro_after_init = {
    /* 파일 훅 */
    LSM_HOOK_INIT(file_permission, selinux_file_permission),
    LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
    LSM_HOOK_INIT(file_open, selinux_file_open),
    /* 프로세스 훅 */
    LSM_HOOK_INIT(task_alloc, selinux_task_alloc),
    LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare),
    LSM_HOOK_INIT(cred_free, selinux_cred_free),
    /* 소켓 훅 */
    LSM_HOOK_INIT(socket_create, selinux_socket_create),
    LSM_HOOK_INIT(socket_connect, selinux_socket_connect),
    /* ... 약 200개 */
};

static const struct lsm_id selinux_lsmid = {
    .name = "selinux",
    .id = LSM_ID_SELINUX,
};

static int __init selinux_init(void)
{
    /* ... 초기화 ... */
    security_add_hooks(selinux_hooks,
                       ARRAY_SIZE(selinux_hooks),
                       &selinux_lsmid);
    return 0;
}
코드 설명
  • 3행selinux_hooks[] 배열은 __ro_after_init으로 표시되어, 부팅 초기화 완료 후 읽기 전용이 됩니다. 런타임에 훅 함수 포인터가 변조되는 것을 방지합니다.
  • 5-7행LSM_HOOK_INIT() 매크로는 security_hook_list를 초기화합니다. 첫 인자는 훅 이름(security_hook_heads의 필드명), 둘째 인자는 SELinux의 콜백 함수입니다.
  • 18-21행lsm_id는 LSM 모듈의 이름과 고유 ID를 정의합니다. LSM_ID_SELINUX는 정수 상수로, 커널 내부에서 LSM 식별에 사용됩니다.
  • 26-28행security_add_hooks() 호출로 약 200개 훅이 한번에 security_hook_heads의 각 리스트에 등록됩니다. 이 호출 순서(lsm= 파라미터)가 훅 실행 순서를 결정합니다.

LSM 스태킹과 호출 순서

커널 부팅 파라미터 lsm=으로 LSM 활성화 순서를 지정합니다. 이 순서가 곧 훅 호출 순서입니다:

# 기본 LSM 순서 (배포판별 상이)
lsm=lockdown,capability,landlock,yama,apparmor,selinux,bpf

# 현재 활성 LSM 확인
cat /sys/kernel/security/lsm
# 출력: lockdown,capability,landlock,yama,apparmor

# CONFIG_LSM 기본값 (빌드 시 결정)
# CONFIG_LSM="lockdown,capability,landlock,yama,integrity,apparmor"
LSM 유형예시특징
Minor LSMcapability, yama, lockdown, landlock, loadpin, safesetid항상 활성화 가능. 동시 다수 등록
Major LSMSELinux, AppArmor, SMACK, TOMOYO커널 6.x부터 동시 활성화 지원 (LSM 스태킹)
BPF LSMbpfeBPF 프로그램으로 런타임 정책 정의. 커널 5.7+
LSM 스태킹 역사: 전통적으로 Major LSM은 하나만 활성화할 수 있었습니다 (SELinux와 AppArmor 동시 사용 불가). 커널 6.x에서 LSM 스태킹이 도입되어 Major LSM도 동시 활성화가 가능해졌습니다. 모든 활성 LSM이 허용해야 접근이 승인되는 AND 로직은 동일합니다.

Capability 검사 경로: capable() 내부

커널에서 특권 작업을 수행하기 전에는 반드시 capable() 또는 ns_capable()을 호출하여 현재 프로세스가 해당 capability를 보유하는지 검사합니다. 이 검사는 POSIX capability 시스템과 LSM 프레임워크를 모두 거치는 다단계 과정입니다.

호출 체인 분석

커널 코드: capable(CAP_NET_ADMIN)
    │
    ▼
ns_capable(&init_user_ns, CAP_NET_ADMIN)
    │
    ▼
ns_capable_common(&init_user_ns, CAP_NET_ADMIN, CAP_OPT_NONE)
    │
    ├─ 1. cap_capable() 호출      /* POSIX capability 비트 검사 */
    │     │
    │     ├─ user namespace 계층 확인
    │     ├─ cap_raised(cred->cap_effective, cap) 비트 테스트
    │     └─ 0 (보유) 또는 -EPERM (미보유)
    │
    ├─ 2. security_capable() 호출  /* LSM 추가 검사 */
    │     │
    │     └─ call_int_hook(capable, cred, ns, cap, opts)
    │           ├─ selinux_capable()   → AVC 정책 검사
    │           └─ apparmor_capable()  → 프로파일 검사
    │
    └─ 최종 결과: 0 (허용) 또는 -EPERM (거부)

capable() / ns_capable() 구현 분석

/* kernel/capability.c — capability 검사 코어 */

bool capable(int cap)
{
    return ns_capable(&init_user_ns, cap);
}

bool ns_capable(struct user_namespace *ns, int cap)
{
    return ns_capable_common(ns, cap, CAP_OPT_NONE);
}

static bool ns_capable_common(struct user_namespace *ns,
                              int cap, unsigned int opts)
{
    int ret;

    /* CAP 번호 유효성 검사 */
    if (unlikely(!cap_valid(cap)))
        return false;

    /* 1단계: POSIX capability 비트 검사 */
    ret = cap_capable(current_cred(), ns, cap, opts);

    /* 2단계: LSM 추가 검사 (cap_capable 통과 시에만) */
    if (!ret)
        ret = security_capable(current_cred(), ns, cap, opts);

    /* 3단계: Audit 기록 (실패 시 포함) */
    if (!ret && !(opts & CAP_OPT_NOAUDIT))
        current_audit_context()->...;  /* 감사 로그 */

    return !ret;  /* 0이면 true(보유), 음수면 false(미보유) */
}
코드 설명
  • 3-6행capable()ns_capable()의 단축형으로, 항상 초기 user namespace(init_user_ns)를 기준으로 검사합니다. 즉, 전역 root 권한을 확인합니다.
  • 8-11행ns_capable()은 특정 user namespace 내에서의 capability를 검사합니다. 컨테이너 환경에서 namespace root(uid 0 in ns)가 전역 root와 다른 권한을 갖는 것이 이 함수로 구현됩니다.
  • 23행cap_capable()security/commoncap.c에 구현된 POSIX capability 비트 검사입니다. cred->cap_effective에서 해당 비트가 설정되어 있는지 확인하고, user namespace 계층 구조를 따라 올라가며 소유권을 검증합니다.
  • 27행security_capable()는 LSM 훅을 호출합니다. POSIX capability가 있어도 SELinux 정책이 해당 capability 사용을 거부할 수 있습니다. 예: allow httpd_t self:capability net_bind_service; 규칙이 없으면 Apache가 80번 포트 바인드 불가.
  • 30행CAP_OPT_NOAUDIT 플래그가 없으면 capability 사용(성공/실패)을 감사 로그에 기록합니다. CAP_OPT_NOAUDIT는 "탐색적 검사"(실패해도 정상인 경우)에서 불필요한 로그를 억제할 때 사용합니다.

cap_capable(): namespace 인식 비트 검사

cap_capable()은 user namespace 계층을 고려한 capability 비트 검사를 수행합니다:

/* security/commoncap.c — POSIX capability 비트 검사 (간소화) */

int cap_capable(const struct cred *cred,
               struct user_namespace *targ_ns,
               int cap, unsigned int opts)
{
    struct user_namespace *ns = targ_ns;

    /* user namespace 계층을 거슬러 올라가며 검사 */
    for (;;) {
        /* cred의 user_ns가 대상 ns와 같으면 비트 검사 */
        if (ns == cred->user_ns)
            return cap_raised(cred->cap_effective, cap) ?
                   0 : -EPERM;

        /* 초기 namespace까지 올라왔으면 실패 */
        if (ns == &init_user_ns)
            return -EPERM;

        /* ns를 생성한 프로세스가 부모 ns에서 CAP을 가졌는지 */
        if (ns->level <= cred->user_ns->level)
            return -EPERM;

        /* 부모 namespace로 이동 */
        ns = ns->parent;
    }
}
코드 설명
  • 10행대상 namespace에서 시작하여 부모 방향으로 거슬러 올라가는 루프입니다. credential의 user namespace가 대상 namespace와 일치하거나, 대상의 조상 namespace와 일치하는 경우를 찾습니다.
  • 12-14행credential의 user namespace가 대상 namespace와 같으면, cap_raised()cap_effective의 해당 비트를 직접 검사합니다. cap_raised()cap_t의 비트맵에서 특정 비트를 테스트하는 매크로입니다.
  • 17-18행초기 user namespace(init_user_ns)까지 올라갔는데도 일치하는 namespace를 찾지 못하면 -EPERM을 반환합니다. 이는 credential이 대상 namespace에 대한 권한이 없음을 의미합니다.
  • 21-22행namespace 레벨 비교로 불필요한 순회를 최적화합니다. credential의 namespace가 대상보다 높은(상위) 레벨이면 권한이 있을 수 없으므로 즉시 실패합니다.
  • 25행부모 namespace로 이동하여 검사를 계속합니다. 이 로직으로 인해, init_user_ns에서 CAP_SYS_ADMIN을 가진 프로세스는 모든 하위 namespace에서도 해당 capability를 갖게 됩니다.

execve() 시 capability 계산

프로세스가 execve()로 새 프로그램을 실행할 때, capability 세트는 다음 공식에 의해 재계산됩니다:

세트계산 공식설명
P'(ambient)(file is privileged) ? 0 : P(ambient)setuid/setgid 파일이면 ambient 초기화
P'(permitted)(P(inheritable) & F(inheritable)) | (F(permitted) & cap_bset) | P'(ambient)새 permitted = 상속 교집합 + 파일 허용 + ambient
P'(effective)F(effective) ? P'(permitted) : P'(ambient)파일 effective 비트가 설정되면 permitted 전체 활성화
P'(inheritable)P(inheritable)변경 없이 유지
P'(bounding)P(bounding)변경 없이 유지 (단, prctl로 삭제만 가능)

여기서 P()는 프로세스, F()는 파일, P'()는 exec 후의 새 값을 나타냅니다.

실무 활용: cap_ambient는 커널 4.3에서 추가되어, setuid 없이도 서비스 프로세스가 필요한 최소 capability만 유지할 수 있게 합니다. systemd의 AmbientCapabilities= 지시어가 이를 활용합니다. 예: AmbientCapabilities=CAP_NET_BIND_SERVICE로 1024 이하 포트만 바인드 허용.

capability 디버깅 실전

# 프로세스의 capability 세트 확인
cat /proc/$$status | grep -i cap
# CapInh: 0000000000000000   (inheritable)
# CapPrm: 000001ffffffffff   (permitted)
# CapEff: 000001ffffffffff   (effective)
# CapBnd: 000001ffffffffff   (bounding)
# CapAmb: 0000000000000000   (ambient)

# 비트마스크 → 이름 변환
capsh --decode=000001ffffffffff

# 특정 capability로 프로그램 실행 (capsh)
capsh --caps="cap_net_bind_service+eip" -- -c "/usr/bin/myserver"

# BPF 프로그램의 CAP_BPF 사용 추적 (ftrace)
echo 1 > /sys/kernel/debug/tracing/events/capability/cap_capable/enable
cat /sys/kernel/debug/tracing/trace_pipe | grep CAP_BPF

# Audit 로그에서 capability 사용 검색
ausearch -m SYSCALL --comm nginx -k cap_check
CAP_SYS_ADMIN 분리 추세:

CAP_SYS_ADMIN은 mount, swapon, ioctl 등 200개 이상의 작업을 하나의 capability로 묶고 있어 "새로운 root"라 불립니다. 커널 5.8+에서 CAP_BPF, CAP_PERFMON, CAP_CHECKPOINT_RESTORE 등으로 일부 기능이 분리되고 있으며, 이 추세는 계속되고 있습니다. 새 코드에서는 가능한 한 세분화된 capability를 사용하세요.

메모리 보호 메커니즘

커널 메모리 보호는 익스플로잇의 성공 확률을 낮추는 완화(Mitigation) 기술입니다. 취약점 자체를 제거하지는 않지만, 공격자가 취약점을 이용하여 코드 실행이나 권한 상승에 성공하는 것을 극도로 어렵게 만듭니다.

KASLR (커널 주소 공간 배치 랜덤화)

KASLR(Kernel Address Space Layout Randomization)은 부팅 시 커널 코드와 데이터의 기본 주소를 무작위로 배치합니다. 공격자가 가젯(Gadget) 주소를 예측하는 것을 방지하여 ROP(Return-Oriented Programming) 공격을 어렵게 만듭니다.

기술보호 대상커널 설정우회 방법
KASLR커널 텍스트/데이터 기본 주소CONFIG_RANDOMIZE_BASE정보 유출(Info Leak)로 주소 노출
FKASLR개별 함수 단위 랜덤화 (5.16+)CONFIG_FG_KASLR하나의 주소 유출로도 가젯 찾기 어려움
KPTI커널/사용자 페이지 테이블 분리CONFIG_PAGE_TABLE_ISOLATIONMeltdown 방어. 성능 1~5% 감소
# 실습 예제: KASLR 활성화 확인

# KASLR 활성화 여부 확인 (nokaslr 부팅 파라미터 사용 시 비활성화)
cat /proc/cmdline | grep -o 'nokaslr' || echo "KASLR enabled"

# 커널 기본 주소 확인 (root 필요, KASLR 활성화 시 부팅마다 변경)
# 주의: /proc/kallsyms는 비특권 사용자에게 0으로 표시됨
sudo cat /proc/kallsyms | head -5

# KPTI 활성화 확인
dmesg | grep -i "page table isolation"
# 출력 예: Kernel/User page tables isolation: enabled

# kptr_restrict 설정으로 커널 주소 유출 방지
cat /proc/sys/kernel/kptr_restrict
# 0: 제한 없음, 1: 비특권 사용자에게 숨김, 2: 모두에게 숨김
sudo sysctl kernel.kptr_restrict=1

SMEP/SMAP (하드웨어 접근 방지)

SMEP(Supervisor Mode Execution Prevention)은 커널 모드에서 사용자 공간 코드를 실행하는 것을 하드웨어 수준에서 차단합니다. SMAP(Supervisor Mode Access Prevention)은 커널이 사용자 공간 메모리에 직접 접근하는 것을 차단합니다. 두 기능 모두 CPU의 CR4 레지스터 비트로 제어됩니다.

기술차단 대상CPU 지원차단하는 공격
SMEPRing 0에서 Ring 3 코드 실행Intel Ivy Bridge+, AMD Zen+ret2user (사용자 공간 셸코드 실행)
SMAPRing 0에서 Ring 3 데이터 접근Intel Broadwell+, AMD Zen+커널이 사용자 구조체 직접 참조
NX/XD데이터 페이지에서 코드 실행대부분의 x86_64스택/힙 셸코드 실행

스택 보호 (Stack Canary)

스택 카나리(Stack Canary)는 함수의 반환 주소 앞에 랜덤 값(카나리)을 배치하여, 버퍼 오버플로 공격이 반환 주소를 덮어쓸 때 카나리 변조를 감지합니다. GCC의 -fstack-protector-strong 옵션으로 활성화됩니다.

개념 예시: 스택 카나리 메커니즘


스택 프레임 레이아웃 (CONFIG_STACKPROTECTOR_STRONG):

 높은 주소  ┌──────────────────┐
           │ 반환 주소 (RIP)    │  ← 공격 목표
           ├──────────────────┤
           │ 저장된 RBP        │
           ├──────────────────┤
           │ ★ 스택 카나리 ★   │  ← 랜덤 값 (부팅 시 생성)
           ├──────────────────┤
           │ 로컬 변수         │
           ├──────────────────┤
 낮은 주소  │ 로컬 버퍼[]       │  ← 오버플로 시작점
           └──────────────────┘

함수 종료 시 카나리 값 검사:
  if (canary != __stack_chk_guard)
      __stack_chk_fail();  → panic() 또는 프로세스 종료

KFENCE (커널 메모리 오류 감지)

KFENCE(Kernel Electric Fence)는 프로덕션 환경에서도 사용 가능한 저오버헤드 메모리 오류 감지기입니다. Slab 할당에서 버퍼 오버플로, Use-After-Free(UAF), 잘못된 해제(Double Free)를 감지합니다. KASAN보다 훨씬 낮은 오버헤드(~1%)로 동작합니다.

# KFENCE 활성화 확인 및 설정
cat /sys/module/kfence/parameters/sample_interval
# 기본값: 100 (100ms마다 하나의 할당을 KFENCE 보호 대상으로 선택)

# KFENCE 오류 발생 시 dmesg 출력 예시:
# BUG: KFENCE: out-of-bounds read in some_function+0x42/0x80
# Out-of-bounds read at 0xffff888012345000 (1b left of kfence-#73):

제어 흐름 무결성 (CFI)

CFI(Control Flow Integrity)는 간접 호출(indirect call)과 반환(return)의 대상이 유효한 함수 진입점인지 검증합니다. 함수 포인터 변조를 통한 코드 재사용 공격(ROP/JOP)을 방어합니다.

기술보호 범위구현커널 설정
kCFI간접 호출 (indirect call)Clang 컴파일러CONFIG_CFI_CLANG
FineIBT간접 호출 (forward-edge)Intel CET + Clang kCFICONFIG_FINEIBT
Shadow Call Stack반환 주소 (backward-edge)ARM64 전용CONFIG_SHADOW_CALL_STACK
IBT간접 분기 대상 검증Intel CET 하드웨어CONFIG_X86_KERNEL_IBT

메모리 보호 기술 종합

메모리 보호 기술 vs 공격 기법 공격 기법 방어 기술 효과 주소 예측 공격 KASLR + FKASLR + kptr_restrict 주소 랜덤화로 예측 불가 스택 버퍼 오버플로 Stack Canary + FORTIFY_SOURCE RIP 덮어쓰기 감지/차단 ROP/JOP (코드 재사용) kCFI + FineIBT + Shadow Call Stack 간접 호출/반환 대상 검증 ret2user (사용자 코드 실행) SMEP + SMAP + KPTI 커널↔사용자 경계 하드웨어 강제 힙 오버플로 / UAF KFENCE + Slab 랜덤화 + HARDENED_USERCOPY 힙 메모리 오류 감지/완화 Spectre / Meltdown (투기적 실행) Retpoline + IBRS/STIBP + KPTI 사이드 채널 차단 (성능 저하) 각 방어 기술은 독립적이며, 동시 활성화 시 종심 방어(Defense in Depth) 형성
참고:

각 메모리 보호 기술의 상세 구현, 성능 영향 벤치마크, 우회 사례, 아키텍처별 차이(x86 vs ARM64 vs RISC-V)에 대한 내용은 커널 하드닝 문서를 참조하세요.

커널 자체 보호 프로젝트 (KSPP)

KSPP(Kernel Self-Protection Project)는 리눅스 커널 자체의 공격 표면을 줄이고, 취약점이 존재하더라도 익스플로잇을 어렵게 만드는 것을 목표로 하는 업스트림 프로젝트입니다. "커널은 사용자 공간 프로세스를 보호하지만, 커널 자체도 보호해야 합니다"는 원칙에 기반합니다.

KSPP 핵심 목표

목표설명구현 상태
구조체 레이아웃 랜덤화중요 구조체의 필드 순서를 빌드마다 랜덤화하여 오프셋 예측 차단완료 (__randomize_layout)
초기화 강제스택/힙 변수 자동 0 초기화로 정보 유출 방지완료 (CONFIG_INIT_STACK_ALL_ZERO)
Slab 보호freelist 포인터 암호화/랜덤화완료 (SLAB_FREELIST_HARDENED)
정수 오버플로 감지산술 오버플로를 undefined behavior에서 감지 가능한 에러로 전환진행 중 (Clang UBSan)
배열 경계 검사가변 길이 배열 대체, __counted_by 어트리뷰트진행 중
전역 함수 포인터 보호읽기 전용 전환 (__ro_after_init)대부분 완료
W^X 강제모든 커널 메모리가 쓰기 가능이면서 실행 가능하지 않도록완료 (CONFIG_STRICT_KERNEL_RWX)
usercopy 검증copy_from_user()/copy_to_user()에서 slab 객체 경계 검사완료 (CONFIG_HARDENED_USERCOPY)
# 실습 예제: KSPP 관련 커널 설정 확인

# 현재 커널에서 KSPP 관련 설정 확인
zcat /proc/config.gz 2>/dev/null | grep -E "(STACKPROTECTOR|FORTIFY|HARDENED|RANDOMIZE|INIT_ON)" || \
  grep -E "(STACKPROTECTOR|FORTIFY|HARDENED|RANDOMIZE|INIT_ON)" /boot/config-$(uname -r)

# 권장 설정 (KSPP 체크리스트):
# CONFIG_STACKPROTECTOR_STRONG=y      — 스택 보호 (강화)
# CONFIG_FORTIFY_SOURCE=y              — 버퍼 오버플로 감지
# CONFIG_HARDENED_USERCOPY=y           — user/kernel copy 검증
# CONFIG_RANDOMIZE_BASE=y              — KASLR
# CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y    — 할당 시 0 초기화
# CONFIG_INIT_STACK_ALL_ZERO=y         — 스택 0 초기화
# CONFIG_SLAB_FREELIST_RANDOM=y        — Slab 랜덤화
# CONFIG_SLAB_FREELIST_HARDENED=y      — Slab 포인터 보호
# CONFIG_STRICT_KERNEL_RWX=y           — 커널 코드 W^X
# CONFIG_STRICT_MODULE_RWX=y           — 모듈 코드 W^X
# CONFIG_DEBUG_WX=y                    — W+X 매핑 감지 경고

# 현재 커널의 W+X 매핑 검사 결과
dmesg | grep "W+X"
# 정상: "x86/mm: Checked W+X mappings: passed, no W+X pages found."

FORTIFY_SOURCE 메커니즘

CONFIG_FORTIFY_SOURCEmemcpy(), strcpy(), sprintf() 등 문자열/메모리 조작 함수에서 버퍼 크기를 컴파일 시점과 런타임에 검사합니다. 알려진 크기의 버퍼에 대한 오버플로를 감지하여 panic() 또는 BUG()를 호출합니다.

/* 개념 예시: FORTIFY_SOURCE가 감지하는 오류 */

struct my_struct {
    char name[16];
    int value;
};

void vulnerable_function(struct my_struct *s, const char *input)
{
    /* FORTIFY_SOURCE 활성화 시:
     * 컴파일러가 s->name의 크기(16)를 알고 있으므로,
     * strlen(input) > 16이면 런타임에 감지하여 panic() 호출
     */
    strcpy(s->name, input);  /* → __fortify_panic() if overflow */

    /* 안전한 대안 */
    strscpy(s->name, input, sizeof(s->name));  /* 커널 권장 */
}

/* FORTIFY_SOURCE가 보호하는 함수 목록:
 * memcpy, memmove, memset, memcmp, memchr
 * strcpy, strncpy, strcat, strncat, strlen, strlcpy, strlcat
 * sprintf, snprintf, vsprintf, vsnprintf
 * kmemdup, memscan
 */

초기화 강제 (정보 유출 방지)

초기화되지 않은 스택/힙 변수는 이전 실행의 민감한 데이터(포인터, 키, 자격증명)를 노출할 수 있습니다. KSPP는 모든 변수를 자동으로 0으로 초기화하는 옵션을 제공합니다.

설정초기화 대상성능 영향보안 효과
CONFIG_INIT_STACK_ALL_ZERO모든 스택 로컬 변수1~2%스택 기반 정보 유출 차단
CONFIG_INIT_ON_ALLOC_DEFAULT_ONSlab/page 할당1~3%힙 기반 정보 유출 차단
CONFIG_INIT_ON_FREE_DEFAULT_ONSlab/page 해제 시3~5%UAF로 인한 정보 유출 차단

구조체 레이아웃 랜덤화

__randomize_layout 어트리뷰트가 적용된 구조체는 빌드마다 필드 순서가 랜덤화됩니다. 공격자가 구조체 내 특정 필드의 오프셋을 예측할 수 없게 하여, 정밀한 메모리 조작을 방지합니다.

/* 개념 예시: 구조체 레이아웃 랜덤화 */

struct cred {
    atomic_long_t usage;
    kuid_t        uid;
    kuid_t        euid;
    kernel_cap_t  cap_effective;
    /* ... */
} __randomize_layout;  /* ← 빌드마다 필드 순서 변경 */

/*
 * 빌드 A: [usage, uid, euid, cap_effective, ...]
 * 빌드 B: [cap_effective, usage, euid, uid, ...]
 * 빌드 C: [euid, cap_effective, uid, usage, ...]
 *
 * 공격자가 offsetof(struct cred, cap_effective)를
 * 컴파일 없이 알 수 없음 → 정밀한 커널 익스플로잇 작성이 어려워짐
 *
 * 적용 대상: task_struct, cred, file, inode 등 핵심 구조체
 */
KSPP vs 전통적 보안 모듈:

LSM/seccomp 같은 접근 제어 메커니즘은 "누가 무엇에 접근할 수 있는가"를 제어합니다. 반면 KSPP는 "취약점이 발견되었을 때 공격자가 얼마나 쉽게 악용할 수 있는가"를 제어합니다. 두 접근은 상호 보완적이며, 동시에 적용해야 종심 방어가 완성됩니다.

암호화(Crypto) 서브시스템

리눅스 커널의 Crypto API는 암호화 알고리즘을 통합된 인터페이스로 제공하는 프레임워크입니다. dm-crypt, IPsec, kTLS, 파일시스템 암호화(fscrypt), IMA 해시 등 커널 내부의 모든 암호화 연산이 이 API를 통해 이루어집니다.

Crypto API 구조

Linux Crypto API 아키텍처 소비자 계층 (Consumer) dm-crypt IPsec (ESP) kTLS fscrypt IMA WireGuard Crypto API (crypto_alloc_* / crypto_*_encrypt) skcipher, aead, hash, rng, akcipher, kpp 등 타입별 인터페이스 알고리즘 관리자 — 우선순위 기반 자동 선택 (HW > SW) 소프트웨어 구현 aes-generic, sha256-generic CPU 명령어 가속 aes-ni, sha256-avx2, arm-ce 하드웨어 가속기 QAT, CCP, CAAM, ccree 알고리즘 관리자가 우선순위에 따라 자동으로 최적 구현 선택 (HW 가속 > SIMD > 범용)
알고리즘 타입Crypto API 타입용도주요 알고리즘
대칭 암호skcipher블록/스트림 암호화AES-CBC, AES-XTS, ChaCha20
AEADaead인증 암호화AES-GCM, ChaCha20-Poly1305
해시shash / ahash무결성 검증SHA-256, SHA-512, BLAKE2
난수 생성rng키 생성, KASLRDRBG (CTR, HMAC, Hash)
공개키akcipher서명 검증, 키 교환RSA, ECDSA, EdDSA
키 합의kppDiffie-Hellman 키 교환ECDH, DH
# 실습 예제: Crypto API 정보 확인

# 등록된 암호화 알고리즘 목록
cat /proc/crypto | grep -E "^name|^driver|^priority" | head -30

# AES-NI 하드웨어 가속 확인
cat /proc/crypto | grep -A 4 "aes"
# priority가 높은 것이 우선 선택됨 (예: aes-ni → 300, aes-generic → 100)

# 커널 FIPS 모드 확인
cat /proc/sys/crypto/fips_enabled
# 1이면 FIPS 140-2/3 모드 활성화 (비인증 알고리즘 사용 차단)

키 관리 (Key Retention Service)

커널 Keyring 서브시스템은 암호화 키, 인증 토큰(Token), 인증서(Certificate)를 커널 메모리에 안전하게 저장하고 관리합니다. 사용자 공간의 keyctl 명령으로 키를 추가/조회/삭제할 수 있습니다.

키링 유형범위용도
@u (user)사용자별사용자 인증 토큰, SSH 키
@s (session)세션별세션 인증 정보
@p (process)프로세스별프로세스 고유 키
.builtin_trusted_keys시스템 전역UEFI Secure Boot 키, 커널 모듈 서명 검증
.ima시스템 전역IMA 파일 서명 검증 키
.evm시스템 전역EVM HMAC/서명 검증 키
# 실습 예제: 커널 키링 사용

# 사용자 키링에 키 추가
keyctl add user mykey "secret_data" @u

# 키 목록 확인
keyctl show @u

# 키 읽기
keyctl read $(keyctl search @u user mykey)

# 시스템 키링의 신뢰 키 확인 (Secure Boot 인증서)
sudo keyctl list %:.builtin_trusted_keys

# 커널 모듈 서명 검증에 사용되는 키
sudo keyctl list %:.system_keyring
참고:

Crypto API의 내부 구조, 알고리즘 등록/해제 메커니즘, 비동기(async) 처리, 하드웨어 가속기 드라이버 개발, FIPS 모드, 키 관리(Keyring)에 대한 상세 내용은 Linux Crypto FrameworkKey Retention Service 문서를 참조하세요.

Integrity(무결성) 서브시스템

Integrity 서브시스템은 부팅부터 런타임까지의 신뢰 체인(Chain of Trust)을 구성합니다. 파일이 변조되지 않았음을 암호학적으로 검증하며, 원격 증명(Remote Attestation)을 통해 시스템의 무결성 상태를 외부에서 확인할 수 있게 합니다.

기술검증 대상검증 방식용도
IMA파일 내용해시 측정(Measure) + 검증(Appraise)파일 변조 감지, TPM PCR 10 기록
EVM파일 확장 속성(xattr)HMAC 또는 디지털 서명보안 xattr(SELinux 라벨, IMA 해시) 변조 방지
dm-verity블록 디바이스(Block Device)Merkle 트리(Tree) 해시 검증읽기 전용 파티션 무결성 (Android, ChromeOS)
fs-verity개별 파일Merkle 트리 해시 검증개별 파일 무결성 (APK, 커널 모듈)
Lockdown LSM커널 자체 수정커널 변경 작업 차단integrity/confidentiality 2단계
# 실습 예제: IMA 정책 및 상태 확인

# IMA 측정 로그 확인 (부팅 이후 측정된 파일 목록)
sudo cat /sys/kernel/security/ima/ascii_runtime_measurements | head -10
# PCR  template-hash  filedata-hash  filename-hint
# 10   sha256:abc...  sha256:def...  /usr/bin/bash

# IMA 정책 확인
sudo cat /sys/kernel/security/ima/policy

# dm-verity 상태 확인 (Android/ChromeOS)
dmsetup status | grep verity

# fs-verity 파일 설정
# fsverity enable --hash-algorithm=sha256 /path/to/file
# fsverity measure /path/to/file

dm-verity: 블록 디바이스 무결성

dm-verity는 device-mapper 타겟으로, 블록 디바이스의 모든 블록에 대한 Merkle 트리 해시를 미리 계산하고, 읽기 시점에 해시를 검증합니다. Android의 Verified Boot(AVB)과 ChromeOS의 핵심 무결성 메커니즘입니다.

# 실습 예제: dm-verity 설정

# 해시 테이블 생성 (읽기 전용 파티션)
veritysetup format /dev/sda3 /dev/sda4
# Root hash: a1b2c3d4e5f6... ← 이 값을 부팅 파라미터에 전달

# dm-verity 활성화
veritysetup open /dev/sda3 verified-root /dev/sda4 a1b2c3d4e5f6...

# 검증된 디바이스 마운트
mount /dev/mapper/verified-root /mnt/verified

# dm-verity 상태 확인
dmsetup status verified-root
# 0 1048576 verity V 1 ... verified

fs-verity: 파일 단위 무결성

fs-verity는 개별 파일에 대한 Merkle 트리 해시를 파일 메타데이터에 저장하고, 읽기 시점에 페이지 단위로 검증합니다. dm-verity가 전체 파티션을 보호하는 것과 달리, fs-verity는 파일 단위로 선택적으로 적용됩니다.

특성dm-verityfs-verityIMA
검증 대상블록 디바이스 전체개별 파일정책에 매칭되는 파일
검증 시점블록 읽기 시페이지 읽기 시파일 열기 시
해시 저장별도 파티션파일 메타데이터 (inode)xattr (security.ima)
수정 가능불가 (읽기 전용)불가 (verity 설정 후)정책에 따라 허용
지원 파일시스템모든 블록 디바이스ext4, f2fs, btrfs모든 파일시스템
주 사용처Android AVB, ChromeOSAPK 서명, 커널 모듈컴플라이언스, 원격 증명
# fs-verity 설정 (ext4/f2fs/btrfs)

# 파일에 verity 활성화
fsverity enable --hash-algorithm=sha256 /path/to/file

# verity 해시 측정
fsverity measure /path/to/file
# sha256:a1b2c3d4e5... /path/to/file

# 파일 변조 시도 시 → I/O 오류 발생
# echo "tampered" > /path/to/file  → -EIO

# IMA와 연동: fs-verity 다이제스트를 IMA 정책에 사용
# ima 정책: appraise func=BPRM_CHECK fsverity_digest=sha256:a1b2c3d4e5...
참고:

IMA 정책 문법, 템플릿(ima-ng/ima-sig), 다이제스트 리스트, EVM 디지털 서명, 원격 증명(keylime), dm-verity/fs-verity 연동에 대한 상세 내용은 IMA/EVM 문서를 참조하세요.

네트워크 보안

리눅스 커널의 네트워크 보안은 패킷 필터링(netfilter), 프로토콜 수준 암호화(kTLS, IPsec), 네트워크 접근 제어(SELinux netlabel) 등 여러 계층에서 동작합니다.

netfilter / nftables

netfilter는 커널의 네트워크 패킷 처리 프레임워크로, 패킷 필터링, NAT(Network Address Translation), 연결 추적(Connection Tracking)을 제공합니다. nftables는 iptables의 후속으로, 통합된 규칙 엔진과 향상된 성능을 제공합니다.

도구커널 프레임워크특징상태
iptablesx_tables레거시, 체인별 순차 규칙 매칭유지보수 모드
nftables (nft)nf_tables통합 프레임워크, 세트(Set)/맵(Map), JIT 지원활발히 개발 중
iptables-nftnf_tables (호환 계층)iptables 문법으로 nftables 백엔드 사용전환 기간 사용
eBPF (XDP)BPF초고속 패킷 처리 (NIC 수준)성장 중
# 실습 예제: nftables 보안 규칙

# nftables 방화벽 규칙 확인
nft list ruleset

# 기본 방화벽 정책 예시
nft add table inet filter
nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
nft add chain inet filter forward '{ type filter hook forward priority 0; policy drop; }'
nft add chain inet filter output '{ type filter hook output priority 0; policy accept; }'

# 로컬호스트(Loopback) 허용
nft add rule inet filter input iif lo accept

# 기존 연결 허용
nft add rule inet filter input ct state established,related accept

# SSH (22번 포트) 접근 제한 — 특정 IP만 허용
nft add rule inet filter input ip saddr 192.168.1.0/24 tcp dport 22 accept

# 속도 제한 (Rate Limiting) — SYN flood 방어
nft add rule inet filter input tcp dport 80 ct state new limit rate 25/second accept

# 로깅 규칙
nft add rule inet filter input log prefix "DROP: " drop

kTLS (커널 TLS)

kTLS(Kernel TLS)는 TLS 레코드 계층의 암호화/복호화를 커널에서 처리하여, 사용자 공간과 커널 사이의 데이터 복사를 줄이고 sendfile() 같은 zero-copy 최적화를 가능하게 합니다. 사용자 공간 라이브러리(OpenSSL, GnuTLS)가 TLS 핸드셰이크를 완료한 후, 세션 키를 setsockopt(SOL_TLS)으로 커널에 전달하여 이후 데이터 전송/수신의 암호화를 커널이 담당합니다.

모드암호화복호화성능 이점
SW kTLS커널 (소프트웨어)커널 (소프트웨어)sendfile() zero-copy
HW kTLS (TX)NIC 하드웨어커널 (소프트웨어)CPU 오프로드 + zero-copy
HW kTLS (RX/TX)NIC 하드웨어NIC 하드웨어완전 오프로드
# kTLS 활성화 확인
cat /proc/net/tls_stat
# TlsCurrTxSw    0
# TlsCurrRxSw    0
# TlsTxSw        145232

# nginx에서 kTLS 활성화 (nginx 1.21.4+, OpenSSL 3.0+)
# ssl_conf_command Options KTLS;  ← nginx.conf에 추가

SELinux 네트워크 라벨링

SELinux는 netlabellabeled IPsec을 통해 네트워크 패킷에 보안 컨텍스트를 부여하고, 네트워크 수준의 MAC을 실현합니다.

# SELinux 네트워크 정책 예시

# 네트워크 인터페이스 라벨 확인
seinfo --netifcon

# 포트 라벨 확인 — 어떤 도메인이 어떤 포트를 사용할 수 있는지
semanage port -l | grep http
# http_port_t    tcp    80, 81, 443, 488, 8008, 8009, 8443, 9000

# 커스텀 포트에 HTTP 라벨 추가
sudo semanage port -a -t http_port_t -p tcp 8080

# SECMARK — iptables/nftables와 SELinux 통합
# 패킷에 SELinux 컨텍스트 태깅
iptables -t security -A INPUT -p tcp --dport 80 \
    -j SECMARK --selctx system_u:object_r:http_packet_t:s0

IPsec 및 WireGuard

커널 수준의 네트워크 암호화는 IPsec(xfrm 프레임워크)과 WireGuard(Noise 프로토콜 기반) 두 가지가 주로 사용됩니다.

특성IPsecWireGuard
코드 크기~40만 줄~4천 줄
암호 스위트다수 (AES, 3DES, ChaCha20 등)고정 (ChaCha20-Poly1305, Curve25519)
프로토콜IKEv2 + ESP/AHNoise 프레임워크
커널 설정CONFIG_XFRM, CONFIG_INET_ESPCONFIG_WIREGUARD
도입 커널2.5+ (오래됨)5.6
FIPS 인증가능 (배포판별)미지원 (고정 알고리즘)

보안 취약점 대응

리눅스 커널의 보안 취약점 보고와 패치 배포는 security@kernel.org 메일링 리스트를 통해 조율됩니다. 2024년부터 CVE 할당이 kernel.org CNA(CVE Numbering Authority)를 통해 직접 이루어지며, 취약점 처리 프로세스가 체계화되었습니다. 커널 취약점은 공격 표면의 크기와 커널의 특권 수준 때문에 다른 소프트웨어보다 더 심각한 보안 영향을 미칩니다.

보안 보고 시 주의사항:

커널 보안 취약점을 발견한 경우, 공개 메일링 리스트(LKML)에 바로 보고하지 마세요. security@kernel.org로 비공개 보고하고, PGP 암호화를 사용하세요. 협력 공개일(CRD) 전에 취약점 정보를 공개하면 패치 적용 전 공격에 노출될 수 있습니다. 자세한 절차는 Documentation/process/security-bugs.rst를 참고하세요.

CVE 처리 프로세스

단계행위자설명기간
1. 보고발견자security@kernel.org로 비공개 보고 (PGP 암호화 권장)즉시
2. 확인보안팀취약점 영향 범위 분석, 재현 확인1~7일
3. 패치 개발개발자+보안팀수정 패치 작성 및 내부 검토7~14일
4. CRD 배포보안팀협력 공개일(CRD) 전 배포판에 사전 통보공개 7일 전
5. 공개보안팀패치 공개, CVE 할당, 보안 권고문 발행CRD 당일
6. 안정화Greg K-H 등stable 트리에 백포트(Backport)1~7일

주요 커널 취약점 유형

유형설명대표 CVE방어 기술
UAF (Use-After-Free)해제된 메모리 참조CVE-2023-0386KFENCE, SLAB_FREELIST_HARDENED
스택 오버플로스택 버퍼 경계 초과 쓰기CVE-2021-33909Stack Canary, FORTIFY_SOURCE
정보 유출초기화되지 않은 메모리 노출CVE-2022-0847 (Dirty Pipe)INIT_ON_ALLOC, INIT_STACK_ALL_ZERO
경쟁 조건(Race)TOCTOU, 동시 접근CVE-2016-5195 (Dirty COW)올바른 동기화, KCSAN
타입 혼동잘못된 타입 캐스팅CVE-2022-27666kCFI, 구조체 레이아웃 랜덤화
정수 오버플로산술 오버플로로 잘못된 크기 계산CVE-2021-22555UBSan, 경계 검사
투기적 실행CPU 사이드 채널 공격CVE-2017-5753 (Spectre)Retpoline, IBRS, KPTI

주요 익스플로잇 기법과 완화

커널 취약점 익스플로잇은 일반적으로 다음 단계를 거칩니다. 각 단계에서 방어 기술이 개입하여 성공 확률을 낮춥니다.

단계공격자 목표방어 기술효과
1. 취약점 트리거메모리 손상 유발FORTIFY_SOURCE, KFENCE오버플로/UAF 조기 감지
2. 메모리 레이아웃 파악커널 주소 정보 획득KASLR, kptr_restrict주소 예측 불가
3. 제어 흐름 탈취함수 포인터/RIP 변조kCFI, Stack Canary변조 감지/차단
4. 코드 실행셸코드 또는 ROP 체인 실행SMEP/SMAP, W^X실행 경로 차단
5. 권한 상승credential 변조 (uid=0)구조체 랜덤화, LKRG오프셋 예측 불가/변조 감지
6. 지속성 확보루트킷 설치, 모듈 로드Lockdown, Module Sig커널 변경 차단

CVE 추적 및 패치 관리

# 실습 예제: 커널 취약점 확인 및 대응

# 현재 커널 버전 확인
uname -r

# 알려진 CPU 취약점 상태 확인
grep . /sys/devices/system/cpu/vulnerabilities/*

# 커널 보안 공지 확인 (kernel.org)
# https://www.kernel.org/category/releases.html

# 특정 CVE의 패치 여부 확인
# git log --grep="CVE-2024-" --oneline 커널_소스_디렉터리

# 커널 라이브 패치 상태 확인 (kpatch/livepatch)
ls /sys/kernel/livepatch/ 2>/dev/null
cat /sys/kernel/livepatch/*/enabled 2>/dev/null

# 배포판별 보안 업데이트 확인
# RHEL/CentOS:
yum updateinfo list security 2>/dev/null
# Ubuntu/Debian:
apt list --upgradable 2>/dev/null | grep -i security
LTS 커널 선택 가이드:

프로덕션 환경에서는 LTS(Long Term Support) 커널을 사용하되, 반드시 해당 LTS 시리즈의 최신 패치 릴리스를 사용해야 합니다. 예를 들어 6.6 LTS를 선택했다면, 6.6.1이 아니라 6.6.x(x가 최대)를 사용하세요. 각 패치 릴리스에는 보안 수정이 포함되어 있으며, Greg Kroah-Hartman은 "모든 stable 릴리스의 모든 수정은 보안 관련일 수 있습니다"고 강조합니다.

참고:

Spectre, Meltdown, Dirty COW, Dirty Pipe 등 주요 취약점의 상세 분석과 커널 패치 내용은 커널 보안 취약점 사례 문서를 참조하세요.

보안 설정 가이드

프로덕션 환경에서 커널 보안을 강화하기 위한 sysctl 설정과 커널 컴파일 옵션을 정리합니다.

sysctl 보안 설정

sysctl 경로권장값설명
kernel.kptr_restrict1 또는 2커널 포인터 주소 노출 차단 (KASLR 보호)
kernel.dmesg_restrict1비특권 사용자의 dmesg 접근 차단
kernel.perf_event_paranoid3비특권 사용자의 perf_event 사용 차단
kernel.unprivileged_bpf_disabled1비특권 BPF 프로그램 로드 차단
kernel.yama.ptrace_scope1ptrace 대상 제한 (부모→자식만 허용)
kernel.randomize_va_space2사용자 공간 ASLR 완전 활성화
kernel.modules_disabled1 (주의)커널 모듈 로드 완전 차단 (설정 후 해제 불가)
net.core.bpf_jit_harden2BPF JIT 상수 블라인딩(Constant Blinding)
net.ipv4.conf.all.rp_filter1역경로 필터링 — IP 스푸핑(Spoofing) 방지
net.ipv4.conf.all.accept_redirects0ICMP 리다이렉트 수신 차단
net.ipv4.conf.all.send_redirects0ICMP 리다이렉트 전송 차단
net.ipv4.tcp_syncookies1SYN flood 방어
fs.protected_hardlinks1하드 링크 생성 제한 (TMP 경쟁 조건 방지)
fs.protected_symlinks1심볼릭 링크 추적 제한
fs.protected_fifos2FIFO 파일 보호
fs.protected_regular2일반 파일 보호
# 실습 예제: 보안 sysctl 일괄 적용

# /etc/sysctl.d/99-security.conf 파일 생성
cat <<'SYSCTL_EOF' | sudo tee /etc/sysctl.d/99-security.conf
# === 커널 보안 ===
kernel.kptr_restrict = 1
kernel.dmesg_restrict = 1
kernel.perf_event_paranoid = 3
kernel.unprivileged_bpf_disabled = 1
kernel.yama.ptrace_scope = 1
kernel.randomize_va_space = 2

# === BPF 보안 ===
net.core.bpf_jit_harden = 2

# === 네트워크 보안 ===
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.tcp_syncookies = 1
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0

# === 파일시스템 보안 ===
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
fs.protected_fifos = 2
fs.protected_regular = 2
SYSCTL_EOF

# 적용
sudo sysctl --system

부팅 파라미터 보안 설정

커널 명령줄 파라미터(/proc/cmdline)를 통해 부팅 시점에 보안 설정을 제어할 수 있습니다.

파라미터효과
lockdown=integrityintegrity커널 무결성 보호 (Lockdown 1단계)
lockdown=confidentialityconfidentiality커널 기밀성 보호 (Lockdown 2단계, 더 엄격)
lsm=순서 문자열LSM 활성화 및 순서 지정
ima_policy=tcb, appraise_tcb 등IMA 기본 정책 선택
selinux=10 또는 1SELinux 활성화/비활성화
enforcing=10 또는 1SELinux enforce 모드 설정
apparmor=10 또는 1AppArmor 활성화/비활성화
init_on_alloc=10 또는 1Slab/page 할당 시 0 초기화
init_on_free=10 또는 1Slab/page 해제 시 0 초기화
slab_nomerge(플래그)Slab 캐시 병합 비활성화 (UAF 완화)
page_alloc.shuffle=10 또는 1페이지 할당 순서 랜덤화
vsyscall=nonenone, emulate, xonlyvsyscall 페이지 비활성화 (ROP 가젯 제거)
debugfs=offoff, ondebugfs 비활성화 (정보 유출 방지)
# /etc/default/grub에 보안 부팅 파라미터 추가
GRUB_CMDLINE_LINUX="lockdown=integrity lsm=lockdown,capability,landlock,yama,apparmor \
    init_on_alloc=1 slab_nomerge page_alloc.shuffle=1 vsyscall=none debugfs=off \
    randomize_kstack_offset=on"

# 적용
sudo update-grub  # Debian/Ubuntu
sudo grub2-mkconfig -o /boot/grub2/grub.cfg  # RHEL/Fedora

커널 컴파일 보안 옵션 체크리스트

카테고리CONFIG 옵션설명성능 영향
메모리 보호CONFIG_RANDOMIZE_BASE=yKASLR 활성화무시 가능
CONFIG_PAGE_TABLE_ISOLATION=yKPTI (Meltdown 방어)1~5%
CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y할당 시 0 초기화1~3%
스택 보호CONFIG_STACKPROTECTOR_STRONG=y스택 카나리 (강화)<1%
CONFIG_INIT_STACK_ALL_ZERO=y스택 0 초기화1~2%
CONFIG_SHADOW_CALL_STACK=y반환 주소 보호 (ARM64)<1%
코드 무결성CONFIG_CFI_CLANG=ykCFI (간접 호출 검증)1~2%
CONFIG_STRICT_KERNEL_RWX=y커널 코드 W^X무시 가능
CONFIG_DEBUG_WX=yW+X 매핑 경고부팅 시만
힙 보호CONFIG_SLAB_FREELIST_RANDOM=ySlab 랜덤화무시 가능
CONFIG_SLAB_FREELIST_HARDENED=ySlab 포인터 보호무시 가능
CONFIG_HARDENED_USERCOPY=yuser/kernel copy 검증<1%
기능 제한CONFIG_SECURITY_DMESG_RESTRICT=ydmesg 비특권 접근 차단없음
CONFIG_SECURITY_LOCKDOWN_LSM=yLockdown LSM 활성화없음
투기적 실행CONFIG_RETPOLINE=ySpectre v2 방어~2%
CONFIG_CPU_IBRS_ENTRY=yIBRS 기반 Spectre 방어~1%

컨테이너 보안 아키텍처

컨테이너는 Linux 커널의 여러 보안 메커니즘을 조합하여 격리를 구현합니다. 단일 기술이 아닌 Namespace + Cgroup + seccomp + LSM + Capabilities의 다층 보안 모델이 컨테이너의 격리 경계를 형성합니다.

컨테이너 격리 메커니즘

메커니즘격리 대상커널 기능보안 기여
PID namespace프로세스 ID 공간CLONE_NEWPID호스트 프로세스 숨김
Net namespace네트워크 스택CLONE_NEWNET독립 네트워크 인터페이스
Mount namespace파일시스템 마운트CLONE_NEWNS호스트 파일시스템 숨김
User namespaceUID/GID 매핑CLONE_NEWUSERrootless 컨테이너
UTS namespace호스트명CLONE_NEWUTS호스트 정보 숨김
IPC namespaceSystem V IPCCLONE_NEWIPCIPC 자원 격리
Cgroup namespacecgroup 뷰CLONE_NEWCGROUPcgroup 경로 숨김
Time namespace시스템 시계CLONE_NEWTIME (5.6+)가상 시간 격리
Cgroups v2자원 사용량CPU/메모리/IO 제한자원 고갈 공격 방지
seccomp-BPF시스템 콜~50개 위험 syscall 차단공격 표면 축소
AppArmor/SELinux파일/네트워크 접근LSM 프로파일/정책MAC 접근 제어
Capabilities 제한root 권한14개만 부여 (기본)최소 권한

컨테이너 기본 Capability (Docker/Podman)

컨테이너 런타임은 기본적으로 root 컨테이너에도 제한된 Capability만 부여합니다:

기본 허용 (14개)기본 차단 (주요)
CHOWN, DAC_OVERRIDE, FSETIDSYS_ADMIN (mount, swapon 등)
FOWNER, KILL, SETGID, SETUIDSYS_MODULE (커널 모듈 로드)
NET_BIND_SERVICE, NET_RAWSYS_RAWIO (I/O 포트 접근)
SETFCAP, SETPCAPSYS_PTRACE (프로세스 디버깅)
SYS_CHROOT, MKNOD, AUDIT_WRITENET_ADMIN (네트워크 관리)
# 실습 예제: 컨테이너 보안 설정

# 모든 capability 차단 후 필요한 것만 추가
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx

# seccomp 프로파일 지정
docker run --security-opt seccomp=custom-profile.json myapp

# read-only 루트 파일시스템
docker run --read-only --tmpfs /tmp myapp

# user namespace 활성화 (rootless)
podman run --userns=keep-id myapp

# 컨테이너의 보안 상태 확인
docker inspect --format '{{.HostConfig.SecurityOpt}}' container_name
docker inspect --format '{{.HostConfig.CapAdd}} {{.HostConfig.CapDrop}}' container_name
컨테이너는 VM이 아닙니다:

컨테이너는 호스트 커널을 공유하므로, 커널 취약점은 컨테이너 탈출로 직결됩니다. 보안이 중요한 환경에서는 gVisor(사용자 공간 커널), Kata Containers(경량 VM), Firecracker(마이크로VM) 등의 추가 격리 기술을 고려하세요. 또한 --privileged 플래그는 모든 보안 제한을 해제하므로 프로덕션에서 절대 사용하지 않아야 합니다.

User Namespace와 Capability 격리

User namespace는 UID/GID 매핑과 Capability 격리를 제공하여 rootless 컨테이너의 핵심을 이룹니다. User namespace 내부의 root(uid 0)는 해당 namespace 내에서만 capability를 갖고, 호스트에서는 일반 사용자로 동작합니다.

개념 예시: User namespace 기반 권한 격리


호스트: uid=1000 (일반 사용자)
  │
  └─ unshare(CLONE_NEWUSER) → User namespace 생성
       │
       ├─ /proc/self/uid_map: "0 1000 1"
       │   → ns 내부 uid 0 = 호스트 uid 1000
       │
       ├─ ns 내부: uid=0, cap_effective=ALL
       │   → mount, chroot 등 가능 (ns 내부에서만)
       │
       └─ 호스트 관점: uid=1000, 일반 사용자
           → 호스트 자원에 대한 권한 없음

capable(CAP_SYS_ADMIN) 호출 시:
  1. ns_capable(current_user_ns, CAP_SYS_ADMIN) → true (ns 내부)
  2. ns_capable(init_user_ns, CAP_SYS_ADMIN) → false (호스트)
  → 결과는 검사 대상 namespace에 따라 달라짐
작업User NS 내부 (uid 0)호스트 관점
mount()허용 (mount ns 내)호스트 마운트 불가
chown()허용 (매핑된 UID 범위 내)호스트 파일 소유권 변경 불가
iptables허용 (net ns 내)호스트 방화벽 변경 불가
커널 모듈 로드거부 (init_user_ns 필요)거부
mknod()거부 (device ns 필요)거부

보안 모니터링과 침해 감지

커널 보안 메커니즘은 사전 방어(Prevention)뿐 아니라 감지(Detection)와 대응(Response)도 포함합니다. 런타임 보안 모니터링은 eBPF, Audit, ftrace를 기반으로 합니다.

모니터링 접근 방식

커널 보안 모니터링은 크게 세 가지 접근 방식으로 나뉩니다:

접근 방식특징적용 시점대표 도구
사전 방어 (Prevention)보안 위반 시도를 차단접근 시도 시점SELinux, seccomp, Landlock
감지 (Detection)보안 이벤트를 탐지하고 경고실시간 또는 준실시간Falco, Tetragon, Audit
사후 분석 (Forensics)침해 후 원인과 영향 분석사건 발생 후ausearch, crash, AIDE

커널 기반 보안 모니터링 도구

도구기반 기술감지 대상오버헤드
FalcoeBPF / 커널 모듈비정상 시스템 콜, 파일 접근, 네트워크 연결낮음
TetragoneBPF (Cilium)프로세스 실행, 네트워크 정책 위반, 파일 무결성낮음
TraceeeBPF (Aqua Security)커널 이벤트 기반 위협 감지낮음
LKRG커널 모듈커널 코드/데이터 무결성, credential 변조낮음
auditd커널 Audit시스템 콜, 파일 변경, 인증 이벤트중간
AIDE/OSSEC파일 해시 비교파일시스템 변조주기적 스캔
# 실습 예제: eBPF 기반 보안 모니터링

# bpftrace로 권한 상승 시도 감지
sudo bpftrace -e '
kprobe:commit_creds {
    $new = (struct cred *)arg0;
    $old = (struct cred *)curtask->real_cred;
    if ($new->uid.val == 0 && $old->uid.val != 0) {
        printf("ALERT: uid escalation %d -> 0, comm=%s, pid=%d\n",
               $old->uid.val, comm, pid);
    }
}'

# 커널 모듈 로드 감시
sudo bpftrace -e '
kprobe:do_init_module {
    printf("MODULE LOAD: %s by pid=%d comm=%s\n",
           str(((struct module *)arg0)->name), pid, comm);
}'

# 의심스러운 프로세스 실행 감시 (ftrace)
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_process_exec/enable
cat /sys/kernel/debug/tracing/trace_pipe

커널 수준 침해 지표 (IoC)

침해 지표감지 방법의미
예상치 못한 커널 모듈lsmod, Audit KERN_MODULE루트킷(Rootkit) 가능성
credential uid 0 전이 (비정상 경로)bpftrace commit_creds 모니터링권한 상승 익스플로잇
/dev/mem, /dev/kmem 접근Audit 파일 감시커널 메모리 직접 조작
숨겨진 프로세스/proc 와 syscall 비교루트킷에 의한 프로세스 숨김
syscall 테이블 변조LKRG, 주기적 해시 검증시스템 콜 후킹
비정상 네트워크 연결eBPF socket 모니터링백도어(Backdoor) 통신
참고:

LKRG의 커널 코드 무결성 검증, 자격증명 모니터링, SELinux/기타 보안 강제에 대한 상세 내용은 LKRG (커널 런타임 가드) 문서를 참조하세요.

최신 보안 기술 동향

리눅스 커널 보안은 지속적으로 발전하고 있으며, 최근에는 io_uring 보안 강화, eBPF 기반 보안, Rust 언어 도입이 주요 화제입니다.

io_uring 보안

io_uring은 고성능 비동기 I/O 인터페이스이지만, 커널 내에서 많은 시스템 콜을 대신 실행하므로 보안 표면이 매우 넓습니다. seccomp 필터를 우회하여 제한된 시스템 콜을 io_uring을 통해 실행할 수 있는 문제가 보고되어, 보안이 중요한 환경에서는 io_uring 사용을 제한하는 것이 권장됩니다.

문제설명대응
seccomp 우회io_uring 내부에서 실행되는 작업은 seccomp 필터를 거치지 않음커널 6.0+에서 IORING_OP_*별 seccomp 지원 추가
넓은 공격 표면io_uring 커널 코드가 많은 서브시스템과 상호작용Google은 프로덕션에서 io_uring 비활성화
권한 에스컬레이션여러 CVE 발생 (2021-2023)io_uring_disabled sysctl (6.6+)
# io_uring 제한 설정 (커널 6.6+)
# 0: 모두 허용, 1: 비특권 사용자 차단, 2: 완전 비활성화
sudo sysctl kernel.io_uring_disabled=2

# 컨테이너 환경에서 io_uring 차단 (seccomp 프로파일)
# docker run --security-opt seccomp=io_uring_blocked.json ...

eBPF 보안

eBPF(extended Berkeley Packet Filter)는 강력한 커널 프로그래밍 능력을 제공하지만, 동시에 보안 위험을 내포합니다. 검증기(verifier) 우회 취약점이 발견되면 커널 메모리를 읽고 쓸 수 있으므로, 비특권 eBPF 사용을 제한하는 것이 중요합니다.

보안 제어설정효과
비특권 BPF 차단kernel.unprivileged_bpf_disabled=1CAP_BPF 없는 사용자의 BPF 프로그램 로드 차단
JIT 하드닝net.core.bpf_jit_harden=2상수 블라인딩으로 JIT 스프레이 공격 방지
BPF LSMCONFIG_BPF_LSM=yeBPF로 런타임 보안 정책 정의 가능
서명 검증CONFIG_BPF_UNPRIV_DEFAULT_OFF=y빌드 시 기본적으로 비특권 BPF 차단
참고:

eBPF 기반 보안 모니터링, BPF LSM 정책 작성, Cilium/Falco/Tetragon 같은 eBPF 보안 도구에 대한 상세 내용은 eBPF 보안 정책 문서를 참조하세요.

Rust 도입과 메모리 안전성

리눅스 커널 6.1부터 Rust 언어 지원이 공식 도입되었습니다. Rust의 소유권(Ownership) 시스템과 빌림 검사(Borrow Checker)는 C 코드에서 빈번한 메모리 오류(UAF, 버퍼 오버플로, 초기화되지 않은 변수)를 컴파일 시점에 차단합니다.

측면C 코드Rust 코드
UAF (Use-After-Free)런타임 오류 (취약점)컴파일 오류 (소유권 위반)
버퍼 오버플로런타임 오류 (FORTIFY_SOURCE로 일부 감지)컴파일 오류 (배열 경계 검사)
데이터 레이스런타임 오류 (KCSAN으로 일부 감지)컴파일 오류 (Send/Sync 트레잇)
Null 역참조런타임 panicOption<T> 타입으로 컴파일 시 강제 처리
초기화되지 않은 변수정보 유출 가능컴파일 오류 (초기화 필수)
성능최적화된 수동 관리C와 동등 (zero-cost abstraction)

현재 Rust로 작성된 커널 구성요소는 초기 단계이며, 주로 드라이버(예: Apple GPU 드라이버, Android Binder IPC) 개발에 활용되고 있습니다. 핵심 서브시스템(스케줄러, 메모리 관리)의 Rust 전환은 장기적 과제입니다.

// 개념 예시: Rust 커널 모듈의 안전성

// C 코드에서 흔한 UAF 패턴:
//   struct cred *old = current_cred();
//   put_cred(old);        /* 해제 */
//   old->uid;             /* ← UAF! 컴파일 오류 없음 */

// Rust에서는 소유권 시스템이 이를 컴파일 시점에 차단:
fn example(cred: Box<Cred>) {
    let uid = cred.uid();  // OK: cred 소유권 있음
    drop(cred);            // 명시적 해제
    // cred.uid();          // ← 컴파일 오류!
    // error[E0382]: borrow of moved value: `cred`
}

// 데이터 레이스도 타입 시스템에서 차단:
// Send + Sync 트레잇이 없는 타입은 스레드 간 공유 불가
// Arc<Mutex<T>>로 명시적 동기화 필요

공급망 보안 (Supply Chain Security)

커널과 배포판의 공급망 보안은 빌드 과정, 패키지 서명, 재현 가능 빌드(Reproducible Build)를 통해 소프트웨어 변조를 방지합니다.

기술보호 대상구현
커널 모듈 서명커널 모듈 변조CONFIG_MODULE_SIG_FORCE, 빌드 시 자동 서명
UEFI Secure Boot부트로더/커널 변조UEFI DB에 등록된 키로 서명 검증
패키지 서명배포판 패키지 변조GPG 서명 (rpm --checksig, apt-secure)
재현 가능 빌드빌드 과정 변조동일 소스 → 동일 바이너리 검증
SLSA 프레임워크전체 공급망빌드 출처 증명, 소스 무결성
SBOM구성 요소 추적소프트웨어 자재 명세서
참고:

SLSA 프레임워크, 재현 가능 빌드 설정, SBOM 생성, 커널 모듈 서명 인프라에 대한 상세 내용은 공급망 보안 문서를 참조하세요.

커널 버전별 주요 보안 기능

커널 버전보안 기능카테고리
3.5seccomp-BPF 필터 모드접근 제어
3.14KASLR (x86_64)메모리 보호
4.3Ambient capabilities권한 관리
4.8KPTI 초기 구현메모리 보호
4.14Spectre/Meltdown 대응하드웨어 방어
4.20STACKLEAK (스택 지움)정보 유출 방지
5.2KFENCE메모리 오류 감지
5.7BPF LSM런타임 정책
5.8CAP_BPF, CAP_PERFMON 분리권한 세분화
5.13Landlock LSM비특권 샌드박싱
5.16FKASLR (함수 단위 랜덤화)메모리 보호
6.0seccomp Bitmap 최적화성능
6.1Rust 언어 지원메모리 안전성
6.2kCFI (Clang CFI)코드 무결성
6.6io_uring_disabled sysctl공격 표면 축소
6.8LSM 스태킹 완성접근 제어
보안 업데이트 전략:

프로덕션 환경에서는 LTS(Long Term Support) 커널을 사용하고, stable 보안 패치를 신속히 적용하는 것이 권장됩니다. Greg Kroah-Hartman은 "사용 중인 stable 커널의 최신 릴리스를 항상 사용하라"고 강조합니다. 커널 라이브 패치(kpatch, livepatch)를 활용하면 재부팅 없이 보안 패치를 적용할 수 있습니다.

보안 트러블슈팅

보안 메커니즘이 정상 동작을 차단하는 경우, 체계적인 디버깅 절차가 필요합니다. "보안을 비활성화"하는 것이 아니라 "올바른 정책을 설정"하는 것이 목표입니다.

SELinux AVC 거부 디버깅

# 1단계: AVC 거부 로그 확인
sudo ausearch -m avc -ts recent
# type=AVC msg=audit(1234567890.123:456): avc:  denied  { read }
#   for  pid=1234 comm="nginx" name="index.html"
#   scontext=system_u:system_r:httpd_t:s0
#   tcontext=system_u:object_r:default_t:s0 tclass=file

# 2단계: 문제 원인 분석 (잘못된 파일 컨텍스트인 경우가 많음)
ls -Z /var/www/html/index.html
# default_t ← 잘못된 타입. httpd_sys_content_t여야 함

# 3단계: 파일 컨텍스트 복원
sudo restorecon -Rv /var/www/html/

# 또는: 수동 컨텍스트 변경
sudo chcon -t httpd_sys_content_t /var/www/html/index.html

# 4단계: 정책 모듈이 필요한 경우
sudo ausearch -m avc -ts recent | audit2allow -m myfix
# 생성된 .te 파일을 검토한 후:
sudo ausearch -m avc -ts recent | audit2allow -M myfix
sudo semodule -i myfix.pp

# 주의: audit2allow를 무분별하게 사용하면 보안이 약화됩니다
# 반드시 생성된 규칙을 검토하고 최소 권한만 허용하세요

AppArmor 프로파일 디버깅

# 1단계: AppArmor 로그 확인
sudo journalctl -k | grep -i apparmor
# audit: type=1400 ... apparmor="DENIED" operation="open"
#   profile="/usr/sbin/nginx" name="/var/log/custom/access.log"

# 2단계: complain 모드로 전환 (차단 없이 로깅만)
sudo aa-complain /usr/sbin/nginx

# 3단계: 정상 동작 수행 후 로그에서 필요한 규칙 확인
sudo aa-logprof

# 4단계: 프로파일에 규칙 추가 후 enforce 모드로 복귀
sudo aa-enforce /usr/sbin/nginx

seccomp 차단 디버깅

# seccomp에 의한 SIGSYS 감지
dmesg | grep -i seccomp
# audit: type=1326 ... sig=31 syscall=314 compat=0 ip=0x7f... code=0x0

# syscall 번호 → 이름 변환
ausyscall 314
# io_uring_setup

# strace로 차단되는 시스템 콜 확인
strace -f -e trace=process,network ./myapp 2>&1 | grep -i seccomp

# 컨테이너에서 seccomp 프로파일 디버깅
docker run --security-opt seccomp=unconfined myapp  # 임시 비활성화 (디버깅 전용)

Capability 부족 디버깅

# Audit 로그에서 capability 거부 확인
sudo ausearch -m SYSCALL,PROCTITLE -k cap_check -ts recent

# 프로세스의 현재 capability 확인
cat /proc/$(pidof nginx)/status | grep Cap
# CapEff: 0000000000002000  ← CAP_NET_BIND_SERVICE만 있음

# 비트마스크 디코딩
capsh --decode=0000000000002000
# 0x0000000000002000=cap_net_bind_service

# 필요한 capability 추가 (systemd 서비스)
# [Service]
# CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_RAW
# AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_RAW

# 또는 파일 capability 설정
sudo setcap 'cap_net_bind_service,cap_net_raw=ep' /usr/sbin/myserver

보안 점검 체크리스트

점검 항목확인 명령기대값
KASLR 활성화grep nokaslr /proc/cmdline출력 없음 (활성화)
KPTI 활성화dmesg | grep "page table isolation""enabled"
Spectre 완화cat /sys/devices/system/cpu/vulnerabilities/spectre_v2"Mitigation: ..."
kptr_restrictcat /proc/sys/kernel/kptr_restrict1 또는 2
dmesg 제한cat /proc/sys/kernel/dmesg_restrict1
ptrace 제한cat /proc/sys/kernel/yama/ptrace_scope1 이상
비특권 BPF 차단cat /proc/sys/kernel/unprivileged_bpf_disabled1
SELinux/AppArmorcat /sys/kernel/security/lsmselinux 또는 apparmor 포함
SYN cookiescat /proc/sys/net/ipv4/tcp_syncookies1
IP 스푸핑 방지cat /proc/sys/net/ipv4/conf/all/rp_filter1
W+X 매핑 없음dmesg | grep "W+X""no W+X pages found"
서명되지 않은 모듈 없음cat /proc/sys/kernel/tainted0
# 자동화된 보안 점검 스크립트 예시
#!/bin/bash

echo "=== 커널 보안 점검 ==="

# KASLR
if ! grep -q nokaslr /proc/cmdline 2>/dev/null; then
    echo "[OK] KASLR 활성화"
else
    echo "[WARN] KASLR 비활성화 (nokaslr 부팅 파라미터)"
fi

# kptr_restrict
kptr=$(cat /proc/sys/kernel/kptr_restrict)
if [ "$kptr" -ge 1 ]; then
    echo "[OK] kptr_restrict = $kptr"
else
    echo "[WARN] kptr_restrict = $kptr (권장: 1 이상)"
fi

# Spectre/Meltdown
for vuln in /sys/devices/system/cpu/vulnerabilities/*; do
    status=$(cat "$vuln" 2>/dev/null)
    name=$(basename "$vuln")
    if echo "$status" | grep -qi "mitigation"; then
        echo "[OK] $name: $status"
    elif echo "$status" | grep -qi "not affected"; then
        echo "[OK] $name: Not affected"
    else
        echo "[WARN] $name: $status"
    fi
done

# LSM
lsm=$(cat /sys/kernel/security/lsm 2>/dev/null)
echo "[INFO] 활성 LSM: $lsm"

echo "=== 점검 완료 ==="

커널 보안은 단일 기술이 아닌 여러 계층의 메커니즘이 조합되어 동작합니다. 각 하위 주제에 대한 내용은 아래 전용 문서를 참고하세요. 허브 페이지인 이 문서에서 전체적인 아키텍처와 상호 관계를 파악한 후, 개별 주제의 기술 문서로 진행하는 것이 권장됩니다.

카테고리문서주요 내용
접근 제어LSM / SeccompLSM 프레임워크 내부, SELinux/AppArmor
eBPF 보안 정책BPF LSM, Cilium, Tetragon
Linux ContainersNamespace/Cgroup 격리
하드닝커널 하드닝KASLR, CFI, Spectre/Meltdown
보안 취약점 사례주요 CVE 분석
무결성IMA/EVM정책 문법, 원격 증명
Secure BootUEFI 부팅 보안
LKRG런타임 무결성 검증
암호화Crypto FrameworkCrypto API, 하드웨어 가속
Key Retention Service키 관리, TPM Sealed Keys
기밀 컴퓨팅기밀 컴퓨팅AMD SEV, Intel TDX, ARM CCA
TrustZone & OP-TEESecure World, TEE
공급망공급망 보안SLSA, 재현 가능 빌드

외부 참고 자료