Kubescape HTTP Handler 实战指南:用 REST API 将 Kubernetes 安全扫描嵌入 CI/CD 与自动化平台
2026/9/15 17:37:59 网站建设 项目流程

Kubescape HTTP Handler 实战指南:用 REST API 将 Kubernetes 安全扫描嵌入 CI/CD 与自动化平台

【免费下载链接】kubescapeKubescape is an open-source Kubernetes security platform for your IDE, CI/CD pipelines, and clusters. It includes risk analysis, security, compliance, and misconfiguration scanning, saving Kubernetes users and administrators precious time, effort, and resources.项目地址: https://gitcode.com/GitHub_Trending/ku/kubescape

导读

Kubescape 不仅是一个命令行扫描工具,还内置了一个可独立部署的 HTTP Handler(服务端)模块:当以服务形式运行时,它会在8080端口启动 Web 服务器,对外暴露一套 REST API,用于以编程方式触发安全扫描、拉取扫描结果、查询扫描状态以及管理缓存结果。本文以仓库中的 httphandler/README.md 为骨架,结合 httphandler 目录下的源码与部署示例,完整讲解四个核心 API 的请求/响应契约、请求体字段、全部环境变量、优雅停机语义、pprof 性能剖析,以及微服务与 Prometheus 两种落地部署方式。读完本文,你将能独立部署 Kubescape 扫描服务,并把"扫描即 API"的能力接入 CI/CD 流水线、自定义仪表盘和定时巡检任务。


Overview:服务化扫描的整体形态

当 Kubescape 以服务(Server)模式运行时,它不再是一次性执行的 CLI 进程,而是一个常驻的 Web 服务。从入口 httphandler/main.go 可以看到,服务启动后会依次完成配置加载(config.LoadConfig("/etc/config"))、凭据加载、服务发现(SaaS 后端接线)、存储初始化,最终调用listener.SetupHTTPListener(ctx)拉起 HTTP 监听。

