AWS 메타데이터로 외부 접근 잠그기

AWS 메타데이터로 외부 접근 잠그기 (Locking Down External Access Using AWS Metadata)

이 문서는 AWS 메타데이터를 기반으로 정책을 강제하도록 Cilium을 사용하는 방법을 소개해요. 머신에서 단일 노드 Cilium 환경을 실행하는 과정을 자세히 안내합니다. Kubernetes 실행 경험이 있는 사용자라면 15-30분 정도 걸리도록 설계되었어요.

출처: Locking Down External Access Using AWS Metadata

본문

Cilium 설정

이 가이드는 minikube를 포함한 Cilium 설치 방법 모두에서 동작해요. 단, 배포의 cilium-operator 파드가 AWS API 서버에 도달할 수 있어야 합니다. 다만 이 메커니즘의 가장 흔한 사용처는 AWS에서 실행되는 Kubernetes 클러스터이므로, 가이드와 함께 시도해 보는 것을 권장합니다: Cilium 빠른 설치.

AWS secrets 생성

Cilium을 설치하기 전에 AWS 토큰이 담긴 새 Kubernetes Secret을 클러스터에 추가해야 해요. 이 Secret은 Cilium이 ToGroups 정책을 구현하는 데 필요한 정보를 AWS API에서 수집하도록 해줍니다.

AWS 액세스 키와 IAM 역할

새 액세스 토큰을 만들려면 다음 가이드를 사용할 수 있어요. 이 키들에는 특정 권한이 설정되어 있어야 합니다:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ec2:Describe*",
            "Resource": "*"
        }
    ]
}

액세스 토큰이 준비되면, 각 빈 문자열을 관련 값으로 base64 인코딩해 대체한 다음 다음 secret을 추가해야 해요:

apiVersion: v1
kind: Secret
metadata:
  name: cilium-aws
  namespace: kube-system
type: Opaque
data:
  AWS_ACCESS_KEY_ID: ""
  AWS_SECRET_ACCESS_KEY: ""
  AWS_DEFAULT_REGION: ""

각 값을 생성하려면 base64 커맨드라인 유틸리티를 사용할 수 있어요. 예를 들어:

$ echo -n "eu-west-1"  | base64
ZXUtd2VzdC0x

이 secret은 AWS API에 연결하는 데 사용될 AWS 자격 증명을 저장해요.

$ kubectl create -f cilium-secret.yaml

자격 증명이 올바른지 검증하려면 디버깅을 위해 다음 파드를 만들 수 있어요:

apiVersion: v1
kind: Pod
metadata:
  name: testing-aws-pod
  namespace: kube-system
spec:
  containers:
  - name: aws-cli
    image: amazon/aws-cli
    command: ['sh', '-c', 'sleep 3600']
    env:
      - name: AWS_ACCESS_KEY_ID
        valueFrom:
          secretKeyRef:
            name: cilium-aws
            key: AWS_ACCESS_KEY_ID
            optional: true
      - name: AWS_SECRET_ACCESS_KEY
        valueFrom:
          secretKeyRef:
            name: cilium-aws
            key: AWS_SECRET_ACCESS_KEY
            optional: true
      - name: AWS_DEFAULT_REGION
        valueFrom:
          secretKeyRef:
            name: cilium-aws
            key: AWS_DEFAULT_REGION
            optional: true

사용 가능한 모든 AWS 인스턴스를 나열하려면 다음 명령을 사용할 수 있어요:

$ kubectl  -n kube-system exec -ti testing-aws-pod -- aws ec2 describe-instances

secret이 생성되고 검증되면, secret의 자격 증명을 가져오기 위해 cilium-operator 파드를 재시작해야 해요. 이를 위해 기존 cilium-operator 파드를 찾아 삭제하면 자동으로 다시 생성됩니다:

$ kubectl get pods -l name=cilium-operator -n kube-system
NAME                              READY   STATUS    RESTARTS   AGE
cilium-operator-7c9d69f7c-97vqx   1/1     Running   0          36h

$ kubectl delete pod cilium-operator-7c9d69f7c-97vqx

이 데모에서 coredns가 올바르게 동작하는 것이 중요해요. coredns의 상태를 알려면 다음 명령을 실행할 수 있습니다:

$ kubectl get deployment -l k8s-app=kube-dns -n kube-system
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
coredns    2         2         2            2           13h

여기서 최소 하나의 파드가 사용 가능해야 해요.

AWS 보안 그룹 구성

Cilium의 AWS 메타데이터 필터링 기능은 파드의 하위 집합(Kubernetes 라벨로 식별)과 대상 EC2 ENI 집합(AWS 보안 그룹 구성원으로 식별) 사이의 통신을 명시적으로 화이트리스트할 수 있게 해줘요.

이 예제에서 대상 EC2 탄력적 네트워크 인터페이스는 단일 AWS 보안 그룹('sg-0f2146100a88d03c3')의 구성원인 EC2 인스턴스에 연결되어 있어요. class=xwing 라벨이 있는 파드만 클러스터 밖으로 해당 보안 그룹의 대상 네트워크 인터페이스에 연결할 수 있어야 합니다.

이를 활성화하려면 Kubernetes 워커 노드 역할을 하는 VM이 파드가 접근하는 대상 VM에 트래픽을 보낼 수 있어야 해요. 이를 달성하는 한 가지 방법은 모든 Kubernetes 워커 VM을 단일 'k8s-worker' 보안 그룹에 넣고, Cilium toGroups 정책에서 참조하는 모든 보안 그룹이 'k8s-worker' 보안 그룹에서 오는 연결에 대해 (모든 포트의) allow all ingress 규칙을 갖도록 하는 것입니다. 그러면 Cilium 필터링이 정책에 의해 허용된 파드만 대상 VM에 도달할 수 있게 보장해요.

샘플 정책 만들기

데모 애플리케이션 배포:

여기서는 다른 가이드에서 사용되는 데모 애플리케이션을 사용할 거예요. 이 매니페스트는 deathstar, tiefighter, xwing 세 개의 마이크로서비스 애플리케이션을 만듭니다. 이 경우 기존 AWS 인스턴스와의 통신을 보호하는 데 xwing 마이크로서비스만 사용할 거예요.

$ kubectl create -f https://raw.githubusercontent.com/cilium/cilium/1.20.2/examples/minikube/http-sw-app.yaml
service "deathstar" created
deployment "deathstar" created
deployment "tiefighter" created
deployment "xwing" created

Kubernetes는 백그라운드에서 파드와 서비스를 배포해요. kubectl get pods,svc를 실행하면 진행 상황을 알 수 있습니다. 각 파드는 파드가 준비되는 Running 상태에 도달할 때까지 여러 상태를 거칩니다.

$ kubectl get pods,svc
NAME                             READY     STATUS    RESTARTS   AGE
po/deathstar-76995f4687-2mxb2    1/1       Running   0          1m
po/deathstar-76995f4687-xbgnl    1/1       Running   0          1m
po/tiefighter                    1/1       Running   0          1m
po/xwing                         1/1       Running   0          1m

NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
svc/deathstar    ClusterIP   10.109.254.198   <none>        80/TCP    3h
svc/kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP   3h

정책 언어 (Policy Language):

ToGroups 규칙은 AWS 같은 클라우드 제공자와 관련해 정책을 정의하는 데 사용할 수 있어요.

---
kind: CiliumNetworkPolicy
apiVersion: cilium.io/v2
metadata:
  name: to-groups-sample
  namespace: default
spec:
  endpointSelector:
    matchLabels:
      org: alliance
      class: xwing
  egress:
  - toPorts:
    - ports:
      - port: '80'
        protocol: TCP
    toGroups:
    - aws:
        securityGroupsIds:
        - 'sg-0f2146100a88d03c3'

