Host Firewall

Host Firewall

Kubernetes 노드를 위한 보안 정책을 시행하는 Cilium의 호스트 방화벽(Host Firewall) 기능을 소개하는 문서예요. 호스트 정책 활성화부터 감사 모드, 정책 적용, 복구까지 전체 흐름을 다뤄요.

출처: Host Firewall

본문

이 문서는 Kubernetes 노드를 위한 보안 정책을 시행하는 Cilium의 호스트 방화벽에 대한 소개예요.

Cilium 호스트 방화벽이 동작하는 모습의 영상을 eCHO Episode 40: Cilium Host Firewall에서 볼 수도 있어요.

Cilium에서 호스트 방화벽 활성화 (Enable the Host Firewall in Cilium)

Helm 저장소를 설정하세요.

$ helm repo add cilium https://helm.cilium.io/

Cilium 차트는 OCI 레지스트리(Quay.io와 Docker Hub)에서도 사용할 수 있어요. 별도 설정이 필요 없어요. oci:// URL로 직접 설치하면 돼요. 차트 서명 검증과 다이제스트 기반 설치를 포함한 자세한 내용은 OCI Registry 섹션을 참고하세요.

Helm으로 Cilium 릴리스를 배포하세요.

$ helm install cilium cilium/cilium --version 1.20.2 \
   --namespace kube-system \
   --set hostFirewall.enabled=true \
   --set devices='{ethX,ethY}'
$ helm install cilium oci://quay.io/cilium/charts/cilium 1.20.2 \
   --namespace kube-system \
   --set hostFirewall.enabled=true \
   --set devices='{ethX,ethY}'

devices 플래그는 eth0 같은 Cilium이 구성되는 네트워크 디바이스를 가리켜요. 이 옵션을 생략하면 Cilium이 호스트 방화벽이 적용되는 인터페이스를 자동 감지해요. 결과 인터페이스는 cilium-dbg status 명령 출력에서 확인할 수 있어요.

$ kubectl exec -n kube-system ds/cilium -- \
     cilium-dbg status | grep 'Host firewall'

이 시점에서 Cilium 관리 노드는 네트워크 정책을 시행할 준비가 됐어요.

노드에 라벨 부착 (Attach a Label to the Node)

이 가이드에서 호스트 정책은 node-access=ssh 라벨이 있는 노드에만 적용돼요. 따라서 먼저 클러스터의 노드에 이 라벨을 부착해야 해요.

$ export NODE_NAME=k8s1
$ kubectl label node $NODE_NAME node-access=ssh
node/k8s1 labeled

호스트 엔드포인트에 대한 정책 감사 모드 활성화 (Enable Policy Audit Mode for the Host Endpoint)

호스트 정책은 노드로/노드로부터의 연결에 대한 접근 제어를 시행해요. 호스트 정책을 가져올 때 Cilium이 노드 접근을 차단하거나(kube-apiserver와의 통신 차단 같은) 클러스터의 정상 동작을 깨뜨리지 않도록 특별히 주의해야 해요. 이런 문제를 피하려면 호스트 방화벽을 감사(audit) 모드로 전환하고, 호스트 정책을 시행하기 전에 그 영향을 검증하세요.

경고: 정책 감사 모드가 활성화되면 어떤 네트워크 정책도 시행되지 않으므로 이 설정은 프로덕션 배포에 권장되지 않아요.

참고: 감사 모드는 cilium-agent 재시작 후에도 유지되지 않아요. 에이전트가 재시작되면 기존 호스트 정책을 즉시 시행해요.

주어진 노드의 호스트 엔드포인트에 대해 정책 감사 모드를 활성화하고 상태를 확인하려면 다음 명령을 사용하세요.

$ CILIUM_NAMESPACE=kube-system
$ CILIUM_POD_NAME=$(kubectl -n $CILIUM_NAMESPACE get pods -l "k8s-app=cilium" -o jsonpath="{.items[?(@.spec.nodeName=='$NODE_NAME')].metadata.name}")
$ alias kexec="kubectl -n $CILIUM_NAMESPACE exec $CILIUM_POD_NAME --"
$ HOST_EP_ID=$(kexec cilium-dbg endpoint list -o jsonpath='{[?(@.status.identity.id==1)].id}')
$ kexec cilium-dbg status | grep 'Host firewall'
Host firewall:           Enabled   [eth0]
$ kexec cilium-dbg endpoint config $HOST_EP_ID PolicyAuditMode=Enabled
Endpoint 3353 configuration updated successfully
$ kexec cilium-dbg endpoint config $HOST_EP_ID | grep PolicyAuditMode
PolicyAuditMode        : Enabled

