Air-Gap Deployment
Overview
Deploy Sovra in completely isolated networks for SECRET classification workloads.
Use Cases
- Military installations
- Intelligence agencies
- Critical infrastructure
- Classified research facilities
Architecture
[Offline Network - Classification: SECRET]
Control Plane Cluster
├── Kubernetes (no internet)
├── PostgreSQL (local)
└── Local Container Registry
Edge Nodes
├── Vault (3 nodes)
└── Edge Agent (manual sync)
[Physical Separation]
Management Station (Connected)
├── Download artifacts
├── Prepare USB packages
└── Transfer to offline network
Prerequisites
Hardware
- USB drives (encrypted, classified)
- Air-gapped Kubernetes cluster
- Offline container registry
- Offline artifact repository
Software
All software must be transferred offline:
- Sovra container images
- Kubernetes manifests
- PostgreSQL binaries
- TLS certificates
- Policies and configurations
Preparation (Connected Network)
Step 1: Download Artifacts
# Create artifact directory
mkdir -p /tmp/sovra-airgap/{images,manifests,binaries,certs}
# Pull container images
docker pull ghcr.io/witlox/sovra:v0.5.0
docker pull vault:1.16.0
docker pull postgres:15.4
# Save images to tarball
docker save -o /tmp/sovra-airgap/images/sovra-images.tar \
ghcr.io/witlox/sovra:v0.5.0 \
vault:1.16.0 \
postgres:15.4
Step 2: Prepare Manifests
# Copy manifests
cp -r infrastructure/kubernetes/airgap/* /tmp/sovra-airgap/manifests/
# Copy binaries
cp bin/sovra /tmp/sovra-airgap/binaries/
Step 3: Generate Certificates
# Generate all certificates on connected machine
cd /tmp/sovra-airgap/certs
# Root CA
openssl genrsa -out ca-key.pem 4096
openssl req -new -x509 -days 3650 -key ca-key.pem -out ca.crt
# Control plane certificate (valid for 1 year)
openssl genrsa -out api-gateway-key.pem 2048
openssl req -new -key api-gateway-key.pem -out api-gateway.csr
openssl x509 -req -in api-gateway.csr -CA ca.crt -CAkey ca-key.pem -CAcreateserial -out api-gateway.crt -days 365
# Edge node certificates (100 pre-generated)
for i in {1..100}; do
openssl genrsa -out edge-node-${i}-key.pem 2048
openssl req -new -key edge-node-${i}-key.pem -out edge-node-${i}.csr
openssl x509 -req -in edge-node-${i}.csr -CA ca.crt -CAkey ca-key.pem -CAcreateserial -out edge-node-${i}.crt -days 365
done
Step 4: Package for Transfer
# Create encrypted archive
tar czf sovra-airgap-package.tar.gz /tmp/sovra-airgap
# Encrypt with GPG (classification: SECRET)
gpg --symmetric --cipher-algo AES256 sovra-airgap-package.tar.gz
# Verify integrity
sha256sum sovra-airgap-package.tar.gz.gpg > sovra-airgap-package.sha256
# Copy to encrypted USB drive
cp sovra-airgap-package.tar.gz.gpg /media/usb-secret/
cp sovra-airgap-package.sha256 /media/usb-secret/
Installation (Air-Gap Network)
Step 1: Verify Transfer
# Verify checksum
sha256sum -c sovra-airgap-package.sha256
# Decrypt
gpg --decrypt sovra-airgap-package.tar.gz.gpg > sovra-airgap-package.tar.gz
# Extract
tar xzf sovra-airgap-package.tar.gz
cd sovra-airgap
Step 2: Setup Container Registry
# Load images into local registry
docker load < images/sovra-images.tar
# Tag for local registry
docker tag ghcr.io/witlox/sovra:v0.5.0 localhost:5000/sovra/sovra:v0.5.0
docker tag vault:1.16.0 localhost:5000/sovra/vault:1.16.0
docker tag postgres:15.4 localhost:5000/sovra/postgres:15.4
# Push to local registry
docker push localhost:5000/sovra/sovra:v0.5.0
docker push localhost:5000/sovra/vault:1.16.0
docker push localhost:5000/sovra/postgres:15.4
Step 3: Deploy PostgreSQL
# Deploy PostgreSQL
kubectl apply -f manifests/postgresql/
# Wait for ready
kubectl wait --for=condition=ready pod -l app=postgres -n sovra --timeout=300s
# Initialize database
kubectl exec -it postgres-0 -n sovra -- psql -U postgres << 'SQLEOF'
CREATE DATABASE sovra;
CREATE USER sovra WITH PASSWORD 'CHANGE_ME';
GRANT ALL PRIVILEGES ON DATABASE sovra TO sovra;
SQLEOF
Step 4: Install Certificates
# Create secrets
kubectl create secret tls api-gateway-tls \
--cert=certs/api-gateway.crt \
--key=certs/api-gateway-key.pem \
-n sovra
# Repeat for all services
# ... (create secrets for all components)
# Create CA secret
kubectl create secret generic sovra-ca \
--from-file=ca.crt=certs/ca.crt \
-n sovra
Step 5: Deploy Control Plane
# Update image references in manifests to use local registry
sed -i 's|ghcr.io/witlox/sovra|localhost:5000/sovra/sovra|g' manifests/*.yaml
# Deploy
kubectl apply -k manifests/
# Wait for pods
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sovra -n sovra --timeout=600s
Step 6: Initialize
# Run the standard init script
./binaries/sovra --api-url https://localhost:8080 \
identity admin bootstrap \
--email admin@example.org \
--name "First Admin" \
--shares 5 \
--threshold 3
# Generate CRK with two-factor protection (fully offline)
./binaries/sovra crk init \
--org-id admin-org \
--shares 5 \
--threshold 3 \
--output crk-init.json
Edge Node Deployment
Transfer Edge Node Package
# On connected machine
mkdir /tmp/edge-node-package
cp binaries/sovra /tmp/edge-node-package/
cp certs/edge-node-1.crt /tmp/edge-node-package/
cp certs/edge-node-1-key.pem /tmp/edge-node-package/
cp manifests/edge-node/* /tmp/edge-node-package/
# Package
tar czf edge-node-1-package.tar.gz /tmp/edge-node-package
gpg --symmetric --cipher-algo AES256 edge-node-1-package.tar.gz
# Transfer via USB
cp edge-node-1-package.tar.gz.gpg /media/usb-secret/
Deploy Edge Node (Air-Gap)
# On air-gap network
gpg --decrypt edge-node-1-package.tar.gz.gpg > edge-node-1-package.tar.gz
tar xzf edge-node-1-package.tar.gz
# Deploy Vault
kubectl apply -f edge-node-package/vault-deployment.yaml
# Install certificates
kubectl create secret tls edge-node-1-tls \
--cert=edge-node-1.crt \
--key=edge-node-1-key.pem \
-n sovra-edge
Operations
Certificate Rotation (Manual)
Certificates valid for 1 year must be rotated manually.
90 days before expiry:
- Generate new certificates on connected machine
- Package and transfer via USB
- Update secrets in air-gap cluster
- Restart affected pods
# Update certificate secret
kubectl create secret tls api-gateway-tls \
--cert=new-api-gateway.crt \
--key=new-api-gateway-key.pem \
--dry-run=client -o yaml | kubectl apply -f -
# Restart pods
kubectl rollout restart deployment/api-gateway -n sovra
Policy Updates
# On connected machine: prepare policy
cat > new-policy.rego << 'POLICYEOF'
package workspace.classified_project
# ... policy content ...
POLICYEOF
# Transfer to air-gap via USB
# On air-gap: apply policy
sovra --cert admin.crt --key admin.key \
policy create \
--name classified-project-policy \
--workspace classified-project \
--rego-file new-policy.rego
Audit Log Export
# Export audit logs
sovra audit export \
--since "2026-01-01" \
--output /media/usb-secret/audit-export-2026-Q1.json
# Transfer USB to connected network for analysis
Software Updates
Quarterly update cycle:
- Download new versions on connected machine
- Test in connected staging environment
- Package and encrypt
- Schedule maintenance window (4 hours)
- Transfer via USB
- Deploy in air-gap environment
- Verify functionality
Federation (Air-Gap ↔ Air-Gap)
Two air-gapped organizations can federate via manual certificate and public key exchange.
Each organization must export both their federation certificate and their RSA public key (PEM-encoded) for out-of-band transfer via USB courier. The public key is used to encrypt workspace DEKs during cross-org workspace sharing.
# Org A: Initialize federation
sovra --cert admin.crt --key admin.key \
federation init
# Export federation certificate AND RSA public key (out-of-band to Org B via courier)
# Org B: Import Org A's certificate and public key
sovra --cert admin.crt --key admin.key \
federation import-cert \
--partner-org org-a \
--cert-file org-a-federation-cert.pem \
--public-key-file org-a-pubkey.pem
# Org B: Establish federation
sovra --cert admin.crt --key admin.key \
federation establish \
--partner-org org-a \
--partner-url https://org-a-control-plane:8080
# Transfer Org B's certificate AND public key back to Org A
# Org A: Import Org B's certificate and public key
sovra --cert admin.crt --key admin.key \
federation import-cert \
--partner-org org-b \
--cert-file org-b-federation-cert.pem \
--public-key-file org-b-pubkey.pem
Workspace Sharing (Air-Gap)
When exporting a workspace in air-gap mode, Sovra automatically re-encrypts the workspace’s DEK for each participant organization using RSA-OAEP (SHA-256). The exported bundle contains an ExportDEK map keyed by org ID. On import, the receiving organization decrypts their DEK entry with their RSA private key and re-wraps it with their local KEK.
Prerequisites: Partner public keys must be exchanged during federation setup (see above).
# Org A: Create workspace with group binding
sovra --cert admin.crt --key admin.key \
workspace create \
--name classified-intel \
--group-id group-123 \
--classification SECRET
# Org A: Invite Org B
sovra --cert admin.crt --key admin.key \
workspace invite ws-123 --org-id org-b
# Export workspace bundle (DEK is RSA-encrypted per participant)
sovra --cert admin.crt --key admin.key \
workspace export ws-123 --output workspace-bundle.json
# Transfer workspace-bundle.json to Org B via USB courier
# Org B: Import workspace (DEK is decrypted and re-wrapped with local KEK)
sovra --cert admin.crt --key admin.key \
workspace import --input workspace-bundle.json
Security Considerations
Physical Security
- USB drives must be encrypted (AES-256)
- USB drives must be classified and labeled
- All transfers logged and approved
- Couriers must have appropriate clearance
- Dual control for CRK shares
Operational Security
- No network connectivity to internet
- No WiFi/Bluetooth enabled
- Firewalls configured (deny all by default)
- All personnel background-checked
- Regular security audits
Audit Trail
- All USB transfers logged
- All certificate operations logged
- All policy changes logged
- All workspace operations logged
- Quarterly audit review
Disaster Recovery
Backup Procedure
# Database backup
pg_dump -U sovra sovra > /media/usb-secret/sovra-backup.sql
# Vault snapshot
vault operator raft snapshot save /media/usb-secret/vault-snapshot.snap
# Application-level backup (encrypted)
sovra --cert admin.crt --key admin.key \
backup create --crk-signature <base64-signature>
# Export audit logs
sovra --cert admin.crt --key admin.key \
audit export \
--since "2026-01-01" \
--output /media/usb-secret/audit-export.json
# CRK shares on SEPARATE encrypted USB