보안 체크리스트

보안 체크리스트 (Security Checklist)

Kubernetes 클러스터의 보안을 보장하기 위한 기본 체크리스트예요.

이 체크리스트는 각 주제에 대한 더 포괄적인 문서로 연결되는 기본 지침 목록을 제공하는 것을 목표로 해요. 완전한 것을 주장하지 않으며 계속 진화하도록 의도됐어요.

이 문서를 읽고 사용하는 방법:

  • 주제의 순서는 우선순위 순서를 반영하지 않아요.
  • 일부 체크리스트 항목은 각 섹션 목록 아래의 단락에서 자세히 설명돼요.

주의:

체크리스트는 그 자체만으로 좋은 보안 태세를 달성하기에 충분하지 않아요. 좋은 보안 태세는 지속적인 주의와 개선을 요구하지만, 체크리스트는 보안 준비를 향한 끝없는 여정의 첫 단계가 될 수 있어요. 이 체크리스트의 일부 권장 사항은 특정 보안 요구에 너무 제한적이거나 너무 느슨할 수도 있어요. Kubernetes 보안은 "일률적인 것"이 아니므로, 각 체크리스트 항목 범주를 장단점에 따라 평가해야 해요.

  • 부트스트래핑 후 system:masters 그룹이 사용자 또는 컴포넌트 인증에 사용되지 않아요.
  • kube-controller-manager가 --use-service-account-credentials를 활성화한 상태로 실행돼요.
  • 루트 인증서가 보호돼요(오프라인 CA, 또는 효과적인 접근 제어가 있는 관리형 온라인 CA).
  • 중간 및 리프 인증서의 만료일이 미래로 3년 이내예요.
  • 정기적인 접근 검토 절차가 존재하고, 검토가 24개월 이상 간격을 두지 않아요.
  • 인증 및 권한 부여와 관련된 지침은 Role Based Access Control 모범 사례를 따라요.

부트스트래핑 후에는 사용자나 컴포넌트가 system:masters로 Kubernetes API에 인증하지 않아야 해요. 마찬가지로 모든 kube-controller-manager를 system:masters로 실행하는 것도 피해야 해요. 사실 system:masters는 관리자 사용자가 아니라 비상 탈출(break-glass) 메커니즘으로만 사용해야 해요.

네트워크 보안

  • 사용 중인 CNI 플러그인이 네트워크 정책을 지원해요.
  • 인그레스 및 이그레스 네트워크 정책이 클러스터의 모든 워크로드에 적용돼요.
  • 각 네임스페이스 내에 모든 pod를 선택하고 모든 것을 거부하는 기본 네트워크 정책이 마련돼 있어요.
  • 적절하다면 서비스 메시가 클러스터 내부의 모든 통신을 암호화하는 데 사용돼요.
  • Kubernetes API, kubelet API, etcd가 인터넷에 공개적으로 노출되지 않아요.
  • 워크로드에서 클라우드 메타데이터 API로의 접근이 필터링돼요.
  • LoadBalancer와 ExternalIPs 사용이 제한돼요.

여러 CNI(Container Network Interface) 플러그인이 pod가 통신할 수 있는 네트워크 리소스를 제한하는 기능을 제공해요. 이는 대부분 규칙을 정의하는 네임스페이스 범위 리소스인 네트워크 정책을 통해 수행돼요. 모든 이그레스와 인그레스를 차단하고 모든 pod를 선택하는 기본 네트워크 정책은 어떤 워크로드도 빠뜨리지 않도록 허용 목록 접근 방식을 채택하는 데 유용할 수 있어요.

모든 CNI 플러그인이 전송 중 암호화를 제공하는 것은 아니에요. 선택한 플러그인에 이 기능이 없으면 서비스 메시를 사용해 그 기능을 제공하는 것이 대안이 될 수 있어요.

컨트롤 플레인의 etcd 데이터 저장소는 접근을 제한하는 제어 장치가 있어야 하고 인터넷에 공개적으로 노출되지 않아야 해요. 또한 mTLS(상호 TLS)를 사용해 안전하게 통신해야 해요. 이 인증 기관은 etcd에 고유해야 해요.

