閉域網で DKMS モジュールを本当にビルドする
한국어 원문으로 표시합니다.
이 실습은 진짜 VM 에서 돕니다
우분투 24.04 VM 한 대입니다. 커널을 빌드하고 모듈을 올리려면 커널 권한이 필요해서 파드가 아니라 VM 입니다. dkms 와 빌드 도구가 깔려 있고, 예제 모듈 소스가 /opt/airgap-hello/(airgap_hello.c, Makefile)에 있습니다. NVIDIA 드라이버는 재배포할 수 없으므로 같은 경로(헤더 반입 → DKMS 빌드 → modprobe)를 이 20줄짜리 GPL 모듈로 밟습니다. VM 은 처음에는 인터넷(80/443)에 닿습니다 — 2단계까지가 연결된 반입 준비이고, 3단계에서 바깥 송신을 막습니다. 채점은 바깥에서 VM 의 8899 포트로 들어오는 에이전트가 하므로 이미 맺은 연결을 막으면 채점이 끊깁니다.
목표
실행 중인 커널과 정확히 같은 헤더를 반입해 오프라인으로 설치하고, DKMS 로 모듈을 빌드·설치·로드한 뒤, 헤더가 어긋났을 때의 오류를 채증한다.
왜 중요한가
폐쇄망 드라이버 설치에서 가장 많이 깨지는 곳은 드라이버가 아니라 커널 헤더다. DKMS 는 실행 중인 커널의 빌드 트리(/lib/modules/<커널>/build)로 모듈을 빌드하는데, 반입한 헤더가 한 버전만 달라도 빌드가 시작조차 되지 않는다. 그리고 커널 업데이트가 들어오는 순간 같은 일이 다시 일어난다. 이 실습은 그 경로를 진짜 커널에서 밟는다.
예상 50분이다. 세션이 끝나면 VM 이 사라지니 남기고 싶은 파일은 종료 전에 따로 보관한다.
단계
/root/dkms/kernel.txt에running=(실행 중인 커널),headers_pkg=(그 커널의 헤더 패키지 이름),build_dir=(yes 또는 no — 지금/lib/modules/<커널>/build가 있는가)를 적는다.- 연결된 동안 그 커널의 헤더 패키지와, 그것이 요구하는 헤더 패키지를 설치하지 않고
/root/dkms/bundle/에 받고, 그 안에서/root/dkms/bundle/SHA256SUMS를 만든다. - 다시 돌려도 규칙이 한 벌만 남는
/root/dkms/egress.sh로 바깥 송신을 막는다(체인AIRGAP-EGRESS, 루프백·이미 맺은 연결은 살린다). - 해시를 확인한 뒤 번들의 .deb 로 헤더를 오프라인 설치해
/lib/modules/<커널>/build가 생기게 한다. - 모듈 소스를
/usr/src/airgap-hello-1.0/에 두고/usr/src/airgap-hello-1.0/dkms.conf를 써서 DKMS 에 add 한다. - 실행 중인 커널로 build·install 해
dkms status에 installed 가 나오게 한다. - 모듈을
site=lab으로 로드하고,/root/dkms/load.txt에vermagic=·signer=(없으면 none)·sb_state=(mokutil --sb-state 의 결과 한 줄)를 적는다. - 헤더가 없는 커널 버전
6.8.0-9999-generic으로 빌드를 시켜 DKMS 가 내는 오류를/root/dkms/mismatch.txt에 저장한다.
참고
- 실행 중인 커널:
uname -r· 헤더 패키지 이름:linux-headers-$(uname -r) - 받기만:
apt-get download <패키지...>· 요구하는 패키지:apt-cache depends <패키지>또는dpkg-deb -f <deb> Depends - DKMS:
dkms add <소스디렉터리>·dkms build -m <이름> -v <버전> [-k <커널>]·dkms install ...·dkms status - 모듈 정보:
modinfo -F vermagic <모듈>,modinfo -F signer <모듈>· 로그:dmesg | grep airgap_hello - 흔한 실수 1:
linux-headers-generic을 받기. 이것은 최신 커널 헤더를 가리키는 메타 패키지라 실행 중인 커널과 다를 수 있습니다. - 흔한 실수 2: 헤더 패키지 하나만 받기. 커널 헤더는 두 패키지(플레이버별 + 공통)로 나뉘어 있습니다.
실행 중인 커널과 필요한 헤더
/root/dkms/kernel.txt 에 running=·headers_pkg=·build_dir= 세 줄을 적는다.
DKMS 가 쓰는 빌드 트리는 /lib/modules/<커널>/build 입니다. 우분투의 헤더 패키지 이름은 실행 중인 커널 문자열을 그대로 붙인 것입니다. 지금 그 디렉터리가 있는지 확인하세요.
헤더를 설치하지 않고 받아 해시 목록 만들기
실행 중인 커널의 헤더 패키지와 그것이 요구하는 헤더 패키지를 /root/dkms/bundle/ 에 받고 /root/dkms/bundle/SHA256SUMS 를 만든다.
apt-get download 는 설치하지 않고 .deb 만 받습니다. 받은 헤더 패키지의 Depends 를 보면 함께 필요한 공통 헤더 패키지가 나옵니다. 해시 목록은 번들 디렉터리 안에서 상대 경로로 만드세요.
바깥 송신을 막아 폐쇄망 만들기
다시 돌려도 규칙이 한 벌만 남는 /root/dkms/egress.sh 로 바깥 송신을 막는다.
새 체인을 만들어 OUTPUT 맨 앞에서 그리로 보내고, 루프백·이미 맺은 연결·채점 에이전트를 먼저 살린 뒤 나머지를 막습니다. 이 뒤로는 apt 가 바깥에서 받아 올 수 없습니다.
반입한 헤더를 오프라인 설치
해시를 확인한 뒤 번들의 .deb 로 헤더를 설치해 /lib/modules/<커널>/build 가 생기게 한다.
번들 디렉터리에서 해시 목록으로 먼저 검사합니다. dpkg 에 두 .deb 를 함께 주면 서로의 의존을 한 번에 풉니다. 설치 뒤 build 가 어디를 가리키는 링크인지 보세요.
dkms.conf 쓰고 add
모듈 소스를 /usr/src/airgap-hello-1.0/ 에 두고 /usr/src/airgap-hello-1.0/dkms.conf 를 써서 DKMS 에 add 한다.
DKMS 는 /usr/src/<이름>-<버전> 에서 소스를 찾습니다. dkms.conf 에는 패키지 이름·버전, 만들어질 모듈 이름(.ko 없이), 설치 위치, 새 커널에서 자동으로 다시 빌드할지를 적습니다.
실행 중인 커널로 build·install
실행 중인 커널로 build·install 해 dkms status 에 installed 가 나오게 한다.
build 는 /var/lib/dkms 아래에서 모듈을 만들고, install 이 그것을 /lib/modules/<커널>/ 아래로 옮기고 depmod 를 돌립니다. 실패하면 make.log 의 경로가 출력에 나옵니다.
로드하고 vermagic·서명·Secure Boot 기록
모듈을 site=lab 으로 로드하고 /root/dkms/load.txt 에 vermagic=·signer=·sb_state= 를 적는다.
modprobe 는 depmod 가 만든 목록에서 모듈을 찾아 의존까지 올리고, 매개변수를 이름=값으로 받습니다. modinfo -F 로 필드 하나만 뽑을 수 있습니다. Secure Boot 상태는 mokutil 이 알려 줍니다(EFI 변수가 없는 VM 이면 그렇다고 말합니다).
헤더가 없는 커널로 빌드시키기
커널 버전 6.8.0-9999-generic 으로 빌드를 시켜 DKMS 가 내는 오류를 /root/dkms/mismatch.txt 에 저장한다.
dkms build 에는 대상 커널을 고르는 옵션이 있습니다. 그 커널의 빌드 트리가 없으면 DKMS 는 컴파일을 시작하기 전에 멈춥니다. 출력(표준 오류 포함)을 그대로 저장하세요.