在 httphandler/listener/setup.go 中,路由注册清晰地划分了三个边界:

  • 健康检查(无鉴权)GET /livezGET /readyz,供 kubelet 探活使用,因此刻意不设鉴权;
  • OpenAPI 文档GET /docs前缀下的 OpenAPI UI;
  • 核心业务 API(/v1/*:包括扫描触发、状态查询、结果获取与删除,以及 Prometheus 指标端点。

完整的/v1路由如下(见 setup.go):

方法路径说明
POST/v1/scan触发一次扫描(异步/同步)
DELETE/v1/scan取消正在排队或执行的扫描
GET/v1/status查询扫描是否仍在进行
GET/v1/results获取扫描结果
DELETE/v1/results删除缓存的扫描结果
GET/v1/metricsPrometheus 指标(标记为 deprecated)

端口默认8080,可通过KS_PORT环境变量覆盖(见 setup.go)。服务支持通过KS_CERT_FILE/KS_KEY_FILE配置 TLS 证书对内网启用 HTTPS,两者必须同时设置。


优雅停机(Graceful Shutdown)

生产环境中最容易忽略的细节是停机行为。HTTP Handler 在收到SIGTERMSIGINT后不会立刻杀进程,而是执行一套分阶段的优雅停机协议:

  1. 停止接收新连接与新扫描:服务关闭 admission(准入),正在解析阶段的扫描请求(包括 metrics 扫描)会收到503 Service Unavailable
  2. 排空已接受扫描(最长 20 秒):已入队的扫描会继续执行完,包括结果处理与持久化;
  3. 取消剩余扫描:超过排空窗口仍未完成的扫描,会复用与DELETE /v1/scan相同的取消机制(context 取消)终止;
  4. 等待 worker 退出:成功的停机必须等到扫描 worker 完全退出;如果 HTTP 排空与 worker 退出在25 秒内未完成,停机会报错、关闭剩余连接,并在尝试一次 OpenTelemetry flush(额外最多 5 秒)后以失败状态退出——该失败路径不保证持久化;
  5. Completion 回调不参与 worker-join 保证:回调仍保持原有的 best-effort 语义。

上述常量在 listener/setup.go 中定义为scanDrainPeriod = 20 * time.SecondapplicationShutdownTimeout = 25 * time.Second,停机编排逻辑实现在serveUntilShutdown(见 setup.go)。worker 侧的排空/取消逻辑见 requestshandler.go 的BeginShutdownShutdown

实践提示:文档明确建议根据自身负载合理设置 Pod 的terminationGracePeriodSeconds,否则 Kubernetes 可能在清理完成前强制终止进程。仓库自带的部署清单 ks-deployment.yaml 给出的参考值是35s(25 秒停机 + 5 秒遥测 + 调度余量)。


API Reference:四个核心端点

POST /v1/scan —— 触发扫描

扫描默认异步执行:请求立即返回一个 scan ID,扫描在后台队列中执行。

Query 参数:

参数类型默认值说明
waitboolfalse是否等待扫描完成(同步模式)
keepboolfalse返回后是否在缓存中保留结果
skipPersistenceboolfalse扫描后不持久化数据
callbackstring-扫描完成信号回调 URL(仅携带 scan ID,结果仍需GET /v1/results获取)

说明:skipPersistencecallback在 README 参数表中未列出,但它们真实存在于 requestparser.go 的ScanQueryParams结构中,是同步扫描与事件驱动集成的有用补充。

异步响应示例:

{ "id": "scan-12345", "type": "busy", "response": "scanning in progress" }

同步响应(wait=true:与GET /v1/results的响应一致,直接返回结果对象。

背压与限流:服务接受"有界数量"的排队扫描。从源码看,requestshandler.go 定义了defaultScanQueueCapacity = 10defaultMaxScanRequestBodyBytes = 1 MiB,分别对应环境变量KS_SCAN_QUEUE_CAPACITYKS_SCAN_REQUEST_MAX_BYTES。行为包括:

  • 队列满时返回429 Too Many Requests,并携带Retry-After响应头(源码中固定为1秒,见 requestshandler.go);
  • 请求体超过配置上限时,在扫描被准入之前即返回413 Request Entity Too Large(通过http.MaxBytesReader实现,见 requestshandler.go);
  • 停机期间的新扫描请求返回503 Service Unavailable

取消扫描(DELETE /v1/scan):虽然 README 的 API 参考未单列,但源码与路由表都支持该端点。带id参数取消指定扫描,不带则取消正在运行的用户扫描;无进行中扫描时返回404,成功则返回type: notBusy的响应(见 requestshandler.go)。

GET /v1/results —— 获取结果

Query 参数:

参数类型默认值说明
idstring-扫描 ID;为空时返回最新一次扫描结果
keepboolfalse返回后是否在缓存中保留结果

响应(成功):

{ "id": "scan-12345", "type": "v1results", "response": { /* scan results object */ } }

响应(错误):

{ "id": "scan-12345", "type": "error", "response": "error message" }

响应(进行中):

{ "id": "scan-12345", "type": "busy", "response": "scanning in progress" }

源码层面的行为细节(见 requestshandler.go 的validateScanIDGetResults):

  • 在线(非 offline)模式下,id为空会被视为参数错误返回400;offline 模式下才回退到"最新用户扫描";
  • 请求的扫描仍在进行时返回type: busy
  • 结果文件不存在时返回204 No Content
  • 扫描执行失败时,服务会把错误明文写入FailedOutputDir,此时GetResults返回500response为真正的失败原因(ScanFailedError),而不是 JSON 解析错误(见 requestshandlerutils.go);
  • 默认情况下结果在返回后被删除(keep=false),但"最新扫描回退"场景刻意保留结果以防误删。

GET /v1/status —— 检查状态

适合轮询场景:只查状态,不拉取完整结果,开销更小。

Query 参数:

参数类型默认值说明
idstring-扫描 ID;为空时检查是否有任意扫描在进行

响应(进行中):

{ "id": "scan-12345", "type": "busy", "response": "scanning in progress" }

响应(完成):

{ "id": "scan-12345", "type": "notBusy", "response": "scanning completed" }