Kubernetes API 서버에 대한 외부 인터넷 접근은 API를 공개적으로 노출하지 않도록 제한해야 해요. 관리형 Kubernetes 배포판 중 상당수가 기본적으로 API 서버를 공개적으로 노출하고 있으니 주의하세요. 그런 경우 배스천 호스트(bastion host)를 사용해 서버에 접근할 수 있어요.

kubelet API 접근은 제한되고 공개적으로 노출되지 않아야 해요. --config 플래그로 구성 파일을 지정하지 않았을 때의 기본 인증 및 권한 부여 설정은 과도하게 관대해요.

Kubernetes 호스팅에 클라우드 제공자를 사용한다면, pod에서 클라우드 메타데이터 API 169.254.169.254로의 접근도 정보를 누출할 수 있으므로 필요하지 않으면 제한하거나 차단해야 해요.

LoadBalancer와 ExternalIPs 사용 제한에 대해서는 CVE-2020-8554: LoadBalancer 또는 ExternalIPs를 사용한 중간자 공격DenyServiceExternalIPs 어드미션 컨트롤러를 참고하세요.

Pod 보안

  • 워크로드에 대한 create, update, patch, delete 권한의 RBAC 권한은 필요한 경우에만 부여돼요.
  • 적절한 Pod Security Standards 정책이 모든 네임스페이스에 적용되고 시행돼요.
  • request와 같거나 낮은 limit로 워크로드에 메모리 limit가 설정돼요.
  • 민감한 워크로드에는 CPU limit가 설정될 수 있어요.
  • 지원하는 노드에서 프로그램에 적절한 syscall 프로필로 Seccomp가 활성화돼요.
  • 지원하는 노드에서 프로그램에 적절한 프로필로 AppArmor 또는 SELinux가 활성화돼요.

RBAC 권한 부여는 중요하지만 Pod의 리소스에 대한 권한 부여를 제공할 만큼 세분화될 수 없어요(또는 Pod를 관리하는 어떤 리소스에 대해서도). 세분화의 유일한 수준은 리소스 자체의 API 동사예요. 예를 들어 Pod에 대한 create처럼요. 추가 어드미션 없이는 이 리소스를 생성할 권한이 클러스터의 스케줄링 가능한 노드에 대한 직접적이고 제한 없는 접근을 허용해요.

Pod Security StandardsPodSpec에서 보안과 관련해 필드를 어떻게 설정할 수 있는지 제한하는 세 가지 정책, privileged, baseline, restricted를 정의해요. 이러한 표준은 기본적으로 활성화된 새로운 Pod Security 어드미션으로 네임스페이스 수준에서 시행되거나, 타사 어드미션 웹훅으로 시행될 수 있어요. 이것이 대체하는 제거된 PodSecurityPolicy 어드미션과 달리, Pod Security 어드미션은 어드미션 웹훅 및 외부 서비스와 쉽게 결합될 수 있다는 점을 기억하세요.

Pod Security 어드미션 restricted 정책은 Pod Security Standards 세트 중 가장 제한적인 정책으로, 여러 모드warn, audit, enforce로 동작할 수 있어서 보안 모범 사례에 따라 가장 적절한 보안 컨텍스트를 점진적으로 적용할 수 있어요. 그럼에도 pod의 보안 컨텍스트는 특정 사용 사례에 대해 미리 정의된 보안 표준 위에서 pod가 가질 수 있는 권한과 접근을 제한하기 위해 별도로 조사해야 해요.

Pod Security에 대한 실습 튜토리얼은 블로그 글 Kubernetes 1.23: Pod Security Graduates to Beta를 참고하세요.

메모리 및 CPU limit는 pod가 노드에서 소비할 수 있는 메모리와 CPU 리소스를 제한하기 위해 설정해야 해요. 그래야 악의적이거나 침해된 워크로드의 잠재적 DoS 공격을 방지할 수 있어요. 이러한 정책은 어드미션 컨트롤러로 시행될 수 있어요. CPU limit는 사용량을 조절(throttle)해서 오토스케일링 기능이나 효율성(예: 사용 가능한 CPU 리소스로 최선을 다해(best effort) 프로세스를 실행하는 것)에 의도하지 않은 영향을 줄 수 있다는 점을 기억하세요.

주의:

request보다 큰 메모리 limit는 전체 노드를 OOM 문제에 노출시킬 수 있어요.

Seccomp 활성화

