1. 什么是webhook
- 以下内容来自wiki


- 总结一下: webhook可以看作是一个钩子,例如当我们从harbor上拉取镜像时,harbor会将请求转换成一个可读的JSON格式,并且转发给webhook endpoint(钩子的实现)进行处理。下面就是harbor对pull请求的json概括,包括了镜像的具体信息,用户的具体信息以及项目信息等等。当endpoint拿到这些信息之后,就可以根据信息进行相关的操作。例如对pull的镜像进行恶意文件检查,如果检查不通过就阻止pull的操作。
{
"type":"PULL_ARTIFACT",
"occur_at":1656324256,
"operator":"admin",
"event_data":{
"resources":[
{
"digest":"sha256:f841a2abd0422364ec94bb633a56707a38c330179f2bbccebd95f9aff4a36808",
"tag":"sha256:f841a2abd0422364ec94bb633a56707a38c330179f2bbccebd95f9aff4a36808",
"resource_url":"10.9.33.98/library/java@sha256:f841a2abd0422364ec94bb633a56707a38c330179f2bbccebd95f9aff4a36808"
}
],
"repository":{
"date_created":1656315229,
"name":"java",
"namespace":"library",
"repo_full_name":"library/java",
"repo_type":"private"
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
2. harbor如何使用webhook
- 官方文档

- 就跟我上面总结的一样,webhook endpoint 根据 harbor 传过来的信息对镜像进行相关的扫描操作,最后根据扫描结果执行相关操作。
3. 如何开启harbor webhook
- harbor配置,在你的项目中选择webhook,直接配置就行,这里要注意endpoint地址是我们自己实现的一个httpserver。

- 在全局配置中开启webhook检测,在配置管理中的系统设置中

4. 构建一个webhook endpoint
- 这里的webhook endpoint实际上就是一个httpserver,这里我们使用gin构建一个httpserver,并将harbor穿过来的信息打印出来,这里只要看api即可
func main() {
r := gin.Default()
r.POST("/push_image", func(c *gin.Context) {
postData := &PushImage{}
data, _ := ioutil.ReadAll(c.Request.Body)
fmt.Println("string => ", string(data))
if err := json.Unmarshal(data, &postData); err != nil {
fmt.Println(err)
}
fmt.Printf("ctx.Request.body: %s", postData.EventData.Resources[0].Digest)
})
r.POST("/pullimage", func(c *gin.Context) {
postData := &PullImage{}
data, _ := ioutil.ReadAll(c.Request.Body)
if err := json.Unmarshal(data, &postData); err != nil {
fmt.Println(err)
}
fmt.Println("api TYPE => ", postData.Type)
fmt.Println("iamge name => ", postData.EventData.Resources[0].ResourceURL)
})
r.POST("/api", func(c *gin.Context) {
var body map[string]interface{}
data, _ := ioutil.ReadAll(c.Request.Body)
if err := json.Unmarshal(data, &body); err != nil {
fmt.Println(err)
}
fmt.Println("body data => ", string(data))
for k, v := range c.Request.Header {
fmt.Println(k, v)
}
})
r.Run()
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 接下来启动server并从harbor中pull镜像,我们可以在server终端看到如下的信息

- 当然,我们也可以将请求的json转成go中的对象,然后从对象中获取信息
type PushImage struct {
Type string `json:"type"`
OccurAt int `json:"occur_at"`
Operator string `json:"operator"`
EventData struct {
Resources []struct {
Digest string `json:"digest"`
Tag string `json:"tag"`
ResourceURL string `json:"resource_url"`
} `json:"resources"`
Repository struct {
DateCreated int `json:"date_created"`
Name string `json:"name"`
Namespace string `json:"namespace"`
RepoFullName string `json:"repo_full_name"`
RepoType string `json:"repo_type"`
} `json:"repository"`
} `json:"event_data"`
}
type PullImage struct {
Type string `json:"type"`
OccurAt int `json:"occur_at"`
Operator string `json:"operator"`
EventData struct {
Resources []struct {
Digest string `json:"digest"`
Tag string `json:"tag"`
ResourceURL string `json:"resource_url"`
} `json:"resources"`
Repository struct {
DateCreated int `json:"date_created"`
Name string `json:"name"`
Namespace string `json:"namespace"`
RepoFullName string `json:"repo_full_name"`
RepoType string `json:"repo_type"`
} `json:"repository"`
} `json:"event_data"`
}

- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38

5. endpoint的结果如何反馈给用户
- 不同于
docker plugin, harbor webhook没有相关的authorization库,能够根绝扫描结果直接返回相关结果(允许/禁止),就像下面这样
func defaultAuthResponse() *authorization.Response {
return &authorization.Response{
Allow: true,
}
}
- 因此,如果需要进行相关的阻断操作,我们需要通过harbor的api来进行相关操作,其实上面的authorization库就是对docker api的封装,当时我还issue 了 harbor webhook的维护者


- 所以,总结一下,首先harbor 将操作请求转成json发送给webhook endpoint,然后enpoint进行相关检查,最后,enpoint可以根据检查结果调用harbor api进行相关操作,关于操作这一部分我还没有考虑好,所以没有示例,待续。。。