Air-Gap Satellite Deployment — step-ca + Offline PKI
Overview
Tier A customers in government, defense, critical infrastructure, or regulated industries may operate in fully air-gapped environments with zero internet connectivity. KuiperDesk supports this with:
- step-ca (Smallstep Certificate Authority) for all TLS — no Let's Encrypt dependency
- Contract-length certificates — cert lifetime = contract duration + 3 months grace
- Pre-loaded Harbor cache — all container images baked into the install media
- Offline agent enrollment — enrollment keys + CA bundle shipped with install media
How Air-Gap Install Works
Phase 1: Preparation (Online, at Arivaran or VAR)
Arivaran Hub (online) Install Media (USB/ISO)
┌──────────────────────┐ ┌──────────────────────────┐
│ Dashboard: Generate │ │ │
│ air-gap bundle for │ ───export───→ │ airgap-bundle-<tenant>/ │
│ tenant X, contract │ │ images/ │
│ 3 years │ │ aa-hash-svc.tar │
│ │ │ aa-twin-svc.tar │
│ Creates: │ │ aa-enroll-svc.tar │
│ - Satellite inter CA │ │ aa-dash.tar │
│ (3yr + 3mo = 39mo) │ │ cnpg-citus.tar │
│ - Enrollment keys │ │ seaweedfs.tar │
│ - step-ca root+inter │ │ step-ca.tar │
│ - Agent MSI │ │ keycloak.tar │
│ - Helm chart │ │ nginx.tar │
│ - Bootstrap script │ │ charts/ │
│ │ │ satellite-0.1.0.tgz │
│ │ │ pki/ │
│ │ │ root-ca.crt │
│ │ │ satellite-inter.crt │
│ │ │ satellite-inter.key │
│ │ │ step-ca-config.json │
│ │ │ enrollment/ │
│ │ │ keys.json │
│ │ │ agent.msi │
│ │ │ bootstrap.sh │
│ │ │ values-airgap.yaml │
│ │ │ README.md │
└──────────────────────┘ └──────────────────────────┘Phase 2: Deployment (Offline, at Customer Site)
# 1. Insert install media, mount
mount /dev/sdb1 /mnt/kuiperdesk
# 2. Run bootstrap (detects air-gap: no internet, uses local media)
/mnt/kuiperdesk/bootstrap.sh --airgap --media /mnt/kuiperdesk
# What happens:
# a) Installs RKE2 from local RPMs/debs (bundled on media)
# b) Loads container images from .tar files into containerd
# c) Installs step-ca as the cluster CA (replaces cert-manager + Let's Encrypt)
# d) Imports satellite intermediate CA (pre-signed by hub)
# e) Helm installs satellite chart with values-airgap.yaml
# f) No hub registration (offline) — satellite runs fully autonomousPhase 3: Operation (Offline)
The satellite operates fully autonomously:
- TLS certs issued by local step-ca (auto-renews within the cluster)
- Agent enrollment uses pre-generated keys from install media
- Backups stored on local S3 (SeaweedFS/CubeFS)
- No hub sync — all data stays local
- Dashboard accessible at the satellite's local IP/domain
Phase 4: Renewal (Before Contract Expiry)
90 days before certificate expiry:
- Dashboard shows "Certificate renewal required" banner
- Customer contacts Arivaran/VAR for renewal bundle
- New bundle generated with extended cert lifetime
- Applied via USB/sneakernet (same bootstrap process,
--renewflag)
step-ca Configuration
Why step-ca (Not cert-manager + Let's Encrypt)
| Feature | cert-manager + LE | step-ca |
|---|---|---|
| Internet required | Yes (ACME HTTP-01/DNS-01) | No |
| Cert lifetime | 90 days max | Custom (contract-length) |
| CA control | Let's Encrypt controls | Customer/Arivaran controls |
| ACME support | Yes | Yes (local ACME server) |
| FedRAMP | LE not FedRAMP-authorized CA | Own PKI = full control |
| Air-gap | Impossible | Native |
step-ca Deployment on Satellite
# In Helm chart values-airgap.yaml
pki:
provider: step-ca # instead of cert-manager + letsencrypt
stepCA:
enabled: true
image: "registry.arivaran.ai/proxy-dockerhub/smallstep/step-ca:0.28.0"
# Pre-configured: root CA + intermediate loaded from install media
rootCert: /pki/root-ca.crt
intermediateCert: /pki/satellite-inter.crt
intermediateKey: /pki/satellite-inter.key
# ACME provisioner — local services request certs via standard ACME
acme:
enabled: true
endpoint: "https://step-ca.kd-system.svc:9000/acme/acme/directory"
# Certificate lifetimes
defaultTTL: "2160h" # 90 days (service certs)
maxTTL: "{{ .Values.pki.contractMonths }}mo" # contract length
ingress:
tls:
enabled: true
clusterIssuer: step-ca-issuer # cert-manager StepIssuer (not LE)Certificate Lifetimes
| Cert Type | Online Satellite | Air-Gap Satellite |
|---|---|---|
| Satellite intermediate CA | 5 years | Contract + 3 months |
| Service TLS (inter-service) | 90 days (auto-renew) | 90 days (auto-renew via local step-ca) |
| Agent mTLS | 1 year (auto-renew) | Contract + 3 months (no renewal without media) |
| Ingress TLS (dashboard) | 90 days (LE) | Contract + 3 months (step-ca) |
| Keycloak signing keys | 1 year | Contract + 3 months |
Why contract + 3 months for air-gap:
- Service certs (90-day) auto-renew within the cluster via step-ca — no issue
- But the intermediate CA and agent mTLS certs can't be renewed without hub contact
- Setting them to contract + 3 months ensures everything works through the contract period
- The 3-month grace gives time for renewal even if the contract renewal is delayed
Agent Configuration for Air-Gap
CA Trust Bundle
The agent needs to trust the satellite's step-ca root instead of Let's Encrypt:
// prod_config.json on air-gap agents
{
"mode": "prod",
"grpc_hash_check_endpoint": "api.satellite.local:443",
"grpc_twin_endpoint": "api.satellite.local:443",
"cloud_upload_urls": ["https://api.satellite.local/kuiperdesk-blocks"],
"ca_bundle": "C:\\Program Files\\Arivaran\\pki\\root-ca.crt",
"tenant_id": "uuid",
"endpoint_id": "uuid"
}Agent code change required (CloudComm.cpp):
// If ca_bundle is set, use it instead of system CA store
if (!cfg.caBundlePath.empty()) {
curl_easy_setopt(curl, CURLOPT_CAINFO, cfg.caBundlePath.c_str());
}MSI Filename for Air-Gap
aaagent_{version}_{KEY}_{CA_FINGERPRINT}_{base64url(apiHost)}.msiThe CA_FINGERPRINT field tells the installer DLL to:
- Fetch the CA cert from
https://{apiHost}/api/v1/ca/{fingerprint} - Verify the fingerprint matches
- Install to Windows Machine cert store (Local Machine → Trusted Root)
- For air-gap: CA cert is bundled ON the install media (no fetch needed)
Air-Gap MSI Installation
# Air-gap: CA cert pre-installed from media, KEY from media, no download
msiexec /i D:\kuiperdesk\enrollment\agent.msi `
URL=api.satellite.local `
KEY=kdenr_<from-media> `
AIRGAPFP=<ca-fingerprint> `
AIRGAPCA=D:\kuiperdesk\pki\root-ca.crt `
/qn /l*v C:\temp\install.logThe installer DLL:
- Reads
AIRGAPCApath → installs cert to machine store (no download) - Reads
KEY→ enrollment (against local satellite, not hub) - Agent starts, trusts the satellite's TLS via installed CA
Browser Trust
For users accessing the dashboard in a browser on air-gapped machines:
Option A: GPO/MDM deployment (enterprise)
# Deploy root CA to all domain machines via Group Policy
certutil -addstore "Root" \\fileserver\kuiperdesk\root-ca.crtOption B: Manual trust (per-machine)
# Admin runs on each machine
Import-Certificate -FilePath "D:\kuiperdesk\pki\root-ca.crt" `
-CertStoreLocation Cert:\LocalMachine\RootOption C: Firefox (uses own store, ignores Windows)
# Deploy to Firefox via policies.json
{
"policies": {
"Certificates": {
"Install": ["D:\\kuiperdesk\\pki\\root-ca.crt"]
}
}
}Air-Gap Bundle Generation (Hub-Side)
# Dashboard: Settings → Satellite → Generate Air-Gap Bundle
# Or CLI:
kuiperdesk-cli airgap-bundle \
--tenant-id <uuid> \
--contract-months 36 \
--platform bare-metal \
--output /tmp/airgap-bundle-tenant-x/The bundle generator:
- Pulls all required container images from Harbor → saves as .tar
- Generates step-ca root + intermediate keypair
- Signs satellite intermediate CA with hub CA (lifetime = contract + 3mo)
- Generates enrollment keys (batch of 100, pre-hashed)
- Builds agent MSI with embedded CA fingerprint
- Packages Helm chart + values-airgap.yaml
- Generates bootstrap.sh (offline-capable)
- Creates checksum manifest (SHA-256 for integrity)
- Optionally: creates bootable ISO
Hub ↔ Air-Gap Satellite Sync (Sneakernet)
For customers who periodically connect (e.g., monthly data export):
Air-Gap Satellite USB Drive
┌──────────────┐ ┌──────────────┐
│ Export: │ ───copy to USB──→ │ export/ │
│ - Metrics │ │ metrics.json│
│ - Audit log │ │ audit.jsonl │
│ - Billing │ │ billing.json│
│ │ │ │
│ Import: │ ←───copy from USB─── │ import/ │
│ - Agent MSI │ │ agent.msi │
│ - Policy │ │ policy.json│
│ - CA renewal │ │ renewal.pem│
└──────────────┘ └──────────────┘Compliance Mapping
| Framework | Control | How Air-Gap Satisfies |
|---|---|---|
| FedRAMP High | SC-7 | Complete network boundary — zero external connections |
| FedRAMP High | SC-8 | All data encrypted in transit (step-ca TLS) and at rest (LUKS) |
| FedRAMP High | SC-12 | FIPS-validated crypto (step-ca supports FIPS mode) |
| FedRAMP High | CM-3 | Configuration changes only via install media (auditable) |
| ITAR | — | Data never leaves customer facility |
| CJIS | 5.10.1.2 | Encryption in transit via customer-controlled CA |
| IL4/IL5 | — | No internet dependency, all crypto under government control |
Revision History
| Date | Change | Author |
|---|---|---|
| 2026-03-22 | Initial — step-ca air-gap, contract-length certs, bundle generation, sneakernet sync | Claire Dubois (Security), Viktor Kowalski (Agent Security), Navid Ahmadi (Compliance) |