[GCP]규칙 기반 평가 서비스 제공을 위한 Workload Manager Evaluation 알아보기 — #2 REGO 활용해서 Custom rule 작성하기

이정운 (Jungwoon Lee)
google-cloud-apac
Published in
28 min readAug 6, 2024

안녕하세요 이정운 입니다.

[GCP]규칙 기반 평가 서비스 제공을 위한 Workload Manager Evaluation 알아보기 — #1 Workload Manager Evaluation

지난 이야기에서 Workload Manager Evaluation 에 대해서 이해하는 시간을 가져봤고 Custom rule 작성 및 Evalutation 을 수행하게 되면 어떻게 동작하는지 간단하게 살펴봤습니다. 이때 중요한 부분은 결국 각 사용자가 원하는데로 Custom rule 을 위한 REGO 소스를 작성하는 일인데 지난 이야기에서 사용한 것과 같이 Github 에 Google 에서 작성해서 올려놓은 샘플을 가지고 바로 활용하거나 이를 참고해서 직접 작성하는 것이 가능합니다.

https://github.com/GoogleCloudPlatform/workload-manager/tree/main/rules

Workload Manager 는 Policy language 로 REGO 를 지원하는 것이므로 이를 활용해서 자신이 원하는 형태로 Custom rule 을 다양하게 만들수도 있으며 이번 이야기에서는 어떻게 Custom rule 을 만들수 있는지 예제를 가지고 좀 더 상세히 살펴보도록 하겠습니다.

#1) REGO 를 통한 Workload Manager Custom rule 작성을 위한 준비

Introduction to Cloud Asset Inventory
https://cloud.google.com/asset-inventory/docs/overview

Workload Manager 가 제공하는 규칙 기반의 평가를 하기 위해서는 policy language 인 REGO 를 이용해서 Custom rule 을 생성해야 합니다. 이때 하단의 아키텍처를 보시면 아시겠지만 Workload Manager 의 OPA(Rego) Engine 이 활용할 수 있는 소스는 Cloud Asset Inventory 와 Cloud Monitoring metrics(Supported metrics for Compute Engine) 입니다. 보통 배치된 자원에 대한 규정이나 보안 평가에서 주로 쓰이는 소스는 현재 자원의 상태 정보와 메타데이터를 가지고 있는 Cloud Asset Inventory 입니다.

https://cloud.google.com/workload-manager/docs/evaluate/custom-rules/about-custom-rules

Cloud Asset Inventory 는 이름 그대로 시계열 데이터베이스를 기반으로 자원에 대한 인벤토리 서비스를 제공하는 기능입니다. 즉, Google cloud 환경의 가장 최신의 자원(GCE, GKE 등이 다 자원) 상태와 메타데이터를 보관하고 있으며 필요한 경우에 확인 및 export 가능하다고 보시면 됩니다. 실제적으로 관리콘솔에서 IAM&Admin > Asset Inventory > Resources 메뉴를 통해서 Google cloud 에서 자원을 확인할 수 있습니다.

예를 들어서 Asset Inventory 를 통해서 GCE instance 의 asset 을 확인할 수 있습니다.

또는 해당 GCE instance 를 클릭후에 Full metadata 탭을 클릭하여 어떤 메타데이터들이 있는지 확인하는 것이 가능합니다.

또 다른 예제로, ServiceAccountKey 와 같은 자원도 확인 가능합니다.

해당 자원을 클릭한 후 Full metadata 탭을 선택하여 해당 자원도 모든 메타데이터를 확인 가능합니다.

기 언급드린 것처럼 Workload Manager 의 OPA(Rego) Engine 이 Cloud Asset Inventory 를 활용할 수 있기 때문에 이렇게 반환되는 Full Metadata 를 활용하여 원하는 형태의 규칙을 만드는 것이 가능합니다. 예를 들면 ServiceAccountKey 에 있는 validAfterTime 과 같은 메타데이터 값을 추출하여 ServiceAccountKey 가 생성된지 며칠이 지났는지 확인하는 형태의 평가 규칙을 만들 수 있습니다.

