Devops

Tekton 사용해보기 (4) GCP Terraform 인프라를 Validation하는 파이프라인 구축하기

Seungwoo Lee 2022. 12. 31. 17:49

본 포스팅은 "Tekton 사용해보기" 시리즈의 네 번째 글입니다.

 

해당 시리즈의 첫 번째 글은 Tekton 사용해보기 (1) Tekton으로 쿠버네티스 CI/CD 파이프라인을 구성해보자 에서 볼 수 있습니다. 

해당 시리즈의 두 번째 글은 Tekton 사용해보기 (2) Tekton으로 인프라를 자동 배포하는 Terraform Pipeline을 만들어보자 에서 볼 수 있습니다.

해당 시리즈의 세 번째 글은 Tekton 사용해보기 (3) Tekton Dashboard에 OIDC를 기반으로 RBAC 적용하기 에서 볼 수 있습니다.

 

이번 포스팅에서는 Tekton에서 Google Cloud Platform의 인프라를 Terraform으로 배포할 시 배포 코드에 대한 정책 검사, 즉 Validation을 수행하는 파이프라인을 구성해보겠습니다. 

 

Terraform 코드에 대한 Validation이란 테라폼 코드로 인프라를 배포하기 전 단계에서 코드가 정책을 준수하는지 검사한 후에 배포를 진행하는 것을 말합니다.

 

Validation을 배포 프로세스에 포함시키면 Terrform 코드로 인해 배포되는 인프라가 조직이 세운 정책을 준수하게 할 수 있기 때문에 일종의 정책 가드레일을 세울 수 있다는 이점이 존재합니다. 

 

 

정책 가드레일을 세워놓으면 인프라 배포 조직원이 정책을 위반할 수 있는 리소스를 배포하고자 해도 이를 자동적으로 방지하기 때문에 정책 위반의 사전 예방책으로써 기능을 할 수 있습니다.

 

이러한 이점이 존재하는 Validation 프로세스를 Tekton 파이프라인 내부에 포함시켜 자동으로 Terraform 정책 가드레일을 세울 수 있는 GCP 테라폼 배포 파이프라인을 구축해보겠습니다.

 

 

 

1. gcloud terraform vet?

 

gcloud terraform vet은 Terraform을 사용한 인프라 배포 및 기존 인프라 파이프라인 정책 검사를 수행할 수 있는 CLI 도구입니다.

 

gcloud terraform vet을 이용해 정책 가드레일을 세울 수 있어 조직이 규정한 컴플라이언스 혹은 정책을 자동화해서 검사할 수 있습니.
이로써 감사를 통해 문제가 드러날 위험이 줄어들며 조직의 규모가 커질수록 위험에 노출되는 인프라가 줄어들 수 있습니다.

 

gcloud terraform vet 사용시 다음과 같은 이점이 있습니다.

  • 애플리케이션 개발의 어느 단계에서나 조직의 정책 시행
  • 정책 검사 자동화로 수동 오류 방지
  • 배포 전 확인을 통한 빠른 실패

 

1-1. gcloud terraform vet 구성

 

gcloud terraform vet을 사용하기 위해서는 다음과 같은 Requirements들이 요구됩니다.

  • "resourcemanager.projects.getIamPolicy" Permission : Organization의 Security Reviewer Role을 통해 획득 가능
  • "resourcemanager.projects.get" Permission : Organization의 Project Viewer Role을 통해 획득 가능
  • Cloud SDK

 

gcloud terraform vet은 Terraform plan 명세서를 Constraint라고 하는 정책과 비교하는 방식으로 정책 위반 여부를 검사하는 프로세스로 진행합니다.

 

Constraint들은 Policy Library 디렉토리 내부에 존재해야 하며 gcloud terraform vet은 Policy Library 디렉토리를 참조해 정책들을 인지하게 됩니다.

 

gcloud terraform vet은 제출한 Terraform plan 명세서가 Policy Library의 Constraints를 위반할 경우 에러를 출력하며(code 1), 위반하지 않을 경우 에러를 출력하지 않습니다(code 0) 

 

 

1-2. gcloud terraform vet 사용해보기

 

간단한 사례로 gcloud terraform vet을 직접 사용해보며 이해도를 높여보겠습니다.

 

 

1.아래 명령어로 Google Cloud에서 제공하는 Sample Policy library를 clone 합니다.

