checkov를 reviewdog에 대응시킨 이야기

타이틀대로입니다만 checkov 는 출력 결과가 reviewdog 의 입력 형식에 대응하고 있지 않기 때문에, 그대로와 조합해 사용할 수 없습니다. 그래서 다음과 같이 스크립트를 써서 대응했습니다.

하고 싶은 일



terraform을 관리하는 Github의 리포지토리에서 PR을 작성했을 때에 terraform 코드의 정적 해석을 실시해, 문제가 있으면 PR에 코멘트를 투고해 준다, 라고 하는 형태를 목표로 합니다.

여기서는 checkov 에서 정적 해석의 결과를 표준 출력하고 reviewdog 가 파이프로 결과를 받고 PR 에 코멘트합니다.

실제로는 다음과 같은 표시가 됩니다.



각 도구에 대해


  • checkov
  • reviewdog

  • 구현



    우선 reviewdog 가 받을 수 있는 형식에 대해서는 이하에 기재가 있습니다.

    그 가운데 이번은 errorformat 라고 하는 형식에 맞추어 가려고 생각합니다. errorformat 는 행 단위로 지정한 형식의 표준 출력을 받을 수 있습니다.

    예를 들어 표준 출력이 {file}:{line number}:{column number}: {message} 와 같은 형식이라고 하면 errorformat%f:%l:%c: %m 가 됩니다.
    $ golint ./...
    comment_iowriter.go:11:6: exported type CommentWriter should have comment or be unexported
    $ golint ./... | reviewdog -efm="%f:%l:%c: %m" -diff="git diff FETCH_HEAD"
    

    출력 형식 변환


    reviewdog 에서 어떻게 받는지 알았으므로 다음은 checkov 의 출력을 조정합니다.
    checkov의 출력 형식은 아래에 설명되어 있습니다.

    이러한 출력 형식 중 하나를 선택하여 reviewdog의 수신 형식에 맞게 변경합니다. 선택 가능한지로 형식 변경이 가장 쉬운 것은 JSON이므로, 일단 JSON 형식으로 출력한 결과를 파이프로 받아, 자작의 스크립트를 통해 errorformat 로 변환해 갑니다.

    명령은 다음과 같습니다.
    $ checkov -d . -o json | python3 parse.py
    

    여기에서는 python 를 이용하고 있습니다만 언어는 무엇이든 상관없습니다.
    python 의 코드는 다음과 같습니다.
    (상당히 혼잡하게 쓰고 있기 때문에 세세한 버그가 있을 것 같습니다만...)

    파 르세. py


    import json, sys
    
    def main():
        data = json.load(sys.stdin)
        failed_checks = data["results"].get("failed_checks")
        if failed_checks is None:
            exit(0)
    
        for failed_check in failed_checks:
            file_name = failed_check["file_path"].replace('/', '')
            line_number = failed_check["file_line_range"][0]
            error_message = failed_check["check_name"]
            print('{}:{}: {}'.format(file_name, line_number, error_message))
    
        exit(1)
    
    
    if __name__ == "__main__":
        main()
    

    테스트 실행



    여기까지 할 수 있으면 로컬 환경에서 실제로 tf 파일을 작성해 테스트해 보겠습니다.

    g 집. tf


    resource "google_service_account" "default" {
      account_id   = "service-account-id"
      display_name = "Service Account"
      project      = var.project_id
    }
    
    resource "google_container_cluster" "primary" {
      name     = "my-gke-cluster"
      location = var.region
    
      # We can't create a cluster with no node pool defined, but we want to only use
      # separately managed node pools. So we create the smallest possible default
      # node pool and immediately delete it.
      remove_default_node_pool = true
      initial_node_count       = 1
    }
    
    resource "google_container_node_pool" "primary_preemptible_nodes" {
      name       = "my-node-pool"
      location   = var.region
      cluster    = google_container_cluster.primary.name
      node_count = 1
    
      node_config {
        preemptible  = true
        machine_type = "e2-medium"
    
        # Google recommends custom service accounts that have cloud-platform scope and permissions granted via IAM Roles.
        service_account = google_service_account.default.email
        oauth_scopes = [
          "https://www.googleapis.com/auth/cloud-platform"
        ]
      }
    }
    

    로컬에서 테스트 실행하면 다음과 같은 표시가 됩니다.
    $ checkov -d . -o json | python3 parse.py | reviewdog -efm="%f:%l: %m" -name="checkov" -reporter=local -filter-mode=nofilter
    gke.tf:7: Ensure PodSecurityPolicy controller is enabled on the Kubernetes Engine Clusters
    gke.tf:7: Ensure Kubernetes Cluster is created with Private cluster enabled
    gke.tf:7: Ensure Kubernetes Cluster is created with Alias IP ranges enabled
    gke.tf:7: Ensure master authorized networks is set to enabled in GKE clusters
    gke.tf:7: Ensure Kubernetes Clusters are configured with Labels
    gke.tf:7: Ensure a client certificate is used by clients to authenticate to Kubernetes Engine Clusters
    gke.tf:7: Ensure GKE basic auth is disabled
    gke.tf:7: Ensure Network Policy is enabled on Kubernetes Engine Clusters
    gke.tf:18: Ensure 'Automatic node upgrade' is enabled for Kubernetes Clusters
    gke.tf:18: Ensure Container-Optimized OS (cos) is used for Kubernetes Engine Clusters Node image
    gke.tf:18: Ensure 'Automatic node repair' is enabled for Kubernetes Clusters
    

    후에는 Github Actions 의 yaml에 설정 파일로서 써 주면 CI로서 이용할 수 있습니다.

    이상, 「checkov를 reviewdog에 대응시킨 이야기」였습니다.

    좋은 웹페이지 즐겨찾기