예를 들어서 “ServiceAccountKey 는 생성 후 90일 전에 교체되어야 한다”가 보안 규정이라면 해당 Key 의 생성일자인 validAfterTime 값을 추출하여 90일이 넘었는지 체크해서 해당 보안 규정을 위반하고 있는 ServiceAccountKey 가 있는지 점검하는 목적으로 Workload Manager 를 활용할 수 있습니다.

참고 #1.1) 관리콘솔을 통해서 Full Metadata 를 확인할 수 있지만 Time 같은 경우 포멧이 변경되어 동작할 수 있으므로 사전 주의가 필요합니다. 예를 들어서 상단에서 확인 가능한 validAfterTime 의 경우 콘솔에서 보여지는 형태(“Jul 23, 2024, 10:02:10 AM”) 와 실제 Asset inventory 에 저장된 형태(“2024–07–23T09:17:30Z”) 의 차이가 있으며 이게 Workload Manager 엔진에서 동작할때 또 한번 형태가(“2024–07–23 09:17:30 UTC”) 다르게 동작합니다.

참고 #1.2) 기 언급한 것처럼 Asset inventory 에서 보는 데이터 타입과 실제 Workload Manager 엔진에서 동작할 때의 타입이 다를수 있습니다. 이런 부분을 확인하기 가장 좋은 방법은 조금 불편할 수 있지만 Custom rule 을 아주 간단하게 작성하면서 message 에 그 결과값을 찍도록 한 후에 Workload Manager 의 결과를 저장하는 BigQuery 테이블에서 실제 데이터 타입을 확인하는 방법입니다. 예를 들어 하단의 REGO 소스의 경우에는 자원이 “iam.googleapis.com/ServiceAccountKey” 인 경우에 무조건 결과를 출력하게 하며 이때 validAfterTime 을 같이 찍는 예제 입니다.

########################################################################
# DETAILS: Service Account Key have to rotate within 90 days
# SEVERITY: Medium
# ASSET_TYPE: iam.googleapis.com/ServiceAccountKey
# TAGS: Security, ServiceAccount
########################################################################

# Package: Defines a namespace for the Rego policy
package templates.google.serviceaccountkeyexpire

# 필요한 라이브러리를 가져옵니다.
import data.validator.google.lib as lib
import future.keywords

# 평가할 입력 자산을 'asset' 변수에 설정합니다.
asset := input.asset

# 이 정책 규칙의 대상 자산 유형을 설정합니다.
asset_type := "iam.googleapis.com/ServiceAccountKey"

# 정책을 어긴 자원을 결정하는 핵심 정책 규칙입니다. 디버깅을 위해서 조건없이 찍어내는 형태를 사용
deny[{
"msg": message,
"details": metadata,
}] {
not lib.asset_type_should_be_skipped(asset_type) # 자산 유형이 무시되어야 하는 경우 건너뜁니다.

# 위반을 나타내는 메시지를 생성합니다.
message := "The Service account key has a validity period exceeding 90 days."


# 위반 세부 정보가 포함된 메타데이터 객체를 생성합니다. 자산 이름, validAfterTime 를 metadata 로 포함해서 결과값에서 확인할 수 있도록 설정합니다.
metadata := {"name": asset.name,
"validAfterTime": asset.resource.data.validAfterTime}

}

해당 Custom rule 을 가지고 Evaluation 을 수행한 후에 BigQuery 테이블에서 하단과 같이 실제 Workload Engine 에서 동작하는 값을 확인 할 수 있습니다.

#2) REGO 를 통한 Workload Manager Custom rule 작성

Policy Language
https://www.openpolicyagent.org/docs/latest/policy-language/

Write custom rules using Rego
https://cloud.google.com/workload-manager/docs/evaluate/custom-rules/rego-custom-rules

