Security Scan APIs
Four automated scan APIs built into the stack-agent. Each scan reads the Stacktic topology, discovers targets automatically, runs real tests, and returns structured JSON with scores.
Scan Overview
| Scan | Endpoint | Light | Full | What It Validates |
|---|---|---|---|---|
| Network | GET /network_scan | Policy existence (~2s) | nmap TCP probes (~2-5min) | NetworkPolicies match architecture |
| OPA | GET /opa_scan | Inventory + violations (~2-5s) | Admission dry-run tests (~15-45s) | OPA/Gatekeeper policies enforce correctly |
| Kubescape | GET /kubescape_scan | Per-namespace scores (~30-60s) | Per-control findings (~2-4min) | K8s compliance (NSA/MITRE frameworks) |
| Nuclei | GET /nuclei_scan | Ingress route scan (~1-3min) | Tech-specific CVEs (~5-15min) | Vulnerabilities, exposed panels, misconfigs |
All scans accept ?mode=light or ?mode=full query parameter.
Network Scan
Validates that NetworkPolicies match the stack topology defined in Stacktic.
Data Source
Reads networkpolicy-manifest.json — pre-generated by the Stacktic pipeline. Contains every expected policy rule derived from component links.
Light Mode
| What It Checks | How |
|---|---|
| Policy existence | Compares manifest rules against kubectl get networkpolicies |
| Missing policies | Rules in manifest but not in cluster |
| Extra policies | Policies in cluster but not in manifest |
| Bidirectional coverage | Both ingress + egress exist for each link |
| Namespace breakdown | Missing policies grouped by namespace |
Returns: score, policies breakdown (link/exception/internet/intra-namespace/default-deny), bidirectional analysis, extra policies list, missing by namespace.
Full Mode
Everything in light, plus real TCP connectivity probes:
| Test Layer | What It Probes |
|---|---|
| Link tests | Every manifest connection (src→dst:port) via nmap |
| Exception tests | DNS (port 53) and kube-api (port 443) from each namespace |
| Internet tests | Outbound 80/443 from stack namespaces |
| Intra-namespace | Service connectivity within same namespace |
| Negative tests | Unauthorized paths verified as BLOCKED |
Creates temporary nmap pods in each source namespace (10 concurrent). For failed connections, runs blocking analysis matching against actual NetworkPolicy specs.
Verdicts: PASS (connection matches expectation), FAIL (should connect but can't), SECURITY_RISK (blocked path is actually open), BLOCKED (expected block confirmed), NO_SERVICE (target service doesn't exist).
OPA Scan
Validates OPA/Gatekeeper policies are correctly deployed and actually enforcing.
Data Source
Auto-discovers Gatekeeper in any namespace (searches: gatekeeper-system, opa, gatekeeper). Reads all ConstraintTemplates and Constraints via Kubernetes API.
Light Mode
| What It Returns | Details |
|---|---|
| Gatekeeper health | Pod readiness status |
| Constraint templates | Total count + names |
| Constraints | Per-constraint: name, template, action, target kinds, violation count |
| Violations | Grouped by constraint + by namespace |
| Namespace coverage | Which namespaces are scoped by policies |
| Score | % of constraints with zero violations |
Full Mode
Everything in light, plus admission dry-run tests per constraint:
| Per Constraint | What Happens |
|---|---|
| Valid resource | Resource that should be ADMITTED — satisfies all active constraints |
| Invalid resource | Resource that should be DENIED — violates this specific constraint |
| Dry-run | Both submitted via kubectl apply --dry-run=server |
Cross-policy aware: each valid test resource satisfies ALL active constraints simultaneously (security context, resources, probes, labels, image registries).
Verdict Reference
| Status | Valid Test | Invalid Test | Meaning |
|---|---|---|---|
ENFORCING | Admitted | Denied | Policy working correctly |
NOT_ENFORCING | Admitted | Admitted | Policy not blocking violations |
OVER_ENFORCING | Denied | Denied | Policy too strict, blocks legitimate resources |
BROKEN | Denied | Admitted | Misconfigured constraint |
AUDIT_ONLY | — | — | Constraint requires cluster state (skipped) |
Test generators: SecurityContextPolicy, ResourcePolicy, ProbesPolicy, DisallowDangerousSettings, K8sAllowedImageRequirements, ImageSigningPolicy, BlockNamespaceDeployments, BlockWideOpenNetworkPolicy, DisallowDefaultServiceAccount, DisallowWildcardRBAC, RequiredLabelsPolicy, RequireNetworkPolicy.
Kubescape Scan
Validates Kubernetes compliance against NSA/CISA and MITRE ATT&CK frameworks.
Data Source
Reads stack-metadata.yaml (Stacktic topology) to discover all stack namespaces. Runs kubescape CLI per namespace.
Light Mode
| What It Returns | Details |
|---|---|
| Per-namespace scores | NSA + MITRE framework scores separately |
| Component list | Which components run in each namespace |
| Overall scores | Average across all namespaces |
| Flagged namespaces | Only namespaces below threshold (NSA < 70%, MITRE < 60%) |
Full Mode
Everything in light, plus per-control findings:
| Per Failed Control | Details |
|---|---|
| Control ID + name | NSA/MITRE control identifier |
| Severity | Critical, high, medium |
| Affected resources | Pod/deployment names that fail the control |
| Stacktic remediation | Which Stacktic attribute to set to fix |
| K8s field | The specific securityContext/spec field that needs fixing |
Exclusion-aware: Filters out legitimate operator requirements that cannot be changed:
| Exclusion Type | Examples |
|---|---|
| Operators needing elevated RBAC | ArgoCD, cert-manager, ESO, Velero |
| Components requiring root | ClickHouse (by design) |
| Components needing writable FS | Prometheus (TSDB), Grafana (plugins) |
| CSI drivers needing privileged | Storage provisioners |
Separates "known architectural limitation" from "actual security misconfiguration".
Nuclei Scan
Vulnerability scanning (DAST) with four modes. Discovers targets from APISIX topology.
Data Source
Reads stack-metadata.yaml to discover APISIX routes (externally exposed services) and internal services.
Modes
| Mode | Targets | Templates | Time |
|---|---|---|---|
light | APISIX ingress routes only | exposure, misconfiguration, default-login, panel | ~1-3min |
full | Routes + internal services | Tech-specific CVE templates per component type | ~5-15min |
network | TCP services (databases, queues) | unauth, default-login, misconfig | ~2-5min |
ssl | HTTPS routes only | SSL/TLS certificate checks | ~30s |
Full Mode — Tech-Specific Scanning
Maps component types to nuclei template tags:
| Component Type | Nuclei Tags |
|---|---|
| grafana | grafana CVEs |
| keycloak | keycloak CVEs |
| mongodb | mongodb unauth |
| cnpg / postgresql | postgres CVEs |
| opensearch / elasticsearch | elasticsearch CVEs |
| rabbitmq | rabbitmq CVEs |
| minio | minio CVEs |
| redis / valkey | redis CVEs |
Network Mode
Scans TCP-only services for:
- Unauthenticated database access
- Exposed management ports
- Default credentials
- Protocol-level vulnerabilities
SSL Mode
Checks all HTTPS ingress routes for:
- Expired certificates
- Weak cipher suites
- Deprecated TLS versions
- Self-signed certificates
- Revoked certificates
What Makes These Scans Smart
| Feature | How It Works |
|---|---|
| Topology-aware | All scans read Stacktic metadata — know what SHOULD exist, not just what DOES exist |
| Link-driven | Network scan validates every component link has a matching policy |
| Cross-policy testing | OPA scan tests that valid resources satisfy ALL constraints simultaneously |
| Exclusion-aware | Kubescape filters known operator requirements from findings |
| Tech-specific | Nuclei selects vulnerability templates based on component type |
| Bidirectional | Network scan checks both ingress and egress for every connection |
| Negative testing | Network scan verifies unauthorized paths are actually blocked |
| Remediation mapping | Kubescape maps failures to Stacktic attributes (not just K8s fields) |
Access
# Via port-forward
kubectl port-forward -n {agent-namespace} svc/{agent-name} 8081:8080
# Light scan
curl -s http://localhost:8081/{scan}?mode=light | jq
# Full scan
curl -s http://localhost:8081/{scan}?mode=full | jq
Via APISIX (if ingress configured):
curl -s -H 'apikey: {api-key}' 'https://{subdomain}.{domain}/{scan}?mode=light' | jq
Replace {scan} with: network_scan, opa_scan, kubescape_scan, or nuclei_scan.
Response Structure
All scans return:
| Field | Description |
|---|---|
scan_type | light or full |
score | Overall compliance score (0-100) |
| Summary section | Counts, breakdowns, grouped results |
| Findings (full) | Per-test results with verdicts and details |