TT Lab
Get started
Learn Learning paths Courses

Air-Gapped GPU Driver Installation

Really build a DKMS module in an air gap

Continue in TT Lab

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

  1. In /root/dkms/kernel.txt, write running= (the running kernel), headers_pkg= (the name of that kernel's headers package), and build_dir= (yes or no — does /lib/modules/<커널>/build exist now, where the placeholder is the kernel).
  2. 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/SHA256SUMS inside it.
  3. Block outbound traffic with /root/dkms/egress.sh, which leaves only one set of rules even if run again (the chain AIRGAP-EGRESS, keeping loopback and established connections).
  4. 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.
  5. 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.
  6. Build and install for the running kernel so that dkms status shows installed.
  7. Load the module with site=lab, and write vermagic=, signer= (none if absent), and sb_state= (the one-line result of mokutil --sb-state) in /root/dkms/load.txt.
  8. 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 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.