Policy language 인 REGO 를 통해서 Workload Manager Evaluation 을 위한 Custom rule 을 작성하기 위해서 여러가지 방법이 있겠지만 가장 좋은 방법 중의 하나는 Asset Inventory 에서 점검하려고 하는 자원의 메타데이터를 export 한 후 Rego Playground 를 활용해서 시뮬레이션 해보는 것입니다.

이전 파트에서 언급한 것처럼 관리콘솔을 통해서 Full Metadata 를 확인할 수 있지만 하단과 같이 gcloud 명령을 통해서 asset 을 export 할 수 있으며 이렇게 메타데이터를 export 한 뒤에 필요에 따라 REGO 소스를 작성하고 시뮬레이션 해보는 것이 다른 방안에 비해 좀 더 나은거 같아서 가이드 드립니다.

gcloud asset export \
--project='jwlee-argolis-202104' \
--asset-types='iam.googleapis.com/ServiceAccountKey' \
--content-type='resource' \
--output-path="gs://genai-test-001/out.txt"

이렇게 gcloud asset export 명령을 수행하면 하단과 같이 Jsonl 형태로 output 이 나오는 것을 확인할 수 있습니다.

{"name":"//iam.googleapis.com/projects/jwlee-argolis-202104/serviceAccounts/100806371580163861997/keys/11c825a20436b459c953fd02c5d63ac539209c51","asset_type":"iam.googleapis.com/ServiceAccountKey","resource":{"version":"v1","discovery_document_uri":"https://iam.googleapis.com/$discovery/rest","discovery_name":"ServiceAccountKey","parent":"//iam.googleapis.com/projects/jwlee-argolis-202104/serviceAccounts/100806371580163861997","data":{"keyAlgorithm":"KEY_ALG_RSA_2048","keyOrigin":"GOOGLE_PROVIDED","keyType":"USER_MANAGED","name":"projects/jwlee-argolis-202104/serviceAccounts/test002@jwlee-argolis-202104.iam.gserviceaccount.com/keys/11c825a20436b459c953fd02c5d63ac539209c51","validAfterTime":"2024-07-23T09:17:30Z","validBeforeTime":"2024-10-21T09:17:30Z"}},"ancestors":["projects/904902969656","folders/880861329185","organizations/55454119248"],"update_time":"2024-07-24T01:03:46Z"}

이렇게 준비된 자원에 대한 메타데이터를 이용해서 Policy language 인 REGO 를 통해서 원하는 형태로 작성해야 하는데 REGO 에 익숙하지 않으신분들은 기본 메뉴얼 부터 참고하시는 것을 추천드리며 REGO 를 작성하고 기본적인 테스트를 수행해보기에 가장 좋은 환경중에 하나는 Rego Playground 입니다.

Policy Language
https://www.openpolicyagent.org/docs/latest/policy-language/

The Rego Playground
https://play.openpolicyagent.org/

예를 들어 ServiceAccountKey 에 대한 Custom rule 을 작성한다고 하면 Rego Playground 에서 이전에 export 받았던 ServiceAccountKey 메타데이터를 INPUT 항목에 넣고 원하는 형태대로 REGO 로직을 작성하면서 Evaluate 를 돌리면서 테스트 해볼 수 있습니다. (Time 항목에 대해서는 이전 파트에서 이야기 한것처럼 주의가 필요하며 사전에 변경해서 테스트 하는 것을 추천합니다.)

