공부/Kubernetes

Kubernetes Audit (감사로그)

토고미 2021. 8. 9. 18:56

k8s에는 audit이라는 기능이 있다. 한국어로는 감사로그라고 한다.

오늘은 이 audit에 대해 써본다.

 

공식 문서는 아래 링크에서 확인할 수 있다.

https://kubernetes.io/docs/tasks/debug-application-cluster/audit/

 

1. Audit이란?

k8s에서는 어떤 작업을 하든 반드시 kube-apiserver를 거쳐야한다. 그리고 이 kube-apiserver에 요청을 보낼 때는 HTTP API를 통해 이루어진다. audit이란 kube-apiserver를 오가는 API를 로깅하는 것이다. 

 

기본적으로 auidt 로그는 event라는 객체의 구조로 아래와 같은 정보들을 담을 수 있다.

  • 콜을 날린 클라이언트가 누구인지
  • 언제 날렸는지
  • 어떤 리소스에 대한 것인지
  • HTTP의 성공 여부
  • verb는 무엇인지
  • URI
  • request body, reponse body

등등

 

또, 언제 event 객체를 생성할지 총 4가지의 경우에 대해 정할 수 있다.

  • RequestReceived : audit 핸들러가 request를 받자마자
  • ResponseStarted : response 헤더만 보내지고, reponse body는 아직 안보내졌을 때. long-running request의 경우에만 발생한다 (예: watch)
  • ResponseComplete : response body까지 전부 보내진 후
  • Panic : panic이 발생 했을 때

어느정도 수준의 정보를 남길 것인지도 선택할 수 있다.

아래로 갈 수록 더 많은 양의 정보를 담고 있다.

  • None : 남기지 않는다
  • Metadata : request의 metadata (request한 유저, timestamp, resource, verb, 등) (body는 포함하지 않음)
  • Request : Metadata + request body (response body는 포함하지 않음) (non-resource request에는 적용 안 됨)
  • RequestResponse : Request + response body (non-resource request에는 적용 안 됨)

 

마지막으로 audit을 저장하는 방식 2가지가 있다.

  • 파일시스템으로 작성
  • 웹훅을 이용해 외부 서버에 HTTP 콜 전송

 

요약하자면

audit이란 kube-apiserver를 오가는 HTTP 콜을 로깅하는 것이고,

audit을 언제, 얼마만큼의 정보로, 어떤 방식으로 저장할지 커스터마이징 할 수 있다.

 

2. Audit 설정하기

언제, 얼마만큼의 정보로 저장할지는 policy 객체를 작성하여 설정할 수 있다.

아래는 공식 문서에 나와있는 예제의 일부이다.

apiVersion: audit.k8s.io/v1
kind: Policy
# RequestReceived 단계의 request는 무시
omitStages:
  - "RequestReceived"
rules:
  # "pod"에 대해서 RequestResponse 수준의 정보를 로깅
  - level: RequestResponse
    resources:
    - group: ""
      # pod의 subresource들은 해당되지 않음
      resources: ["pods"]
  # "pods/log", "pods/status"에 대해서 Metadata 수준의 정보를 로깅
  - level: Metadata
    resources:
    - group: ""
      resources: ["pods/log", "pods/status"]

  # "controller-leader" 이름의 configmap은 로깅하지 않는다
  - level: None
    resources:
    - group: ""
      resources: ["configmaps"]
      resourceNames: ["controller-leader"]

  # "system:kube-proxy"유저의 endpoints와 services에 대한 watch 요청은 로깅하지 않는다
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
    - group: "" # core API group
      resources: ["endpoints", "services"]

  # /api*, /version 요청에 대해 request 요청자가 authenticated 유저 그룹에 속해있다면 로깅하지 않는다.
  - level: None
    userGroups: ["system:authenticated"]
    nonResourceURLs:
    - "/api*" # Wildcard matching.
    - "/version"

 

예제처럼 yaml의 array 형식으로 rule을 정의하면 된다.

