🏢

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:latest

Two endpoints, two protocols:

VariableHostPurpose
ECHELON_AGENT_ENDPOINTapp.echelongraph.io (HTTPS / REST)Enrollment + heartbeat + scan progress callbacks
TIER2_INGESTER_ADDRingest.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/healthz

Configuration Reference

Core Scanner

VariableDefaultRequiredDescription
TIER2_TENANT_IDYesTenant identifier
TIER2_AGENT_IDtier2-agentAgent identifier
TIER2_TARGET_CIDRYesCIDR range(s) to scan
TIER2_MODEscanscan (one-shot) or daemon (persistent)
TIER2_PORTSTop 100Comma-separated port list
TIER2_CONCURRENCY200Max concurrent goroutines
TIER2_PPS1000Packets per second rate limit
TIER2_CONNECT_TIMEOUT2sTCP connect timeout
TIER2_SCAN_WINDOW10mMax scan duration
TIER2_SCAN_INTERVAL900Seconds between scans (daemon mode)
TIER2_HEALTH_PORT8086Health/metrics HTTP port

Feature Flags

VariableDefaultDescription
TIER2_ENABLE_SSLtrueSSL/TLS certificate scanning
TIER2_ENABLE_HTTPtrueHTTP header fingerprinting
TIER2_ENABLE_DNStrueDNS enumeration
TIER2_ENABLE_SHADOW_ITtrueShadow IT detection
TIER2_ENABLE_CONTAINERStrueContainer image CVE scanning
TIER2_ENABLE_K8SautoKubernetes scanning (auto-detects)
TIER2_ENABLE_MITREtrueMITRE ATT&CK technique mapping
TIER2_ENABLE_PCItruePCI DSS v4.0 control mapping

Connectivity

VariableDefaultDescription
ECHELONGRAPH_INGESTER_ADDRgRPC ingester address
TIER2_INGESTER_TLSfalseEnable TLS for gRPC
TIER2_HTTP_PROXYHTTP proxy URL
TIER2_SOCKS5_PROXYSOCKS5 proxy URL

Enterprise: License

VariableDefaultDescription
TIER2_LICENSE_KEYHMAC-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

VariableDefaultDescription
TIER2_ENCRYPTION_KEY32-byte hex AES-256-GCM key
TIER2_ENCRYPTION_KEY_NEXTRotation 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

VariableDefaultDescription
TIER2_AIRGAPPEDfalseEnable air-gapped operation
TIER2_RESULTS_DIR/var/lib/echelongraph/resultsLocal result storage path
TIER2_CVEDB_PATH/var/lib/echelongraph/cve-dbBundled 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

ErrorCauseFix
"license key expired"License expiredContact support for renewal
"HMAC signature verification failed"Key corrupted/tamperedRequest a new key
"tier X is below minimum required tier 2"Wrong license tierUpgrade license

No Findings Generated

  1. Verify target CIDR is reachable from the scanner pod
  2. Check scanner logs for connectivity errors
  3. 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-tier2

BYOK 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-values

BYOK Key Rotation

  1. Set the new key as the rotation key (encryption.keyNext)
  2. Wait for 2+ scan cycles (new data encrypted with new key)
  3. 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-values

The scanner validates the new key on the next scan cycle. No restart required.

Rollback

helm rollback echelongraph-tier2 1 -n echelongraph-system

Monitoring

Prometheus Metrics

The scanner exposes a /metrics endpoint (default port 8086) with counters for:

MetricDescription
tier2_assets_discoveredTotal assets discovered
tier2_findings_totalTotal findings generated
tier2_enrichment_mitre_totalMITRE ATT&CK enrichments
tier2_enrichment_pcidss_totalPCI DSS v4.0 enrichments
tier2_attack_paths_totalAttack path edges generated
tier2_auto_resolved_totalAuto-resolved findings
tier2_risk_decay_totalRisk decay amplifications

Health Check

curl http://localhost:8086/healthz

Uninstall

helm uninstall echelongraph-tier2 -n echelongraph-system
kubectl delete namespace echelongraph-system