Validating/Mutating Admission Policy 살펴보기
Validating/Mutating Admission Policy 살펴보기 (Explore Validating and Mutating Admission Policies)
이 페이지에서는 Common Expression Language(CEL)를 사용해 리소스를 검증(validate)하거나 변경(mutate)할 수 있는 선언적 admission policy 를 시험해 볼 수 있어요.
쿠버네티스 1.37은 두 종류의 admission policy를 지원해요:
이 튜토리얼은 두 종류의 admission policy를 모두 다뤄요.
출처: 문서
본문
시작하기 전에 (Before you begin)
쿠버네티스 클러스터가 있어야 하고, kubectl 명령줄 도구가 클러스터와 통신하도록 구성되어 있어야 해요. 이 튜토리얼은 제어 플레인 호스트로 동작하지 않는 노드가 최소 두 개 있는 클러스터에서 실행하는 것이 권장돼요. 아직 클러스터가 없다면 minikube로 만들거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있어요:
Admission policy를 정의하려면 클러스터 관리자여야 해요. 학습 중인 클러스터에 관리자 접근이 있는지 확인해요.
ValidatingAdmissionPolicy에는 다음이 필요해요:
- 버전 1.30 이상을 실행하는 클러스터.
MutatingAdmissionPolicy에는 다음이 필요해요:
- 버전 1.36 이상을 실행하는 클러스터.
버전을 확인하려면 kubectl version 을 실행해요. 더 오래된 쿠버네티스 버전을 실행 중이라면 해당 버전의 문서를 확인해요.
선언적 admission policy란 무엇인가요?
선언적 admission policy는 admission webhook에 대한 선언적이고 프로세스 내(in-process) 대안을 제공해요.
Common Expression Language (CEL)로 정책 규칙을 선언함으로써, 이 정책들은 API 서버 안에서 직접 평가돼요.
이 정책들은 매우 구성 가능하며, 정책 작성자가 클러스터 관리자가 필요로 하는 대로 매개변수화되고 리소스에 범위 지정될 수 있는 로직을 정의할 수 있게 해줘요.
admission policy의 API 유형
두 유형의 정책은 서로 다른 목적을 가져요.
ValidatingAdmissionPolicy는 제약 강제(enforcing constraints) 를 위한 것이에요.
MutatingAdmissionPolicy는 admission 중 리소스 수정(modifying resources during admission) 을 위한 것이에요.
정책 요소 (Policy elements)
적용된 각 정책은 항상 정책 객체(ValidatingAdmissionPolicy 또는 MutatingAdmissionPolicy)와 별도의 바인딩 객체(ValidatingAdmissionPolicyBinding 또는 MutatingAdmissionPolicyBinding)를 가져요.
매개변수(parameters) 를 사용할 수도 있는데, 이는 선택적 이에요. 자세한 내용은 parameter resources(ValidatingAdmissionPolicy) 또는 parameter resources(MutatingAdmissionPolicy)를 참고해요.
정책 객체는 Common Expression Language (CEL)를 사용해 정책의 추상 로직을 설명해요. 예를 들어 ValidatingAdmissionPolicy는 레플리카 한도를 강제하거나 특정 라벨이 존재하는지 확인할 수 있는 반면, MutatingAdmissionPolicy는 네임스페이스에 기본 라벨을 추가하는 것 같은 리소스를 수정할 수 있어요.
바인딩 객체는 정책을 클러스터에 연결하고 범위 지정을 제공해요. ValidatingAdmissionPolicyBinding 또는 MutatingAdmissionPolicyBinding은 정책을 특정 리소스에 연결해요. 특정 리소스 하위 집합에만 정책을 강제하려면 바인딩이 정책의 범위를 좁히는 곳(matchResources 사용)이에요.
매개변수는 정책 동작에 대한 구성을 정의와 분리할 수 있게 해줘요. 매개변수 리소스는 API에서 사용할 수 있는 쿠버네티스 리소스를 가리켜요. 그것들은 내장 API 유형(예: ConfigMap)이거나 사용자 정의 리소스일 수 있어요. 그러면 정책 바인딩이 spec.paramRef 를 사용해 실제 매개변수 리소스를 참조해요.
정책에 매개변수가 필요하지 않으면 spec.paramKind 를 지정하지 않고 두어요.
CEL 표현식
두 종류의 정책 모두 Common Expression Language (CEL)로 알려진 표현 언어에 의존해요. 더 배우려면 쿠버네티스의 CEL을 읽어요.
CEL이 처음이라면 아주 간단한 표현식(예: false || true)을 작성하며 연습해 보세요. CEL Playground에서 CEL 표현식을 테스트할 수 있어요.
정책 동작 (Policy actions)
각 admission policy 바인딩은 정책이 어떻게 강제되는지 선언하기 위해 하나 이상의 동작을 지정해야 해요.
ValidatingAdmissionPolicyBinding
ValidatingAdmissionPolicyBinding의 경우 지원되는 validationActions 는:
위반하거나 오류가 발생한 정책 검사는 이러한 동작에 따라 강제돼요. failurePolicy 에 의해 정의된 실패는 failurePolicy 가 Fail(또는 지정되지 않음)로 설정된 경우에만 이러한 동작에 따라 강제돼요.
정책에 대한 감사 로깅에 대한 자세한 내용은 Audit Annotations: validation failures를 참고해요.
Deny와 Warn을 함께 사용할 수 없어요. 이 조합은 API 응답 본문과 HTTP Warning: 헤더 양쪽에서 검증 실패를 중복하게 되기 때문이에요.
MutatingAdmissionPolicyBinding
MutatingAdmissionPolicyBinding의 경우 동작은 항상 객체를 변경하는 것이에요.
JSON Patch 또는 쿠버네티스 apply configuration 을 사용할 수 있어요.
검증을 통한 강제
이제 ValidatingAdmissionPolicy를 정의해 보세요.
다음은 어떤 Deployment든 여러 레플리카가 있어야 한다고 요구하는 ValidatingAdmissionPolicy의 예시예요.
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: "enforce-multiple-replicas-deployments"
annotations:
kubernetes.io/description: "Require that matching Deployments have multiple replicas"
spec:
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
resources: ["deployments"] # the Deployment API
operations: ["CREATE", "UPDATE"] # the API verb, converted to upper case
validations:
- expression: "object.spec.replicas >= 2"
spec.validations 은 Common Expression Language (CEL)을 사용해 요청을 검증하는 CEL 표현식을 포함해요. 표현식이 false로 평가되면 검증 검사는 spec.failurePolicy 필드에 따라 강제돼요.
이런 정책을 작성해 적용해요.
또는 미리 만들어진 매니페스트를 적용하려면:
kubectl apply --server-side -f https://k8s.io/examples/access/manifest-admission-control/vap-min-replicas.yaml
이것만으로는 아무것도 하지 않아요.
0개 또는 1개 레플리카의 Deployment를 만들어 볼 수 있어요. (다른 정책이 방지하지 않으면) 동작할 거예요.
동작하게 하려면 ValidatingAdmissionPolicyBinding을 정의해요.
새 정책을 강제할 네임스페이스를 선택해요.
다음은 만든 정책에 대한 예시 ValidatingAdmissionPolicyBinding이에요:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: enforce-multiple-replicas-deployments-binding
spec:
policyName: "enforce-multiple-replicas-deployments"
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: default # change this to match the namespace you're using
주의:
네임스페이스에 대해 전체/관리자 접근이 있는 사람은 누구나 그 라벨에 쓸 수 있어요. 여기에는 네임스페이스에서 라벨을 삭제하는 것도 포함돼요.
kubernetes.io/metadata.name 라벨은 보호되지만 다른 라벨을 사용한다면 선택한 라벨을 신뢰된 사용자만 제거하거나 편집할 수 있는지 확인해야 해요.
그 예시 YAML에 기반한 매니페스트를 작성해요(default 네임스페이스를 사용한다면 변경 없이 사용할 수 있어요). 그 매니페스트를 kubectl apply 로 적용해요.
정책 테스트
이제 정책을 테스트해요. Deployment 만들기를 시도한 다음 kubectl scale 로 0 레플리카로 축소해 보세요. 무슨 일이 일어날까요?
ValidatingAdmissionPolicyBinding을 Deny 대신 다른 검증 동작으로 변경할 수 있어요. Warning 검증 동작을 선택하고 Deployment를 0 레플리카로 축소하려 하면 무슨 일이 일어날까요?
참고:
ValidatingAdmissionPolicyBinding을 방금 경고만 하도록 변경했다면 문제가 있어요…
이름이 틀렸어요! ValidatingAdmissionPolicyBinding이나 관련 ValidatingAdmissionPolicy를 사람들에게 경고만 하도록 변경한다면 정책 이름도 바꿔야 하는지 확인해야 해요. 이름이 사람들을 오도하지 않도록 하기 위해 정책 이름을 바꿀 거예요.
기존 리소스는 영향을 받지 않아요
0개 또는 1개 레플리카의 Deployment가 있고 ValidatingAdmissionPolicyBinding을 다시 Deny 모드로 변경해도 기존 리소스에는 영향을 미치지 않아요.
(Deployment를 최소 2개 레플리카로 확장하도록 시도하려면 컨트롤러](/docs/concepts/architecture/controller/)를 사용하는 등 다른 방식으로 달성할 수 있어요.)
ValidatingAdmissionPolicy에 대한 내용은 여기까지예요. 이제 MutatingAdmissionPolicy를 배워볼 거예요.
생성되거나 변경될 때 리소스 수정하기
이 예시를 위해 Pod security admission을 사용해 시스템 네임스페이스 외의 네임스페이스가 Pod 보안 표준을 강제하도록 하려 한다고 상상해 보세요.
검증과 유사하게 admission 중 리소스를 수정할 수 있는 MutatingAdmissionPolicy를 만들 수 있어요. 수정해야 하는 API 유형은 Namespace예요.
이 중 일부를 수행하는 MutatingAdmissionPolicy는 다음과 같아요:
---
# Caution: read the notes for this default-pod-security-baseline policy before
# you apply it, so that you understand the information security consequences.
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
name: "default-pod-security-baseline"
annotations:
kubernetes.io/description: "Default new namespaces to enforce the Baseline pod security standard"
spec:
reinvocationPolicy: IfNeeded
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["namespaces"]
matchConditions:
- name: "exclude-system-namespaces"
# If the name begins with "kube-", it's a system namespace.
# Assume that the API server or cluster management tooling applies relevant defaults.
expression: "!object.metadata.name.startsWith('kube-')"
- name: "no-existing-pod-security-label"
expression: "!('pod-security.kubernetes.io/enforce' in object.metadata.labels)"
mutations:
- patchType: "ApplyConfiguration"
applyConfiguration:
expression: "Object{metadata: Object.metadata{labels: {'pod-security.kubernetes.io/enforce': 'baseline'}}}"
주의:
이 정책은 기본값 을 설정해요. Namespace를 업데이트할 수 있는 사람은 네임스페이스에서 pod-security.kubernetes.io/enforce 라벨을 제거할 수 있어요.
이것이 무엇을 의미하는지 확실하지 않다면 Security 문서를 읽거나 외부 정보 보안 조언을 받아요.
그 정책을 적용하려면:
kubectl apply --server-side -f https://k8s.io/examples/access/manifest-admission-control/default-pod-security-baseline.yaml
이 정책을 활성화하려면 MutatingAdmissionPolicyBinding이 필요해요. 예:
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicyBinding
metadata:
name: default-pod-security-baseline
spec:
# the name of the MutatingAdmissionPolicy to apply
policyName: default-pod-security-baseline
정책 테스트
example 이라는 새 네임스페이스를 만들어 보세요:
kubectl create ns example
그 라벨을 살펴보세요:
kubectl describe ns example
Pod security admission 강제 수준을 지정하지 않았는데도 라벨이 설정됐어요.
다음으로 보안 설정을 우회할 방법을 찾을 수 있는지 확인해 보세요. 다른 Namespace의 YAML 매니페스트를 만들어요:
apiVersion: v1
kind: Namespace
metadata:
name: another-example
labels:
pod-security.kubernetes.io/enforce: privileged
kubectl apply --server-side 로 로컬 매니페스트에서 그 네임스페이스를 만들 수 있어요. 동작할까요?
그렇고, 새 네임스페이스는 privileged 파드 실행을 허용해요.
이 admission policy는 검증하거나 제한하도록 설정되지 않았어요. 기본값을 제공하지만 직접 설정할 수 있어요.
하지만 mutating admission을 validating admission policy와 결합해 무언가를 강제하면서도 준수하기 쉽게 만들 수 있어요. (튜토리얼은 이것을 설명하지 않지만 할 수 있어요.)
유용한 기본값을 제공한다는 것은 사람들이 아무것도 설정하지 않을 때 오류 메시지만 보는 것보다 더 나은 결과를 얻는다는 뜻이에요. 모든 네임스페이스가 최소한 baseline 표준을 강제해야 한다는 검증 규칙이 있었다고 상상해 보세요. 그 규칙을 모르는 사람은 무언가를 배포하려 하다가 네임스페이스를 만들 때 즉시 오류 메시지를 볼 거예요.
매개변수 리소스 사용
매개변수 리소스는 정책 구성을 정의와 분리할 수 있게 해줘요. 정책은 매개변수 리소스의 그룹·버전·종류(GVK로도 알려진)를 개략 설명하는 paramKind 를 정의할 수 있어요. 그러면 정책 바인딩이 특정 매개변수 리소스로 구성된 바인딩 범위에 그 정책을 묶어요.
---
# Caution: read the notes for this default-pod-security-configurable policy before
# you apply it, so that you understand the information security consequences.
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
name: "default-pod-security-configurable"
annotations:
kubernetes.io/description: "Default new namespaces to enforce the chosen pod security standard"
spec:
reinvocationPolicy: IfNeeded
paramKind:
apiVersion: v1
kind: ConfigMap
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["namespaces"]
matchConditions:
- name: "exclude-system-namespaces"
# If the name begins with "kube-", it's a system namespace.
# Assume that the API server or cluster management tooling applies relevant defaults.
expression: "!object.metadata.name.startsWith('kube-')"
- name: "no-existing-pod-security-label"
expression: "!('pod-security.kubernetes.io/enforce' in object.metadata.labels)"
mutations:
- patchType: "ApplyConfiguration"
applyConfiguration:
expression: "Object{metadata: Object.metadata{labels: {'pod-security.kubernetes.io/enforce': params.data.default}}}"
다음은 샘플 MutatingAdmissionPolicyBinding이에요:
---
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicyBinding
metadata:
name: default-pod-security-configurable
spec:
# the name of the MutatingAdmissionPolicy to apply
policyName: default-pod-security-configurable
# parameters to use
paramRef:
# if the ConfigMap is missing or empty, don't set a default
# (but do allow namespace creation)
parameterNotFoundAction: Allow
# where to find the parameter
namespace: kube-system
name: default-pod-security-standard
그리고 kube-system 네임스페이스에 넣을 샘플 ConfigMap은 다음과 같아요:
---
apiVersion: v1
kind: ConfigMap
metadata:
namespace: kube-system
name: default-pod-security-standard
data:
default: baseline # could also be "restricted"
둘 다 정의해요. ConfigMap을 먼저 만들어야 해요. 바인딩이 매개변수 리소스가 이미 존재하기를 기대하므로(나중에 변경할 계획이라도).
이제 이전 MutatingAdmissionPolicyBinding을 삭제해요:
kubectl delete mutatingadmissionpolicybindings/default-pod-security-baseline
그리고 새 네임스페이스를 만들어요:
kubectl create ns yet-another-example
kubectl describe ns yet-another-example
라벨이 기본값으로 설정됐나요?
매개변수 변경하기
# This starts an editor that lets you change .data.default for the parameter
kubectl --namespace kube-system edit configmap default-pod-security-standard
변경한 후 네임스페이스를 하나 더 만들어 보세요. 무슨 일이 일어날까요?
정리 (Clean up)
생성된 리소스를 제거하려면 다음 명령을 실행해요:
kubectl delete validatingadmissionpolices/enforce-multiple-replicas-deployments \
validatingadmissionpolicybindings/enforce-multiple-replicas-deployments
kubectl delete mutatingadmissionpolicies/default-pod-security-baseline \
mutatingadmissionpolicybindings/default-pod-security-baseline
kubectl delete mutatingadmissionpolicies/default-pod-security-configurable \
mutatingadmissionpolicybindings/default-pod-security-configurable
kubectl --namespace kube-system delete configmaps/default-pod-security-standard
kubectl delete namespaces/example namespaces/another-example namespaces/yet-another-example
만든 테스트 Pod나 테스트 Namespace가 있다면 그것도 정리해요.