호스트 네트워크 정책 적용 (Apply a Host Network Policy)

호스트 정책은 Node Selector를 사용해 노드 라벨을 일치시켜 정책이 적용되는 노드를 식별해요. 호스트 정책은 호스트 네임스페이스에만 적용되며, 호스트 네트워킹 파드도 포함해요. 파드 간 또는 파드와 클러스터 외부 사이의 통신에는 적용되지 않아요(호스트 네트워킹 파드인 경우는 제외).

다음 정책은 node-access=ssh 라벨을 가진 모든 노드에 적용돼요. 클러스터 외부에서의 통신은 TCP/22와 ICMP(ping) 에코 요청만 허용해요. 클러스터에서 호스트로의 모든 통신은 허용돼요.

apiVersion: "cilium.io/v2"
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: "demo-host-policy"
spec:
  description: ""
  nodeSelector:
    matchLabels:
      node-access: ssh
  ingress:
  - fromEntities:
    - cluster
  - toPorts:
    - ports:
      - port: "22"
        protocol: TCP
  - icmps:
    - fields:
      - type: EchoRequest
        family: IPv4

이 정책을 적용하려면 다음을 실행하세요.

$ kubectl create -f https://raw.githubusercontent.com/cilium/cilium/1.20.2/examples/policies/host/demo-host-policy.yaml
ciliumclusterwidenetworkpolicy.cilium.io/demo-host-policy created

호스트는 cilium-dbg endpoint list 명령 출력에서 reserved:host 라벨을 가진 특별한 엔드포인트로 나타나요. 이 명령으로 호스트 정책의 상태를 검사하세요.

$ kexec cilium-dbg endpoint list
ENDPOINT   POLICY (ingress)   POLICY (egress)   IDENTITY   LABELS (source:key[=value])                       IPv6                 IPv4           STATUS
           ENFORCEMENT        ENFORCEMENT
266        Disabled           Disabled          104        k8s:io.cilium.k8s.policy.cluster=default          f00d::a0b:0:0:ef4e   10.16.172.63   ready
                                                           k8s:io.cilium.k8s.policy.serviceaccount=coredns
                                                           k8s:io.kubernetes.pod.namespace=kube-system
                                                           k8s:k8s-app=kube-dns
1687       Disabled (Audit)   Disabled          1          k8s:node-access=ssh                                                                   ready
                                                           reserved:host
3362       Disabled           Disabled          4          reserved:health                                   f00d::a0b:0:0:49cf   10.16.87.66    ready

이 예제에서 호스트 엔드포인트의 정책 시행이 ingress 트래픽에 대해 감사 모드이고, egress 트래픽에 대해서는 비활성화된 것을 볼 수 있어요.

호스트 정책을 환경에 맞게 조정 (Adjust the Host Policy to Your Environment)

호스트 엔드포인트가 감사 모드로 실행되는 한, 정책에서 허용되지 않는 통신은 버려지지 않아요. 대신 cilium-dbg monitor에 action audit로 보고돼요. 이 보고 덕분에 감사 모드는 예상치 못한 연결 끊김을 피하기 위해 호스트 정책을 여러분의 환경에 맞게 조정할 수 있게 해 줘요.

$ kexec cilium-dbg monitor -t policy-verdict --related-to $HOST_EP_ID
Policy verdict log: flow 0x0 local EP ID 1687, remote ID 6, proto 1, ingress, action allow, match L3-Only, 192.168.60.12 -> 192.168.60.11 EchoRequest
Policy verdict log: flow 0x0 local EP ID 1687, remote ID 6, proto 6, ingress, action allow, match L3-Only, 192.168.60.12:37278 -> 192.168.60.11:2379 tcp SYN
Policy verdict log: flow 0x0 local EP ID 1687, remote ID 2, proto 6, ingress, action audit, match none, 10.0.2.2:47500 -> 10.0.2.15:6443 tcp SYN

cilium monitor의 출력에서 네트워크 정책을 파생하는 방법에 대한 자세한 내용은 Creating Policies from Verdicts 가이드의 Observe policy verdicts와 Create the Network Policy를 참고하세요.

Entities 기반 규칙은 호스트 정책과 함께 사용할 때 편리해요. 예를 들어 모든 원격 노드(remote-node)나 전체 클러스터(cluster) 같은 전체 목적지 클래스를 대상으로 한 통신을 허용할 때 유용해요.

경고: 클러스터에 접근하거나 클러스터가 제대로 동작하는 데 필요한 통신이 거부되지 않도록 하세요. 감사 모드를 끄기 전에 모두 action allow로 나타나는지 확인하세요.

정책 감사 모드 비활성화 (Disable Policy Audit Mode)

