• Harbor webhook从原理到构建


    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。
      在这里插入图片描述
    1. 在全局配置中开启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() // listen and serve on 0.0.0.0:8080
    }
    
    • 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,
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 因此,如果需要进行相关的阻断操作,我们需要通过harbor的api来进行相关操作,其实上面的authorization库就是对docker api的封装,当时我还issue 了 harbor webhook的维护者
      在这里插入图片描述
      在这里插入图片描述
    • 所以,总结一下,首先harbor 将操作请求转成json发送给webhook endpoint,然后enpoint进行相关检查,最后,enpoint可以根据检查结果调用harbor api进行相关操作,关于操作这一部分我还没有考虑好,所以没有示例,待续。。。
  • 相关阅读:
    No module ‘xformers‘. Proceeding without it.
    8. 将各层的任务调度起来(使用海豚调度)
    Java 计算两个字符串的相似度 CosineSimilarity实现
    发票识别神器推荐,告别繁琐手动录入,轻松管理财务
    【C++ 结构体的构造函数使用】
    序列化与反序列化及不同序列化方式的性能对比
    python 替换字符串中 windows非法字符 到对应的 全角合法字符
    决策树算法的一点基础知识补充
    nginx参数调优能提升多少性能
    二手闲置物品交易数据快照
  • 原文地址:https://blog.csdn.net/huzai9527/article/details/125488842