👉:VeinMind Tools 正式发布 v2.0 版本 | 支持镜像/容器漏洞扫描等多种插件
👉:VeinMind Tools v2.0 技术分享 |(二):容器逃逸风险检测
👉:VeinMind Tools v2.0 技术分享 |(一):容器/镜像漏洞扫描
问脉商业版是由长亭牧云团队倾力打造的一款云原生安全平台。首推零侵入探针,采用 Agentless 方案进行部署,保证业务节点实现严格意义上的零侵入检测,让用户能够轻装上阵,轻松解决云原生安全问题。
问脉 Tools 社区版是长亭牧云团队孵化的一款开源容器安全检测工具集。目前已支持镜像/容器漏洞、逃逸风险、恶意文件、后门、敏感信息、弱口令、资产识别等扫描功能。
🌒 技术思路
🌓 小试牛刀
🌔 如何使用报告器功能?
🌕 自定义插件如何快速接入报告器?
VeinMind-Tools 最初设计时,采用了插件系统的模式,目的是实现在尽可能多的场景下,复用公共功能部分的代码,让工具开发者最大程度减少非关键业务的代码编写。
举个例子,在 v1.0 版本中,我们提供了本地扫描的整体逻辑,插件仅需要通过注册命令服务,即可生成一个扫描本地镜像/本地容器的 cli 工具, 如:
func scanImage(ctx context.Context, api.Image) {
// do some things...
}scanCommand.AddCommand(
cmd.MapImageCommand(scanImageCommand, scanImage)
)
这样,使用时只需关心具体的扫描逻辑,也就是scanImage
函数内如何对镜像进行操作即可,让开发者的注意力集中在安全扫描能力的建设上去。
同样,为了实现在各种场景下被复用,我们在 runner
内扩展了扫描对象,这样设计不仅支持扫描本地镜像,还可以直接扫描远程仓库内的镜像信息;对于 IaC 文件配置,同样支持直接扫描 git 仓库、kubernetes 集群。对于插件来说,无需做出任何修改,解决了每个插件需要单独做认证、下载、存储、删除等等一系列重复行为的问题。
基于以上思路,在 v2.0 版本中,我们期望能帮助开发者和使用者做更多的事情。
我们发现,各插件为了满足独立使用,都实现了一个report
包用于数据的展示和输出。于是,我们提供了一个报告器模块,用户只需将安全事件进行上报,无需再多考虑输出模式和结果的渲染。进一步让开发者将注意力聚焦到 “如何扫描镜像” 的业务上。
要实现一个报告器,首先会面临两个场景:
插件独立运行时,报告器应该独立输出。 插件被 runner 宿主调用时,报告器应该汇总所有插件的事件结果,统一输出。
对于场景1,我们可以提供一个对象来存储所有被上报的安全事件,并监听至程序运行结束时,运行输出函数,根据用户指定的输出格式参数,进行事件渲染。
由此,我们实现了MapReportCmd
方法,通过该方法将报告器相关的操作注入到cmd
指令的调用流程,伪代码如下:
func MapReportCmd(c *cmd.Command, service *Service){
// inject flags
c.Flags().StringP("format", "f" ...)
c.Flags().BoolP("verbose", "v" ...)
c.PreRun = func() {
// init service struct...
}
if !libService.Hosted() {
c.PostRun = func(c *cmd.Command, args []string) {
// do output ...
}
}
}
可以发现,我们通过sdk
的Hosted()
方法,来判断插件在运行时是否为宿主调用。以此来解决当插件被宿主调用时会被重复输出的问题。
实现了独立运行的输出后,报告器还需要解决场景2:宿主统一输出。
我们通过sdk
的服务机制,将上报事件的服务注册到Registry
中进行索引,为所有运行的插件提供 report.Service
服务,由此,插件与宿主之间即可进行安全事件的上报通信。
当我们独立使用插件进行扫描时,可以发现,所有插件均包含了 -f/--format
参数(即使插件自身代码内没有指定接收该参数),此时可以通过指定该参数快速输出想要的报告格式:(目前支持 cli/html/json)
./veinmind-plugins scan image xxxx -f cli
扫描结果将会输出在控制台。
报告器还可以指定多个输出格式,同时生成多份报表:
./veinmind-plugins scan image xxxx -f cli,html,json
对于插件的开发者,目前仅需要通过MapRerportCmd
函数即可帮助插件快速生成Service
实例,并将格式化参数注入到插件的命令中。通过该函数,插件将不再需要关心任何与输出相关的问题。
Talk is easy, show me the code.
import(
"github.com/chaitin/veinmind-common-go/service/report"
"github.com/chaitin/veinmind-common-go/service/report/event"
"github.com/chaitin/veinmind-common-go/service/report/service"
)
var (
reportService = &report.Service{}
rootCommand = &cmd.Command{}
scanCommand = &cmd.Command{
Use: "scan",
Short: "scan mode",
}
scanImageCommand = &cmd.Command{
Use: "image",
Short: "scan mode",
}
)func scanImage(c *cmd.Command, image api.Image) error {
// ...do some scan
evt := &event.Event{}
// now you only need report evt to service,
// report.Service will render
err = reportService.Client.Report(evt)
}
func init() {
rootCommand.AddCommand(scanCommand)
// use MapReportCmd inject preRun/postRun
// which init reportSerivce and reportOoutput at end
scanCommand.AddCommand(report.MapReportCmd(
cmd.MapImageCommand(scanImageCommand, scanImage),
reportService
))
}