Seccomp는 secure computing mode의 약자로, Linux 커널 2.6.12부터 있는 기능이에요. 프로세스의 권한을 샌드박싱해서 사용자 공간에서 커널로 호출할 수 있는 호출(call)을 제한하는 데 사용할 수 있어요. Kubernetes는 노드에 로드된 seccomp 프로필을 Pod와 컨테이너에 자동으로 적용하게 해 줘요.

Seccomp는 컨테이너 안에서 사용할 수 있는 Linux 커널 syscall 공격 표면을 줄여 워크로드의 보안을 향상시킬 수 있어요. seccomp 필터 모드는 BPF를 활용해 특정 syscall의 허용 또는 거부 목록(프로필이라고 함)을 만들어요.

Kubernetes 1.27부터 모든 워크로드에 기본 seccomp 프로필로 RuntimeDefault를 사용할 수 있어요. 이 주제에 대한 보안 튜토리얼을 이용할 수 있어요. 또한 Kubernetes Security Profiles Operator는 클러스터에서 seccomp의 관리와 사용을 용이하게 하는 프로젝트예요.

참고:

Seccomp는 Linux 노드에서만 사용할 수 있어요.

AppArmor 또는 SELinux 활성화

AppArmor

AppArmor는 MAC(Mandatory Access Control)을 구현하고 시스템 로그를 통한 더 나은 감사를 제공하는 쉬운 방법을 제공할 수 있는 Linux 커널 보안 모듈이에요. 지원하는 노드에서는 기본 AppArmor 프로필이 시행되거나, 커스텀 프로필을 구성할 수 있어요. seccomp와 마찬가지로 AppArmor도 프로필로 구성돼요. 각 프로필은 허용되지 않은 리소스에 대한 접근을 차단하는 enforcing 모드 또는 위반만 보고하는 complain 모드 중 하나로 실행돼요. AppArmor 프로필은 어노테이션과 함께 컨테이너별로 시행되어, 프로세스가 정확히 적절한 권한만 가질 수 있게 해요.

참고:

AppArmor는 Linux 노드에서만 사용할 수 있으며 일부 Linux 배포판에서 활성화돼 있어요.

SELinux

SELinux 역시 MAC(Mandatory Access Control)을 포함한 접근 제어 보안 정책을 지원하는 메커니즘을 제공할 수 있는 Linux 커널 보안 모듈이에요. SELinux 레이블은 컨테이너나 pod에 해당 securityContext 섹션을 통해 할당할 수 있어요.

참고:

SELinux는 Linux 노드에서만 사용할 수 있으며 일부 Linux 배포판에서 활성화돼 있어요.

로그 및 감사

  • 감사 로그는, 활성화된 경우 일반적인 접근으로부터 보호돼요.

Pod 배치

  • Pod 배치는 애플리케이션의 민감도 계층에 따라 수행돼요.
  • 민감한 애플리케이션이 노드에서 격리되어 실행되거나 특정 샌드박스 런타임으로 실행돼요.

민감도 계층이 다른 Pod, 예를 들어 애플리케이션 pod와 Kubernetes API 서버는 별도의 노드에 배포해야 해요. 노드 격리의 목적은 애플리케이션 컨테이너 탈출이 더 높은 민감도의 애플리케이션에 대한 직접적인 접근을 제공해 클러스터 내에서 쉽게 이동(pivot)하는 것을 방지하는 거예요. pod가 우연히 같은 노드에 배포되지 않도록 이 분리를 시행해야 해요. 다음 기능들로 시행할 수 있어요.

  • Node Selectors 키-값 쌍으로, pod 스펙의 일부이며 어떤 노드에 배포할지 지정해요. PodNodeSelector 어드미션 컨트롤러로 네임스페이스와 클러스터 수준에서 시행할 수 있어요.
  • PodTolerationRestriction 관리자가 네임스페이스 내에서 허용된 톨러레이션을 제한할 수 있게 하는 어드미션 컨트롤러예요. 네임스페이스 내의 Pod는 기본 및 허용된 톨러레이션 세트를 제공하는 네임스페이스 객체 어노테이션 키에 지정된 톨러레이션만 사용할 수 있어요.
  • RuntimeClass RuntimeClass는 컨테이너 런타임 구성을 선택하기 위한 기능이에요. 컨테이너 런타임 구성은 Pod의 컨테이너를 실행하는 데 사용되며, 성능 오버헤드 비용으로 호스트로부터 더 많거나 더 적은 격리를 제공할 수 있어요.

