Air-Gapped GPU Driver Installation
Really build a DKMS module in an air gap
This lab runs on a real VM
It is a single Ubuntu 24.04 VM. Building a kernel module and loading it requires kernel privileges, so it is a VM rather than a Pod. dkms and the build tools are installed, and the example module source is in /opt/airgap-hello/ (airgap_hello.c, Makefile). The NVIDIA driver cannot be redistributed, so you walk the same path (header import → DKMS build → modprobe) with this 20-line GPL module. At first the VM can reach the internet (80/443) — up to step 2 is the connected import preparation, and in step 3 you block outbound traffic. Grading is done by an agent that comes in from outside to port 8899 of the VM, so blocking already-established connections cuts off grading.
Goal
Import headers exactly matching the running kernel and install them offline, build, install, and load a module with DKMS, and then capture the error that occurs when the headers do not match.
Why it matters
What breaks most often in an air-gapped driver installation is not the driver but the kernel headers. DKMS builds the module with the build tree of the running kernel (/lib/modules/<커널>/build, where the placeholder is the kernel), and if the imported headers differ by even one version, the build does not even start. And the same thing happens again the moment a kernel update comes in. This lab walks that path on a real kernel.
Estimated time is 50 minutes. The VM disappears when the session ends, so keep separately, before ending, any files you want to retain.
Steps
- In
/root/dkms/kernel.txt, writerunning=(the running kernel),headers_pkg=(the name of that kernel's headers package), andbuild_dir=(yes or no — does/lib/modules/<커널>/buildexist now, where the placeholder is the kernel). - While connected, download that kernel's headers package, and the headers package it requires, without installing into
/root/dkms/bundle/, and create/root/dkms/bundle/SHA256SUMSinside it. - Block outbound traffic with
/root/dkms/egress.sh, which leaves only one set of rules even if run again (the chainAIRGAP-EGRESS, keeping loopback and established connections). - After checking the hashes, install the headers offline from the bundle's .deb files so that
/lib/modules/<커널>/build(the placeholder is the kernel) appears. - Put the module source in
/usr/src/airgap-hello-1.0/, write/usr/src/airgap-hello-1.0/dkms.conf, and add it to DKMS. - Build and install for the running kernel so that
dkms statusshows installed. - Load the module with
site=lab, and writevermagic=,signer=(none if absent), andsb_state=(the one-line result of mokutil --sb-state) in/root/dkms/load.txt. - Ask for a build for the kernel version
6.8.0-9999-generic, which has no headers, and save the error DKMS produces in/root/dkms/mismatch.txt.
Notes
- The running kernel:
uname -r· the headers package name:linux-headers-$(uname -r) - Download only:
apt-get download <패키지...>(the placeholder is the packages) · required packages:apt-cache depends <패키지>ordpkg-deb -f <deb> Depends(the placeholders are the package and the deb file) - DKMS:
dkms add <소스디렉터리>·dkms build -m <이름> -v <버전> [-k <커널>]·dkms install ...·dkms status(the placeholders are the source directory, the name, the version, and the kernel) - Module information:
modinfo -F vermagic <모듈>,modinfo -F signer <모듈>(the placeholder is the module) · log:dmesg | grep airgap_hello - Common mistake 1: downloading
linux-headers-generic. This is a metapackage pointing to the newest kernel's headers, so it can differ from the running kernel. - Common mistake 2: downloading only one headers package. The kernel headers are split into two packages (per flavor plus common).
The running kernel and the headers it needs
In /root/dkms/kernel.txt, write the three lines running=, headers_pkg=, and build_dir=.
The build tree DKMS uses is /lib/modules//build. The name of Ubuntu's headers package is the running kernel string attached as it is. Check whether that directory exists right now.
Download the headers without installing and make a hash list
Download the running kernel's headers package and the headers package it requires into /root/dkms/bundle/, and create /root/dkms/bundle/SHA256SUMS.
apt-get download downloads only the .deb without installing. If you look at the Depends of the headers package you downloaded, the common headers package needed together appears. Make the hash list with relative paths inside the bundle directory.
Block outbound traffic to make an air-gapped network
Block outbound traffic with /root/dkms/egress.sh, which leaves only one set of rules even if run again.
Create a new chain and jump to it at the very front of OUTPUT, keep loopback, established connections, and the grading agent open first, and block the rest. After this, apt cannot download from outside.
Install the imported headers offline
After checking the hashes, install the headers from the bundle's .deb files so that /lib/modules/<커널>/build (the placeholder is the kernel) appears.
First check against the hash list in the bundle directory. If you give dpkg both .deb files together, it resolves their mutual dependencies in one go. After installing, look at where build points as a link.
Write dkms.conf and add
Put the module source in /usr/src/airgap-hello-1.0/, write /usr/src/airgap-hello-1.0/dkms.conf, and add it to DKMS.
DKMS looks for the source in /usr/src/-. In dkms.conf you write the package name and version, the name of the module to be built (without .ko), the install location, and whether to build again automatically on a new kernel.
Build and install for the running kernel
Build and install for the running kernel so that dkms status shows installed.
build creates the module under /var/lib/dkms, and install moves it under /lib/modules// and runs depmod. If it fails, the path of make.log appears in the output.
Load, and record the vermagic, signature, and Secure Boot state
Load the module with site=lab and write vermagic=, signer=, and sb_state= in /root/dkms/load.txt.
modprobe finds the module in the list depmod made and loads even its dependencies, and takes parameters as name=value. You can extract a single field with modinfo -F. mokutil tells you the Secure Boot state (on a VM without EFI variables, it says so).
Ask for a build for a kernel without headers
Ask for a build for the kernel version 6.8.0-9999-generic and save the error DKMS produces in /root/dkms/mismatch.txt.
dkms build has an option to choose the target kernel. If that kernel's build tree does not exist, DKMS stops before compilation starts. Save the output (including standard error) as it is.