Secure Boot 심화
Secure Boot 심화: UEFI 키 계층과 모드 상태, dbx/SBAT 폐기, Shim/MOK, UKI, UEFI HTTP Boot와 서명된 iPXE, 모듈 서명, TPM/LUKS2, OVMF 실습, 배포판 운영 패턴, dm-verity/fs-verity, Lockdown을 다룹니다.
핵심 요약
- PK/KEK/db/dbx — 허용 목록과 차단 목록이 어디에 저장되고 누가 갱신 권한을 갖는지 구분합니다.
- Shim/MOK — 배포판 키와 사용자 키가 펌웨어 db를 어떻게 보완하는지 이해합니다.
- UKI — 커널, initrd, 커맨드라인, SBAT를 한 PE 파일로 묶어 검증 단위를 단순화합니다.
- PCR — Secure Boot는 차단, Measured Boot는 기록을 담당하며 두 메커니즘은 서로 대체 관계가 아닙니다.
- Lockdown/IMA — 부팅이 끝난 뒤에도 모듈, 펌웨어, 사용자 공간 파일의 무결성 계층이 계속 필요합니다.
단계별 이해
- 펌웨어 상태 고정
SecureBoot,SetupMode,db/dbx,MokList상태를 먼저 확인합니다. - 누가 누구를 검증하는지 추적
펌웨어, shim, GRUB 또는 UKI, 커널, 모듈이 각각 어떤 키링을 사용하는지 연결합니다. - 허용과 차단을 분리해서 본다
db와MOK는 허용,dbx와SbatLevel은 차단 경로라는 점을 구분합니다. - 부팅 이후 계층까지 점검
Lockdown, IMA, dm-verity, fs-verity, LoadPin 같은 런타임 보호가 실제로 이어지는지 확인합니다.
UEFI Secure Boot 신뢰 체인, Setup/User/Audit/Deployed 모드, Authenticode 서명 구조, SBAT 폐기 메커니즘, UKI(systemd-stub), 커널 모듈 서명, IMA/EVM, TPM Measured Boot, dm-verity/fs-verity — 부트에서 런타임까지 이어지는 무결성 계층을 소스 코드 수준에서 분석합니다.
Secure Boot 아키텍처 개요
UEFI Secure Boot는 플랫폼 펌웨어가 부팅 과정에서 실행하는 모든 EFI 바이너리의 디지털 서명을 검증하여, 신뢰할 수 없는 코드의 실행을 차단하는 보안 메커니즘입니다. 이 검증은 PKI(Public Key Infrastructure) 기반의 신뢰 체인(Chain of Trust)으로 구현됩니다.
보안 목표와 신뢰 경계
Secure Boot를 정확히 쓰려면 "무엇을 보장하고 무엇을 보장하지 않는지"를 먼저 분리해야 합니다. Secure Boot는 허용된 EFI 이미지 실행에는 강하지만, 펌웨어 취약점 자체나 런타임 파일 변조까지 자동으로 해결해 주지는 않습니다.
| 보장하는 것 | 보장하지 않는 것 | 실전 보완책 |
|---|---|---|
| 허용된 EFI 이미지와 드라이버만 실행 | 펌웨어 자체가 이미 손상되었는지 여부 | 펌웨어 업데이트 검증, 플랫폼 고유 보호 기능, 공급망 검증 |
| 부트 체인 중 다음 단계 이미지의 서명 확인 | 부팅 이후 읽기/쓰기 가능한 사용자 공간 파일의 변조 | IMA/EVM, dm-verity, fs-verity, IPE |
| 폐기된 인증서/해시/dbx/SBAT 기준으로 구버전 차단 | 무엇이 실행되었는지 원격에서 증명 | TPM Measured Boot, PCR 정책, 원격 증명 |
| 커널이 Secure Boot 상태를 감지하여 Lockdown 연동 | 루트 권한 획득 후 커널 외부 파일을 마음대로 바꾸는 시도 전체 | LoadPin, 읽기 전용 루트, 정책형 LSM |
| 서명 없는 모듈/커널/kexec 이미지 차단 기반 제공 | 운영자가 잘못 배포한 합법적이지만 취약한 이미지 | SBAT 세대 관리, 키 교체, 복구 미디어 갱신 절차 |
Secure Boot UEFI 변수
Secure Boot의 핵심은 NVRAM에 저장된 인증된(authenticated) UEFI 변수들입니다:
| 변수 | GUID | 역할 | 업데이트 권한 |
|---|---|---|---|
| PK (Platform Key) | EFI_GLOBAL_VARIABLE | 신뢰 체인의 루트. 1개만 존재 | 물리적 접근 + 현재 PK 소유자 |
| KEK (Key Exchange Key) | EFI_GLOBAL_VARIABLE | db/dbx 업데이트 권한 부여 | PK 소유자 |
| db (Signature DB) | EFI_IMAGE_SECURITY_DATABASE | 허용된 서명/해시 목록 | KEK 소유자 |
| dbx (Forbidden DB) | EFI_IMAGE_SECURITY_DATABASE | 폐기된 서명/해시 목록 | KEK 소유자 |
| dbt (Timestamp DB) | EFI_IMAGE_SECURITY_DATABASE | 타임스탬프 서명 검증용 | KEK 소유자 |
| SecureBoot | EFI_GLOBAL_VARIABLE | 활성 상태 (0/1, 읽기 전용) | 펌웨어 |
| SetupMode | EFI_GLOBAL_VARIABLE | Setup 모드 (PK 미설치 시 1) | 펌웨어 |
| AuditMode | EFI_GLOBAL_VARIABLE | 감사 모드 상태. 서명 강제를 끄고 IEIT 로그를 강화 | 부트 서비스 시점 펌웨어 |
| DeployedMode | EFI_GLOBAL_VARIABLE | 최종 배포 모드. 보안 강도를 가장 높게 유지 | 펌웨어 |
| VendorKeys | EFI_GLOBAL_VARIABLE | 플랫폼이 OEM 기본 키만 사용하는지 여부 (1=기본, 0=커스텀 변경 감지) | 펌웨어 |
Secure Boot 모드 전이
UEFI 2.10은 Secure Boot 상태를 단순히 켜짐/꺼짐으로만 보지 않습니다. 실제 운영에서는 Setup Mode, User Mode, Audit Mode, Deployed Mode 네 상태를 이해해야 키 등록, 검증 우회, 현장 복구 절차를 정확히 설명할 수 있습니다.
| 보조 변수 | 의미 |
|---|---|
PKDefault, KEKDefault | OEM이 공장 기본값으로 제공하는 키 사본. 런타임 검증에는 직접 사용하지 않지만 공장 상태 복구 시 기준점이 됩니다. |
dbDefault, dbxDefault | 기본 허용/차단 목록 사본. 복구 툴이나 관리 에이전트가 OEM 기준 상태와 현재 상태를 비교할 때 유용합니다. |
Image Execution Information Table (IEIT) | Audit Mode에서 특히 중요합니다. 어떤 EFI 이미지가 허용/거부되었는지, 왜 실패했는지를 기록하여 새 키 조합 시험 시 시스템을 벽돌 상태로 만들지 않고 분석할 수 있습니다. |
EFI_VARIABLE_AUTHENTICATION_2 구조체
Secure Boot 변수(db, dbx, KEK 등)는 인증된 업데이트만 허용합니다. 변수를 수정하려면 EFI_VARIABLE_AUTHENTICATION_2 래퍼로 감싸야 합니다:
typedef struct {
EFI_TIME TimeStamp; /* 재전송 방지용 타임스탬프 */
WIN_CERTIFICATE_UEFI_GUID AuthInfo; /* PKCS#7 서명 래퍼 */
} EFI_VARIABLE_AUTHENTICATION_2;
typedef struct {
UINT32 dwLength; /* 인증서 전체 크기 */
UINT16 wRevision; /* WIN_CERT_REVISION = 0x0200 */
UINT16 wCertificateType; /* WIN_CERT_TYPE_EFI_GUID */
EFI_GUID CertType; /* EFI_CERT_TYPE_PKCS7_GUID */
UINT8 CertData[1]; /* DER 인코딩된 PKCS#7 SignedData */
} WIN_CERTIFICATE_UEFI_GUID;
EFI_VARIABLE_AUTHENTICATION_2 기반으로 정의되지만, UEFI 2.10의 일부 키 등록 절차 설명은 EFI_VARIABLE_APPEND_WRITE를 사용하는 경로에서 EFI_VARIABLE_AUTHENTICATION_3 서술도 함께 등장합니다.
실무에서는 efitools가 생성하는 인증 파일 형식과 펌웨어 구현의 허용 범위를 같이 확인하는 편이 안전합니다.
Authenticode 서명 형식
UEFI 실행 파일(shimx64.efi, grubx64.efi, vmlinuz.efi)은 PE/COFF(Portable Executable) 형식이며, Authenticode 서명으로 무결성이 검증됩니다.
PE/COFF 서명 구조
Authenticode 해시 계산
Authenticode 해시는 일반적인 파일 해시와 다릅니다. 서명 자체와 체크섬 필드를 제외하고 해시를 계산합니다:
/* Authenticode 해시에서 제외되는 영역:
* 1. PE Optional Header의 Checksum 필드 (4 bytes)
* 2. Data Directory[4] Certificate Table 엔트리 (8 bytes)
* — PE32/PE32+ 공통 인덱스(구조적 정의).
* 파일 내 절대 오프셋은 Optional Header 크기에 따라 다름:
* PE32(32-bit): Certificate Table → 오프셋 0x98
* PE32+(64-bit): Certificate Table → 오프셋 0xA8
* → 절대 오프셋 대신 PE 헤더 파서의 구조적 필드 접근 필요
* 3. WIN_CERTIFICATE 영역 전체 (서명 데이터)
*
* 나머지 모든 바이트가 해시에 포함됨
*/
# sbsign으로 커널 서명
$ sbsign --key db.key --cert db.crt --output vmlinuz.signed vmlinuz.efi
# 서명 검증
$ sbverify --cert db.crt vmlinuz.signed
Signature verification OK
# Authenticode 해시 확인
$ pesign -i vmlinuz.signed -h -P
# SHA-256 해시 출력
# 서명 정보 상세 출력
$ pesign -i vmlinuz.signed -S
---------------------------------------------
certificate address is 0x...
Content was not encrypted.
Content is a detached PKCS#7 signature
Signing time: ...
Signer's common name: My Secure Boot Key
...
서명 도구 비교
| 도구 | 용도 | 특징 |
|---|---|---|
sbsign | PE 바이너리 Authenticode 서명 | sbsigntool 패키지, OpenSSL 기반 |
sbverify | Authenticode 서명 검증 | sbsigntool 패키지 |
pesign | PE 서명/검증/해시 | NSS 기반, Red Hat 계열 |
osslsigncode | Authenticode 서명 (범용) | OpenSSL 기반, 타임스탬프 지원 |
sign-file | 커널 모듈 서명 (PKCS#7) | 커널 빌드 시스템 내장 |
kmodsign | 커널 모듈 서명 (레거시) | sign-file의 이전 이름 |
폐기 메커니즘: dbx와 SBAT
취약점이 발견된 부트로더나 커널을 차단하려면 폐기(revocation) 메커니즘이 필요합니다. UEFI는 두 가지 방식을 제공합니다.
dbx (전통적 방식)
dbx는 차단할 바이너리의 해시 또는 서명 인증서를 기록합니다:
# dbx 현재 내용 확인
$ efi-readvar -v dbx
Variable dbx, length 13588
dbx: List 0, type SHA256
Hash: a4:6f:0c:2e:... # 차단된 바이너리 해시
# dbx 업데이트 (KEK로 서명된 업데이트 적용)
$ efi-updatevar -f dbx-update.auth dbx
# dbx에 새 해시 추가 (Linux fwupd 사용)
$ fwupdmgr get-updates
$ fwupdmgr update
SBAT (Secure Boot Advanced Targeting)
SBAT는 dbx의 확장성 문제를 해결하기 위해 도입된 세대 기반(generation-based) 폐기 메커니즘입니다. 개별 해시 대신 컴포넌트의 보안 세대 번호를 비교합니다.
/* SBAT 메타데이터 (.sbat 섹션, CSV 형식)
*
* shimx64.efi의 .sbat 섹션 예시:
*/
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
shim,4,UEFI shim,shim,1,https://github.com/rhboot/shim
shim.ubuntu,2,Canonical Ltd.,shim,15.8,https://launchpad.net/ubuntu/+source/shim
/* GRUB2의 .sbat 섹션 예시: */
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
grub,4,Free Software Foundation,grub,2.12,https://www.gnu.org/software/grub/
grub.ubuntu,2,Canonical Ltd.,grub2,2.12-1ubuntu1,https://launchpad.net/ubuntu/+source/grub2
SBAT 폐기 정책은 SbatLevel 변수에 저장됩니다. upstream shim 저장소의 2025년 5월 10일 기준 샘플은 이미 2024년 shim 취약점, 2025년 GRUB 취약점, 특정 배포판별 추가 폐기를 반영하고 있습니다:
# SbatLevel 확인
$ mokutil --list-sbat-revocations
sbat,1,2025051000
shim,4
grub,5
grub.peimage,2
grub.proxmox,2
# 해석:
# - shim 세대가 4 미만이면 거부
# - grub 전역 세대가 5 미만이면 거부
# - grub.proxmox 세대가 2 미만이면 해당 제품 계열만 추가 거부
grub,5)와 제품별 세대(grub.proxmox,2)가 분리되므로, 현장에서는 "업스트림 세대"와 "배포판 포크 세대"를 모두 확인해야 합니다.
SBAT vs dbx 비교
| 특성 | dbx (해시 기반) | SBAT (세대 기반) |
|---|---|---|
| 식별 방식 | SHA-256 해시 | 컴포넌트명 + 세대 번호 |
| NVRAM 사용량 | 차단 바이너리 수 × 32B | 컴포넌트 수 × ~50B (고정) |
| 확장성 | NVRAM 제한에 빠르게 도달 | 우수 (컴포넌트 단위) |
| 세분화 | 개별 바이너리 | 배포판별 컴포넌트 |
| 적용 대상 | 모든 EFI 바이너리 | .sbat 섹션이 있는 바이너리 |
| 업데이트 | KEK 서명 필요 | SbatLevel 변수 업데이트 |
Shim과 MOK 인프라
대부분의 Linux 배포판은 Microsoft UEFI CA가 서명한 Shim 부트로더를 1단계로 사용합니다. Shim은 자체 키 데이터베이스(MOK)를 관리하여 배포판별 2단계 부트로더와 커널을 검증합니다.
Shim 검증 로직
/*
* shim의 EFI 바이너리 검증 순서 (verify_buffer 함수):
*
* 1. dbx (UEFI 폐기 목록) 확인 → 해시/인증서 매치 시 거부
* 2. SBAT 검증 → SbatLevel 미달 시 거부
* 3. db (UEFI 허용 목록) 검증 → 매치 시 허용
* 4. MOK (Machine Owner Key) 검증 → 매치 시 허용
* 5. shim 내장 인증서 검증 → 매치 시 허용
* 6. 모두 실패 → 거부
*/
/* shim 소스 코드에서의 핵심 검증 흐름 (간략화): */
static EFI_STATUS
verify_buffer(char *data, int datasize,
PE_COFF_LOADER_IMAGE_CONTEXT *context)
{
/* Step 1: dbx 확인 (차단 목록) */
if (check_denylist(context, data, datasize) == FOUND)
return EFI_ACCESS_DENIED;
/* Step 2: SBAT 확인 */
if (verify_sbat_section(data, datasize) != EFI_SUCCESS)
return EFI_SECURITY_VIOLATION;
/* Step 3-5: 허용 목록 순서대로 확인 */
if (check_allowlist(context, data, datasize) == FOUND)
return EFI_SUCCESS;
return EFI_ACCESS_DENIED;
}
MOK 관리
# 자체 서명 키 생성
$ openssl req -new -x509 -newkey rsa:2048 -keyout MOK.key \
-out MOK.crt -nodes -days 3650 \
-subj "/CN=My Secure Boot MOK"
# DER 형식으로 변환 (MOK 등록에 필요)
$ openssl x509 -in MOK.crt -outform DER -out MOK.der
# MOK 등록 요청 (다음 부팅 시 MokManager에서 확인)
$ mokutil --import MOK.der
# 비밀번호 입력 → 재부팅 시 MokManager UI에서 동일 비밀번호 입력
# 등록된 MOK 목록 확인
$ mokutil --list-enrolled
[key 1]
SHA1 Fingerprint: ab:cd:ef:...
Subject: CN = My Secure Boot MOK
Issuer: CN = My Secure Boot MOK
# MOK 삭제
$ mokutil --delete MOK.der
# Secure Boot 상태 확인
$ mokutil --sb-state
SecureBoot enabled
SecureBoot validation is enabled in shim
MOK UEFI 변수
| 변수 | 설명 |
|---|---|
MokList / MokListRT | 허용된 MOK 인증서/해시 목록과 그 런타임 사본. 커널은 보통 MokListRT를 읽어 키링으로 가져옵니다. |
MokListX / MokListXRT | MOK 차단 목록과 그 런타임 사본. shim의 MOK 버전 denylist입니다. |
MokNew / MokAuth | 다음 부팅에서 추가 또는 제거할 키 요청과 그 요청의 비밀번호 기반 확인 해시입니다. |
MokPW / MokPWStore | MokManager 상호작용용 암호 해시입니다. 물리 콘솔 확인 절차의 핵심입니다. |
MokSBState / MokSBStateRT | shim의 자체 검증 강제 상태입니다. 1이면 shim이 insecure mode로 전환됩니다. |
MokDBState / MokIgnoreDB | shim이 펌웨어 db를 검증에 사용할지 제어합니다. Linux는 이 상태를 보고 펌웨어 db 인증서 import 여부를 결정할 수 있습니다. |
MokListTrusted / MokListTrustedRT | MOK에 들어 있는 CA 키를 Linux가 신뢰할지 알려주는 힌트입니다. |
ShimRetainProtocol | shim 검증 프로토콜을 후속 단계까지 유지할지 제어합니다. 2단계 로더가 추가 PE 파일 검증을 이어갈 때 사용합니다. |
SbatLevel | SBAT 세대 기반 폐기 정책입니다. |
Secure Boot와 네트워크 부팅 체인
PXE나 HTTP Boot는 EFI 이미지를 어디서 가져올지를 정하는 전달 경로이고, Secure Boot는 그 이미지를 실행해도 되는지를 서명으로 판단하는 정책입니다. 따라서 UEFI 네트워크 부팅에서는 "전송 경로"와 "신뢰 체인"을 분리해서 봐야 합니다. 실무에서 가장 흔한 경로는 firmware db가 shimx64.efi를 검증하고, shim이 grubx64.efi 또는 서명된 ipxe.efi를 검증한 뒤, 그 다음 단계 로더가 설치 커널이나 UKI를 넘기는 구조입니다.
| 부팅 경로 | 첫 번째 EFI 이미지 | 신뢰 체인 | 실무 포인트 |
|---|---|---|---|
| UEFI HTTP Boot + shim + GRUB | shimx64.efi | 펌웨어 db → shim → GRUB → 커널/UKI | 배포판 기본 Secure Boot 경로와 가장 잘 맞습니다. 다만 HTTPS와 Secure Boot는 서로 대체 관계가 아닙니다. |
| UEFI PXE + shim + GRUB | shimx64.efi 또는 grubx64.efi | 동일한 서명 체인을 TFTP 경로 위에서 사용 | 레거시 인프라와 호환성은 좋지만, 대형 이미지 전송 성능은 HTTP Boot나 iPXE보다 불리합니다. |
| UEFI PXE/HTTP Boot + 서명된 iPXE | ipxe.efi 또는 shim 뒤의 ipxe.efi | 펌웨어 db 또는 shim/MOK가 iPXE를 허용 | 동적 메뉴, API 연동, Kickstart/Autoinstall/Preseed 분기에 유리합니다. 대신 EFI 부트로더 서명 키와 모듈 전용 서명 키를 혼용하지 않는 편이 안전합니다. |
| UEFI direct EFIStub/UKI | vmlinuz.efi 또는 UKI .efi | 펌웨어 db가 이미지를 직접 검증 | shim을 우회하면 MOK 경로도 함께 사라집니다. 따라서 Debian UEFI CA나 자체 db 키를 펌웨어에 직접 등록해야 할 수 있습니다. |
| Legacy BIOS PXE | PXE ROM / pxelinux.0 | UEFI Secure Boot 적용 대상 아님 | BIOS PXE 자체에는 Secure Boot 개념이 없습니다. 서명 기반 부트 무결성이 필요하면 UEFI 경로로 옮기는 편이 맞습니다. |
ipxe.efi를 직접 서명해 db 또는 MOK로 신뢰시키거나, 이미 신뢰되는 shimx64.efi가 다음 단계로 검증하게 배치합니다. 배포판이 DKMS용으로 자동 생성하는 MOK는 "커널 모듈 서명"에는 편리하지만, EFI 부트로더까지 같은 키로 승인할지는 별도로 판단하는 편이 안전합니다.
UKI (Unified Kernel Image)와 최신 Linux 부트 체인
최근 Linux 배포판은 UKI(Unified Kernel Image)를 적극 채택하고 있습니다. UKI는 커널, initrd, 커맨드라인, SBAT, PCR 정책 정보를 하나의 PE/COFF UEFI 애플리케이션에 묶어서 Secure Boot 검증 단위를 단순화합니다.
UKI 구성 요소
UAPI Group UKI 명세는 UKI를 IMAGE_SUBSYSTEM_EFI_APPLICATION을 가진 PE/COFF 파일로 정의합니다. 최소 필수 섹션은 .linux이고, 여기에 운영 정보와 보안 메타데이터를 추가합니다.
| 섹션 | 역할 | Secure Boot 관점 |
|---|---|---|
.linux | 실제 커널 이미지 | UKI가 직접 서명되므로 커널도 같은 PE 서명 보호 경계 안에 들어갑니다. |
.initrd | 초기 사용자 공간 | 외부 파일로 따로 두지 않으면 "커널은 서명됐지만 initrd는 바뀜" 문제를 줄일 수 있습니다. |
.cmdline | 고정 커맨드라인 | 보안상 민감한 커널 파라미터를 이미지에 묶을 수 있습니다. |
.osrel, .uname | 표시용 운영체제/릴리스 정보 | 부트 메뉴 정렬과 운영 자동화에 유용합니다. |
.sbat | SBAT CSV 메타데이터 | dbx 대신 세대 기반 폐기 대상으로 삼을 수 있습니다. |
.pcrsig, .pcrpkey | PCR 11 예상값과 검증 키 | TPM 바인딩 비밀을 "특정 UKI"에 정교하게 묶는 데 사용됩니다. |
# UKI 내부 섹션 확인
$ objdump -h vmlinuz.efi | grep -E "\\.(linux|initrd|cmdline|osrel|uname|sbat|pcrsig|pcrpkey)"
# SBAT 섹션만 추출
$ objcopy --dump-section .sbat=/tmp/sbat.csv vmlinuz.efi
# PE 서명 검증
$ sbverify --list vmlinuz.efi
UKI와 TPM PCR 11/12/13
Linux TPM PCR Registry는 systemd 기반 흐름에서 PCR 11을 UKI의 정적 구성 측정에, PCR 12를 커맨드라인/credential/add-on 입력에, PCR 13을 initrd 확장 이미지에 사용하는 패턴을 정리해 두고 있습니다.
| PCR | 주요 생산자 | 무엇이 바뀌면 값이 달라지나 | 어디에 쓰기 좋은가 |
|---|---|---|---|
| 11 | systemd-stub | UKI 내부의 정적 섹션(.linux, .initrd, .osrel, .sbat 등) | 특정 커널 빌드, 특정 initrd 조합에 TPM 비밀 바인딩 |
| 12 | systemd-stub / boot loader | 수동 커맨드라인, credential, initrd add-on, devicetree add-on | 운영 파라미터 차이에 따른 시크릿 분리 |
| 13 | systemd-stub | initrd용 system extension 이미지 | 부가 확장 이미지 신뢰 분리 |
GRUB 체인과 UKI 체인의 차이
| 항목 | shim → GRUB → kernel/initrd | shim → UKI |
|---|---|---|
| 검증 단위 | GRUB 바이너리, 설정, 외부 커널, 외부 initrd가 분리 | 커널과 initrd를 하나의 PE 이미지로 묶을 수 있음 |
| 운영 유연성 | 부트 메뉴/스크립트 유연성이 높음 | 구성 고정성이 높고 재현성이 좋음 |
| PCR 예측 가능성 | GRUB 설정과 파일 읽기 순서 영향이 큼 | PCR 11 기준으로 정적 구성 예측이 상대적으로 단순 |
| 장애 원인 | GRUB 스크립트, 외부 initrd 불일치, 메뉴 엔트리 오류가 섞임 | 단일 이미지 서명/폐기 문제로 수렴하는 경우가 많음 |
systemd 기반 UKI 제작·서명·자동 등록
최근 systemd 도구 체인은 ukify, systemd-sbsign, bootctl, loader.conf를 조합해 GRUB 없이도 Secure Boot 체인을 구축할 수 있게 설계되어 있습니다. 특히 테스트용 가상 머신에서는 secure-boot-enroll=if-safe를 사용해 Setup Mode에서 자동 키 등록까지 실험할 수 있습니다.
| 도구 | 역할 | 언제 쓰는가 |
|---|---|---|
ukify | UKI 생성과 선택적 Secure Boot/PCR 서명 | 커널, initrd, cmdline을 하나의 PE 이미지로 묶고 싶을 때 |
systemd-sbsign | PE 바이너리 재서명 | 별도로 생성된 UKI, add-on, stub를 후처리 서명할 때 |
bootctl install --secure-boot-auto-enroll=yes | sd-boot 설치와 auto-enroll용 .auth 데이터 배치 | Setup Mode 가상 머신 또는 통제된 현장 배포에서 |
loader.conf | auto-enroll 정책 제어 | if-safe는 VM 우선, force는 명시적 실험용 |
# 1. UKI 빌드
$ ukify build \
--linux=/usr/lib/modules/$(uname -r)/vmlinuz \
--initrd=/boot/initramfs-$(uname -r).img \
--cmdline=@/etc/kernel/cmdline \
--os-release=@/etc/os-release \
--secureboot-private-key=/etc/kernel/secureboot-private-key.pem \
--secureboot-certificate=/etc/kernel/secureboot-certificate.pem \
--pcr-private-key=/etc/systemd/tpm2-pcr-private-key.pem \
--pcr-public-key=/etc/systemd/tpm2-pcr-public-key.pem \
--output=/efi/EFI/Linux/linux-$(uname -r).efi
# 2. 필요하면 별도 재서명
$ systemd-sbsign sign \
--private-key=/etc/kernel/secureboot-private-key.pem \
--certificate=/etc/kernel/secureboot-certificate.pem \
--output=/efi/EFI/Linux/linux-$(uname -r).efi.signed \
/efi/EFI/Linux/linux-$(uname -r).efi
# 3. sd-boot 설치 + auto-enroll payload 생성
$ bootctl install \
--secure-boot-auto-enroll=yes \
--certificate=/etc/kernel/secureboot-certificate.pem \
--private-key=/etc/kernel/secureboot-private-key.pem
# 4. /efi/loader/loader.conf
default @saved
timeout 3
secure-boot-enroll if-safe
secure-boot-enroll-action reboot
loader.conf 문서가 직접 경고하듯이 secure-boot-enroll=force는 잘못 쓰면 장비를 soft-brick 상태로 만들 수 있습니다.
실무에서는 먼저 VM에서 시험하고, 실제 하드웨어에는 복구 수단과 원격 콘솔이 확보된 경우에만 적용하는 것이 안전합니다.
커널 모듈 서명
Secure Boot 환경에서 커널 모듈은 로드 시 서명이 검증됩니다. 서명 정책은 CONFIG_MODULE_SIG 관련 설정으로 제어합니다.
커널 설정
# 모듈 서명 관련 커널 설정 (Security options → Module signature verification)
CONFIG_MODULE_SIG=y # 모듈 서명 검증 활성화
CONFIG_MODULE_SIG_FORCE=y # 서명 없는 모듈 로드 거부 (Secure Boot 시 필수)
CONFIG_MODULE_SIG_ALL=y # 빌드 시 모든 모듈 자동 서명
# 서명 해시 알고리즘 선택
CONFIG_MODULE_SIG_SHA256=y # SHA-256 (기본)
# CONFIG_MODULE_SIG_SHA384=y
# CONFIG_MODULE_SIG_SHA512=y
# CONFIG_MODULE_SIG_SHA3_256=y
# CONFIG_MODULE_SIG_SHA3_384=y
# CONFIG_MODULE_SIG_SHA3_512=y
# 서명 키 경로
CONFIG_MODULE_SIG_KEY="certs/signing_key.pem" # 서명용 개인키
CONFIG_SYSTEM_TRUSTED_KEYRING=y # 시스템 신뢰 키링
CONFIG_SYSTEM_TRUSTED_KEYS="certs/signing_key.pem" # 빌트인 인증서
# 추가 신뢰 키 소스
CONFIG_SECONDARY_TRUSTED_KEYRING=y # 런타임 추가 키링
CONFIG_INTEGRITY_PLATFORM_KEYRING=y # 플랫폼(Secure Boot) 키링
sign-file 도구
# 커널 빌드 시 자동으로 호출되는 모듈 서명 명령
$ scripts/sign-file sha256 \
certs/signing_key.pem \
certs/signing_key.x509 \
drivers/my_driver.ko
# sign-file 내부 동작:
# 1. 모듈 ELF 바이너리 전체를 SHA-256으로 해시
# 2. 개인키로 PKCS#7 서명 생성
# 3. 서명을 모듈 파일 끝에 추가 (append)
# 4. module_signature 구조체를 맨 끝에 추가
# HSM/토큰을 쓰는 경우 PKCS#11 URI도 가능
$ scripts/sign-file sha512 \
"pkcs11:token=KernelSigner;object=signing-key;type=private" \
kernel-signing.x509 \
drivers/my_driver.ko
모듈 서명 구조
/* include/linux/module_signature.h */
struct module_signature {
u8 algo; /* 공개키 알고리즘 (RSA, ECDSA ...) */
u8 hash; /* 해시 알고리즘 (SHA-256, SHA-512 ...) */
u8 id_type; /* 키 식별 타입 (PKEY_ID_PKCS7) */
u8 signer_len; /* 서명자 이름 길이 (PKCS#7에서는 0) */
u8 key_id_len; /* 키 ID 길이 (PKCS#7에서는 0) */
u8 __pad[3]; /* 패딩 */
__be32 sig_len; /* 서명 데이터 길이 (빅 엔디안) */
};
/*
* 모듈 파일 레이아웃:
* (시각화는 아래 SVG 참고)
*/
모듈 서명 검증 흐름
/* kernel/module/signing.c - mod_verify_sig() 간략화 */
int mod_verify_sig(const void *mod, struct load_info *info)
{
struct module_signature ms;
size_t sig_len, modlen = info->len;
/* 매직 문자열 확인 */
if (memcmp(mod + modlen - 28,
"~Module signature appended~\\n", 28) != 0)
return -ENODATA; /* 서명 없음 */
/* module_signature 구조체 읽기 */
memcpy(&ms, mod + modlen - 28 - sizeof(ms), sizeof(ms));
sig_len = be32_to_cpu(ms.sig_len);
/* PKCS#7 서명 검증 (시스템 키링의 인증서 사용) */
return verify_pkcs7_signature(
mod, /* 서명 대상 데이터 */
modlen - sig_len - 28 - sizeof(ms), /* 데이터 길이 */
mod + modlen - 28 - sizeof(ms) - sig_len,
sig_len,
VERIFY_USE_SECONDARY_KEYRING, /* .secondary_trusted_keys */
VERIFYING_MODULE_SIGNATURE,
NULL, NULL);
}
/*
* 키링 검색 순서:
* 1. .builtin_trusted_keys (커널 빌드 시 내장된 인증서)
* 2. .secondary_trusted_keys (런타임 추가, MOK에서 import된 키)
* 3. .platform_keyring (UEFI Secure Boot db에서 가져온 키)
*/
모듈 서명 실무 명령
# 모듈 서명 확인
$ modinfo drivers/my_driver.ko | grep sig
sig_id: PKCS#7
signer: Build time autogenerated kernel key
sig_key: AB:CD:12:34:...
sig_hashalgo: sha256
# 서명 유효성 검증 (kmod 도구)
$ modprobe --dump-modversions drivers/my_driver.ko
# 시스템 키링 확인
$ keyctl list %:.builtin_trusted_keys
1 key in keyring:
123456789: ---lswrv 0 0 asymmetri: Build time autogenerated kernel key: ab:cd:...
$ keyctl list %:.secondary_trusted_keys
2 keys in keyring:
234567890: ---lswrv 0 0 asymmetri: My Secure Boot MOK: 12:34:...
# DKMS 모듈 자동 서명 (MOK 키 사용)
# /etc/dkms/framework.conf:
# sign_tool="/etc/dkms/sign_helper.sh"
# mok_signing_key="/var/lib/shim-signed/mok/MOK.priv"
# mok_certificate="/var/lib/shim-signed/mok/MOK.der"
# 런타임 강제 여부 확인
$ cat /proc/cmdline | tr ' ' '\\n' | grep module.sig_enforce
module.sig_enforce=1
Kernel Lockdown LSM
Secure Boot의 신뢰 체인은 커널이 부팅된 후에도 유지되어야 합니다. Lockdown LSM은 커널 무결성을 우회할 수 있는 사용자 공간의 인터페이스를 제한합니다.
Lockdown 레벨
| 레벨 | 설정값 | 제한 범위 |
|---|---|---|
| none | LOCK_NONE | 제한 없음 (Secure Boot 비활성 시) |
| integrity | LOCK_INTEGRITY | 커널 이미지 수정 차단 |
| confidentiality | LOCK_CONFIDENTIALITY | integrity + 커널 정보 유출 차단 |
Lockdown 제한 항목
/* security/lockdown/lockdown.c - Lockdown이 차단하는 기능들 */
/* integrity 레벨에서 차단: */
LOCKDOWN_MODULE_SIGNATURE, /* 서명 없는 모듈 로드 */
LOCKDOWN_DEV_MEM, /* /dev/mem, /dev/kmem 쓰기 */
LOCKDOWN_EFI_TEST, /* EFI 테스트 모드 변수 */
LOCKDOWN_KEXEC, /* 서명 없는 kexec 이미지 */
LOCKDOWN_HIBERNATION, /* 하이버네이션 (이미지 변조 가능) */
LOCKDOWN_PCI_ACCESS, /* PCI BAR 직접 접근 */
LOCKDOWN_IOPORT, /* I/O 포트 직접 접근 */
LOCKDOWN_MSR, /* MSR 쓰기 (/dev/cpu/*/msr) */
LOCKDOWN_ACPI_TABLES, /* 커스텀 ACPI 테이블 로드 */
LOCKDOWN_DEVICE_TREE, /* 디바이스 트리 오버레이 */
LOCKDOWN_PCMCIA_CIS, /* PCMCIA CIS 오버라이드 */
LOCKDOWN_TIOCSSERIAL, /* 시리얼 포트 I/O 변경 */
LOCKDOWN_MODULE_PARAMETERS, /* 위험한 모듈 파라미터 */
LOCKDOWN_MMIOTRACE, /* MMIO 트레이싱 */
LOCKDOWN_DEBUGFS, /* debugfs 접근 */
LOCKDOWN_XMON_WR, /* PowerPC xmon 쓰기 */
LOCKDOWN_BPF_WRITE_USER, /* BPF 사용자 메모리 쓰기 */
LOCKDOWN_DBG_WRITE_KERNEL, /* /proc/kcore 등 커널 메모리 쓰기 */
/* confidentiality 레벨에서 추가 차단: */
LOCKDOWN_KPROBES, /* kprobes 사용 */
LOCKDOWN_TRACEFS, /* tracefs (ftrace) 접근 */
LOCKDOWN_PERF, /* perf 하드웨어 이벤트 */
LOCKDOWN_XMON_RW, /* PowerPC xmon 읽기/쓰기 */
LOCKDOWN_DBG_READ_KERNEL, /* /proc/kcore 등 커널 메모리 읽기 */
LOCKDOWN_BPF_READ_KERNEL, /* BPF 커널 메모리 읽기 */
Lockdown 상태 확인 및 제어
# Lockdown 상태 확인
$ cat /sys/kernel/security/lockdown
none [integrity] confidentiality
# dmesg에서 Lockdown 메시지
$ dmesg | grep -i lockdown
Lockdown: Kernel is locked down from EFI Secure Boot mode; see man kernel_lockdown.7
Lockdown: debugfs: restricted; see man kernel_lockdown.7
Lockdown: tracefs: restricted; see man kernel_lockdown.7
# 커널 커맨드라인에서 Lockdown 설정
# lockdown=integrity (integrity 모드 강제)
# lockdown=confidentiality (confidentiality 모드 강제)
# 커널 설정
CONFIG_SECURITY_LOCKDOWN_LSM=y
CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y
CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT=y # Secure Boot 시 자동 integrity
/dev/mem, debugfs, MSR 접근이 차단됩니다. Secure Boot 환경에서는 자동 lockdown이 켜지므로, 일부 커널/배포판에서는 lockdown=none만으로 충분하지 않을 수 있습니다. 실무에서는 Secure Boot를 일시적으로 끈 디버그 부트 엔트리나 별도 디버그 커널을 준비하는 편이 안전합니다. Confidentiality 모드에서는 ftrace, perf, kprobes까지 차단되어 성능 프로파일링도 제한됩니다.
IMA/EVM — 런타임 무결성 검증
IMA(Integrity Measurement Architecture)와 EVM(Extended Verification Module)은 부팅 이후 파일 시스템 수준에서 무결성을 보장합니다. Secure Boot가 부팅 체인의 무결성을 보장하면, IMA는 런타임 파일 접근의 무결성을 보장합니다.
IMA 아키텍처
IMA 동작 모드
| 모드 | 설명 | 용도 |
|---|---|---|
| measure | 파일 해시를 측정 목록에 기록 + TPM PCR 확장 | 원격 증명(Remote Attestation) |
| appraise | 파일의 IMA 확장 속성과 실제 해시 비교 | 로컬 무결성 강제 |
| audit | 감사 로그에 해시 기록 | 감사 추적 |
| hash | 확장 속성에 해시만 저장 (서명 없음) | IMA appraise 사전 준비 |
IMA 정책
# 커널 커맨드라인 IMA 옵션
ima_policy=tcb # 기본 TCB 정책 (실행 파일, 라이브러리, 모듈)
ima_policy=appraise_tcb # TCB + appraise 모드
ima_policy=secure_boot # Secure Boot 연동 정책
ima_appraise=enforce # appraise 실패 시 파일 접근 거부
ima_appraise=log # appraise 실패 시 로그만 기록
# 런타임 정책 파일 (/etc/ima/ima-policy 또는 securityfs)
# 정책 형식: action condition [condition ...]
# 실행되는 모든 파일 측정
measure func=BPRM_CHECK
# root가 실행하는 파일의 서명 검증 강제
appraise func=BPRM_CHECK uid=0 appraise_type=imasig
# 커널 모듈 로드 시 서명 검증
appraise func=MODULE_CHECK appraise_type=imasig
# 펌웨어 로드 시 서명 검증
appraise func=FIRMWARE_CHECK appraise_type=imasig
# kexec 커널 이미지 서명 검증
appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig
# 특정 라벨의 파일만 측정 (SELinux 연동)
measure func=FILE_MMAP obj_type=lib_t
# 정책 로드 (securityfs)
$ cat /etc/ima/ima-policy > /sys/kernel/security/ima/policy
IMA 확장 속성
# IMA 확장 속성 확인
$ getfattr -m ^security -d /sbin/init
security.ima=0x... # IMA 서명 또는 해시
security.evm=0x... # EVM HMAC 또는 서명
# IMA 서명 설정 (evmctl 사용)
$ evmctl ima_sign -k /path/to/private_key.pem /sbin/init
# IMA 해시 설정
$ evmctl ima_hash /sbin/init
# 측정 목록 확인
$ cat /sys/kernel/security/ima/ascii_runtime_measurements
10 abc123... ima-sig sha256:def456... /sbin/init
# 위반 목록 확인
$ cat /sys/kernel/security/ima/violations
0
EVM (Extended Verification Module)
EVM은 IMA 확장 속성 자체의 무결성을 보호합니다. 공격자가 security.ima 속성을 위변조하는 것을 방지합니다:
/*
* EVM 보호 대상 확장 속성:
* - security.ima (IMA 해시/서명)
* - security.selinux (SELinux 라벨)
* - security.SMACK64 (SMACK 라벨)
* - security.capability (파일 capability)
*
* EVM은 이 속성들의 HMAC 또는 디지털 서명을
* security.evm에 저장하여 변조를 감지합니다.
*/
# EVM 초기화 (HMAC 모드 — TPM 기반 키)
$ echo 1 > /sys/kernel/security/evm
# EVM 서명 모드 (공개키 기반)
$ evmctl sign -k /path/to/evm-key.pem /sbin/init
# 커널 설정
CONFIG_EVM=y
CONFIG_EVM_ATTR_FSUUID=y # 파일시스템 UUID도 HMAC에 포함
CONFIG_EVM_ADD_XATTRS=y # 추가 xattr을 EVM 보호에 포함
TPM과 Measured Boot
Secure Boot(검증 부팅)과 Measured Boot(측정 부팅)는 상호 보완적인 메커니즘입니다. Secure Boot는 신뢰할 수 없는 코드의 실행을 차단하고, Measured Boot는 실행된 코드의 해시를 기록하여 사후 검증을 가능하게 합니다.
TPM PCR (Platform Configuration Register)
/*
* TPM PCR 할당 (TCG PC Client 규격):
*
* PCR 0: SRTM, BIOS, 호스트 플랫폼 Extension, 임베디드 옵션 ROM
* PCR 1: 호스트 플랫폼 설정
* PCR 2: 옵션 ROM 코드
* PCR 3: 옵션 ROM 설정 및 데이터
* PCR 4: IPL 코드 (부트로더), MBR
* PCR 5: IPL 코드 설정 및 데이터
* PCR 6: 상태 전이 및 Wake 이벤트
* PCR 7: Secure Boot 정책 — PK, KEK, db, dbx,
* SecureBoot 변수, 부트 관리자 측정
* PCR 8-15: OS 정의 — Linux에서는:
* PCR 8: GRUB2 커맨드라인/설정
* PCR 9: GRUB2에서 로드한 파일 (커널, initrd)
* PCR 10: IMA 측정 목록
* PCR 11-13: (배포판별 정의)
* PCR 14: MOK 인증서/키
*/
# TPM PCR 값 읽기
$ tpm2_pcrread sha256:0,1,4,7,10
sha256:
0 : 0x3A5F... # BIOS 측정
1 : 0xA2B1... # 플랫폼 설정
4 : 0xF1E2... # 부트로더 측정
7 : 0xC3D4... # Secure Boot 정책
10 : 0xE5F6... # IMA 측정
# PCR 확장(Extend) 원리:
# PCR_new = Hash(PCR_old || measurement)
# → 한번 확장되면 이전 값으로 되돌릴 수 없음 (단방향)
# → 부팅 체인의 모든 측정이 누적된 해시로 표현됨
TCG 이벤트 로그
# 이벤트 로그 확인 (개별 측정 이벤트)
$ tpm2_eventlog /sys/kernel/security/tpm0/binary_bios_measurements
---
- EventNum: 0
PCRIndex: 0
EventType: EV_S_CRTM_VERSION
DigestCount: 1
Digests:
- AlgorithmId: sha256
Digest: "3a5f..."
Event: ...
- EventNum: 15
PCRIndex: 7
EventType: EV_EFI_VARIABLE_DRIVER_CONFIG
Digests:
- AlgorithmId: sha256
Digest: "c3d4..."
Event:
UnicodeName: SecureBoot
VariableData: 01 # Secure Boot 활성
# 커널에서 이벤트 로그 접근
$ ls /sys/kernel/security/tpm0/
binary_bios_measurements # 바이너리 형식
ascii_bios_measurements # 텍스트 형식
원격 증명 (Remote Attestation)
/*
* 원격 증명 흐름:
*
* 1. 검증 서버(Verifier)가 난스(nonce)를 전송
* 2. 클라이언트가 TPM에 Quote 요청:
* - TPM이 PCR 값들에 AIK(Attestation Identity Key)로 서명
* - 난스를 포함하여 재전송 공격 방지
* 3. Quote + 이벤트 로그를 검증 서버에 전송
* 4. 검증 서버가:
* a. AIK 서명 검증
* b. 이벤트 로그를 재연하여 PCR 값 재계산
* c. 재계산된 PCR과 Quote의 PCR 비교
* d. 개별 이벤트를 알려진 양호값(golden values)과 비교
*/
# TPM Quote 생성
$ tpm2_createak -C 0x81010001 -c ak.ctx -G rsa -g sha256
$ tpm2_quote -c ak.ctx -l sha256:0,1,4,7,10 \
-q "nonce_from_verifier" -m quote.msg -s quote.sig
# IMA 측정 목록 (원격 증명에 사용)
$ head -5 /sys/kernel/security/ima/ascii_runtime_measurements
10 a1b2c3... ima-ng sha256:d4e5f6... /sbin/init
10 b2c3d4... ima-ng sha256:e5f6a7... /usr/lib64/ld-linux-x86-64.so.2
10 c3d4e5... ima-ng sha256:f6a7b8... /usr/lib64/libc.so.6
TPM2와 LUKS2 봉인 실무
실제 운영에서는 TPM 봉인을 두 가지 방식으로 나눠 생각해야 합니다. 하나는 현재 PCR 값에 직접 묶는 방식(--tpm2-pcrs=)이고, 다른 하나는 서명된 PCR 정책에 묶는 방식(--tpm2-public-key-pcrs=)입니다. systemd 문서는 후자가 소프트웨어 업데이트와 훨씬 잘 맞는다고 설명합니다.
| 모델 | 설정 예 | 장점 | 주의점 |
|---|---|---|---|
| 직접 PCR 바인딩 | --tpm2-pcrs=7+11 | 구성이 단순하고 별도 서명 키가 필요 없음 | dbx 갱신, SBAT 갱신, UKI 교체 때마다 잠금 해제가 깨질 수 있음 |
| 서명된 PCR 정책 | --tpm2-public-key-pcrs=11 | 새 UKI를 같은 PCR 서명 키로 승인할 수 있어 업데이트 친화적 | PCR 서명 키 관리와 JSON 서명 배포 절차가 필요함 |
# 1. 예상 PCR 11 값 서명 JSON 생성
$ systemd-measure sign \
--linux=/usr/lib/modules/$(uname -r)/vmlinuz \
--initrd=/boot/initramfs-$(uname -r).img \
--cmdline="root=UUID=... rw quiet" \
--private-key=/etc/systemd/tpm2-pcr-private-key.pem \
--public-key=/etc/systemd/tpm2-pcr-public-key.pem \
> /etc/systemd/tpm2-pcr-signature.json
# 2. LUKS2 슬롯에 서명된 PCR 정책 등록
$ systemd-cryptenroll --tpm2-device=auto \
--tpm2-public-key=/etc/systemd/tpm2-pcr-public-key.pem \
--tpm2-public-key-pcrs=11 \
--tpm2-signature=/etc/systemd/tpm2-pcr-signature.json \
--tpm2-pcrs="" \
/dev/nvme0n1p3
--tpm2-public-key-pcrs=를 생략하면 기본값은 11입니다.
반면 crypttab 기반 단순 TPM 해제는 메타데이터가 없을 때 기본적으로 PCR 7 하나를 사용하므로, "왜 키 교체나 dbx 업데이트 후 잠금 해제가 깨졌는가"를 해석할 때 두 기본값을 혼동하면 안 됩니다.
dm-verity — 블록 장치 무결성
dm-verity는 device-mapper 타겟으로, 블록 장치 전체의 무결성을 Merkle 트리(해시 트리)로 검증합니다. 읽기 전용 파일시스템의 변조를 런타임에 탐지합니다.
Merkle 트리 구조
dm-verity 설정
# 해시 트리 생성
$ veritysetup format /dev/sda2 /dev/sda3
VERITY header information for /dev/sda3
UUID: a1b2c3d4-...
Hash type: 1
Data blocks: 262144
Data block size: 4096
Hash block size: 4096
Hash algorithm: sha256
Salt: abc123...
Root hash: 5f8c3d2a1b4e7f9c0d6a3b8e5f2c1d4a7b0e3f6c9d2a5b8e1f4c7d0a3b6e9f
# verity 장치 활성화
$ veritysetup open /dev/sda2 verified-root /dev/sda3 \
5f8c3d2a1b4e7f9c0d6a3b8e5f2c1d4a7b0e3f6c9d2a5b8e1f4c7d0a3b6e9f
# 마운트
$ mount /dev/mapper/verified-root /mnt -o ro
# 커널 커맨드라인에서 root dm-verity 설정
# root=/dev/dm-0
# dm-mod.create="verified-root,,,ro,0 262144 verity 1 /dev/sda2 /dev/sda3 4096 4096 262144 1 sha256 ROOT_HASH SALT"
# 커널 설정
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y # 루트 해시 서명 검증
CONFIG_DM_VERITY_FEC=y # Forward Error Correction
AVB(Android Verified Boot) 상세
AVB 2.0은 Android 8.0+에서 사용되는 검증 부팅 체계로, 부트로더에서 커널까지의 신뢰 체인(chain of trust)을 구축한다. 핵심 구성 요소는 vbmeta 파티션으로, RSA/ECDSA 서명된 메타데이터에 각 파티션의 해시(hash descriptor) 또는 해시 트리 루트(hashtree descriptor)를 포함한다.
AVB와 dm-verity 연동, A/B 파티션 구조, 부팅 흐름 등 종합 내용은 Android 커널 — AVB를 참고하라.
런타임 무결성 보강 계층
Secure Boot와 dm-verity만으로는 최신 Linux 시스템의 모든 변경 표면을 막기 어렵습니다. 읽기 전용 루트, 커널이 읽는 파일, 개별 실행 파일, 정책 엔진을 각각 다른 계층으로 나눠서 설계해야 합니다.
| 기술 | 보호 단위 | 강점 | 제한 |
|---|---|---|---|
| dm-verity | 블록 장치/루트 파일시스템 | 루트 전체를 읽기 전용 기준으로 강하게 보호 | 개별 파일만 자주 교체되는 쓰기 가능 환경에는 부적합 |
| fs-verity | 개별 읽기 전용 파일 | 대용량 파일도 부분 읽기 시 상시 검증 가능 | 파일 자체가 read-only여야 함 |
| LoadPin | 커널이 읽는 파일의 출처 | 모듈/펌웨어가 같은 검증된 파일시스템에서만 오도록 강제 | 처음 pin된 파일시스템 설계가 잘못되면 정책도 흔들림 |
| IPE | 실행 정책 | dm-verity/fs-verity/initramfs 같은 불변 속성 기반 허용/차단 | 일반 목적 배포판보다는 고정 기능 장비에 더 적합 |
fs-verity — 파일 단위 Merkle 트리
fs-verity는 파일 단위 dm-verity라고 이해하면 빠릅니다. ext4, f2fs, btrfs에서 읽기 전용 파일에 Merkle 트리를 붙이고, 이후 mmap을 포함한 모든 읽기를 검증합니다. 커널 문서가 명확히 말하듯, fs-verity는 dm-verity를 대체하지 않고 쓰기 가능한 파일시스템 위의 개별 파일 보호에 적합합니다.
# 파일을 read-only로 만든 뒤 fs-verity 활성화
$ chmod 0444 /usr/libexec/myhelper
$ fsverity enable /usr/libexec/myhelper
# 파일 digest 확인 (상수 시간)
$ fsverity digest /usr/libexec/myhelper
# 커널 설정
CONFIG_FS_VERITY=y
CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y # 선택: in-kernel builtin signature
LoadPin — 커널이 읽는 파일의 출처 고정
LoadPin은 모듈, 펌웨어, kexec 이미지처럼 커널이 직접 읽는 파일이 모두 같은 파일시스템에서 오도록 강제합니다. 커널 문서의 표현을 그대로 옮기면, 보통 이 파일시스템이 dm-verity나 CD-ROM처럼 읽기 전용 백엔드일 것을 기대합니다.
# 커널 설정
CONFIG_SECURITY_LOADPIN=y
# 일반적인 부트 파라미터
loadpin.enforce=1
# 모듈과 kexec는 자체 서명 검증을 쓰고, 나머지만 LoadPin으로 묶는 예
loadpin.exclude=kernel-module,kexec-image
IPE — immutable property 기반 실행 정책
IPE(Integrity Policy Enforcement)는 IMA와 달리 측정/원격 증명보다 로컬 실행 허용 정책에 집중합니다. dm-verity 서명 여부, 특정 root hash, fs-verity digest, initramfs 출처 같은 바뀔 수 없는 속성에 기반해 실행을 허용하거나 차단합니다.
# dm-verity로 서명 검증된 루트와 initramfs만 허용하는 IPE 정책 예
policy_name=Secure_Appliance policy_version=1.0.0
DEFAULT action=DENY
op=EXECUTE boot_verified=TRUE action=ALLOW
op=EXECUTE dmverity_signature=TRUE action=ALLOW
op=EXECUTE fsverity_signature=TRUE action=ALLOW
kexec 서명 검증
kexec는 재부팅 없이 새 커널을 로드하는 메커니즘입니다. Secure Boot 환경에서는 kexec로 로드하는 커널 이미지에도 서명 검증이 필요합니다.
# 서명된 kexec (Secure Boot 환경에서 허용)
$ kexec -l /boot/vmlinuz-signed --initrd=/boot/initrd.img \
--command-line="root=/dev/sda1" --kexec-file-syscall
# kexec_file_load() 시스템 콜에서의 서명 검증:
# 1. PE 헤더 파싱 (bzImage는 PE/COFF 형식)
# 2. Authenticode 서명 추출
# 3. 시스템 키링(.builtin_trusted_keys, .platform_keyring)으로 검증
# 4. IMA 정책에 의한 추가 검증 (func=KEXEC_KERNEL_CHECK)
/* arch/x86/kernel/kexec-bzimage64.c - 서명 검증 흐름 */
static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len)
{
return verify_pefile_signature(
kernel, kernel_len,
VERIFY_USE_SECONDARY_KEYRING, /* 보조 키링 검색 */
VERIFYING_KEXEC_PE_SIGNATURE); /* 용도 표시 */
}
/* 관련 커널 설정 */
CONFIG_KEXEC_FILE=y # kexec_file_load() 시스템 콜
CONFIG_KEXEC_SIG=y # kexec 서명 검증 활성화
CONFIG_KEXEC_SIG_FORCE=y # 서명 없으면 kexec 거부
CONFIG_KEXEC_BZIMAGE_VERIFY_SIG=y # bzImage 서명 검증
커스텀 키 관리
기업 환경이나 임베디드 시스템에서는 OEM/벤더 키 대신 자체 Secure Boot 키를 사용하여 완전한 신뢰 체인을 구축할 수 있습니다.
커스텀 PK 등록
# 1. 키 생성 (RSA 2048 또는 4096)
# PK (Platform Key)
$ openssl req -new -x509 -newkey rsa:4096 -keyout PK.key \
-out PK.crt -nodes -days 3650 \
-subj "/CN=My Platform Key"
# KEK (Key Exchange Key)
$ openssl req -new -x509 -newkey rsa:4096 -keyout KEK.key \
-out KEK.crt -nodes -days 3650 \
-subj "/CN=My Key Exchange Key"
# db (Signature Database Key)
$ openssl req -new -x509 -newkey rsa:4096 -keyout db.key \
-out db.crt -nodes -days 3650 \
-subj "/CN=My Signature Database Key"
# 2. EFI Signature List (ESL) 형식으로 변환
$ cert-to-efi-sig-list PK.crt PK.esl
$ cert-to-efi-sig-list KEK.crt KEK.esl
$ cert-to-efi-sig-list db.crt db.esl
# 3. 인증된 업데이트 파일 생성 (EFI_VARIABLE_AUTHENTICATION_2)
$ sign-efi-sig-list -k PK.key -c PK.crt PK PK.esl PK.auth
$ sign-efi-sig-list -k PK.key -c PK.crt KEK KEK.esl KEK.auth
$ sign-efi-sig-list -k KEK.key -c KEK.crt db db.esl db.auth
# 4. UEFI Setup Mode에서 키 등록
# (Setup Mode: PK가 없는 상태 → 누구나 변수 수정 가능)
$ efi-updatevar -f db.auth db # db 먼저
$ efi-updatevar -f KEK.auth KEK # KEK 다음
$ efi-updatevar -f PK.auth PK # PK 마지막 (Setup Mode 종료)
# 5. 커널/부트로더를 자체 키로 서명
$ sbsign --key db.key --cert db.crt \
--output vmlinuz.signed vmlinuz
$ sbsign --key db.key --cert db.crt \
--output grubx64.efi.signed grubx64.efi
키 교체 (Key Rotation)
# db 키 교체 시나리오:
# 1. 새 db 키 생성
$ openssl req -new -x509 -newkey rsa:4096 -keyout db-new.key \
-out db-new.crt -nodes -days 3650 \
-subj "/CN=My New DB Key 2026"
# 2. 새 키를 db에 추가 (기존 키 유지, append 모드)
$ cert-to-efi-sig-list db-new.crt db-new.esl
$ sign-efi-sig-list -a -k KEK.key -c KEK.crt db db-new.esl db-append.auth
$ efi-updatevar -a -f db-append.auth db
# 3. 새 키로 커널/부트로더 재서명
$ sbsign --key db-new.key --cert db-new.crt --output vmlinuz.signed vmlinuz
# 4. 검증 후 구 키 제거 (선택)
# → 새 db를 만들어 덮어씀 (구 키 미포함)
# PK 교체:
# 현재 PK로 새 PK를 서명하여 업데이트
$ sign-efi-sig-list -k PK-old.key -c PK-old.crt PK PK-new.esl PK-new.auth
$ efi-updatevar -f PK-new.auth PK
Windows UEFI CA 2023를 DB에 추가하고, Windows Production PCA 2011을 DBX에 올리는 순차 배포 절차를 안내합니다.
2025년 7월 8일 이후 업데이트 적용이 권고되며, 2026년 3월 6일 현재 공개 문서상 Enforcement 단계의 정확한 날짜는 아직 별도 공지 상태입니다. 오래된 설치 USB, 복구 ISO, 공장 복구 이미지가 이 전환에서 가장 먼저 부팅 실패를 일으키는 경우가 많습니다.
커널 내부 구현
소스 트리 구조
| 경로 | 설명 |
|---|---|
security/integrity/ | IMA/EVM 프레임워크 |
security/integrity/ima/ | IMA 핵심 구현 (정책, 측정, 검증) |
security/integrity/evm/ | EVM 구현 (HMAC/서명) |
security/integrity/platform_certs/ | UEFI db/MOK에서 키 로드 |
security/lockdown/ | Lockdown LSM |
security/loadpin/ | LoadPin LSM — 커널 로드 파일의 출처 고정 |
security/ipe/ | IPE — immutable property 기반 정책 엔진 |
certs/ | 빌드 시 인증서 처리, system_keyring |
crypto/asymmetric_keys/ | 비대칭키, PKCS#7, X.509 파서 |
drivers/firmware/efi/libstub/ | EFI/UKI stub 공통 코드 |
fs/verity/ | fs-verity 공통 계층 |
kernel/module/signing.c | 모듈 서명 검증 |
drivers/firmware/efi/ | UEFI 런타임, efivars, Secure Boot 감지 |
include/linux/verification.h | 서명 검증 API |
플랫폼 키링 로드
/* security/integrity/platform_certs/load_uefi.c
*
* Secure Boot db에서 인증서를 로드하여 .platform_keyring에 추가
* 커널 초기화 시 자동으로 호출됨
*/
static int __init load_uefi_certs(void)
{
efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
efi_guid_t mok_var = EFI_SHIM_LOCK_GUID;
void *db = NULL, *dbx = NULL, *mok = NULL;
unsigned long dbsize, dbxsize, moksize;
/* UEFI db 변수에서 인증서 로드 → .platform_keyring */
db = get_cert_list(L"db", &secure_var, &dbsize);
if (db) {
add_to_platform_keyring("UEFI:db", db, dbsize);
kfree(db);
}
/* UEFI dbx 변수에서 폐기 목록 로드 → .blacklist_keyring */
dbx = get_cert_list(L"dbx", &secure_var, &dbxsize);
if (dbx) {
add_to_blacklist_keyring("UEFI:dbx", dbx, dbxsize);
kfree(dbx);
}
/* Shim MOK 변수에서 인증서 로드 → .platform_keyring */
mok = get_cert_list(L"MokListRT", &mok_var, &moksize);
if (mok) {
add_to_platform_keyring("UEFI:MokListRT", mok, moksize);
kfree(mok);
}
return 0;
}
late_initcall(load_uefi_certs);
키링 계층 구조
/*
* Linux 커널 키링 계층 (아래 SVG 참고)
*/
# 키링 내용 확인
$ keyctl show %:.builtin_trusted_keys
Keyring
123456 ---lswrv 0 0 asymmetric: Build time autogenerated kernel key: ab:cd:...
$ keyctl show %:.platform_keyring
Keyring
234567 ---lswrv 0 0 asymmetric: Microsoft UEFI CA 2011: ...
345678 ---lswrv 0 0 asymmetric: Windows UEFI CA 2023: ...
456780 ---lswrv 0 0 asymmetric: Canonical Ltd. Secure Boot Signing: ...
$ keyctl show %:.blacklist_keyring
Keyring
567890 ---lswrv 0 0 blacklist: tbs:abc123...
OVMF/QEMU 재현 실습
실제 장비의 NVRAM과 TPM 상태를 만지기 전에, Secure Boot 변경은 먼저 QEMU + OVMF + swtpm 조합에서 재현하는 편이 안전합니다. 이 조합은 Setup Mode 전환, auto-enroll, PCR 7/11 확인, LUKS 봉인 테스트를 모두 반복 가능하게 해줍니다.
실습 토폴로지
| 구성 요소 | 역할 | 비고 |
|---|---|---|
OVMF_CODE*.fd | 읽기 전용 UEFI 펌웨어 코드 | 배포판 패키지에 따라 OVMF_CODE.fd, OVMF_CODE.secboot.fd, OVMF_CODE_4M.secboot.fd처럼 이름이 다를 수 있습니다. |
OVMF_VARS*.fd | 가변 NVRAM 저장소 | 반드시 VM마다 복사본을 만들어 써야 키 등록 실험이 재현됩니다. |
swtpm | TPM 2.0 에뮬레이터 | QEMU 공식 문서 예시는 Unix socket 기반 제어 채널을 사용합니다. |
| ESP 또는 UKI 디스크 | shim/UKI/bootctl 실험 대상 | 실습마다 별도 qcow2를 쓰면 복구가 쉽습니다. |
# 1. OVMF 변수 저장소는 복사본 사용
$ cp /usr/share/OVMF/OVMF_VARS.fd ./OVMF_VARS.secboot.fd
# 2. swtpm 시작 (QEMU 공식 문서 예시)
$ mkdir -p /tmp/mytpm1
$ swtpm socket --tpmstate dir=/tmp/mytpm1 \
--ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
--tpm2 \
--log level=20 &
# 3. QEMU 시작 (x86_64 + Q35 + TPM TIS)
$ qemu-system-x86_64 \
-machine q35,accel=kvm \
-cpu host \
-m 4096 \
-drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE.secboot.fd \
-drive if=pflash,format=raw,file=./OVMF_VARS.secboot.fd \
-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
-tpmdev emulator,id=tpm0,chardev=chrtpm \
-device tpm-tis,tpmdev=tpm0 \
-drive file=secureboot-lab.qcow2,format=qcow2,if=virtio \
-serial mon:stdio
OVMF_CODE.secboot.fd, Debian/Ubuntu 계열의 OVMF_CODE_4M.secboot.fd 같은 변형이 흔하므로 패키지 목록을 먼저 확인해야 합니다.
게스트 확인 순서
# 게스트 내부에서 Secure Boot / TPM / PCR 확인
$ mokutil --sb-state
$ bootctl status
$ efi-readvar -v PK
$ tpm2_pcrread sha256:7,11
$ dmesg | grep -iE "efi|secure|tpm|lockdown"
# Setup Mode 시험 시
$ od -An -tx1 /sys/firmware/efi/efivars/SetupMode-*
# auto-enroll 후 다시 부팅하여 User Mode 전환 확인
$ od -An -tx1 /sys/firmware/efi/efivars/SecureBoot-*
$ od -An -tx1 /sys/firmware/efi/efivars/VendorKeys-*
secure-boot-enroll=if-safe를 시험하고, 다음으로 서명된 UKI 부팅, 마지막으로 TPM 봉인 해제를 얹는 순서가 가장 디버깅하기 쉽습니다.
실제 장비에서 키 등록과 TPM 봉인을 동시에 바꾸면, 실패 원인이 펌웨어 변수인지 PCR 정책인지 분리하기 어려워집니다.
배포판별 운영 패턴
2026년 3월 7일 기준 공개 문서를 보면, 주요 배포판은 모두 Secure Boot를 지원하지만 신뢰 체인을 어디에 두는지와 사용자 키를 어디까지 허용하는지에서 차이가 있습니다. 아래 표는 공식 문서를 기반으로 운영자가 바로 판단할 수 있게 정리한 것입니다.
| 배포판/문서 기준 | 기본 부트 체인 | 운영상 특징 | 실무 포인트 |
|---|---|---|---|
| Ubuntu 문서 갱신: 2026-01-22 | Microsoft 서명 shim → Canonical 서명 GRUB → Canonical 서명 커널 | GRUB가 커널을 검증하지만 Ubuntu 공식 문서는 initrd는 검증되지 않는다고 명시합니다. DKMS/서드파티 드라이버용으로 MOK 자동 생성·등록 흐름이 잘 정비되어 있습니다. | 서드파티 모듈이 많은 워크스테이션에서는 편하지만, root가 접근 가능한 자동 생성 MOK는 보안 경계를 약하게 만듭니다. module-signing-only OID가 붙은 키는 shim/GRUB 이미지 검증에는 사용되지 않습니다. |
| Fedora 공식 가이드 기준 | Microsoft 신뢰 체인 + Fedora 서명 부트로더/커널/모듈 | Fedora 문서는 Secure Boot 활성 시 펌웨어 db, dbx, shim 내장 키, MOK가 커널 키링에 반영되는 구조를 자세히 설명합니다. 외부 모듈은 mokutil + sign-file 흐름이 표준입니다. | 외부 모듈을 많이 쓰는 개발 환경에 적합합니다. 문서상 module.sig_enforce가 켜지면 Secure Boot 비활성 환경에서도 강제 정책을 재현할 수 있습니다. |
| RHEL 10 공식 문서 기준 | 기존 shim/GRUB 경로 + kernel-uki-virt 기반 UKI 경로 | Red Hat 문서는 kernel-uki-virt 패키지를 통해 RHEL UKI가 완전 지원 상태라고 밝히고, 특히 가상화·클라우드 환경에서 Secure Boot 강화에 유리하다고 설명합니다. | 가상화/클라우드/기밀 VM처럼 재현성과 측정 가능성이 중요한 환경에서는 UKI 경로가 특히 매력적입니다. 전통적 GRUB 운영보다 이미지 단위 승인이 쉬워집니다. |
| Debian 13 (trixie) 공식 Wiki/문서 기준 | Microsoft 서명 shim + 서명된 GRUB/커널, 또는 shim 뒤의 서명된 systemd-boot | Debian 문서는 서명된 shim, GRUB, 커널을 기본 경로로 제공하고, trixie부터는 systemd-boot를 shim과 함께 쓰는 흐름도 문서화합니다. | EFIStub나 UKI 직부팅처럼 shim을 우회하면 MOK 경로가 사라집니다. 이 경우 Debian UEFI CA나 자체 키를 펌웨어 db에 직접 등록해야 합니다. |
Ubuntu 운영 메모
Ubuntu 보안 문서는 amd64와 arm64에서 Microsoft 서명 shim과 Canonical 서명 GRUB를 제공하며, Secure Boot가 켜진 상태에서 커널과 모듈 검증을 강제한다고 설명합니다. 다만 공식 문서가 분명히 적는 중요한 한 줄이 있습니다. GRUB 경로에서는 initrd가 검증되지 않습니다.
- 장점 — 설치/업그레이드 시 MOK 생성과 DKMS 모듈 서명이 자동화되어 사용자 경험이 좋습니다.
- 주의 — 자동 생성 MOK는 root가 읽을 수 있는 파일로 저장될 수 있으므로, 고신뢰 서버나 어플라이언스에서는 "편의성"과 "루트-커널 경계 약화"를 함께 평가해야 합니다.
- 권장 — Ubuntu에서 Secure Boot 강도를 더 끌어올리고 싶다면, 외부 initrd 검증 공백을 줄이기 위해 UKI 또는 최소한 initrd 불변화 전략을 검토하는 것이 좋습니다. 이것은 공식 문서와 systemd UKI 도구 흐름을 바탕으로 한 운영적 추론입니다.
Fedora 운영 메모
Fedora 문서는 Secure Boot 활성 시 펌웨어 db, dbx, shim 내장 키, MOK가 커널 시스템 키링으로 들어온다고 설명합니다. 이 구조 덕분에 "이미지 검증"과 "모듈 검증"을 같은 키링 시각에서 디버깅하기 쉽습니다.
- 장점 — 외부 모듈 개발 문서가 구체적입니다.
mokutil --import,sign-file,keyctl list까지 공식 흐름이 분명합니다. - 주의 — 외부 모듈을 다수 운영하면 MOK 관리가 배포 파이프라인 일부가 됩니다. 키 교체, 만료, 블랙리스트 충돌을 CI 단계에서 같이 봐야 합니다.
- 권장 — 드라이버 개발 호스트나 CI 빌드 팜에서는 Secure Boot를 끄지 말고, Fedora 문서가 설명하는 MOK +
module.sig_enforce경로로 실제 정책을 재현하는 편이 품질이 높습니다.
RHEL 운영 메모
RHEL 10 문서는 kernel-uki-virt 기반 Unified Kernel Image를 완전 지원 상태로 명시합니다. 또한 이 UKI가 가상화·클라우드 환경에서 Secure Boot 보호를 강화한다고 밝힙니다. 이는 "기밀 VM", "재현 가능한 부팅 측정", "이미지 기반 배포"에 잘 맞는 방향입니다.
- 장점 — VM/클라우드 전용 커널 아티팩트를 하나의 서명된 UEFI 바이너리로 다룰 수 있어, 골든 이미지 운영과 TPM 정책 설계가 단순해집니다.
- 주의 — 문서상
kernel-uki-virt는 가상화·클라우드에 초점을 둔 패키지입니다. 범용 베어메탈 노드에 그대로 등치시키기보다 대상 환경을 먼저 구분해야 합니다. - 권장 — RHEL 기반 가상화 플랫폼에서는 GRUB 스크립트 중심 운영보다 UKI + TPM 측정 정책을 우선 검토하는 것이 합리적입니다. 이 판단은 Red Hat 공식 문서의 지원 상태와 systemd UKI 생태계를 함께 본 운영적 해석입니다.
Debian 운영 메모
Debian 공식 Secure Boot 문서는 대부분의 x86 시스템에서 Microsoft 기본 Secure Boot 인증서 체계 위에 Debian의 서명된 shim, GRUB, 커널을 사용하는 경로를 기본으로 설명합니다. 또한 Debian 13(trixie)에서는 systemd-boot를 shim 뒤에 두는 흐름도 문서화되어 있어, GRUB를 줄이고 싶은 환경에서 선택지가 넓어졌습니다.
- 장점 — Debian은 전통적인
shim + GRUB경로와 trixie의shim + systemd-boot경로를 모두 제공해 테스트베드, 랩, 간결한 서버 이미지 운영에 유연합니다. - 주의 — Debian wiki는
EFIStub직부팅을 사용할 경우 shim이 우회되므로 MOK로는 부족할 수 있다고 설명합니다. 이때는 Debian UEFI CA 또는 자체 키를 펌웨어db에 직접 넣어야 합니다. - 권장 — Debian에서 UKI 또는
systemd-boot를 실험할 때도 처음에는shim뒤에 붙여 MOK/배포판 체인을 유지하고, 펌웨어db직등록 방식은 장비 키 관리 절차가 정리된 뒤에 도입하는 편이 안전합니다. 이것은 Debian Secure Boot wiki와 systemd UKI 문서를 함께 본 운영적 추론입니다.
Windows·Option ROM 공존 메모
Microsoft의 2025년 6월 26일 게시 문서(KB5062713, 2026년 2월 13일 변경 이력 반영)는 최근 Secure Boot 인증서 갱신이 단순히 "Microsoft 2023 키 하나"로 끝나지 않는다고 분명히 설명합니다. 실제 운영에서는 Windows UEFI CA 2023, Microsoft UEFI CA 2023, Microsoft Option ROM UEFI CA 2023, Microsoft Corporation KEK 2K CA 2023가 역할별로 나뉘어 배포됩니다.
- Windows 듀얼 부팅 — Windows Boot Manager를 계속 허용해야 한다면, 최소한 해당 부트 경로가 기대하는 Microsoft DB 항목이 남아 있는지 확인해야 합니다. 커스텀
db만 남기고 Microsoft 체인을 제거하면 Linux보다 Windows가 먼저 부팅 실패할 수 있습니다. - Option ROM/PXE/스토리지 HBA — 사전 부팅 드라이버, PXE ROM, 일부 GPU/스토리지 Option ROM은 별도 신뢰 경로를 기대할 수 있습니다. OVMF에서는 문제가 없는데 실제 OEM 장비에서만 실패하는 이유가 여기에 있는 경우가 많습니다.
- 운영 전략 — 개발 워크스테이션과 듀얼 부팅 노드는 OEM 기본 KEK/DB를 유지한 채 MOK 또는 추가
db키를 얹는 전략이 안전합니다. 반대로 어플라이언스처럼 완전 통제가 목표인 장비에서만 Microsoft 체인을 제거하는 편이 합리적입니다. 이는 Microsoft 배포 가이드와 UEFI 키 계층을 함께 본 운영적 추론입니다.
환경별 선택 매트릭스
| 환경 | 권장 체인 | 이유 |
|---|---|---|
| 개발 워크스테이션 | shim + GRUB + MOK + DKMS 자동 서명 | NVIDIA, VirtualBox, out-of-tree 드라이버 같은 외부 모듈이 많아 운영 유연성이 중요합니다. |
| Windows 듀얼 부팅/벤더 도구 공존 | OEM 기본 KEK/DB 유지 + MOK 또는 추가 db 키 | Windows Boot Manager, Option ROM, 제조사 진단/복구 체인의 호환성을 보존하기 쉽습니다. |
| 원격 베어메탈 프로비저닝 | UEFI HTTP Boot + shim/GRUB 또는 서명된 ipxe.efi | 원격 설치 자동화와 Secure Boot를 동시에 유지해야 하므로, 전송 경로와 EFI 서명 체인을 같이 설계해야 합니다. |
| 가상화/클라우드 이미지 | UKI + TPM PCR 11 정책 + OVMF 사전 검증 | 재현성, 이미지 단위 승인, 부트 측정 일관성이 중요합니다. |
| 고정 기능 어플라이언스 | 커스텀 PK/KEK/db + UKI + dm-verity/fs-verity + LoadPin/IPE | 사용자 임의 확장보다 불변성 유지가 핵심입니다. |
| 복구 미디어/현장 서비스 USB | 최신 shim/GRUB 또는 최신 UKI로 주기 재생성 | 2025~2026 DBX/SBAT 변화 이후 오래된 미디어가 가장 먼저 실패합니다. |
트러블슈팅
일반적인 문제와 해결
| 증상 | 원인 | 해결 |
|---|---|---|
| 부팅 시 "Security Violation" | 서명되지 않은 부트로더/커널 | db에 등록된 키로 서명, 또는 MOK 등록 |
modprobe: ERROR: could not insert module: Required key not available | 모듈 서명 누락 또는 키 불일치 | sign-file로 모듈 서명, 또는 MOK에 키 등록 |
Lockdown: debugfs: restricted | Lockdown integrity 모드 활성 | lockdown=none 커맨드라인 (디버깅 시) |
| DKMS 모듈 로드 실패 | 자동 서명 미설정 | DKMS MOK 키 생성 + mokutil --import |
kexec: Permission denied | CONFIG_KEXEC_SIG_FORCE + 미서명 커널 | 서명된 커널 사용 또는 kexec -s 옵션 |
| Setup Mode에서 PK 등록 불가 | 일부 펌웨어에서 Setup Mode 진입 방식 상이 | BIOS 설정에서 "Clear Secure Boot keys" 후 재시도 |
| SBAT 정책에 의한 GRUB 거부 | SbatLevel이 GRUB의 세대번호보다 높음 | 최신 GRUB 패키지 업데이트 |
| IMA appraise 실패로 파일 실행 불가 | security.ima xattr 누락/불일치 | evmctl ima_sign으로 재서명 |
| 오래된 설치 USB/복구 ISO가 Secure Boot에서 부팅 실패 | DBX/SBAT 폐기로 오래된 boot manager가 차단됨 | 최신 shim/GRUB/boot manager로 미디어 재생성 |
| Hyper-V 가상 머신에서 Event ID 1795로 인증서 업데이트 실패 | Microsoft 문서 기준 일부 Hyper-V VM에서 KEK 갱신이 write-protected firmware 오류로 실패 | KB5062713의 최신 변경 이력을 확인하고, 2026년 2월 13일 기준 문서가 예고한 2026년 3월 Windows/Azure 수정 배포 여부를 점검 |
2025~2026 인증서 전환과 복구 미디어
Secure Boot 운영에서 최근 가장 현실적인 장애 요인은 키 교체보다 복구 미디어의 노후화입니다. Microsoft 공개 가이드(KB5025885)는 2024년 4월 9일 이후 단계에서 Windows UEFI CA 2023 배포, 2025년 7월 8일 이후 업데이트 적용 권고, 이후 Windows Production PCA 2011의 DBX 폐기를 순차적으로 안내하고 있습니다.
- 오래된 외부 부팅 매체 — 예전 Windows 설치 USB, 오래된 shim/GRUB ISO, 제조사 복구 이미지는 새 DBX 정책에서 거부될 수 있습니다.
- 공장 초기화의 역효과 — 이미 새 인증서 체계로 전환된 장비에서 Secure Boot 키를 옛 기본값으로 되돌리면 최신 부트 매니저가 오히려 신뢰되지 않을 수 있습니다.
- 플릿 운영 체크리스트 — DB/DBX 갱신, shim/GRUB/UKI 갱신, 복구 USB 재생성, 원격 콘솔 접근 확보를 같은 변경 묶음으로 취급해야 합니다.
디버깅 명령
# ============ Secure Boot 상태 확인 ============
# Secure Boot 활성 여부
$ mokutil --sb-state
SecureBoot enabled
# UEFI 변수 직접 확인
$ od -An -tx1 /sys/firmware/efi/efivars/SecureBoot-*
06 00 00 00 01
# ^^ 01 = enabled, 00 = disabled
# Setup Mode 확인
$ od -An -tx1 /sys/firmware/efi/efivars/SetupMode-*
06 00 00 00 00
# ^^ 00 = User Mode, 01 = Setup Mode
# Audit / Deployed / VendorKeys
$ for v in AuditMode DeployedMode VendorKeys; do od -An -tx1 /sys/firmware/efi/efivars/${v}-*; done
# ============ 키/인증서 확인 ============
# UEFI 보안 변수 목록
$ efi-readvar
Variable PK, length 862
PK: List 0, type X509
Signature 0, size 834, owner ...
Subject: CN=...
Variable KEK, length 1532
...
# 등록된 MOK 확인
$ mokutil --list-enrolled
# SBAT 폐기 목록
$ mokutil --list-sbat-revocations
# ============ 커널 키링 ============
$ keyctl show %:.builtin_trusted_keys
$ keyctl show %:.secondary_trusted_keys
$ keyctl show %:.platform_keyring
$ keyctl show %:.blacklist_keyring
# ============ 부팅 로그 ============
$ dmesg | grep -iE "secure.boot|lockdown|integrity|efi.*cert|ima|evm"
secureboot: Secure boot enabled
Lockdown: Kernel is locked down from EFI Secure Boot mode
integrity: Platform Keyring initialized
integrity: Loading X.509 certificate: UEFI:db
ima: policy update completed
# ============ 모듈 서명 ============
# 특정 모듈의 서명 정보
$ modinfo -F signer
$ modinfo -F sig_hashalgo
# 서명 없는 모듈 찾기
$ for mod in $(find /lib/modules/$(uname -r) -name "*.ko"); do
if ! modinfo -F signer $mod &>/dev/null; then
echo "Unsigned: $mod"
fi
done
커널 설정 종합
# ===== Secure Boot 관련 커널 설정 종합 =====
# -- 기본 EFI 지원 --
CONFIG_EFI=y
CONFIG_EFI_STUB=y
CONFIG_EFI_SECURE_BOOT_SIG_ENFORCE=y
# -- Lockdown LSM --
CONFIG_SECURITY_LOCKDOWN_LSM=y
CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y
CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT=y
# -- 모듈 서명 --
CONFIG_MODULE_SIG=y
CONFIG_MODULE_SIG_FORCE=y
CONFIG_MODULE_SIG_ALL=y
CONFIG_MODULE_SIG_SHA256=y
CONFIG_MODULE_SIG_KEY="certs/signing_key.pem"
# -- 키링/인증서 --
CONFIG_SYSTEM_TRUSTED_KEYRING=y
CONFIG_SYSTEM_TRUSTED_KEYS="certs/signing_key.pem"
CONFIG_SECONDARY_TRUSTED_KEYRING=y
CONFIG_INTEGRITY_PLATFORM_KEYRING=y
# -- 비대칭키/서명 검증 --
CONFIG_ASYMMETRIC_KEY_TYPE=y
CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
CONFIG_X509_CERTIFICATE_PARSER=y
CONFIG_PKCS7_MESSAGE_PARSER=y
CONFIG_PKCS7_TEST_KEY=y
CONFIG_SIGNED_PE_FILE_VERIFICATION=y
# -- kexec 서명 --
CONFIG_KEXEC_FILE=y
CONFIG_KEXEC_SIG=y
CONFIG_KEXEC_SIG_FORCE=y
CONFIG_KEXEC_BZIMAGE_VERIFY_SIG=y
# -- IMA/EVM --
CONFIG_INTEGRITY=y
CONFIG_INTEGRITY_SIGNATURE=y
CONFIG_IMA=y
CONFIG_IMA_MEASURE_PCR_IDX=10
CONFIG_IMA_APPRAISE=y
CONFIG_IMA_APPRAISE_MODSIG=y
CONFIG_EVM=y
# -- dm-verity --
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y
CONFIG_DM_VERITY_FEC=y
# -- fs-verity / LoadPin / IPE --
CONFIG_FS_VERITY=y
CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y
CONFIG_SECURITY_LOADPIN=y
CONFIG_SECURITY_IPE=y
# -- TPM --
CONFIG_TCG_TPM=y
CONFIG_TCG_TIS=y
CONFIG_TCG_CRB=y
Secure Boot 검증 루틴
Secure Boot 환경에서는 "부팅 성공 여부"만으로 안전성을 판단할 수 없습니다. 키 체인 상태, 서명 검증 경로, 런타임 정책을 함께 점검해야 합니다.
- 펌웨어 상태 확인: Setup Mode/User Mode, SecureBoot 플래그 점검
- 키 체인 확인: PK/KEK/db/dbx와 MOK 상태 점검
- 커널 정책 확인: lockdown, 모듈 서명 강제 여부 확인
- 실패 시 로그 확보: shim, kernel dmesg, audit 로그 동시 확인
# Secure Boot 상태
mokutil --sb-state
bootctl status | grep -i secure
# MOK 등록 키 확인
mokutil --list-enrolled
# 커널 lockdown 상태
cat /sys/kernel/security/lockdown
# 모듈 서명 정보 확인
modinfo -F signer mymodule.ko
dmesg | grep -Ei "secure|lockdown|module verification"
| 오류 메시지 | 원인 후보 | 대응 |
|---|---|---|
| Required key not available | 모듈 서명 키 미신뢰 | MOK 등록 또는 신뢰 키링 정책 점검 |
| Lockdown: ... is restricted | Secure Boot 연동 lockdown 제한 | 허용 가능한 인터페이스로 전환, 정책 재검토 |
| Verification failed | 이미지 서명 불일치/폐기 키 | 서명 체인 재검증, dbx 차단 목록 확인 |
공식 문서 및 원문
Secure Boot는 펌웨어, 부트로더, 배포판 운영 문서가 각각 다른 관점으로 설명하는 분야라서, 아래 원문들을 같이 보는 편이 안전합니다. 특히 네트워크 부팅과 엮일 때는 UEFI 네트워크 규격, iPXE 문서, 배포판 Secure Boot/설치 문서를 함께 대조해야 합니다.
- UEFI 2.11 Network Protocols — UEFI PXE, HTTP Boot, Boot URI 전달 규정
- iPXE Secure Boot Appnote — 서명된
ipxe.efi, shim 연계, 키 배치 실무 - RHEL 10 UKI 문서 —
kernel-uki-virt와 Secure Boot/측정 부팅 운영 맥락 - Debian Secure Boot Wiki — Debian의 서명된
shim, GRUB, 커널, EFIStub 주의점 - Debian UEFI / systemd-boot 가이드 — trixie의
systemd-boot+shim흐름 - Debian EFIStub Wiki — shim 우회 시 펌웨어
db키 등록이 필요한 배경 - systemd-stub — UKI 실행 모델과 EFI Stub 동작
- Ubuntu Autoinstall Quick Start — UEFI 네트워크 설치 이후
autoinstall핸드오프 방식 - RHEL 10 Kickstart 문서 — PXE / UEFI HTTP Boot에서
inst.ks=와 installer 전달 - Debian Installer Preseed 가이드 — UEFI 네트워크 설치와
preseed/url인자 흐름 - Microsoft KB5062713 — 2023 계열 Secure Boot 인증서/KEK/Option ROM 신뢰 경로 갱신
관련 문서
Secure Boot와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.