{"name":"//iam.googleapis.com/projects/jwlee-argolis-202104/serviceAccounts/100806371580163861997/keys/11c825a20436b459c953fd02c5d63ac539209c51","asset_type":"iam.googleapis.com/ServiceAccountKey","resource":{"version":"v1","discovery_document_uri":"https://iam.googleapis.com/$discovery/rest","discovery_name":"ServiceAccountKey","parent":"//iam.googleapis.com/projects/jwlee-argolis-202104/serviceAccounts/100806371580163861997","data":{"keyAlgorithm":"KEY_ALG_RSA_2048","keyOrigin":"GOOGLE_PROVIDED","keyType":"USER_MANAGED","name":"projects/jwlee-argolis-202104/serviceAccounts/test002@jwlee-argolis-202104.iam.gserviceaccount.com/keys/11c825a20436b459c953fd02c5d63ac539209c51","validAfterTime":"2024-07-23T09:17:30Z","validBeforeTime":"2024-10-21T09:17:30Z"}},"ancestors":["projects/904902969656","folders/880861329185","organizations/55454119248"],"update_time":"2024-07-24T01:03:46Z"}

==>

{"name":"//iam.googleapis.com/projects/jwlee-argolis-202104/serviceAccounts/100806371580163861997/keys/11c825a20436b459c953fd02c5d63ac539209c51","asset_type":"iam.googleapis.com/ServiceAccountKey","resource":{"version":"v1","discovery_document_uri":"https://iam.googleapis.com/$discovery/rest","discovery_name":"ServiceAccountKey","parent":"//iam.googleapis.com/projects/jwlee-argolis-202104/serviceAccounts/100806371580163861997","data":{"keyAlgorithm":"KEY_ALG_RSA_2048","keyOrigin":"GOOGLE_PROVIDED","keyType":"USER_MANAGED","name":"projects/jwlee-argolis-202104/serviceAccounts/test002@jwlee-argolis-202104.iam.gserviceaccount.com/keys/11c825a20436b459c953fd02c5d63ac539209c51","validAfterTime":"2024-07-23 09:17:30 UTC","validBeforeTime":"2024-10-21 09:17:30 UTC"}},"ancestors":["projects/904902969656","folders/880861329185","organizations/55454119248"],"update_time":"2024-07-23 09:17:30 UTC"}

우선 먼저 간단하게 Github 에 있는 샘플을 참고하여 “iam.googleapis.com/ServiceAccountKey” 자원을 대상으로 key_type 이 “USER_MANAGED” 인 경우를 찾는 REGO 로직을 작성해서 테스트 해봅니다. (Google 에서 제공되는 library 사용은 잠시 빼두고 진행합니다.)

########################################################################
# DETAILS: Service Account Key have to rotate within 90 days
# SEVERITY: Medium
# ASSET_TYPE: iam.googleapis.com/ServiceAccountKey
# TAGS: Security, ServiceAccount
########################################################################

# Package: Defines a namespace for the Rego policy
package templates.google.serviceaccountkeyexpire

# 필요한 라이브러리를 가져옵니다.
#import data.validator.google.lib as lib
import future.keywords

# 평가할 입력 자산을 'asset' 변수에 설정합니다.
asset := input

# 이 정책 규칙의 대상 자산 유형을 설정합니다.
asset_type := "iam.googleapis.com/ServiceAccountKey"

# 정책을 어긴 자원을 결정하는 핵심 정책 규칙입니다.
deny[{
"msg": message,
"details": metadata,
}] {
#not lib.asset_type_should_be_skipped(asset_type) # 자산 유형이 무시되어야 하는 경우 건너뜁니다.

# 키 유형이 "USER_MANAGED"인지 확인합니다. Google에서 자동으로 생성된 키는 무시합니다.
key_type := asset.resource.data.keyType
key_type == "USER_MANAGED"

# 위반을 나타내는 메시지를 생성합니다.
message := "The Service account key has a validity period exceeding 90 days."
metadata := asset.name
}

위와 같이 REGO 소스를 작성후 Rego Playground 에서 Evaluate 를 돌려보면 “iam.googleapis.com/ServiceAccountKey” 자원중에 keyType 이 USER_MANAGED 인 경우 deny() 로 결과가 나오는 것을 간단하게 확인해보실 수 있습니다.

