E-Kyverno 기본 실습
개요
이 문서는 간단하게 Kyverno를 설치하고 실습해보는 문서이다.
공식 문서에 나온 내용을 단순하게 따라해보는 정도로, 감을 익히기 위해 진행한다.
Vagrant로 Kubernetes v1.32 - Penelope 버전을 구축하고 진행한다.
설치 후 확인
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm install kyverno kyverno/kyverno -n kyverno --create-namespace
간단하게 헬름으로 설치하면 다음과 같은 리소스들이 추가된다.
이들은 전부 CRD로 추가된 것을 확인할 수 있다.
검증
첫 번째로, default 네임스페이스에서 파드 exec을 하려는 행위를 막아보자.
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: deny-pod-exec
spec:
background: false
rules:
- name: pods
match:
any:
- resources:
kinds:
- Pod/exec
validate:
failureAction: Enforce
message: "How dare you execute Pod?"
deny:
conditions:
all:
- key: "{{ request.namespace }}"
operator: Equals
value: default
첫번째는 이렇게 작성했다.
일단 모든 파드에 대해서 검사를 하긴 하는데, 그중에서 default 네임스페이스에 대해서는 exec을 막는다.
background는 policyreport와만 관련되는 줄 알았는데, 이걸 false로 해줘야 정상적으로 exec을 막을 수 있는 정책이 생성되더라.
이런 식의 출력이 나온다.
k exec debug -- whoami
성공적으로 exec 이 막히는 것이 확인된다.
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: deny-hostpath
spec:
validationFailureAction: Enforce
background: true
rules:
- name: host-path
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
HostPath volumes are forbidden. The field spec.volumes[*].hostPath must be unset.
pattern:
spec:
=(volumes):
- X(hostPath): "null"
다음으로는 hostPath를 함부로 생성하는 파드 요청을 막는다.
이번에는 pattern을 이용해서 특정 필드를 직접적으로 들어가 검사를 수행한다.
괄호를 치는 문법은 앵커라 하여, 조건문을 넣는 것과 같은 효과를 낸다.
앞에 등호가 들어가면 해당 값이 있을 경우 하위 필드를 진행하라는 뜻이다.
volumes가 있다면, 하위 필드를 검사하게 된다.
앞에 X가 붙으면 해당 값이 있으면 안 된다는 것이다.
(뒤에 null이 있던 말던 평가되지 않음)
그러니 hostPath라는 필드가 존재하는 순간 검증에서 fail을 반환하는 정책인 것이다.
apiVersion: v1
kind: Pod
metadata:
name: "hostpath-pod"
spec:
containers:
- name: hostpath-pod
image: "debian-slim:latest"
volumeMounts:
- name: localtime
mountPath: /etc/localtime
volumes:
- name: localtime
hostPath:
path: /usr/share/zoneinfo/Asia/Taipei
슬쩍 호스트패스를 사용해보려 했으나..
이런 에러를 내면서 실패한다.
변형
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: mut-replica
spec:
rules:
- name: deployment
match:
any:
- resources:
kinds:
- Deployment
mutate:
patchStrategicMerge:
spec:
replicas: 2
변형에서는 디플로이먼트의 레플리카 무조건 2개로 고정시키는 식으로 짰다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: rep
labels:
app: rep
spec:
selector:
matchLabels:
app: rep
replicas: 10
template:
metadata:
labels:
app: rep
spec:
containers:
- name: rep
image: nginx
imagePullPolicy: IfNotPresent
디플로이먼트에서 레플리카는 10개로 설정했다.
그러나 막상 만들어지는 레플리카를 보면 무조건 2개로 고정된다.
설정이 제대로 적용된 것이다.
그러나 이 설정은 scale 서브리소스를 쓰는 것에 대한 제한을 걸진 않기에, k scale
을 해서 이후에 레플리카를 늘리는 것은 가능하다.
한 정책에 대해, 적용됐던 리포트가 각 대상 별로 만들어지는 것이 확인된다.
k describe policyreports.wgpolicyk8s.io
변형에 대한 policyreport를 확인할 수 있는데, 변형됐다는 사실이 고스란히 남는다.
처음 이걸 보면서 들었던 생각은, Etcd 용량 부하를 일으킬 것이라는 것이었다.
etcd는 8기가라는 제한이 걸려있는데, 이런 정책 리포트가 계속 추가된다면 이 제한을 자칫 우습게 넘겨버리는 상황이 나올 법하다.
남는 걸까 생각했는데, 그런 식으로 기록을 남겨버리면 순식간에 etcd의 용량이 부족해질 것이다.
왜 쿠버네티스 감사를 별도의 로컬 공간이나 웹훅으로 날리는지만 생각해봐도 당연하게 도출되는 고려사항이다.
이걸 걱정하고 있었는데 보니까 1.12버전부터 리포트 서버를 아예 따로 두는 식으로 기능을 추가한 모양이다.[1]
내부적으로는 postgreSQL을 사용하며, 설치도 꽤나 간편하게 되는 것으로 보인다.[2]
방식은 API Aggregation Layer를 통해 기존의 api를 대체하는 방식이다.
내 식견으로는, 이게 부가 세팅을 하는 방식으로 제공될 게 아니라 아예 기본으로 제공해야 한다.
여기에서도 리포트 서버가 없을 때의 문제에 대한 언급이 있다.[3]
24년 5월달에 나온 내용인 걸 보아, 시간이 지나면 자연스레 방식을 바꾸지 않을까 싶다.
생성
마지막으로 실습해볼 것은 생성 기능이다.
이것은 어떤 오브젝트가 생성될 때 다른 오브젝트를 같이 만드는 방식으로 동작한다.
이번에는 네임스페이스가 만들어질 때 별도의 서비스어카운트가 만들어지고, 이 서비스 어카운트는 파드 조회가 가능하도록 만들 것이다.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno:generate-sa-role-rolebinding
labels:
rbac.kyverno.io/aggregate-to-background-controller: "true"
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["roles", "rolebindings"]
verbs: ["get", "create", "update", "delete"]
- apiGroups: [""]
resources: ["serviceaccounts"]
verbs: ["create", "update", "delete"]
첫번째로 해주어야 할 것은 kyverno background controller 서비스 어카운트가 각종 조작을 수행할 수 있도록 적절한 권한을 주는 것이다.
aggregation 방식을 이용해 쉽게 서비스 어카운트에 권한을 주는 것이 가능하다.
다행히 생성할 권한이 없다면 알아서 먼저 에러를 뱉어주므로, 이걸 보고 빠르게 세팅을 해주면 된다.
k get clusterrole kyverno:background-controller -oyaml
덕분에 동적으로 별도의 설정 없이도 편하게 세팅이 가능하다.
제대로 세팅했는지는 이렇게도 확인 가능하다.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: gen-sa
spec:
rules:
- name: sa
match:
any:
- resources:
kinds:
- Namespace
exclude:
any:
- resources:
namespaces:
- kube-system
- default
- kube-public
- kyverno
generate:
apiVersion: v1
kind: ServiceAccount
name: pod-reader
namespace: "{{request.object.metadata.name}}"
- name: role
match:
any:
- resources:
kinds:
- Namespace
exclude:
any:
- resources:
namespaces:
- kube-system
- default
- kube-public
- kyverno
generate:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
name: pod-reader
namespace: "{{request.object.metadata.name}}"
data:
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- name: rolebinding
match:
any:
- resources:
kinds:
- Namespace
exclude:
any:
- resources:
namespaces:
- kube-system
- default
- kube-public
- kyverno
generate:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
name: pod-reader
namespace: "{{request.object.metadata.name}}"
data:
subjects:
- kind: ServiceAccount
name: pod-reader
namespace: "{{request.object.metadata.name}}"
roleRef:
apiGroups: rbac.authorization.k8s.io/v1
kind: Role
name: pod-reader
다음으로는 실제 정책을 작성한다.
설정 파일이 너무 길어져서 이걸 조금 묶어서 처리할 수 없나 찾아봤는데, 별도로 한번에 여러 개의 generate 규칙을 작성하는 방법은 없는 것 같다.
어쩔 수 없이 match 부분까지 전부 복붙을 해서 만들어줬다..
k create ns test
바로 네임스페이스를 하나 만들어본다.
성공적으로 내가 원하는 서비스어카운트가 만들어진 것이 보인다.
k auth can-i --as system:serviceaccount:test:pod-reader get po -n test
k -n test get po --as system:serviceaccount:test:pod-reader
해당 서비스어카운트는 적절하게 인가 권한을 부여받았으므로 자신의 네임스페이스에서 파드를 조죄할 수 있다.
결론
공식문서만 따라하려 했는데.. 이 놈의 호기심을 또 주체 못하고 내 맘대로 마구 실험을 했다..
키베르노를 사용해보니, 일단 정말 다양한 설정을 yaml 파일로 할 수 있다는 점이 큰 장점으로 다가왔다.
아직은 많이 익숙하지 않지만, 승인 제어를 하기 위해 간편하게 사용할 수 있다고 생각이 들었다.
정책 작성을 하는 게 마냥 단순하지는 않다.
다만 예시도 많아서 생각보다 쉽게 자료를 찾을 수 있었다.
추가적으로 모니터링을 쉽게 할 수 있다는 것도 장점!
예시들을 둘러보니까 정말 설정할 수 있는 게 많은데, 가령 크룹백에 빠진 파드의 디버그 정보를 수집하는 정책도 만들 수가 있다..[4]
한편으로 조금 신경 쓰였던 건, 너무 올인원이라는 것..?
다양한 커스텀이 가능하다는 것은 장점이면서도 진입 장벽을 높이기도 한다.
그리고 이 방식 저 방식 다 지원하려다보니 이 애드온 자체가 가진 능력을 단기간에 익혀서 발휘하기가 조금 어렵겠다는 생각이 들었다.
문법이.. OPA와 비교를 하며 장점으로 내세우는 부분 중 하나가 다른 언어를 익힐 필요가 없다인데, 막상 해보니 키베르노를 잘 활용하기 위해 익혀야 할 문법도 조금 있는 것 같다.
처음 검증 정책을 만들 때는 logs에 대해 정책을 걸고 싶었다.
보다시피 파드의 서브 리소스인 exec과 log는 api 엔드포인트 방식이 같아서 막연히 log도 막힐 거라 생각했다.
그러나 이후 실습을 하다가 이유를 알게 됐는데, S-exec 명령어가 승인 제어에 걸리는 이유 참고.
검증 부분은 Validation Admission Policy로도 다시금 재현해보면서 차이점을 더 상세히 비교해보려고 한다.
관련 문서
이름 | noteType | created |
---|---|---|
Kyverno | knowledge | 2025-03-17 |
E-Kyverno 기본 실습 | topic/explain | 2025-03-17 |
S-exec 명령어가 승인 제어에 걸리는 이유 | topic/shooting | 2025-03-17 |
참고
https://kyverno.io/blog/2024/04/26/kyverno-1.12-released/#reports-server---alternative-reports-storage ↩︎
https://github.com/kyverno/reports-server/blob/main/docs/INSTALL.md ↩︎
https://kyverno.io/blog/2024/05/29/kyverno-reports-server-the-ultimate-solution-to-scale-reporting/ ↩︎
https://kyverno.io/policies/other/get-debug-information/get-debug-information/ ↩︎