시크릿 (Secrets)

  • ConfigMap이 기밀 데이터를 보관하는 데 사용되지 않아요.
  • Secret API에 대해 저장 시 암호화(at rest encryption)가 구성돼 있어요.
  • 적절하다면 타사 스토리지에 저장된 시크릿을 주입하는 메커니즘이 배포되어 사용 가능해요.
  • 서비스 어카운트 토큰이 필요하지 않은 pod에 마운트되지 않아요.
  • 만료되지 않는 토큰 대신 바운드 서비스 어카운트 토큰 볼륨이 사용돼요.

pod에 필요한 시크릿은 ConfigMap 같은 대안이 아닌 Kubernetes Secret 안에 저장해야 해요. etcd에 저장된 Secret 리소스는 저장 시 암호화해야 해요.

시크릿이 필요한 pod는 시크릿을 볼륨을 통해 자동으로 마운트해야 해요. 가급적 emptyDir.medium 옵션처럼 메모리에 저장하는 방식을 사용해요. Secrets Store CSI Driver처럼 타사 스토리지에서 시크릿을 볼륨으로 주입하는 메커니즘도 사용할 수 있어요. 이는 pod에 시크릿에 대한 서비스 어카운트 RBAC 접근을 제공하는 것보다 우선적으로 수행해야 해요. 이렇게 하면 시크릿을 환경 변수나 파일로 pod에 추가할 수 있어요. 환경 변수 방식은 파일의 권한 메커니즘과 달리 로그의 크래시 덤프와 Linux에서 환경 변수의 비밀적이지 않은 특성 때문에 누출에 더 취약할 수 있다는 점을 기억하세요.

서비스 어카운트 토큰은 필요하지 않은 pod에 마운트하지 않아야 해요. 이는 서비스 어카운트 내에서 네임스페이스 전체에 적용되도록 automountServiceAccountTokenfalse로 설정하거나 pod에 대해 특별히 설정해서 구성할 수 있어요. Kubernetes v1.22 이상에서는 시간 제한이 있는 서비스 어카운트 자격 증명을 위해 바운드 서비스 어카운트를 사용하세요.

이미지 (Images)

  • 컨테이너 이미지에서 불필요한 콘텐츠를 최소화해요.
  • 컨테이너 이미지가 권한 없는 사용자로 실행되도록 구성돼요.
  • 컨테이너 이미지에 대한 참조는 (태그 대신) sha256 다이제스트로 하거나, 어드미션 제어를 통한 배포 시 이미지의 디지털 서명을 검증해 이미지의 출처를 확인해요.
  • 컨테이너 이미지는 생성 중과 배포 시 정기적으로 스캔되고, 알려진 취약한 소프트웨어는 패치돼요.

컨테이너 이미지에는 패키지하는 프로그램을 실행하는 데 필요한 최소한의 것만 포함해야 해요. 가급적 최소한의 가능한 베이스에서 프로그램과 그 의존성만 빌드해요. 특히 프로덕션에 사용되는 이미지에는 셸이나 디버깅 유틸리티를 포함하지 않아야 해요. 임시 디버그 컨테이너로 문제 해결을 할 수 있기 때문이에요.

Dockerfile의 USER 지시문을 사용해 권한 없는 사용자로 직접 시작하도록 이미지를 빌드해요. 보안 컨텍스트는 이미지 매니페스트에 지정되지 않았더라도 runAsUserrunAsGroup으로 컨테이너 이미지를 특정 사용자와 그룹으로 시작할 수 있게 해 줘요. 다만 이미지 레이어의 파일 권한 때문에 이미지를 수정하지 않고는 새 권한 없는 사용자로 프로세스를 시작하는 것이 불가능할 수도 있어요.

이미지 참조에 이미지 태그 사용, 특히 latest 태그를 피해요. 태그 뒤의 이미지는 레지스트리에서 쉽게 수정될 수 있어요. 이미지 매니페스트에 고유한 전체 sha256 다이제스트를 사용하는 것을 선호해요. 이 정책은 ImagePolicyWebhook으로 시행될 수 있어요. 이미지 서명은 배포 시 어드미션 컨트롤러로 자동 검증되어 그 진위성과 무결성을 확인할 수도 있어요.