1
git clone https://github.com/GoogleCloudPlatform/policy-library.git
cs

 

2. Policy library에서 Sample network_enable_firewall_logs constraint 파일을 policies/constrains 디렉토리로 복사합니다.

1
cp samples/network_enable_firewall_logs.yaml policies/constraints/
cs
 
3. network_enable_firewall_logs.yaml 파일의 컨텐츠는 다음과 같습니다.
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: constraints.gatekeeper.sh/v1alpha1
kind: GCPNetworkEnableFirewallLogsConstraintV1
metadata:
  name: enable-network-firewall-logs
  annotations:
    description: Ensure Firewall logs is enabled for every firewall in VPC Network
    bundles.validator.forsetisecurity.org/healthcare-baseline-v1: security
spec:
  severity: high
  match:
    ancestries:
    - "organizations/**"
  parameters: {}
cs
 
 
4. 정책이 작동하는지 확인하기 위해, 다음과 같은 main.tf 파일을 생성합니다.
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
resource "google_compute_firewall" "rules" {
  name = "test-firewall"
  network = "default"
  direction = "INGRESS"
  priority = 1000
  source_ranges = [
    "0.0.0.0/0"
  ]
  source_tags = [
    "some-tag"
  ]
  allow {
    protocol  = "tcp"
    ports     = ["80", "8080", "1000-2000"]
  }
}
cs
 
 
5. terraform init 명령어로 terrform을 시작합니다.
 
1
terraform init
cs
 
 
6. 다음 명령어로 terraform plan 출력을 test.tfplan 파일로 저장합니다.
 
1
terraform plan -out=test.tfplan
cs
 
 
7. 다음 명령어로 terraform plan 출력 파일을 JSON으로 변환합니다.
 
1
terraform show -json ./test.tfplan > ./tfplan.json
cs
 
 
8. 다음 명령어로 gcloud의 terraform-tools Component를 설치합니다.
 
1
sudo apt-get install google-cloud-sdk-terraform-tools
cs
 
 
9. gcloud terraform vet 명령어로 terraform plan 파일을 정책으로 검증합니다.
 
1
gcloud beta terraform vet tfplan.json --policy-library=. --format=json
cs
 
 
10. terraform plan 파일이 정책을 위반했기 때문에 아래와 같은 에러 메세지를 출력합니다.
 
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
39
40
41
42
43
44
[
  {
    "constraint": "GCPNetworkEnableFirewallLogsConstraintV1.enable-network-firewall-logs",
    "constraint_config": {
      "api_version": "constraints.gatekeeper.sh/v1alpha1",
      "kind": "GCPNetworkEnableFirewallLogsConstraintV1",
      "metadata": {
        "annotations": {
          "bundles.validator.forsetisecurity.org/healthcare-baseline-v1": "security",
          "description": "Ensure Firewall logs is enabled for every firewall in VPC Network",
          "validation.gcp.forsetisecurity.org/yamlpath": "policies/constraints/network_enable_firewall_logs.yaml"
        },
        "name": "enable-network-firewall-logs"
      },
      "spec": {
        "match": {
          "ancestries": [
            "organizations/**"
          ]
        },
        "parameters": {},
        "severity": "high"
      }
    },
    "message": "Firewall logs are disabled in firewall //compute.googleapis.com/projects/prj-sandbox-devops-9999/global/firewalls/test-firewall.",
    "metadata": {
      "ancestry_path": "organizations/1085400699340/folders/599651043362/projects/prj-sandbox-devops-9999",
      "constraint": {
        "annotations": {
          "bundles.validator.forsetisecurity.org/healthcare-baseline-v1": "security",
          "description": "Ensure Firewall logs is enabled for every firewall in VPC Network",
          "validation.gcp.forsetisecurity.org/yamlpath": "policies/constraints/network_enable_firewall_logs.yaml"
        },
        "labels": {},
        "parameters": {}
      },
      "details": {
        "resource": "//compute.googleapis.com/projects/prj-sandbox-devops-9999/global/firewalls/test-firewall"
      }
    },
    "resource": "//compute.googleapis.com/projects/prj-sandbox-devops-9999/global/firewalls/test-firewall",
    "severity": "high"
  }
]
cs
 
 
11. 이제 terraform plan 파일이 정책을 준수하도록 아래와 같이 terraform plan을 변경합니다.
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
resource "google_compute_firewall" "rules" {
  name = "test-firewall"
  network = "default"
  direction = "INGRESS"
  priority = 1000
  source_ranges = [
    "0.0.0.0/0"
  ]
  source_tags = [
    "some-tag"
  ]
  allow {
    protocol  = "tcp"
    ports     = ["80", "8080", "1000-2000"]
  }
  log_config  {
    metadata = "INCLUDE_ALL_METADATA"
  }
}
cs
 
 
12. gcloud beta terraform vet 명령어 실행 시 아래와 같이 에러 메세지가 출력되지 않는 것을 확인할 수 있습니다.
 