이 정책은 파드 xwing이 ID가 sg-0f2146100a88d03c3인 보안 그룹의 모든 AWS 탄력적 네트워크 인터페이스로 트래픽을 보낼 수 있게 허용해요.

파생 정책이 적용되었는지 검증

ToGroups 규칙이 있는 새 정책이 추가될 때마다 동등한 정책("파생 정책(derivative policy)"이라고도 함)이 생성됩니다. 이 정책은 ToGroups 명세에 해당하는 CIDR 집합, 즉 지정된 보안 그룹의 일부인 모든 네트워크 인터페이스의 IP를 포함해요. IP 목록은 주기적으로 업데이트됩니다.

$ kubectl get cnp
NAME                                                             AGE
to-groups-sample                                                 11s
to-groups-sample-togroups-044ba7d1-f491-11e8-ad2e-080027d2d952   10s

결국 파생 정책은 ToCIDR 섹션에 IP를 포함하게 됩니다:

$ kubectl get cnp to-groups-sample-togroups-044ba7d1-f491-11e8-ad2e-080027d2d952
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  creationTimestamp: 2018-11-30T11:13:52Z
  generation: 1
  labels:
    io.cilium.network.policy.kind: derivative
    io.cilium.network.policy.parent.uuid: 044ba7d1-f491-11e8-ad2e-080027d2d952
  name: to-groups-sample-togroups-044ba7d1-f491-11e8-ad2e-080027d2d952
  namespace: default
  ownerReferences:
  - apiVersion: cilium.io/v2
    blockOwnerDeletion: true
    kind: CiliumNetworkPolicy
    name: to-groups-sample
    uid: 044ba7d1-f491-11e8-ad2e-080027d2d952
  resourceVersion: "34853"
  selfLink: /apis/cilium.io/v2/namespaces/default/ciliumnetworkpolicies/to-groups-sample-togroups-044ba7d1-f491-11e8-ad2e-080027d2d952
  uid: 04b289ba-f491-11e8-ad2e-080027d2d952
specs:
- egress:
  - toCIDRSet:
    - cidr: 34.254.113.42/32
    - cidr: 172.31.44.160/32
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP
  endpointSelector:
    matchLabels:
      any:class: xwing
      any:org: alliance
      k8s:io.kubernetes.pod.namespace: default
  labels:
  - key: io.cilium.k8s.policy.name
    source: k8s
    value: to-groups-sample
  - key: io.cilium.k8s.policy.uid
    source: k8s
    value: 044ba7d1-f491-11e8-ad2e-080027d2d952
  - key: io.cilium.k8s.policy.namespace
    source: k8s
    value: default
  - key: io.cilium.k8s.policy.derived-from
    source: k8s
    value: CiliumNetworkPolicy
status:
  nodes:
    k8s1:
      enforcing: true
      lastUpdated: 2018-11-30T11:28:03.907678888Z
      localPolicyRevision: 28
      ok: true

파생 규칙은 다음 정보를 포함해야 해요:

  • metadata.OwnerReferences: ToGroups 정책에 대한 정보를 포함.
  • specs.Egress.ToCIDRSet: 부모 정책의 명세에 해당하는 인스턴스의 사설·공용 IP 목록.
  • status: 정책이 아직 적용되고 있는지 여부와 마지막으로 업데이트된 시점.

xwing의 엔드포인트 상태는 egress 연결에 대해서만 정책 적용이 활성화되어야 해요:

$ kubectl exec -q -it -n kube-system cilium-85vtg -- cilium-dbg endpoint get 23453 -o jsonpath='{$[0].status.policy.realized.policy-enabled}'
egress

이 예제에서 xwing 파드는 34.254.113.42/32와 172.31.44.160/32에만 연결할 수 있고, 다른 IP로의 연결은 거부됩니다.

더 알아보기 (Learn more)