컨테이너 이미지를 스캔하면 컨테이너 이미지와 함께 심각한 취약점이 클러스터에 배포되는 것을 방지할 수 있어요. 이미지 스캔은 컨테이너 이미지를 클러스터에 배포하기 전에 완료해야 하며, 보통 CI/CD 파이프라인의 배포 프로세스 일부로 수행돼요. 이미지 스캔의 목적은 CVSS(Common Vulnerability Scoring System) 점수 같은 컨테이너 이미지의 가능한 취약점과 그 예방에 대한 정보를 얻는 거예요. 이미지 스캔 결과를 파이프라인 준수 규칙과 결합하면, 올바르게 패치된 컨테이너 이미지만 프로덕션에 들어가게 돼요.

어드미션 컨트롤러

  • 적절한 어드미션 컨트롤러 선택이 활성화돼 있어요.
  • pod 보안 정책이 Pod Security Admission 또는/그리고 웹훅 어드미션 컨트롤러에 의해 시행돼요.
  • 어드미션 체인 플러그인과 웹훅이 안전하게 구성돼 있어요.

어드미션 컨트롤러는 클러스터의 보안을 향상시키는 데 도움이 될 수 있어요. 하지만 API 서버를 확장하므로 적절히 보안을 유지해야 하는 자체적인 위험을 제시할 수 있어요.

다음 목록은 클러스터와 애플리케이션의 보안 태세를 향상시키기 위해 고려할 수 있는 여러 어드미션 컨트롤러를 보여줘요. 문서의 다른 부분에서 참조될 수 있는 컨트롤러를 포함해요.

첫 번째 어드미션 컨트롤러 그룹은 기본적으로 활성화된 플러그인을 포함해요. 무엇을 하는지 알지 않는 한 활성화 상태로 두는 것을 고려하세요.

  • CertificateApproval 승인하는 사용자가 인증서 요청을 승인할 권한이 있는지 추가 권한 부여 검사를 수행해요.
  • CertificateSigning 서명하는 사용자가 인증서 요청에 서명할 권한이 있는지 추가 권한 부여 검사를 수행해요.
  • CertificateSubjectRestriction system:masters의 'group'(또는 'organization attribute')을 지정하는 모든 인증서 요청을 거부해요.
  • LimitRanger LimitRange API 제약을 시행해요.
  • MutatingAdmissionWebhook 웹훅을 통해 커스텀 컨트롤러를 사용할 수 있게 해요. 이 컨트롤러는 검토하는 요청을 변형할 수 있어요.
  • PodSecurity Pod Security Policy의 대체이며, 배포된 Pod의 보안 컨텍스트를 제한해요.
  • ResourceQuota 리소스 과다 사용을 방지하기 위해 리소스 쿼터를 시행해요.
  • ValidatingAdmissionWebhook 웹훅을 통해 커스텀 컨트롤러를 사용할 수 있게 해요. 이 컨트롤러는 검토하는 요청을 변형하지 않아요.

두 번째 그룹은 기본적으로 활성화되지 않지만 일반적으로 사용 가능한(GA) 상태이며 보안 태세를 개선하기 위해 권장되는 플러그인을 포함해요.

  • DenyServiceExternalIPs Service.spec.externalIPs 필드의 모든 새로운 사용을 거부해요. CVE-2020-8554: LoadBalancer 또는 ExternalIPs를 사용한 중간자 공격에 대한 완화책이에요.
  • NodeRestriction kubelet의 권한을 자신이 소유한 pods API 리소스나 자신을 나타내는 node API 리소스만 수정하도록 제한해요. 또한 kubelet이 node-restriction.kubernetes.io/ 어노테이션을 사용하는 것을 방지해요. 이 어노테이션은 kubelet의 자격 증명에 접근할 수 있는 공격자가 Pod 배치를 제어된 노드로 조작하는 데 사용될 수 있어요.

세 번째 그룹은 기본적으로 활성화되지 않지만 특정 사용 사례에서 고려할 수 있는 플러그인을 포함해요.

  • AlwaysPullImages 태그된 이미지의 최신 버전 사용을 강제하고 배포자에게 이미지 사용 권한이 있는지 확인해요.
  • ImagePolicyWebhook 웹훅을 통해 이미지에 대한 추가 제어를 시행할 수 있게 해요.

더 알아보기