주의할 점은 룰은 위에서부터 먼저 해당되는 룰에 적용된다는 것이다.

예를 들어, 첫 번째 룰이 pod에 대해 None 수준이라면, 그 밑에 pod에 대해 어떤 룰이 있던 간에 pod에 대한 audit은 로깅되지 않는다.

 

이렇게 policy를 완성했다면 이를 kube-apiserver에게 알려주어야 한다.

kube-apiserver.yaml에서 command 절 밑에 해당 옵션을 추가하면 된다.

$ vi /etc/kubernetes/manifests/kube-apiserver.yaml

 

...
spec:
  containers:
    - command:
    	...
        - --audit-policy-file=/etc/kubernetes/pki/audit-policy.yaml
        ...

policy 파일의 경로가 hostMount가 되어있어야 한다는 점 잊지 말자.

나같은 경우에는 편의를 위해서, 기본적으로 kube-apiserver에 마운트 되어있는 /etc/kuberenets/pki 폴더 밑에 policy파일을 저장시켜 두었다.

 

 

마지막으로 어떤 방식으로 audit을 저장할지 설정하는 방법이다.

policy를 설정한 것과 유사하게, kube-apiserver command 절에 옵션을 추가해주면 된다.

웹훅의 경우에는 추가적으로 config파일을 작성해줘야한다.

 

2-1. 파일로 저장하기

kube-apiserver에 --audit-log-path 설정만 추가해주면 된다.

...
spec:
  containers:
    - command:
    	...
        - --audit-log-path=/where/to/save/audit.log
        ...

추가적으로 로그의 사이즈나 수명도 설정할 수 있다. 자세한 옵션은 공식 문서를 확인 바란다.

 

2-2. 웹훅으로 외부 서버에 보내기

어떤 서버로 보낼지 config 파일 작성 후, 해당 config 파일을 kube-apiserver.yaml에 옵션 추가하면 된다.

$ vi audit-webhook-config
apiVersion: v1
kind: Config
clusters:
- cluster:
     certificate-authority-data: {인증서}
     server: {외부 웹훅 서버 주소}
  name: audit-webhook-service
contexts:
- context:
    cluster: audit-webhook-service
  name: audit-webhook-context
current-context: audit-webhook-context

config에 들어가있는 인증서와 웹훅 서버가 가지고 있는 인증서가 서로를 신뢰할 수 있어야 통신이 된다.

만약 웹훅 서버가 같은 k8s 클러스터내에 있다면, server 값에 service DNS 주소를 쓸 수 있다.

k8s-api-server는 이 Config를 참조해서, server에 적힌 곳으로 POST call을 보내게 된다.

 

마지막으로 이 파일을 kube-apiserver.yaml에 추가하면 된다.

...
spec:
  containers:
    - command:
    	...
        - --audit-webhook-config-file=/etc/kubernetes/pki/audit-webhook-config
        ...

webhook 방식 역시 batch 모드, batch 버퍼 사이즈 등의 여러 옵션이 있다. 자세한 옵션은 공식 문서를 확인 바란다.

 

웹훅 설정 후 오류 발생 시, 추가 설정사항

위 설정만 추가하면 kube-apiserver에서 아래와 같은 에러를 내뱉으며 웹훅이 동작하지 않을 수도 있다.

E0118 06:50:06.669847       1 metrics.go:109] Error in audit plugin 'webhook' affecting 329 audit events: Post "http://go-test-api-server-service.default.svc/hello": dial tcp: lookup go-test-api-server-service.default.svc on 8.8.8.8:53: no such host

이럴 땐 kube-apiserver.yaml에 spec.dnsPolicy를 추가해줘야 한다.

아래와 같이 spec절 밑에 dnsPolicy: ClusterFirstWithHostNet 를 추가해주자

spec:
  ....
  dnsPolicy: ClusterFirstWithHostNet
  ....
status: {}

 

끝~!