1
2
Validating resources...done.                                                                                                                
[]
cs
 

 

2. Tekton으로 gcloud terraform vet을 이용한 validation 파이프라인 구성하기

 

 

gcloud terraform vet을 이용한 validation을 Tekton Pipeline의 일부로 포함시켜 자동화된 검증을 수행할 수 있습니다.

Tekton을 활용한 Terraform CI/CD Pipeline에 대한 정보는 이번 시리즈의 두 번째 글 Tekton 사용해보기 (2) Tekton으로 인프라를 자동 배포하는 Terraform Pipeline을 만들어보자 포스팅을 참고하세요.

 

위 다이어그램은 gcloud terraform vet을 포함한 Tekton Terraform CI/CD Pipeline의 리소스 구성도입니다.

Pipeline의 작업들을 순차적으로 나열하면 다음과 같습니다.

  1. git clone 명령어를 통해 terraform configuration이 존재하는 Source repository를 클론합니다.
  2. terraform init, terraform plan -out=.. 명령어로 Terraform initializing 후 plan 파일을 저장합니다.
  3. terraform show 명령어로 저장된 plan 파일을 json 포맷으로 변경해 저장합니다.
  4. git clone 명령어를 통해 terraform policy가 존재하는 Source repository를 클론합니다.
  5. gcloud beta terraform vet 명령어를 통해 json 포맷의 plan 파일을 policy 디렉토리의 constraints 파일로 검증합니다.
  6. 검증이 통과되면 terraform apply 명령어로 인프라 배포를, 검증을 통과하지 못하면 원인이 포함된 Error 메세지를 출력하고 작업을 종료합니다.

 

2-1. Tekton 파이프라인 구성 코드

 

위에서 소개한 파이프라인의 컴포넌트 중 중요한 부분만 함께 보도록 하겠습니다.

 

1. pipeline.yaml

pipeline.yaml은 Tekton pipeline을 정의하는 yaml 매니페스트입니다.

 

아래와 같은 구성으로 이루어져 있습니다.

 

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: clone-terraform-cli
spec:
  description: |
    This pipeline clones a git repo, run "terraform plan" command based on .tf config file
  params:
  - name: source-repo-url
    type: string
  - name: policy-repo-url
    type: string
  workspaces:
  - name: shared-data
  tasks:
  - name: fetch-source
    taskRef:
      name: git-clone
    workspaces:
    - name: output
      workspace: shared-data
    params:
    - name: url
      value: $(params.source-repo-url)
  - name: terraform-plan
    runAfter: ["fetch-source"]
    taskRef:
      name: terraform-cli
    workspaces:
    - name: source
      workspace: shared-data
    params:
    - name: terraform-secret
      value: "terraform-secret"
    - name: ARGS
      value:
        - plan
        - "-out=tfplan.tfplan"
  - name: terraform-show
    runAfter: ["terraform-plan"]
    taskRef:
      name: terraform-script
    workspaces:
    - name: source
      workspace: shared-data
    params:
    - name: terraform-secret
      value: "terraform-secret"
    - name: SCRIPT
      value: |-
          #!/usr/bin/env sh
          set -e
          terraform show -json ./tfplan.tfplan
          terraform show -json ./tfplan.tfplan > ./tfplan.json
  - name: fetch-policy
    runAfter: ["terraform-show"]
    taskRef:
      name: git-clone
    workspaces:
    - name: output
      workspace: shared-data
    params:
    - name: url
      value: $(params.policy-repo-url)
    - name: subdirectory
      value: policy-library
  - name: gcloud-terraform-vet
    runAfter: ["fetch-policy"]
    taskRef:
      name: gcloud
    workspaces:
    - name: source
      workspace: shared-data
    params:
      - name: SCRIPT
        value: |-
            #!/usr/bin/env sh
            apt-get install google-cloud-sdk-terraform-tools
            VIOLATIONS=$(gcloud beta terraform vet tfplan.json --policy-library=./policy-library/ --format=json)
            retVal=$?
            if [ $retVal -eq 2 ]; then
              # Optional: parse the VIOLATIONS variable as json and check the severity level
              echo "$VIOLATIONS"
              echo "Violations found; not proceeding with terraform apply"
              exit 1
            fi
            if [ $retVal -ne 0 ]; then
              echo "Error during gcloud beta terraform vet; not proceeding with terraform apply"
              exit 1
            fi
            echo "No policy violations detected; proceeding with terraform apply"
  - name: terraform-apply
    runAfter: ["gcloud-terraform-vet"]
    taskRef:
      name: terraform-cli
    workspaces:
    - name: source
      workspace: shared-data
    params:
    - name: terraform-secret
      value: "terraform-secret"
    - name: ARGS
      value:
        - apply
        - "-auto-approve"
