TT Lab
Get started
Learn Learning paths Courses

Air-Gapped Sites — Defence and Government

A Transfer Is Not Moving Files but Closing Dependencies

Continue in TT Lab

In one line

Most failures in air-gapped imports come from dependencies, not signatures, and the mechanism that prevents them is a fully self-contained bundle with a per-file manifest.

Why this was needed

Outside, deployment goes like this. You fetch code from a repository, a package manager downloads the dependencies, and a container registry fills in the image layers. What we move is really an "address," and the network fills in the actual contents at run time.

In an air-gapped network, that network does not exist. Moving an address fills in nothing. So what you move must be not the address but all of the contents. Not only the application code, but the libraries that code calls, and the libraries those libraries call, all of it.

The failure that comes out of this is very distinctive. You pass the import review, carry the media in, start the installation, and only then find out that one library is missing. To fill in that one, you have to go back out, repackage, and go through review again. Where the review cycle is measured in weeks, this costs a week. The fact that one missing file costs a week is the reason for this entire module.

How it works

So an import bundle contains three things together.

One, the payload. All the files needed to run. The principle here is "include everything that the network filled in even once outside." The judgment that it will work because it works in the development environment is the most dangerous — the development environment already has a cache.

Two, the manifest. A list of the SHA-256 of each file in the payload. Why is a single hash of the whole bundle not enough — a whole-bundle hash tells you only that "something is different." If you do not know which file is different, the place to investigate is the entire bundle, and with a bundle of several hundred files that is effectively impossible to investigate. Write paths as relative paths. The receiving side unpacks into a different directory, so absolute paths are useless on the spot.

Three, the signature. A signature over a single manifest. Even with thousands of files, one signature is enough. The manifest is responsible for per-file integrity, and the signature is responsible for the authenticity of the manifest. Separating these two layers is the whole of what follows.

페이로드 파일들 ──(각 파일의 SHA-256)──▶ MANIFEST.sha256 ──(개인키 서명)──▶ MANIFEST.sig
     ▲                                          ▲                              ▲
     └ 파일 해시 대조로 확인                     └ 목록이 맞는지                └ 목록을 우리가
                                                                                  아는 사람이 만들었는지

What it looks like in the field

Many sites do not have gpg. If it is not on the authorized software list, you cannot install it, and getting it onto the list is itself an import review. So in practice you use openssl, which is available everywhere. The two commands openssl dgst -sha256 -sign and -verify complete signing and verification. It is not glamorous, but it is the method that actually works in this environment.

You cannot verify binaries by eye. Text files can be viewed with diff, but nothing shows up when you open a .so or a .whl. On top of that, NUL bytes are mixed in, so if you feed them to head or grep, you cannot trust the result itself. It is safer to think that the tools for handling binaries are only three: file, sha256sum, and openssl.

One thing that burned me once. I once repackaged a bundle and did not recompute the hash file. I carried it in on the media, and on the receiving side the hash did not match and it was rejected. Getting back out took a day. Since then, I always keep the command that packages and the command that computes the hash together in the same script.

What to read next

That is "what you pack and send." The piece that comes right after covers "what the receiving side checks, and in what order." Its content is what hashes and signatures each protect, what kind of tampering gets through if you look at only one layer, and why verification becomes entirely meaningless if the public key is inside the bundle. Then the next lab walks through both pieces by hand in one go.