注意一个细节:id为空时,服务会先解析为"最新用户扫描",再判断其是否忙碌——这是为了避免/v1/metrics抓取覆盖 latestID 导致状态误报(见 requestshandler.go)。

DELETE /v1/results —— 删除缓存结果

Query 参数:

参数类型默认值说明
idstring-要删除的扫描 ID;为空时删除最新结果
allboolfalse删除全部缓存结果

all=true时清空OutputDirFailedOutputDir两个目录(见 requestshandler.go)。removeResultsFile在删除时不仅清理无扩展名的规范 JSON 文件,还会按json/junit/sarif/html/pdf/prometheus/yaml/csv/cyclonedx/spdx等打印器扩展名逐一清理伴生文件(见 requestshandlerutils.go)。


Request / Response Objects:请求体与响应对象详解

Trigger Scan Object(扫描请求体)

{ "format": "json", "excludedNamespaces": ["kube-system", "kube-public"], "includeNamespaces": ["production", "staging"], "useCachedArtifacts": false, "keepLocal": true, "targetType": "framework", "targetNames": ["nsa", "mitre"] }
字段类型说明
formatstring输出格式(默认:json
excludedNamespaces[]string扫描时要排除的命名空间
includeNamespaces[]string扫描时要包含的命名空间
useCachedArtifactsbool使用本地缓存的策略工件(离线模式)
keepLocalbool不把结果提交到后端(SaaS)
targetTypestring"framework""control"
targetNames[]string要扫描的框架/控制项名称

从 datastructuremethods.go 的ToScanInfo可以补充以下语义:

  • targetTypeframework时,targetNames中含"all"或空串会触发全框架扫描;targetTypecontrol时只扫描指定控制项;未知类型则回退为全量扫描;
  • useCachedArtifacts=true等价于让扫描从getter.DefaultLocalStore读取工件,避免每次扫描都联网下载策略;
  • 请求体还支持源码中实现的submit(显式提交结果到 SaaS)、hostScanner(启用主机扫描器)、scanObject(单资源扫描)、exceptions(内联异常策略,服务会将其临时落盘并在扫描后清理)等字段;
  • 请求体中的命名空间列表会被strings.Join为逗号分隔字符串后交给ScanInfo,与KS_EXCLUDE_NAMESPACES/KS_INCLUDE_NAMESPACES环境变量同样处理。

安全边界:account/accessKey会被忽略。这些端点无鉴权,因此服务永远不会从请求体中读取 Kubescape SaaS 身份——否则任何调用者都能把扫描结果(可能包含集群 Secret 与 RBAC 数据)重定向到其控制的账号。身份只来自服务端自身配置:KS_ACCOUNT_ID/KS_ACCESS_KEY,或启动时从凭据文件加载的 credentials(见 datastructuremethods.go 与 main.go 的loadAndSetCredentials)。请求体仍接受这些字段仅为向后兼容,实际不产生任何效果。

Response Object(统一响应对象)

{ "id": "scan-12345", "type": "v1results", "response": { /* payload */ } }
字段类型说明
idstring扫描标识
typestring响应类型(见下表)
responseany响应载荷

响应类型(Response Types):

类型说明
v1results扫描结果对象
busy扫描进行中
notBusy无扫描进行中
ready扫描完成,结果就绪
error发生错误

API Examples:可直接复制的实战命令

1. 基础异步扫描(三连:触发 → 轮询 → 拉结果)

# 1. 触发扫描 curl -X POST http://127.0.0.1:8080/v1/scan \ -H "Content-Type: application/json" \ -d '{"targetType": "framework", "targetNames": ["nsa"]}' # 2. 检查状态 curl http://127.0.0.1:8080/v1/status # 3. 获取结果 curl http://127.0.0.1:8080/v1/results -o results.json

2. 同步扫描(一次调用直接拿到结果)

curl -X POST "http://127.0.0.1:8080/v1/scan?wait=true" \ -H "Content-Type: application/json" \ -d '{"targetType": "framework", "targetNames": ["nsa"]}' \ -o results.json

同步模式非常适合 CI 门禁:wait=true会阻塞直到扫描完成并返回完整结果,此时 HTTP 请求可能持续数分钟——这正是 setup.go 中刻意不设置ReadTimeout/WriteTimeout的原因(只设置ReadHeaderTimeout: 10s防御 slowloris)。

3. 只扫描指定命名空间

curl -X POST http://127.0.0.1:8080/v1/scan \ -H "Content-Type: application/json" \ -d '{ "includeNamespaces": ["production"], "targetType": "framework", "targetNames": ["nsa", "mitre"] }'

4. 账号集成(在服务端配置,而非每次请求携带)

账号与访问密钥配置在服务端,而不是随请求发送(原因见上文安全边界说明):

# 在服务端 / Helm values 中配置 export KS_ACCOUNT_ID="YOUR-ACCOUNT-ID" export KS_ACCESS_KEY="YOUR-ACCESS-KEY"
curl -X POST http://127.0.0.1:8080/v1/scan \ -H "Content-Type: application/json" \ -d '{ "submit": true, "targetType": "framework", "targetNames": ["nsa"] }'

5. 删除全部缓存结果

curl -X DELETE "http://127.0.0.1:8080/v1/results?all=true"

6. 取消进行中的扫描

# 取消指定扫描(也支持不带 id 取消当前运行中的扫描) curl -X DELETE "http://127.0.0.1:8080/v1/scan?id=scan-12345"

7. 事件驱动:扫描完成回调

callback参数允许服务在扫描完成时向指定 URL POST 一个仅含idstatus的信号({"id":"...","status":"completed"|"failed"}),接收方需再通过GET /v1/results拉取数据。回调默认关闭(防止服务被当作任意出站请求发射器),需设置KS_CALLBACK_ENABLED=true或配置KS_CALLBACK_ALLOWED_CIDRS白名单才可用;投递为 at-least-once、best-effort,接收方应基于 ID 去重并保留轮询兜底(详见 requestshandlerutils.go)。


Environment Variables:完整环境变量参考

HTTP Handler 的所有行为都由环境变量控制。除 README 列出的变量外,下表同时整合了 main.go、setup.go 与 requestshandlerutils.go 中实际读取的全部变量:

变量说明示例
KS_ACCOUNT_ID每次扫描使用的 Kubescape SaaS 账号 IDxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
KS_ACCESS_KEY每次扫描使用的 Kubescape SaaS 访问密钥your-access-key
KS_EXCLUDE_NAMESPACES默认排除的命名空间(逗号分隔)kube-system,kube-public
KS_INCLUDE_NAMESPACES默认包含的命名空间(逗号分隔)production,staging
KS_FORMAT默认输出格式json
KS_FORMAT_VERSION输出格式版本v2
KS_LOGGER_NAMELogger 名称kubescape
KS_LOGGER_LEVEL日志级别info,debug,warning,error
KS_DOWNLOAD_ARTIFACTS每次扫描是否下载策略工件;false时从本地缓存加载true,false
KS_SCAN_QUEUE_CAPACITY活动扫描之后最多排队的扫描数(默认1010
KS_SCAN_REQUEST_MAX_BYTESPOST /v1/scan请求体最大字节数(默认1048576,即 1 MiB)1048576
KS_PPROF_ENABLED启用 pprof 调试服务(默认关闭;仅绑定回环地址)true,false
KS_PPROF_ADDR启用时 pprof 调试服务的绑定地址127.0.0.1:6060
KS_API_TOKEN/v1/*API 的 Bearer Token 鉴权(可选,默认关闭)。设置后,每个/v1/scan/v1/results/v1/status请求都必须携带Authorization: Bearer <token>,否则返回401;健康探针/livez/readyz与 OpenAPI 文档保持开放。若将:8080暴露到集群之外,建议设置随机值并启用 TLS(KS_CERT_FILE/KS_KEY_FILE)或使用 TLS 终结的 Ingressopenssl rand -hex 32生成
KS_PORTHTTP 监听端口(默认80808080
KS_CERT_FILE/KS_KEY_FILETLS 证书与私钥路径(须成对设置)/etc/tls/tls.crt
KS_OFFLINEtrue时强制离线模式(使用本地工件缓存)true
KS_CONTEXT集群上下文名称my-cluster
KS_KEEP_LOCAL不向 Kubescape SaaS 提交结果true
KS_SUBMIT显式开启结果提交(存在即生效,解析失败会告警并忽略)true
KS_REGO_PRINT打印 rego 规则false
KS_ENABLE_HOST_SCANNER启用主机扫描器true
KS_HOST_SCAN_YAML主机扫描 YAML 路径/path/to/host-scan.yaml
KS_CALLBACK_ENABLED开启扫描完成回调(默认关闭)true
KS_CALLBACK_ALLOWED_CIDRS回调目标 IP 白名单(逗号分隔 CIDR,配置后即视为开启回调)10.0.0.0/8
KS_SERVICE_DISCOVERY_FILE_PATH文件式服务发现路径(默认/etc/config/services.json),支持 sidecar 与私有集群注入后端端点/etc/config/services.json
KS_CREDENTIALS_SECRET_PATH凭据文件路径(默认/etc/credentials/etc/credentials

鉴权细节(源码佐证)bearerAuthMiddleware使用subtle.ConstantTimeCompare做常数时间比较以防时序侧信道,并接受大小写不敏感的Bearer方案(见 setup.go)。鉴权是"增量式加固":扫描 API 默认开放以保证集群内既有安装不因升级而中断,这与默认关闭的 pprof 不同(见 setup.go)。


Deployment Examples:两种典型落地方式

方式一:微服务部署(API 驱动扫描)

在集群内将 Kubescape 部署为微服务,通过 REST API 驱动扫描,是 CI/CD 集成、自定义仪表盘和自动化编排的基础形态。

快速部署:

kubectl apply -f httphandler/examples/microservice/ks-deployment.yaml

部署清单 ks-deployment.yaml 已经内置了完整资源:kubescape命名空间、kubescape-discoveryServiceAccount、具有get/list/describe全资源权限的 ClusterRole 与绑定、NodePort 类型 Service,以及 Deployment(quay.io/kubescape/kubescape:latest,启动命令ksserver)。其中值得关注的默认配置:

  • terminationGracePeriodSeconds: 35,与优雅停机协议匹配;
  • /livez/readyz健康探针(每 3 秒一次,初始延迟 3 秒);
  • KS_ENABLE_HOST_SCANNER=true启用主机扫描器;
  • KS_DOWNLOAD_ARTIFACTS=true每次扫描都下载最新策略工件。

验证与访问:

# 检查 Pod 状态 kubectl get pods -l app=kubescape # 检查 Service kubectl get svc kubescape # 本地端口转发访问 kubectl port-forward svc/kubescape 8080:8080 # 或获取 LoadBalancer 外部 IP(若修改 serviceType) kubectl get svc kubescape -o jsonpath='{.status.loadBalancer.ingress[0].ip}'

部署前应结合集群实际调整:serviceType(ClusterIP / NodePort / LoadBalancer)、命名空间、资源配额、ServiceAccount 权限。完整部署指南见 httphandler/examples/microservice/README.md。

典型 CI 门禁脚本(来自微服务示例文档):同步扫描后提取合规分数,低于阈值即让流水线失败:

#!/bin/bash RESULT=$(curl -s --header "Content-Type: application/json" \ --request POST \ --data '{"targetType": "framework", "targetNames": ["nsa"]}' \ "http://kubescape:8080/v1/scan?wait=true") # 提取合规分数 SCORE=$(echo $RESULT | jq '.response.summaryDetails.complianceScore') # 分数低于阈值则失败 if (( $(echo "$SCORE < 80" | bc -l) )); then echo "Compliance score $SCORE is below threshold (80)" exit 1 fi

定时扫描 CronJob:每 6 小时触发一次 NSA 与 MITRE 框架扫描:

apiVersion: batch/v1 kind: CronJob metadata: name: kubescape-scheduled-scan spec: schedule: "0 */6 * * *" # 每 6 小时 jobTemplate: spec: template: spec: containers: - name: scanner image: curlimages/curl command: - /bin/sh - -c - | curl -X POST http://kubescape:8080/v1/scan \ -H "Content-Type: application/json" \ -d '{"targetType": "framework", "targetNames": ["nsa", "mitre"]}' restartPolicy: OnFailure

大集群超时建议:异步触发 + 轮询,而不是长阻塞的同步调用:

# 触发扫描(立即返回) curl -X POST http://127.0.0.1:8080/v1/scan \ -H "Content-Type: application/json" \ -d '{"targetType": "framework", "targetNames": ["nsa"]}' # 轮询状态 while true; do STATUS=$(curl -s http://127.0.0.1:8080/v1/status | jq -r '.type') if [ "$STATUS" != "busy" ]; then break fi sleep 10 done # 获取结果 curl http://127.0.0.1:8080/v1/results -o results.json

方式二:Prometheus 集成(指标暴露)

将 Kubescape 指标暴露给 Prometheus 抓取,可把安全态势接入现有可观测体系。指标端点挂载在/v1/metrics(带鉴权与 OpenTelemetry 中间件,见 setup.go),实现在 prometheus.go。抓取方式与 ServiceMonitor 配置请参考 httphandler/examples/prometheus/README.md。


Debugging:调试与性能剖析

开启调试日志

export KS_LOGGER_LEVEL=debug

源码中日志级别通过logger.L().SetLevel(...)应用;级别字符串无效时回退到debug并记录错误(见 main.go)。

pprof 性能剖析

pprof 调试服务默认关闭,且只绑定127.0.0.1,因此只能从 Pod 自身的网络命名空间内访问,无法从网络直接触达(这是刻意的安全设计——历史上它曾因日志级别为 debug 而默认暴露未鉴权的剖析端点,见 setup.go 的注释)。启用后通过kubectl port-forward访问:

export KS_PPROF_ENABLED=true # 然后,例如:kubectl port-forward <pod> 6060:6060 # 堆剖析 go tool pprof http://127.0.0.1:6060/debug/pprof/heap # CPU 剖析(采样 30 秒) go tool pprof http://127.0.0.1:6060/debug/pprof/profile?seconds=30 # Goroutine 剖析 go tool pprof http://127.0.0.1:6060/debug/pprof/goroutine

6060端口与 Pod 内 sidecar 冲突,可用KS_PPROF_ADDR修改绑定地址(如KS_PPROF_ADDR=127.0.0.1:6061)。绑定到非回环地址属于刻意的显式选择,仅在可信网络上才应这样做。pprof 处理器注册在独立 mux 上而非http.DefaultServeMux,保证该依赖自包含且由编译器强制(见 setup.go)。


相关文档

  • CLI Reference
  • Architecture
  • Getting Started Guide
  • Troubleshooting
  • Microservice Deployment Guide
  • Prometheus Integration Guide

小结

Kubescape HTTP Handler 将 Kubernetes 安全扫描包装为一组语义清晰、背压可控的 REST API,配合优雅停机、可选的 Bearer 鉴权、pprof 剖析与完善的部署清单,足以支撑从 CI 门禁到定时巡检的各类自动化场景。理解其请求/响应契约(尤其busy/notBusy/v1results/error四种响应类型)、环境变量矩阵与"账号身份只来自服务端配置"的安全边界,是把它正确接入生产环境的关键。若需从源码层面继续深入,可依次阅读 httphandler/listener/setup.go(路由与停机)、httphandler/handlerequests/v1/requestshandler.go(四端点实现)与 httphandler/handlerequests/v1/requestshandlerutils.go(扫描执行与回调投递)。

【免费下载链接】kubescapeKubescape is an open-source Kubernetes security platform for your IDE, CI/CD pipelines, and clusters. It includes risk analysis, security, compliance, and misconfiguration scanning, saving Kubernetes users and administrators precious time, effort, and resources.项目地址: https://gitcode.com/GitHub_Trending/ku/kubescape

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询