여기서 ServiceAccountKey 의 생성일자인 validAfterTime 을 구한후에 90일이 지났는지 확인하는 추가적인 조건을 더 넣고 싶다면 하단과 같은 code snippet 을 추가할 수 있습니다.

    #서비스 계정 키의 최대 허용 기간 - "2160h" (90일) (예시로 120시간 사용)
max_age := "2160h"

# 자산 리소스 데이터의 validAfterTime 필드에서 생성 날짜를 추출합니다. ("2024-07-23 09:17:30 UTC")
valid_after_time := split(asset.resource.data.validAfterTime, " ")[0]

# "YYYY-MM-DD" 형식을 가정하여 validAfterTime 문자열을 시간 객체로 파싱합니다.
created_time := time.parse_ns("2006-01-02", valid_after_time)

# 설정된 최대 기간(예: "2160h")을 duration 객체로 파싱합니다.
max_age_parsed := time.parse_duration_ns(max_age)

# 현재 시간에서 생성 시간을 빼서 키의 사용 기간을 계산합니다
key_age := time.now_ns() - created_time

# 키의 사용 기간이 허용된 최대 기간을 초과하는지 확인합니다.
key_age > max_age_parsed

그러면 INPUT 으로 받은 ServiceAccountKey asset 은 2024–07–23 에 생성되었고 아직 90일이 지나지 않았기 때문에 deny 결과가 [] 로 비어있는 것을 확인할 수 있습니다.

해당 조건에서 ‘max_age_parsed := time.parse_duration_ns(“2160h”)’ 를 ‘max_age_parsed := time.parse_duration_ns(“120h”)’ 로 줄여서 다시 한번 테스트 해봅니다.

그럼 지금 결과를 보시는 것처럼 deny 에 해당 asset 이 잡히는 것을 확인할 수 있습니다. 이렇게 기본적인 REGO 로직의 골격은 Rego playground 로 테스트 해볼 수 있으며, 어느 정도 완성이 되었다면 Workload Manager Evalutaion 을 위한 Custom rule 로 테스트 하기 전에 Github 로 공유된 다른소스들을 참고하여 몇가지 필요한 라이브러리와 체크로직 등을 추가합니다.

Google Rego Rules for Workload Manager: Custom rules
https://github.com/GoogleCloudPlatform/workload-manager

이를 통해서 ServiceAccountKey 중에 생성된지 90일이 넘었는데 여전히 사용중인 자원을 찾을 수 있는 Custom rule 을 하단과 같이 생성할 수 있습니다. (본 예제는 90일 넘은 ServiceAccountKey 가 없어서 5일(120h)으로 변경해서 테스트 해봤습니다.)

########################################################################
# DETAILS: Service Account Key have to rotate within 90 days
# SEVERITY: Medium
# ASSET_TYPE: iam.googleapis.com/ServiceAccountKey
# TAGS: Security, ServiceAccount
########################################################################

# Package: Defines a namespace for the Rego policy
package templates.google.serviceaccountkeyexpire

# 필요한 라이브러리를 가져옵니다.
import data.validator.google.lib as lib
import future.keywords

# 평가할 입력 자산을 'asset' 변수에 설정합니다.
asset := input.asset

# 이 정책 규칙의 대상 자산 유형을 설정합니다.
asset_type := "iam.googleapis.com/ServiceAccountKey"

#서비스 계정 키의 최대 허용 기간 - "2160h" (90일) (예시로 120시간 사용)
max_age := "120h"

