brunch

You can make anything
by writing

C.S.Lewis

by 이권수 Sep 06. 2023

Pod Security Group 동작 원리

보안을 강화하기 위한 Pod Security Group 알아보기!

AWS는 보안그룹을 통해서 네트워크 인터페이스가 붙어 있는 리소스 간 통신을 제어한다. 예컨대 특정 노드에서만 데이터베이스에 접근하도록 제어하고 싶다면, 데이터베이스의 보안그룹에 애플리케이션 노드의 보안그룹만 허용하면 된다. 인프라 보안을 강화하려면, 데이터베이스와 같은 서비스는 필요한 리소스만 접근할 수 있어야 한다. 만약 접근할 필요가 없는데 접근할 수 있는 경로가 있다면 보안상 취약해질 수 있다.


AWS EKS 환경에서도 이 원리는 동일하게 적용된다. EKS 관리형 노드그룹은 컨트롤 플레인과의 통신을 위해 클러스터 보안그룹을 사용한다. 클러스터 보안그룹은 EKS를 생성하면 자동으로 생성된다. 만약 관리형 노드그룹을 사용하면, EKS에서 직접 필요한 보안그룹 규칙을 추가하여 컨트롤 플레인과 데이터 플레인이 서로 통신할 수 있도록 만들어준다. 만약 직접 노드그룹을 생성해서 사용한다면, 관리자가 적합한 규칙을 직접 추가해야 한다. 


이때 파드는 노드에 적용한 보안그룹을 사용한다. 같은 노드에 올라간 파드들은 모두 같은 보안그룹을 사용한다. 만약 일부 파드가 데이터베이스에 접근해야하는 경우 노드에 적용된 보안그룹을 적용하게 되면, 노드에 있는 모든 파드가 데이터베이스에 접근할 수 있다. 


파드는 기본적으로 노드의 보안그룹을 사용한다.


하지만, 관리자는 보안을 강화하기 위해 특정 파드만 데이터베이스에 접근할 수 있도록 만들어야 한다. 그래야 누군가 파드를 임의로 생성해서 데이터베이스를 접근하는 경우를 막을 수 있다. 또는 누군가 노드에 접속하여 데이터베이스에 접속하려고 하는 경우도 막을 수도 있다. 


파드 보안그룹을 적용한 경우, 접근을 상세하게 제어할 수 있다.


이러한 요구사항을 충족시켜 주고자 AWS에서는 파드 보안그룹(Pod Security Group)을 출시했다.

이번 포스트에서 파드 보안그룹과 관련된 내용을 이해하고, 파드 보안그룹의 원리를 간단하게 살펴보자.


AWS VPC CNI

AWS EKS는 기본적으로 amazon-vpc-cni(VPC CNI)를 권장한다. CNI는 파드를 관리할 때 네트워크 설정을 어떻게 할지에 대한 방식이다. VPC CNI를 사용하면 VPC CIDR에 설정한 대역 내에서 파드 IP를 할당받을 수 있다. VPC CIDR를 사용하면 다른 VPC 종속적인 AWS 서비스와 연동하기 편리하다. 또한 관리자는 별도의 네트워크 대역을 관리할 필요가 없다. 따라서 많은 EKS 사용자들이 VPC CNI를 주로 사용한다.


VPC CNI를 설치하면 CNI를 관리하기 위한 aws-node라는 파드가 생성된다. 해당 파드는 데몬 셋(Daemonset)으로 배포되므로 모든 노드에 하나씩 설치된다. aws-node는 Primary ENI를 하나 생성하고, 해당 ENI가 생성된 서브넷에서 Secondary IP 주소를 생성한다. Primary ENI는 Private IP를 가지고 있고, 해당 Private IP가 EC2 Private IP에 등록된다.


kubelet은 컨테이너 생성요청이 오면 먼저 aws-node에 IP 할당을 요청한다. aws-node는 가지고 있는 Secondary IP 리스트 중에 사용하지 않는 IP가 있으면 해당 IP를 반환한다. 즉, Primary ENI에 붙일 수 있는 여러 개의 Secondary IP 리스트를 미리 확보해 놓고, 파드를 생성할 때마다 하나씩 나눠주는 것이다. 여거시 확보했지만 사용하지 않는 Secondary IP를 warm 상태라고 한다. Warm 상태로 보유한 Secondary IP 덕에 aws-node는 빠르게 IP를 전달할 수 있다.


하지만 Secondary IP가 부족해지면 어떻게 될까? 


하나의 ENI에는 제한된 수의 Secondary IP만 연결할 수 있다. 파드가 많아지면 Secondary IP가 고갈될 수 있다. 고갈 상태에 다다르면, aws-node는 새로운 ENI(Secondary ENI)를 생성하여 인스턴스에 붙인다. 새롭게 생성된 ENI는 여분의 Secondary IP를 확보한다. 여분을 확보하고 나면 다시 파드를 생성할 수 있다.


만약 파드가 삭제되면 aws-node는 Secondary IP를 회수한다. 단, 30초 동안은 재사용되지 않도록 쿨타임을 적용한다. Secondary IP가 완전히 회수되면, aws-node는 다른 파드가 생성될 때 해당 IP를 재할당할 수 있다. 만약 warm 상태인 IP나 ENI가 일정 수준 이상되면, aws-node는 IP와 ENI를 다시 VPC에 반환한다.


요컨대, aws-node는 CNI plugin 역할을 하는 파드이고, 해당 파드는 노드의 IP를 사용한다. 대부분의 파드는 Secondary IP를 할당받고, Secondary IP는 하나의 ENI에 연결된다. 실제 파드에서 네트워크 연결이 발생할 때 자신의 IP가 붙어있는 ENI를 사용한다. 그리고 노드에 붙은 ENI에는 기본적으로 노드의 보안 그룹이 연결된다.


