>> 99%的軟體實作都是CRUD, 但有些CRUD還是能玩出花來的。
背景

Grafeas, 希臘語中Scribe(抄寫員)的意思,是Google在2017年聯合多家廠商發起的開源項目,希望可以定義一套統一的的方法來審計和管理軟體供應鍊,具體的背景可以閱讀
「安全傳遞:GCP 的安全軟體供應鍊」如上圖所示,所謂的軟體供應鍊,可以大抵等同于從源碼到釋出物的整條流水線。感謝區塊鍊的科普,大家對于可信供應鍊的概念倒也不算陌生,一盒在超市裡售賣的雞蛋通過區塊鍊可以回溯到下蛋的母雞,然後孵出母雞的蛋,然後找到下了那個蛋的母雞,如此循環,一直到堆棧溢出...
言歸正傳,首先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接口設計
- 命名的規範一緻性,Get/List/Delete/Update/Create/BatchCreate, 其中Get/Delete/Update/Create針對單條操作用的是名詞的單數形式,List/BatchCreate針對多條操作用的是複數形式。
- enum和anyof type的配對使用分别展現在Note以及Occurrence的枚舉中。
- List接口采用page_token的形式。
- 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)項目了,下回分解。