可信云原生软件供应链(Software Supply Chain) - Grafeas项目调研

>> 99%的软件实现都是CRUD, 但有些CRUD还是能玩出花来的。

背景

可信云原生软件供应链(Software Supply Chain) - Grafeas项目调研

Grafeas, 希腊语中Scribe(抄写员)的意思,是Google在2017年联合多家厂商发起的开源项目,希望可以定义一套统一的的方法来审计和管理软件供应链,具体的背景可以阅读「安全交付:GCP 的安全软件供应链」

可信云原生软件供应链(Software Supply Chain) - Grafeas项目调研

如上图所示,所谓的软件供应链,可以大抵等同于从源码到发布物的整条流水线。感谢区块链的科普,大家对于可信供应链的概念倒也不算陌生,一盒在超市里售卖的鸡蛋通过区块链可以回溯到下蛋的母鸡,然后孵出母鸡的蛋,然后找到下了那个蛋的母鸡,如此循环,一直到堆栈溢出...

言归正传,首先Grafeas不是区块链技术,官方称为「A Component Metadata API」。解释之前先描述一个实际例子, 笔者在Google的时候也维护着MySQL和PostgreSQL的分支以及其包含的一众第三方依赖,时不时的就会被系统分配到一个bug,说某个CVE影响到了当前代码仓库里的MySQL版本,要去修复。可以发现系统要做到这样的通知需要一些信息:

  • 代码仓库里的MySQL版本号
  • 针对当前MySQL的CVE信息
  • 以及当前MySQL的维护者

基本上各公司的安全扫描系统都会存类似信息,没什么太特别的,只是说每家都搞了一套自己的罢了,Google内部一套,JFrog一套,阿里也是自己一套,诸如此类。这时候,还是Google先站了出来,继续奉行标准先行,API先行的策略,推出了Grafeas想来统一一下这套数据模型。这个项目起于2017年, 3年过去了,还是不温不火的状态,Github星星数还没上千,当然这和软件供应链本身就是个小众领域也有关系。不过笔者相信软件供应链,尤其是可信安全这块接下来会受到越来越多的关注,有这么几个原因 :

  • 随着持续集成以及微服务成为主流,我们发布软件的频率极速上升。
  • 随着应用越来越复杂,以及开源的趋势,一个应用依赖了越来越多的三方依赖。
  • 随着应用基本功能的日趋完善同质化,诸如安全/合规类的特性会成为竞争关键点。

代码走读

模型和API

至于Grafeas这个项目,我个人还是比较看好的,主要是因为它的数据建模比较合理。Grafeas里最核心的两个概念叫做NoteOccurence, 其中Note记录了某种分析类型的具体实例,比如某一个具体的CVE就可以作为Vulnerability分析类型的一个实例。而Occurrence记录了针对某一个具体Note实例,在某个特定资源上发现的情况。拿之前MySQL的例子来说,如果MySQL某个版本曝出了某个CVE,这个CVE就是一个Note实例。而基于这个MySQL版本所打的镜像资源就会出现一个Occurrence,记录这个镜像有这么一个CVE。

目前Note的类型有这么些

oneof type {

   // A note describing a package vulnerability.

   grafeas.v1.VulnerabilityNote vulnerability = 10;

   // A note describing build provenance for a verifiable build.

   grafeas.v1.BuildNote build = 11;

   // A note describing a base image.

   grafeas.v1.ImageNote image = 12;

   // A note describing a package hosted by various package managers.

   grafeas.v1.PackageNote package = 13;

   // A note describing something that can be deployed.

   grafeas.v1.DeploymentNote deployment = 14;

   // A note describing the initial analysis of a resource.

   grafeas.v1.DiscoveryNote discovery = 15;

   // A note describing an attestation role.

   grafeas.v1.AttestationNote attestation = 16;

   // A note describing available package upgrades.

   grafeas.v1.UpgradeNote upgrade = 17;

 }

Occurence也有与之对应的

 // Required. Immutable. Describes the details of the note kind found on this

 // resource.

 oneof details {

   // Describes a security vulnerability.

   grafeas.v1.VulnerabilityOccurrence vulnerability = 8;

   // Describes a verifiable build.

   grafeas.v1.BuildOccurrence build = 9;

   // Describes how this resource derives from the basis in the associated

   // note.

   grafeas.v1.ImageOccurrence image = 10;

   // Describes the installation of a package on the linked resource.

   grafeas.v1.PackageOccurrence package = 11;

   // Describes the deployment of an artifact on a runtime.

   grafeas.v1.DeploymentOccurrence deployment = 12;

   // Describes when a resource was discovered.

   grafeas.v1.DiscoveryOccurrence discovery = 13;

   // Describes an attestation of an artifact.

   grafeas.v1.AttestationOccurrence attestation = 14;

   // Describes an available package upgrade on the linked resource.

   grafeas.v1.UpgradeOccurrence upgrade = 15;

 }

Occurrence中这样把资源和Note关联起来

 // Required. Immutable. A URI that represents the resource for which the

 // occurrence applies. For example,

 // `https://gcr.io/project/image@sha256:123abc` for a Docker image.

 string resource_uri = 2;

 // Required. Immutable. The analysis note associated with this occurrence, in

 // the form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`. This field can be

 // used as a filter in list requests.

 string note_name = 3;

 // Output only. This explicitly denotes which of the occurrence details are

 // specified. This field can be used as a filter in list requests.

 grafeas.v1.NoteKind kind = 4;