ENI를 사용하여 패킷이 나가면서부터는 AWS VPC 영역이다. AWS VPC는 자체 구현에 따라 네트워크 패킷을 주고 받는다. AWS VPC 내부 원리에 대한 내용은 다음의 글을 참조하기 바란다.


Pod Security Group(파드 보안그룹)

파드 보안그룹은 EC2 보안그룹을 쿠버네티스 파드에 적용한 개념이다. 해당 기능은 VPC CNI를 사용할 때만 적용이 가능하고, 일부 인스턴스에서만 지원된다. AWS가 파드 보안그룹을 지원하기 위해 구상한 방식이 바로 ENI Trunking/Branching이다. Trunking/Branching은 AWS가 내부적으로 구현한 ENI 관리 방식으로, 전체적인 과정은 다음과 같다.

https://aws.amazon.com/ko/blogs/containers/introducing-security-groups-for-pods/

1. 먼저 VPC CNI인 aws-node에 ENABLE_POD_ENI 값을 활성화한다.

2. 그러면 ipamd(=aws-node 중 일부)는 모든 노드에 "vpc.amazonaws.com/has-trunk-attached:false" 라벨을 붙여달라고 API 서버에 요청한다.

3. 컨트롤 플레인에 속한 Resouce Controller는 2번에서 설정한 라벨이 부착된 노드를 모니터링한다.

4. 만약 라벨이 부착된 노드가 발견되면, EC2에 "trunk" ENI를 부착한다.

5. 그리고 노드의 Capacity에 "vpc.amazonaws.com/pod-eni"를 확장(Advertise)해서 노드 리소스 제한을 추가한다.

6. 마지막으로 "vpc.amazonaws.com/has-trunk-attached" 라벨의 값을 true로 변경하여, 노드에 trunk ENI가 붙어있음을 표시한다.


위와 같은 과정을 거치면, 실제 EC2에는 Trunk용 ENI가 부착된다. AWS 콘솔에서 찾아보면 EC2 노드에 Private IP가 부여된 ENI가 부착되어 있음을 확인할 수 있다.


https://aws.amazon.com/ko/blogs/containers/introducing-security-groups-for-pods/

이제, SecurityGroupPolicy(SGP)라는 커스텀 리소스를 생성한다. SGP는 파드 보안그룹을 적용하기 위한 정책이다. 만약 파드가 SGP에 정한 정책에 부합하면, Webhook이 요청을 중간에 가로챈다. 그리고 이전에 노드에 Capacity로 적용했던 vpc.amazonaws.com/pod-eni를 1만큼 요청한다.

출처: https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/security-groups-for-pods.html


그러고 나면, 쿠버네티스 스케쥴러가 노드를 할당해 줄 때까지 기다린다. 쿠버네티스 스케쥴러는 vpc.amazonaws.com/pod-eni 여유분까지 고려해서 적합한 노드를 선택한다. 파드가 정상적으로 스케쥴링되면, Resource Controller는 Branch ENI를 만들고 SGP에 연동한 보안그룹을 ENI에 연결한다. 그 후에 해당 branch ENI를 노드에 부착된 Trunk ENI에 연결한다. 마지막으로 설정이 완료된 branch ENI 정보를 파드의 annotation에 추가한다.


여기서 한 가지 알아두어야 할 정보는 branch ENI가 EC2 노드에 연결되지 않는다는 점이다. 즉, branch ENI는 실제 노드 상에서 eth으로 존재하지 않는다.


따라서, 별도의 네트워크 작업을 통해서 branch ENI를 사용하여 파드가 외부와 통신할 수 있도록 설정해야 한다.


그 과정은 다음과 같다.


https://aws.amazon.com/ko/blogs/containers/introducing-security-groups-for-pods/


먼저 CNI Plugin(=aws-node 중 일부)에 네트워크 세팅 작업을 요청한다. CNI Plugin은 Ipamd에 branch ENI 정보를 문의한다. Ipamd는 파드에 붙여놓았던 annotation 정보를 읽고 이를 CNI Plugin에 반환한다. CNI Plugin은 trunk ENI로부터 vlan 디바이스를 생성하고, vlan을 위한 별도의 라우팅 테이블을 생성한다. 해당 라우팅 테이블에 host-veth 정보를 추가한다. 마지막으로 vlan/host-veth 이 새로운 라우팅 테이블을 사용하도록 IP Rule을 추가한다.


이렇게 네트워크 설정이 완료되면, 비로소 파드는 자신에게 연결된 보안그룹을 적용하여 통신할 수 있다. 이때 주의할 점은 파드에서 요청을 보내는 경우 coreDNS에 DNS를 먼저 질의하기 때문에 노드와 파드간 보안그룹 설정이 완료되어 있어야 한다. 만약 파드에서 나가는 TCP/UDP 53번 요청이 CoreDNS가 올라간 노드 보안그룹에서 막힌다면 요청을 보내지 못한다.



파드 보안그룹을 사용하면, 관리자는 파드의 보안을 더욱 강화하고 서비스 간 접근 제어를 수월하게 진행할 수 있다. 하지만 파드 보안그룹이 작동하는 원리를 이해하지 못하면 네트워크 통신에 문제를 겪을 수 있으니 꼭 그림을 그려가면서 이해해 보기 바란다.



참고자료

https://aws.amazon.com/ko/blogs/containers/introducing-security-groups-for-pods/

https://aws.github.io/aws-eks-best-practices/networking/vpc-cni/#overview


매거진의 이전글 How VPC works internally
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari