
This guide deploys Pulp in an isolated (air-gapped) network: serve the API over HTTPS with a self-signed certificate authority that you distribute and trust properly, then transfer content with no Internet access thanks to Pulp's export/import. Intended audience: administrators of partitioned zones (industry, defense, healthcare). Prerequisites: a Pulp instance (see Installing Pulp) and openssl. The core rule of this guide: you never disable TLS verification, you trust the authority.
An air-gapped environment raises two distinct problems. First, getting content in without a connection to public repositories: this is the job of Pulp's export/import. Second, serving the instance over HTTPS without a publicly recognized authority: this is the job of a self-signed CA that you trust on every client. Together, these two building blocks make an artifact chain fully sovereign.
What you will learn
- Generate a self-signed authority and a server certificate for Pulp.
- Trust that authority on the clients, without ever disabling TLS.
- Export repositories from a connected instance.
- Import that archive into the isolated instance.
Serving Pulp over HTTPS with a self-signed CA
With no public authority, since Let's Encrypt is not reachable offline, you create your own authority. This is perfectly safe as long as the CA key stays protected and the certificate is trusted by the clients.
-
Create the authority and the server certificate for the public name of your instance (here
pulp.interne):Terminal window # Internal certificate authorityopenssl req -x509 -newkey rsa:4096 -nodes -days 3650 \-keyout ca.key -out ca.crt -subj "/CN=Autorité interne Pulp"# Server certificate signed by this authorityopenssl req -newkey rsa:2048 -nodes -keyout pulp.key -out pulp.csr \-subj "/CN=pulp.interne"printf 'subjectAltName=DNS:pulp.interne' > san.cnfopenssl x509 -req -in pulp.csr -CA ca.crt -CAkey ca.key -CAcreateserial \-out pulp.crt -days 825 -extfile san.cnfThe
subjectAltNamefield is mandatory: modern clients reject a certificate without a SAN, even with a correctCN. -
Place this certificate on the reverse proxy that exposes Pulp (see Installing Pulp for Traefik). The proxy terminates TLS with
pulp.crt/pulp.keyand forwards to the container. -
Align
CONTENT_ORIGINwith the HTTPS URL, insettings/settings.py:CONTENT_ORIGIN = "https://pulp.interne"TOKEN_SERVER = "https://pulp.interne/token/"TOKEN_SIGNATURE_ALGORITHM = "ES256"PUBLIC_KEY_PATH = "/etc/pulp/certs/token_public_key.pem"PRIVATE_KEY_PATH = "/etc/pulp/certs/token_private_key.pem"
Trusting the authority on the clients
This is the sticking point, and the temptation of --tls-verify=false is strong. Do not give in: disabling verification means accepting any certificate, and so opening the door to interception. The right method is to trust the CA.
The cleanest way is to add the CA to the system trust store, once, on each machine. All tools then inherit it.
sudo cp ca.crt /usr/local/share/ca-certificates/pulp-interne.crtsudo update-ca-certificatessudo cp ca.crt /etc/pki/ca-trust/source/anchors/pulp-interne.crtsudo update-ca-trustOnce the CA is in the system store, curl, pip, dnf and most tools validate Pulp with no extra option. For tools that manage their own store, per-client configuration remains possible:
| Tool | Trusting the CA without touching the system |
|---|---|
| podman | Copy ca.crt to /etc/containers/certs.d/pulp.interne/ca.crt |
| pip | pip install --cert ca.crt ... or the PIP_CERT variable |
| cosign / skopeo | Environment variable SSL_CERT_FILE=ca.crt |
| dnf | sslcacert=/path/ca.crt in the .repo file |
Getting content in with no Internet
An isolated instance cannot synchronize from PyPI or Docker Hub. The mechanism Pulp provides is export/import: a connected instance (upstream) exports repository versions into a .tar archive, which you transfer physically, and an isolated instance (downstream) then imports it.
-
Allow the export/import directories, in the settings of both instances:
ALLOWED_EXPORT_PATHS = ["/data/exports"]ALLOWED_IMPORT_PATHS = ["/data/imports"] -
On the connected instance, create an exporter then run the export of the desired repositories:
Terminal window pulp exporter pulp create --name export-mensuel \--path /data/exports \--repository file:livrables --repository python:pypi-cachepulp export pulp run --exporter export-mensuelTwo different subcommands, and this is a source of errors:
pulp exportermanages the exporter object, creating and listing it, whilepulp exportruns the operation and expects--exporter, not--name.Pulp produces a
.tararchive and a-toc.jsonfile containing the artifacts and their metadata. The two travel together: one without the other restores nothing. You can split the archive withchunk_sizeto fit on removable media. -
Transfer the archive and the
-toc.jsonto the isolated instance (physical media, network diode), into its/data/importsdirectory. -
On the isolated instance, create an importer. It carries the mapping between the repository name in the export and the local repository name, and this option takes two arguments, not JSON:
Terminal window pulp importer pulp create --name import-mensuel \--repo-map livrables livrablesThe destination repository must exist and be empty before the import: the operation fills it, it does not create it.
-
Run the import through the API, because the CLI cannot do it. Verified on
pulp-cli0.41.1:pulp importer pulponly exposescreate,destroy,list,showandupdate, andpulp importdoes not exist. The Pulp documentation only shows the operation through the API. The path of the-toc.jsonis the one the container sees, not the host:Terminal window IMP=$(pulp --format json importer pulp show --name import-mensuel \| python3 -c "import sys,json;print(json.load(sys.stdin)['pulp_href'])")curl -u admin:MOT_DE_PASSE -X POST \-H 'Content-Type: application/json' \-d '{"toc": "/data/imports/export-....-toc.json"}' \"https://pulp.interne${IMP}imports/"The response returns a
task_groupand not atask: the import starts several tasks, one per imported resource, and monitoring written for a single task never sees the end. The import recreates the repository versions with their content; your internal clients then consume these repositories over HTTPS, exactly as on a connected instance.
Hands-on practice
The lab cuts the outbound access of the repository and requires that a client is served anyway. Two checks give it its value. The first measures the cut from the repository rather than deducing it from a rule present in the configuration. The second verifies that the client itself is not cut off: isolating the whole network would make the installation succeed without proving anything about the repository.
Key takeaways
- An air-gapped Pulp combines two problems: serving over HTTPS without a public CA, and importing content without Internet.
- You create a self-signed CA (with
subjectAltNamemandatory) and trust that authority on the clients. - The clean method is the system trust store (
update-ca-trust/update-ca-certificates); failing that, per-tool configuration. - Never use
--tls-verify=false,--allow-insecure-registryorsslverify=0: trust the CA, do not ignore verification. - Content comes in through export/import, on repositories synchronized with
policy=immediate, through theALLOWED_EXPORT_PATHS/ALLOWED_IMPORT_PATHSpaths. - Export runs with
pulp export pulp run --exporter; import, on the other hand, exists only in the API and returns atask_group.
Knowledge check
Eight questions on a repository cut off from the Internet: a self-signed authority properly trusted, and content brought in by export and import.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Backing up and restoring Pulp (container and Kubernetes)
A new full quiz with random questions
Work again only on the questions you missed