23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
OPA의 정책정의언어인 Rego의 실제 적용 사례분석을 통해 Policy as Code 및 정책 시스템의 이해를 높히고자 특정 정책을 타겟하여 정리하고자 한다.
라이브러리에서 첫번째 rego룰이고 전체내역을 파악하기 위해 특별히 자세히 조사하고 기록한다. 본 룰의 내용은 컨테이너를 띄워서 권한이 없는 호스트의 영역(파일 또는 디렉토리)에 접근하는 것을 막는 것이다.
사용자가 컨테이너를 띄워서 권한이 없는 파일이나 디렉토리에 접근할 수 있음을 고지한 보안 이슈이다.
filter.rego
package armo_builtins
deny[msga] {
nodes := input[_]
current_version := nodes.status.nodeInfo.kubeletVersion
isVulnerableVersion(current_version)
versionPath = "status.nodeInfo.kubeletVersion"
pod := input[_]
pod.kind == "Pod"
msga := {
"alertMessage": "",
"alertObject": {"k8SApiObjects": [pod]},
"failedPaths": [""],
}
}
deny[msga] {
nodes := input[_]
current_version := nodes.status.nodeInfo.kubeletVersion
isVulnerableVersion(current_version)
versionPath = "status.nodeInfo.kubeletVersion"
wl := input[_]
spec_template_spec_patterns := {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"}
spec_template_spec_patterns[wl.kind]
msga := {
"alertMessage": "",
"alertObject": {"k8SApiObjects": [wl]},
"failedPaths": [""],
}
}
deny[msga] {
nodes := input[_]
current_version := nodes.status.nodeInfo.kubeletVersion
isVulnerableVersion(current_version)
versionPath = "status.nodeInfo.kubeletVersion"
wl := input[_]
wl.kind == "CronJob"
msga := {
"alertMessage": "",
"alertObject": {"k8SApiObjects": [wl]},
"failedPaths": [""],
}
}
isVulnerableVersion(version) {
version <= "v1.19.14"
}
isVulnerableVersion(version){
version >= "v1.22.0"
version <= "v1.22.1"
}
isVulnerableVersion(version){
version >= "v1.21.0"
version <= "v1.21.4"
}
isVulnerableVersion(version){
version >= "v1.20.0"
version <= "v1.20.9"
}
isVulnerableVersion(version){
version == "v1.20.10"
}raw.rego
package armo_builtins
deny[msga] {
nodes := input[_]
current_version := nodes.status.nodeInfo.kubeletVersion
is_vulnerable_version(current_version)
pod := input[_]
pod.kind == "Pod"
container := pod.spec.containers[i]
beggining_of_path := "spec."
final_path := is_sub_path_container(container, i, beggining_of_path)
msga := {
"alertMessage": sprintf("You may be vulnerable to CVE-2021-25741. You have a Node with a vulnerable version and the following container : %v in pod : %v with subPath/subPathExpr", [container.name, pod.metadata.name]),
"alertObject": {"k8SApiObjects": [pod]},
"failedPaths": final_path,
"fixPaths": [],
}
}
deny[msga] {
nodes := input[_]
current_version := nodes.status.nodeInfo.kubeletVersion
is_vulnerable_version(current_version)
wl := input[_]
spec_template_spec_patterns := {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"}
spec_template_spec_patterns[wl.kind]
container := wl.spec.template.spec.containers[i]
beggining_of_path := "spec.template.spec."
final_path := is_sub_path_container(container, i, beggining_of_path)
msga := {
"alertMessage": sprintf("You may be vulnerable to CVE-2021-25741. You have a Node with a vulnerable version and the following container : %v in %v : %v with subPath/subPathExpr", [container.name, wl.kind, wl.metadata.name]),
"alertObject": {"k8SApiObjects": [wl]},
"failedPaths": final_path,
"fixPaths": [],
}
}
deny[msga] {
nodes := input[_]
current_version := nodes.status.nodeInfo.kubeletVersion
is_vulnerable_version(current_version)
wl := input[_]
wl.kind == "CronJob"
container = wl.spec.jobTemplate.spec.template.spec.containers[i]
beggining_of_path := "spec.jobTemplate.spec.template.spec."
final_path := is_sub_path_container(container, i, beggining_of_path)
msga := {
"alertMessage": sprintf("You may be vulnerable to CVE-2021-25741. You have a Node with a vulnerable version and the following container : %v in %v : %v with subPath/subPathExpr", [container.name, wl.kind, wl.metadata.name]),
"alertObject": {"k8SApiObjects": [wl]},
"failedPaths": final_path,
"fixPaths": [],
}
}
is_sub_path_container(container, i, beggining_of_path) = path {
path = [sprintf("%vcontainers[%v].volumeMounts[%v].subPath" ,[beggining_of_path, format_int(i, 10), format_int(j, 10)]) | volume_mount = container.volumeMounts[j]; volume_mount.subPath]
count(path) > 0
}
is_vulnerable_version(version) {
version <= "v1.19.14"
}
is_vulnerable_version(version){
version >= "v1.22.0"
version <= "v1.22.1"
}
is_vulnerable_version(version){
version >= "v1.21.0"
version <= "v1.21.4"
}
is_vulnerable_version(version){
version >= "v1.20.0"
version <= "v1.20.9"
}
is_vulnerable_version(version){
version == "v1.20.10"
}rule.metadata.json
{
"name": "Symlink-Exchange-Can-Allow-Host-Filesystem-Access",
"attributes": {
"armoBuiltin": true
},
"ruleLanguage": "Rego",
"match": [
{
"apiGroups": [
""
],
"apiVersions": [
"v1"
],
"resources": [
"Pod",
"Node"
]
},
{
"apiGroups": [
"apps"
],
"apiVersions": [
"v1"
],
"resources": [
"Deployment",
"ReplicaSet",
"DaemonSet",
"StatefulSet"
]
},
{
"apiGroups": [
"batch"
],
"apiVersions": [
"*"
],
"resources": [
"Job",
"CronJob"
]
}
],
"ruleDependencies": [
],
"description": "A user may be able to create a container with subPath volume mounts to access files & directories outside of the volume, including on the host filesystem. This was affected at the following versions: v1.22.0 - v1.22.1, v1.21.0 - v1.21.4, v1.20.0 - v1.20.10, version v1.19.14 and lower. ",
"remediation": "To mitigate this vulnerability without upgrading kubelet, you can disable the VolumeSubpath feature gate on kubelet and kube-apiserver, and remove any existing Pods making use of the feature.",
"ruleQuery": "armo_builtins"
}쉬운 것부터 시작해서 rule.metadata.json 파일은 말 그대로 본 라이브러이에서 필요한 정보를 기록한 metadata들로 보여진다.
이제 filter와 raw가 들어가는데 이름 상으로 느껴지는 바는 filter를 통해 궂이 필요없는 것 - 예를 들면 이 부분이 패치된 kubernetes 버전을 사용한다면 - 에 대해서는 룰 처리에서 제외하는 것을 나타낼 것으로 보여지고 실질적으로 이러한 동작하는 것을 막는 것은 raw파일을 통하지 않았을까 예상하면서 살펴보자. (→ raw와 filter는 같은 파일이고 내부적으로 컨테이너 수준까지 검사해 보는 것을 추가한 것이 raw.rego 였음)
raw.rego 파일을 살펴보면 다음과 같이 구성된다.
Rule: deny[msga]
위반 사항이 발생하면 msga에 배열로 해단 내역의 json을 반환
msga는 최초 undefined 값을 가지며 deny함수를 통해 정의되면 더 이상의 진행은 없다. (rego의 특성 - 모든 검사의 결과가 or 조건으로 결합)
서브 패스를 갖고 있는지 체크하는 함수: is_sub_path_container
취약 버전인지 확인하는 함수: is_vulnerable_version
is_vulnerable_version 함수 들은 매우 단순하므로 스킵하고 is_sub_path_container 함수를 상세히 살펴보면 다음과 같다. 3개의 파라미터를 받고 있으며
is_sub_path_container(container, i, beggining_of_path) = path {
path = [sprintf("%vcontainers[%v].volumeMounts[%v].subPath" ,[beggining_of_path, format_int(i, 10), format_int(j, 10)]) | volume_mount = container.volumeMounts[j]; volume_mount.subPath]
count(path) > 0
}컨테이너에 연결된 모든 마운트 패스(container.volumeMounts[j])를 토대로 subPath를 갖고 있다면 path 기록
volume_mount = container.volumeMounts[j]; volume_mount.subPath
다음 값들로 path 생성: i는 컨테이너를 특정하는 것으로 호출에서, j는 마운트된 내역을 특정하는 것으로 해당 명령행의 | 뒷부분 가져오고 format_int함수를 통해 정수형으로 전환하여 전달된다.
"%vcontainers[%v].volumeMounts[%v].subPath"
[beggining_of_path, format_int(i, 10), format_int(j, 10)]
하나라도 path가 설정되어 있다면 예정된 path를 반환,
그렇지 않다면 (count(path) > 0)가 False가 되므로 중지 = path 반환없이 함수 종료
deny 룰들 중 하나를 살펴보면
deny[msga] {
nodes := input[_]
current_version := nodes.status.nodeInfo.kubeletVersion
is_vulnerable_version(current_version)
pod := input[_]
pod.kind == "Pod"
container := pod.spec.containers[i]
beggining_of_path := "spec."
final_path := is_sub_path_container(container, i, beggining_of_path)
msga := {
"alertMessage": sprintf("You may be vulnerable to CVE-2021-25741. You have a Node with a vulnerable version and the following container : %v in pod : %v with subPath/subPathExpr", [container.name, pod.metadata.name]),
"alertObject": {"k8SApiObjects": [pod]},
"failedPaths": final_path,
"fixPaths": [],
}
}모든 input을 순회하면서 (input[_]) 노드의 kubelet 버전을 취약점을 갖은 버전인지 확인하고(is_valuerable_version)
그렇다면 모든 input을 다시 순회하며 kind가 pod라면 내부의 컨테이너 별로 다시 순회하면서 해당 컨테이너를 인지하도록 하면서 is_sub_path_container를 수행하고
sub_path를 갖고 있는 것이 있다면 마지막 라인 msga:= 가 실행되면서 필요한 메시지를 만든다.
예시에서는 3개의 deny함수 중 하나라도 msga에 값을 추가하면 그 값을 돌려준다.
rego에서 지원하는 내부 함수: https://www.openpolicyagent.org/docs/latest/policy-reference/
소개한 내역을 테스트: https://play.openpolicyagent.org/p/t3qhGBhIey
이 정책관련하여 세부 파일을 살펴본 결과
정제되지 않은 파일로 부터 매우 세심하게 관리된 결과물은 아니다. (신뢰도 하락)
구체적으로 뭔가를 막아주기 위해 만들어 졌다기 보다는 메시지와 결과를 모으는 용도로 만들어 졌을 것으로 추정된다.
일단 메시지가 장황하고 길다
kubescape를 사용해보면 문제점들을 전체 스캔을 통해 통계낸다.
rego의 특징 중 하나의 신규 룰추가의 간결함도 보여주는 부분이 있다.
취약점 버전이 추가로 발견된다면 파일의 최하단에 is_vulnerable_version(version)를 추가하기만 하면 된다.
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.