Satellite Air-Gap Deployment

Air-gap procedures, ECR image mirroring, offline certificate authority.

NIST controls:SC-7, CA-3, CM-7
Last updated:2026-03-22
Category:Technical
# 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: 1. **step-ca** (Smallstep Certificate Authority) for all TLS — no Let's Encrypt dependency 2. **Contract-length certificates** — cert lifetime = contract duration + 3 months grace 3. **Pre-loaded Harbor cache** — all container images baked into the install media 4. **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) ```bash # 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 autonomous ``` ### Phase 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: 1. Dashboard shows "Certificate renewal required" banner 2. Customer contacts Arivaran/VAR for renewal bundle 3. New bundle generated with extended cert lifetime 4. Applied via USB/sneakernet (same bootstrap process, `--renew` flag) ## 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 ```yaml # 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: ```json // 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): ```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)}.msi ``` The `CA_FINGERPRINT` field tells the installer DLL to: 1. Fetch the CA cert from `https://{apiHost}/api/v1/ca/{fingerprint}` 2. Verify the fingerprint matches 3. Install to Windows Machine cert store (Local Machine → Trusted Root) 4. **For air-gap**: CA cert is bundled ON the install media (no fetch needed) ### Air-Gap MSI Installation ```powershell # 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.log ``` The installer DLL: 1. Reads `AIRGAPCA` path → installs cert to machine store (no download) 2. Reads `KEY` → enrollment (against local satellite, not hub) 3. 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)** ```powershell # Deploy root CA to all domain machines via Group Policy certutil -addstore "Root" \\fileserver\kuiperdesk\root-ca.crt ``` **Option B: Manual trust (per-machine)** ```powershell # Admin runs on each machine Import-Certificate -FilePath "D:\kuiperdesk\pki\root-ca.crt" ` -CertStoreLocation Cert:\LocalMachine\Root ``` **Option 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) ```bash # 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: 1. Pulls all required container images from Harbor → saves as .tar 2. Generates step-ca root + intermediate keypair 3. Signs satellite intermediate CA with hub CA (lifetime = contract + 3mo) 4. Generates enrollment keys (batch of 100, pre-hashed) 5. Builds agent MSI with embedded CA fingerprint 6. Packages Helm chart + values-airgap.yaml 7. Generates bootstrap.sh (offline-capable) 8. Creates checksum manifest (SHA-256 for integrity) 9. 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) |

This document is part of the Arivaran Twin compliance program. For questions or the latest version, contact compliance@arivaran.ai.

Release-ready. Saved on this browser.