cs

 

2. gcloud-terraform-vet

 

위 매니페스트에서 중요한 부분은 아래의 "gcloud-terraform-vet"  Task를 실행하는 코드입니다.

 

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
- name: gcloud-terraform-vet
    runAfter: ["fetch-policy"]
    taskRef:
      name: gcloud
    workspaces:
    - name: source
      workspace: shared-data
    params:
      - name: SCRIPT
        value: |-
            #!/usr/bin/env sh
            apt-get install google-cloud-sdk-terraform-tools
            VIOLATIONS=$(gcloud beta terraform vet tfplan.json --policy-library=./policy-library/ --format=json)
            retVal=$?
            if [ $retVal -eq 2 ]; then
              # Optional: parse the VIOLATIONS variable as json and check the severity level
              echo "$VIOLATIONS"
              echo "Violations found; not proceeding with terraform apply"
              exit 1
            fi
            if [ $retVal -ne 0 ]; then
              echo "Error during gcloud beta terraform vet; not proceeding with terraform apply"
              exit 1
            fi
            echo "No policy violations detected; proceeding with terraform apply"
cs

 

"gcloud-terraform-vet"은 gcloud beta terraform vet 명령어를 수행하여 Error가 발생하면 Status code 1을 반환해 Task를 종료, 검증이 통과되어 Error가 발생하지 않으면 다음 Task로 계속 진행하는 Script를 실행합니다.

 

Tekton hub에서 제공하는 "gcloud" Task를 이용했으며 해당 Task는 아직 beta 버전인 gcloud terraform vet이 설치되어 있지 않으므로 "apt-get install google-cloud-sdk-terraform-tools" 라인을 넣어 해당 도구를 설치해야 함에 유의합니다.

 

 

2-2. Tekton validation 파이프라인 DEMO

 

1. firewall log가 enable되어 있어야 한다는 Constraint가 포함된 Policy library 레포지토리를 준비

 

2. terraform configuration이 포함된 레포지토리를 준비, firewall_log가 disable된 firewall rule 리소스를 Commit & Push

 

3. Tekton Pipeline이 자동으로 실행되나 Policy validation에 실패해 Pipeline이 중단되는 모습

 

4. terraform configuration에서 firewall_log을 enable하는 변경사항을 Commit & Push.

 

5. Tekton Pipeline이 자동으로 실행되고 Policy validation이 성공해 Firewall이 배포되는 모습

 

 

3. 마무리

 

지금까지 gcloud terraform vet을 이용해서 Tekton validation 파이프라인을 구성하는 방법에 대해서 알아봤습니다.

 

gcloud terraform vet과 같이 Terraform 코드에 대해서 정책을 검사할 수 있는 도구를 사용한다면 정책 가드레일을 세울 수 있기 때문에 조직 내 정책 위반을 사전에 예방할 수 있다는 이점이 존재합니다.

 

이같은 도구를 Tekton Terraform 배포 파이프라인에 포함한다면 배포 프로세스 내에서 정책을 검사해 자동적으로 정책을 검사할 수 있는 Validation 프로세스를 정착시킬 수도 있습니다.

 

그래서 이번 포스팅을 통해 Tekton 파이프라인에서 성공적으로 Validation 파이프라인을 구축할 수 있는 것을 확인할 수 있었습니다.

 

포스팅을 보시는 분들이 많은 도움을 얻으셨기를 바랍니다.