# 정책을 어긴 자원을 결정하는 핵심 정책 규칙입니다.
deny[{
"msg": message,
"details": metadata,
}] {
not lib.asset_type_should_be_skipped(asset_type) # 자산 유형이 무시되어야 하는 경우 건너뜁니다.

# 키 유형이 "USER_MANAGED"인지 확인합니다. Google에서 자동으로 생성된 키는 무시합니다.
key_type := lib.get_default(asset.resource.data, "keyType", false)
key_type == "USER_MANAGED"

# 자산 리소스 데이터의 validAfterTime 필드에서 생성 날짜를 추출합니다. ("2024-07-23 09:17:30 UTC")
valid_after_time := split(asset.resource.data.validAfterTime, " ")[0]

# "YYYY-MM-DD" 형식을 가정하여 validAfterTime 문자열을 시간 객체로 파싱합니다.
created_time := time.parse_ns("2006-01-02", valid_after_time)

# 설정된 최대 기간(예: "2160h")을 duration 객체로 파싱합니다.
max_age_parsed := time.parse_duration_ns(max_age)

# 현재 시간에서 생성 시간을 빼서 키의 사용 기간을 계산합니다
key_age := time.now_ns() - created_time

# 키의 사용 기간이 허용된 최대 기간을 초과하는지 확인합니다.
key_age > max_age_parsed

# 위반을 나타내는 메시지를 생성합니다.
message := "The Service account key has a validity period exceeding 90 days."


# 위반 세부 정보가 포함된 메타데이터 객체를 생성합니다. 자산 이름, validAfterTime, 키 사용 기간, 최대 허용 기간이 포함됩니다.
metadata := {"name": asset.name,
"validAfterTime": asset.resource.data.validAfterTime,
"key_age": key_age,
"max_age": max_age_parsed}

}

해당 Custom rule 을 가지고 Workload Manager Evaluation 을 수행하면 하단과 같이 결과를 확인할 수 있습니다.

또한, Workload Manager 의 결과를 BigQuery 로 저장하게 설정했다면 하단과 같이 수행 결과가 BigQuery 의 ViolationResults 라는 테이블에 저장된 것을 확인 가능합니다.

참고 #2.1) Google cloud 의 대부분의 자원을 Asset inventory 에서 확인 가능하며 이를 통해서 Workload Manager Evaluation 을 수행할 수 있습니다. 또한, 하단의 링크를 통해서 지원되는 asset type 을 정확하게 확인 가능합니다.

Supported asset types
https://cloud.google.com/asset-inventory/docs/supported-asset-types

지난이야기에서 Workload Manager 을 사용하는 방법에 대해서 간단히 살펴봤다면 이번 이야기에서는 하나의 예제를 가지고 policy language 인 REGO 로 Custom rule 을 작성하는 방법을 좀 더 상세히 살펴봤습니다. 추가적으로 Custom rule 을 만들면서 계속적으로 Workload Manager 를 반복해서 수행하기 보다는 Asset inventory 에서 export 한 메타데이터와 온라인에서 제공되는 Rego playground 를 활용해서 코어 로직부분을 좀 더 쉽게 작성하는 방법을 같이 살펴봤습니다. 향후 좀 더 편리한 방법이 나오겠지만 현재 기준에서는 실제 운영환경 전에 테스트 해보기 좋은 방법인것으로 보여서 이번 이야기에 담았습니다. 그럼 이번 이야기는 여기서 마무리 하도록 하겠으며 다음에 더 좋은 이야기로 돌아오도록 하겠습니다. ;)

Disclaimer: 본 글의 작성자는 Google 소속이지만, 본 글에 작성자 개인적 견해가 반영된 것으로서, 본 글은 Google 을 대변하지 않으며, Google의 공식적 입장이 아님을 밝힙니다.

참고 자료 #1

Introduction to Cloud Asset Inventory
https://cloud.google.com/asset-inventory/docs/overview

Policy Language
https://www.openpolicyagent.org/docs/latest/policy-language/

Write custom rules using Rego
https://cloud.google.com/workload-manager/docs/evaluate/custom-rules/rego-custom-rules

Google Rego Rules for Workload Manager: Custom rules
https://github.com/GoogleCloudPlatform/workload-manager

Supported asset types
https://cloud.google.com/asset-inventory/docs/supported-asset-types

--

--

이정운 (Jungwoon Lee)
google-cloud-apac

Technical engineer who dreams better future. (These thoughts are my own personal opinions, and do not reflect or represent Google’s opinions or plans.)