Tier 2 Self-Hosted Deployment
Overview
Deploy the EchelonGraph Tier 2 Runtime Security Agent inside your own infrastructure. The agent scans your network, containers, and Kubernetes clusters — all data stays inside your boundary.
Key capabilities:
- Network scanning (TCP, SSL/TLS, HTTP, DNS, Shadow IT)
- Container image CVE scanning with SBOM generation
- Kubernetes security (CIS benchmarks, RBAC audit, network policies)
- Runtime enrichment (MITRE ATT&CK, PCI DSS v4.0, attack path analysis)
- BYOK encryption — SaaS backend never sees plaintext data
- Air-gapped mode — no outbound network required
Installation
Prerequisites
- Kubernetes 1.24+ cluster (or Docker 20.10+)
- Helm 3.10+ (for Kubernetes deployment)
- Network access to target CIDR ranges
- (Optional) Docker socket access for container scanning
- (Optional) Internet access for CVE database updates (not required in air-gapped mode)
Kubernetes — Helm Install (Daemon Mode)
helm install echelongraph-tier2 ./echelongraph-tier2 \
--namespace echelongraph-system --create-namespace \
--set scanner.tenantId="YOUR_TENANT_ID" \
--set scanner.targetCidr="10.0.0.0/8" \
--set ingester.address="ingester.echelongraph.svc:50051" \
--set auth.apiKey="YOUR_API_KEY"Kubernetes — Helm Install (CronJob Mode)
helm install echelongraph-tier2 ./echelongraph-tier2 \
--namespace echelongraph-system --create-namespace \
--set mode=cronjob \
--set scanner.tenantId="YOUR_TENANT_ID" \
--set scanner.targetCidr="10.0.0.0/8" \
--set agent.endpoint="https://app.echelongraph.io" \
--set ingester.address="ingest.echelongraph.io:443" \
--set agent.enrollToken="YOUR_ONE_TIME_ENROLL_TOKEN" \
--set cronjob.schedule="*/15 * * * *"Docker
The EchelonGraph dashboard generates a per-agent install command after you enroll a Scanner Agent. The shape is:
docker run -d \
--name echelongraph-agent \
--restart unless-stopped \
--network host \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /var/lib/echelon:/var/lib/echelon \
-e ECHELON_AGENT_ENROLL_TOKEN="YOUR_ONE_TIME_TOKEN" \
-e ECHELON_AGENT_ENDPOINT="https://app.echelongraph.io" \
-e TIER2_INGESTER_ADDR="ingest.echelongraph.io:443" \
-e TIER2_TENANT_ID="YOUR_TENANT_ID" \
-e TIER2_TARGET_CIDR="10.0.0.0/8" \
ghcr.io/echelongraph/network-scanner:latestTwo endpoints, two protocols:
| Variable | Host | Purpose |
|---|---|---|
ECHELON_AGENT_ENDPOINT | app.echelongraph.io (HTTPS / REST) | Enrollment + heartbeat + scan progress callbacks |
TIER2_INGESTER_ADDR | ingest.echelongraph.io:443 (gRPC over TLS) | Telemetry stream — same edge tier1 + tier3 use |
Firewall: outbound TCP 443 to both hosts. No inbound rules required.
Verify
# Check pod status
kubectl get pods -n echelongraph-system
# Check logs
kubectl logs -n echelongraph-system -l app.kubernetes.io/name=echelongraph-tier2 -f
# Health endpoint (daemon mode)
curl http://localhost:8086/healthzConfiguration Reference
Core Scanner
| Variable | Default | Required | Description |
|---|---|---|---|
| TIER2_TENANT_ID | — | Yes | Tenant identifier |
| TIER2_AGENT_ID | tier2-agent | Agent identifier | |
| TIER2_TARGET_CIDR | — | Yes | CIDR range(s) to scan |
| TIER2_MODE | scan | scan (one-shot) or daemon (persistent) | |
| TIER2_PORTS | Top 100 | Comma-separated port list | |
| TIER2_CONCURRENCY | 200 | Max concurrent goroutines | |
| TIER2_PPS | 1000 | Packets per second rate limit | |
| TIER2_CONNECT_TIMEOUT | 2s | TCP connect timeout | |
| TIER2_SCAN_WINDOW | 10m | Max scan duration | |
| TIER2_SCAN_INTERVAL | 900 | Seconds between scans (daemon mode) | |
| TIER2_HEALTH_PORT | 8086 | Health/metrics HTTP port |
Feature Flags
| Variable | Default | Description |
|---|---|---|
| TIER2_ENABLE_SSL | true | SSL/TLS certificate scanning |
| TIER2_ENABLE_HTTP | true | HTTP header fingerprinting |
| TIER2_ENABLE_DNS | true | DNS enumeration |
| TIER2_ENABLE_SHADOW_IT | true | Shadow IT detection |
| TIER2_ENABLE_CONTAINERS | true | Container image CVE scanning |
| TIER2_ENABLE_K8S | auto | Kubernetes scanning (auto-detects) |
| TIER2_ENABLE_MITRE | true | MITRE ATT&CK technique mapping |
| TIER2_ENABLE_PCI | true | PCI DSS v4.0 control mapping |
Connectivity
| Variable | Default | Description |
|---|---|---|
| ECHELONGRAPH_INGESTER_ADDR | — | gRPC ingester address |
| TIER2_INGESTER_TLS | false | Enable TLS for gRPC |
| TIER2_HTTP_PROXY | — | HTTP proxy URL |
| TIER2_SOCKS5_PROXY | — | SOCKS5 proxy URL |
Enterprise: License
| Variable | Default | Description |
|---|---|---|
| TIER2_LICENSE_KEY | — | HMAC-signed license key |
The license key controls feature access, asset caps, and expiry. Without a license key, the scanner runs in SaaS mode with all features enabled.
Enterprise: BYOK Encryption
| Variable | Default | Description |
|---|---|---|
| TIER2_ENCRYPTION_KEY | — | 32-byte hex AES-256-GCM key |
| TIER2_ENCRYPTION_KEY_NEXT | — | Rotation key (new data encrypted with this) |
When set, all telemetry payloads are encrypted before gRPC transmission. The SaaS backend never sees plaintext data.
Enterprise: Air-Gapped Mode
| Variable | Default | Description |
|---|---|---|
| TIER2_AIRGAPPED | false | Enable air-gapped operation |
| TIER2_RESULTS_DIR | /var/lib/echelongraph/results | Local result storage path |
| TIER2_CVEDB_PATH | /var/lib/echelongraph/cve-db | Bundled CVE database path |
Enterprise Features
License Key
helm upgrade echelongraph-tier2 ./echelongraph-tier2 \
--set license.key="YOUR_LICENSE_KEY"The license key is an HMAC-signed JSON payload that controls:
- Feature flags: Which scanning modules are enabled
- Asset cap: Maximum assets the scanner will discover
- Expiry: When the license expires (scanner exits gracefully)
- Tier binding: Ensures the license matches the scanner tier
BYOK Encryption
Generate a 32-byte encryption key and configure it:
# Generate key
openssl rand -hex 32
# Apply
helm upgrade echelongraph-tier2 ./echelongraph-tier2 \
--set encryption.key="YOUR_64_CHAR_HEX_KEY"All telemetry is encrypted with AES-256-GCM before transmission. The SaaS backend stores encrypted blobs and never decrypts them. An unencrypted correlation ID is included for graph traversal.
Air-Gapped Mode
For environments with no internet access:
helm upgrade echelongraph-tier2 ./echelongraph-tier2 \
--set airgapped.enabled=true \
--set airgapped.resultsDir="/data/scan-results"In air-gapped mode, scan results are written as JSON files to the local filesystem instead of being transmitted via gRPC.
Troubleshooting
Scanner Won't Start
| Error | Cause | Fix |
|---|---|---|
| "license key expired" | License expired | Contact support for renewal |
| "HMAC signature verification failed" | Key corrupted/tampered | Request a new key |
| "tier X is below minimum required tier 2" | Wrong license tier | Upgrade license |
No Findings Generated
- Verify target CIDR is reachable from the scanner pod
- Check scanner logs for connectivity errors
- Ensure feature flags are enabled in the ConfigMap
Container Scanning Issues
- Docker socket must be mounted at /var/run/docker.sock
- For containerd runtimes, container scanning uses the K8s API — ensure features.k8s is enabled
K8s Permission Errors
Verify the ClusterRoleBinding exists and the ServiceAccount has the correct permissions:
kubectl get clusterrolebinding -l app.kubernetes.io/name=echelongraph-tier2BYOK Encryption Issues
The encryption key must be exactly 64 hex characters (32 bytes). Generate with: openssl rand -hex 32
Upgrading
Helm Upgrade
helm upgrade echelongraph-tier2 ./echelongraph-tier2-new \
--namespace echelongraph-system \
--reuse-valuesBYOK Key Rotation
- Set the new key as the rotation key (encryption.keyNext)
- Wait for 2+ scan cycles (new data encrypted with new key)
- Promote: move keyNext to key, clear keyNext
License Renewal
Replace the license key without downtime:
helm upgrade echelongraph-tier2 ./echelongraph-tier2 \
--set license.key="NEW_LICENSE_KEY" \
--reuse-valuesThe scanner validates the new key on the next scan cycle. No restart required.
Rollback
helm rollback echelongraph-tier2 1 -n echelongraph-systemMonitoring
Prometheus Metrics
The scanner exposes a /metrics endpoint (default port 8086) with counters for:
| Metric | Description |
|---|---|
| tier2_assets_discovered | Total assets discovered |
| tier2_findings_total | Total findings generated |
| tier2_enrichment_mitre_total | MITRE ATT&CK enrichments |
| tier2_enrichment_pcidss_total | PCI DSS v4.0 enrichments |
| tier2_attack_paths_total | Attack path edges generated |
| tier2_auto_resolved_total | Auto-resolved findings |
| tier2_risk_decay_total | Risk decay amplifications |
Health Check
curl http://localhost:8086/healthzUninstall
helm uninstall echelongraph-tier2 -n echelongraph-system
kubectl delete namespace echelongraph-system