如果要继续探寻模型,寻着上面这些一路看下去就可以。目前的API版本是v1, 关键的数据模型相对于之前v1beta1增加了Upgrade这个类别,总体上模型比较稳定,体现了Google一贯在数据建模上的老道,这也来源于工程实践的积累。

除了最核心的建模之外,核心的Grafeas API设计也比较严谨,虽然只是普通的CRUD,但也是教科书般的grpc接口设计

  1. 命名的规范一致性,Get/List/Delete/Update/Create/BatchCreate, 其中Get/Delete/Update/Create针对单条操作用的是名词的单数形式,List/BatchCreate针对多条操作用的是复数形式。
  2. enum和anyof type的配对使用分别体现在Note以及Occurrence的枚举中。
  3. List接口采用page_token的形式。
  4. Update接口使用google.protobuf.FieldMask。

还有一个值得一提的是List请求里的filter字段,用的也是Google自己开源的Common Expression Language(CEL)。不过目前代码里还没有真正实现filter的功能,在storage这层是被无视掉的。

一些可改进点

整体的代码结构比较简单,虽然项目是2017年开始的,但是开发活跃度不是很高,主要还是几个Google Cloud的工程师在参与。

API

可以探讨的一个点是Grafeas服务本身是个monolithic service,包含了note和occurrence两个服务,comment中其实已经说:

// Analysis results are stored as a series of occurrences. An `Occurrence`

// contains information about a specific analysis instance on a resource. An

// occurrence refers to a `Note`. A note contains details describing the

// analysis and is generally stored in a separate project, called a `Provider`.

// Multiple occurrences can refer to the same note.

分离的话可以有更好的separation of concerns。还有在运行态,occurrence无论从调用频次还是重要性应该都高于note。比如在每次应用准入的时候,必然会调用occurrence接口校验应用镜像是否符合规范,显然如果occurrence服务这个时候挂了,要么应用无法启动,要么服务降级,暴露风险到线上。

功能

除了上文提到的filter,还有不少功能没有实现,比如看初始化grpcServer的地方,Auth, Filter, Logger均没有实现

 g := grafeas.API{

 Storage:           *db,

 Auth:              &grafeas.NoOpAuth{},

 Filter:            &grafeas.NoOpFilter{},

 Logger:            &grafeas.NoOpLogger{},

 EnforceValidation: true,

}

持久层

如前所示,持久层做了一层Storage接口的抽象,目前实现了基于内存的memstore, 基于BoltDB的embeddedstore, 以及基于PostgreSQL的pgsqlstore。从目前的代码看,storage的整体节奏有点问题,一方面embeddedstore如果选择sqlite的话,应该可以在写的时候做到和pgsqlstore更多的代码复用,抽象出更好的针对关系数据库的接口,方便对接mysql/oceanbase这些。另一方面,选择PostgreSQL,其实是不错的选择,但是没有利用上PostgreSQL的独特功能,可能有兼容性考虑,但还是有暴殄天物之感,例如:

  • 直接是把Note/Occurrenence序列化下存的, 没有使用PostgreSQL强大的JSON功能。然后使用了Text而不是二进制类型bytea。
  • 既然是存了个Blob, 相应的也就无法用到PostgreSQL的ENUM类型。
  • 也没有选择放在单独的schema里面,目前几张表名projects/notes/occurrences/operations还是容易引起名字冲突的。

当然因为模型比较简单,storage层对接个区块链应该也不是太难的问题。

扩展性

毕竟是Google的作品,还是带着Google的烙印,例如project应该就是源自Google Cloud的project概念,目前的deployment note也只支持Google Cloud的GKE和App Engine Flex。

整个项目在扩展性方面还没有太好的规划,比如没有引入any.proto以及配套的插件体系,使得在添加Note类型时需要直接修改核心代码。不过这个更可能是项目本身的克制,还在确定稳定的核心模型。另外还有一个核心扩展点在于提供Analysis的框架,帮助vendor可以更好把各种notes/occurrences集成进来。

集成

目前看到大厂里,Grafeas只集成进了Google Cloud自家的Container Analysis以及JFrog XRay里。像通过插件机制集成到Jenkins里,集成到Gitlab里还停留在Feature Request阶段。

总结

总的来说Grafeas这个项目还有不少工作要做,但是对于最核心的部分,软件供应链建模以及相对应的API,项目设计得比较妥当,担得上「A Component Metadata API」这个称号。这个感觉类似于Google在CI/CD云原生领域的Tekton项目,同样是靠出色的建模脱颖而出。而且有意思的是这两个项目都出自Google,而不是垂直行业里的老大JFrog/Jenkins,反而是等Google推出后,JFrog/Jenkins不约而同地加入了Google的Grafeas/Tekton项目。

好了,单是把各种Notes和Occurrence存起来还只是可信软件供应链里的一部分,另一部分则是在于怎么样使用这些信息,比如作为软件部署时的准入规则,这就要说到随Grafeas应运而生的Kritis(希腊语Judge)项目了,下回分解。

可信云原生软件供应链(Software Supply Chain) - Grafeas项目调研

上一篇:分析了thoughtworks三期的技术雷达


下一篇:阿里云Code已升级为新版Codeup,更大容量、更多功能还免费的新版Codeup真香