클러스터 외부에서 호스트로의 모든 필요한 통신이 허용된다고 확신하면, 정책 감사 모드를 비활성화해 호스트 정책을 시행하세요.

$ kexec cilium-dbg endpoint config $HOST_EP_ID PolicyAuditMode=Disabled
Endpoint 3353 configuration updated successfully

Ingress 호스트 정책이 이제 시행되는 것으로 나타나야 해요.

$ kexec cilium-dbg endpoint list
ENDPOINT   POLICY (ingress)   POLICY (egress)   IDENTITY   LABELS (source:key[=value])                       IPv6                 IPv4           STATUS
           ENFORCEMENT        ENFORCEMENT
266        Disabled           Disabled          104        k8s:io.cilium.k8s.policy.cluster=default          f00d::a0b:0:0:ef4e   10.16.172.63   ready
                                                           k8s:io.cilium.k8s.policy.serviceaccount=coredns
                                                           k8s:io.kubernetes.pod.namespace=kube-system
                                                           k8s:k8s-app=kube-dns
1687       Enabled            Disabled          1          k8s:node-access=ssh                                                                   ready
                                                           reserved:host
3362       Disabled           Disabled          4          reserved:health                                   f00d::a0b:0:0:49cf   10.16.87.66    ready

호스트 정책에 명시적으로 허용되지 않은 통신은 이제 버려져요.

$ kexec cilium-dbg monitor -t policy-verdict --related-to $HOST_EP_ID
Policy verdict log: flow 0x0 local EP ID 1687, remote ID 2, proto 6, ingress, action deny, match none, 10.0.2.2:49038 -> 10.0.2.15:21 tcp SYN

정리 (Clean up)

$ kubectl delete ccnp demo-host-policy
$ kubectl label node $NODE_NAME node-access-

더 읽을거리 (Further Reading)

정책 사용 방법에 대한 추가 세부 정보는 Host Policies 문서를 읽어 보세요. 특히 Host Policies 문제 해결 방법을 이해하려면 Troubleshooting Host Policies 하위 섹션을, 기능의 현재 제한 사항을 이해하려면 Host Policies known issues 섹션을 참고하세요.

비상 복구 (Emergency Recovery)

호스트 정책이 노드에 대한 접근을 제어하므로, 노드에 대한 모든 접근을 버리는 정책을 만들 수 있어요. 특히 Cilium 에이전트가 apiserver에 접근할 수 없게 되면 어떤 정책 갱신이나 삭제도 알 수 없어요. 이 때문에 복구가 복잡해져요.

노드에 대한 out-of-band 접근이 있다면 호스트 정책 시행을 강제로 비활성화하고 통제권을 회복할 수 있어요. 먼저 문제가 되는 호스트 방화벽 정책을 삭제하세요. 그런 다음 노드별로 호스트 정책 시행을 수동으로 비활성화하세요.

Cilium Agent 접근 (Cilium Agent access)

Cilium 에이전트 컨테이너에 접근해야 해요. kubelet이 여전히 네트워크 접근이 가능하다면 kubectl exec을 사용하세요.

$ kubectl -n kube-system exec -ti $(CILIUM_PODNAME) -- bash

이것이 불가능하면 노드에 대한 ssh나 콘솔 접근(예: IPMI)이 필요해요. 그런 다음 crictl exec을 사용하세요.

$ CONTAINERID=$(crictl ps -q --label io.kubernetes.container.name=cilium-agent --label io.kubernetes.pod.namespace=kube-system)
$ crictl exec -ti $CONTAINERID bash

호스트 정책 시행 비활성화 (Disabling Host Policy enforcement)

참고: 이는 호스트 정책 시행을 일시적으로만 비활성화해요. cilium-agent가 재시작되면 다시 호스트 정책을 시행해요.

Cilium 컨테이너에 접근할 수 있게 되면, 내부 호스트 엔드포인트에 대해 감사 모드를 활성화해 호스트 정책 시행을 일시적으로 비활성화할 수 있어요. 감사 모드는 정책 드롭을 경고로 바꿔요.

$ cilium-dbg endpoint config $(cilium-dbg endpoint get -l reserved:host -o 'jsonpath={$[0].id}') PolicyAuditMode=Enabled

이 시점에 노드의 cilium-agent는 apiserver에 다시 연결되고 정책을 동기화해요. 호스트 정책 시행을 다시 활성화하려면 kubectl rollout restart로 Cilium daemonset을 재시작하거나 수동으로 다음을 실행하세요.

$ cilium-dbg endpoint config $(cilium-dbg endpoint get -l reserved:host -o 'jsonpath={$[0].id}') PolicyAuditMode=Disabled

더 알아보기 (Learn more)