天天看點

可信雲原生軟體供應鍊(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裡最核心的兩個概念叫做

Note

Occurence

, 其中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項目調研背景代碼走讀總結

繼續閱讀