[k8s] k8s Security basic 본문
Overview
쿠버네티스 환경에서의 보안이란 쿠버네티스 클러스터의 안정성과 보안성을 유지하는 것을 의미한다.
쿠버네티스는 클러스터의 보안을 위해 많은 기능을 제공하지만, 이번 포스팅에서는 CKA 자격증 준비를 위해 공부했던 세 가지 기능에 대한 소개를 하려고 한다.
- Network Policy
- Role-Based Access Control
- Secret
Network Policy
쿠버네티스의 통신 정책은 기본적으로 모든 Pod 간 통신을 허용한다. 이는 Pod 간 통신을 원활하게 해주지만, 원치 않는 통신에 Pod를 노출시킬 수 있다는 위험 또한 존재한다.
외부의 위협으로부터 Pod를 보호하기 위해 사용되는 쿠버네티스 보안 기능이 바로 네트워크 정책(Network Policy)이다.
네트워크 정책은 Pod 간 통신 또는 다른 네트워크 엔드포인트와 Pod 사이의 통신을 제어하는 기능이다. 네트워크 정책을 사용하면 IP 주소, 포트, 프로토콜 및 레이블(label), 네임스페이스와 같은 다양한 기준에 따라 트래픽을 허용하거나 거부하는 규칙을 정의할 수 있다.
네트워크 정책은 쿠버네티스 네트워크 플러그인 아키텍처를 사용하여 구현되기 때문에 네트워크 플러그인마다 네트워크 정책에 대한 지원 수준에 차이 존재할 수 있다.
네트워크 정책 설정 시 다음 세 가지 요소를 중점으로 살펴보아야 한다.
podSelector
: 정책이 적용될 Pod를 지정한다policyTypes
: 정책이 적용될 트래픽을 지정한다ingress/egress
: 수신 트래픽 / 발신 트래픽에 대한 세부 내용을 지정한다
아래의 예시 YAML 파일을 통해 사용 사례를 살펴보자.
apiVersion: networking.k8s.io/v1 # NetworkPolicy 리소스의 API 버전
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: db # 이 정책이 적용될 Pod를 선택. 여기서는 "role=db" 레이블을 가진 Pod들에 적용됨
policyTypes:
- Ingress # 인바운드 트래픽에 대한 규칙 정의
- Egress # 아웃바운드 트래픽에 대한 규칙 정의
ingress:
- from:
- ipBlock:
cidr: 172.17.0.0/16 # 인바운드 트래픽을 허용할 IP 범위 설정. 여기서는 "172.17.0.0/16" 대역의 IP에서 오는 요청 허용
except:
- 172.17.1.0/24 # 위의 cidr에서 제외할 IP 범위 설정. "172.17.1.0/24" 대역은 제외하여 차단
- namespaceSelector:
matchLabels:
project: myproject # 특정 네임스페이스에서 오는 트래픽을 허용. 여기서는 "project=myproject" 레이블이 있는 네임스페이스에서 오는 요청 허용
- podSelector:
matchLabels:
role: frontend # 특정 레이블을 가진 Pod에서 오는 트래픽을 허용. 여기서는 "role=frontend" 레이블이 있는 Pod에서 오는 요청 허용
ports:
- protocol: TCP # 허용할 프로토콜 지정. 여기서는 TCP만 허용
port: 6379 # 허용할 포트 번호. 여기서는 6379 포트만 허용
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24 # 아웃바운드 트래픽이 허용될 IP 범위 설정. "10.0.0.0/24" 대역으로 나가는 트래픽 허용
ports:
- protocol: TCP # 허용할 프로토콜 지정. 여기서는 TCP만 허용
port: 5978 # 허용할 포트 번호. 여기서는 5978 포트만 허용
위의 YAML 파일은 쿠버네티스의 공식 문서 중 Network Policy 문서의 예제 YAML 파일을 가져온 것이다.
YAML 파일 내에 작성된 주석을 통해 네트워크 정책의 동작 방식에 대해 유심히 살펴보면 클라우드 서비스에서 제공하는 보안 그룹과 유사하게 동작할 것을 예상할 수 있다.
이러한 동작을 통해 Pod에서 동작하는 애플리케이션이 어떤 요청을 받을 수 있고, 어디로 요청 혹은 응답을 보낼 수 있는지 결정하고 통제한다. 이에 더해 Pod의 네트워크 트래픽을 제한하는 것으로 다양한 외부 공격으로부터 클러스터를 보호할 수 있다.
Role-Based Access Control
쿠버네티스에 존재하는 여러 리소스들에 대한 권한을 사용자 및 서비스 계정의 역할을 정의하는 것으로 제어하는 것을 RBAC
, 역할 기반 접근 제어라고 한다.
사용자나 서비스 계정의 역할을 설정하는 Role
, 클러스터 수준에서 역할을 설정하는 ClusterRole
을 사용해 보안 규칙을 정의하고, 정의한 규칙을 사용자의 모음인 Group
에 정의한다.
역할 기반 접근 제어를 설정하기 위해서는 다음 세 가지 요소를 중점적으로 살펴봐야 한다.
Role
: 쿠버네티스 리소스에 대한 권한의 집합이다. 특정 네임스페이스에서만 유효한 권한 할당RoleBinding
: 특정 사용자 혹은 사용자의 계정에Role
을 할당한다ClusterRole
:Role
과 유사하지만, 클러스터 전체에서 유효한 권한을 할당한다.ClusterRoleBinding
을 통해 사용자 또는 사용자의 계정과 연결을 정의해야 한다
다음은 각각 Role
을 생성하여 특정 사용자에게 연결하는 Rolebinding
에 대한 예제 YAML 파일이다.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"] # 접근할 수 있는 리소스 지정
verbs: ["get", "watch", "list"] # 리소스에 대한 작업 권한 부여
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
리소스에 대한 작업 권한을 부여하는 verbs
는 총 9가지가 존재한다.
create
: 새로운 리소스 생성할 수 있는 권한get
: 개별 리소스 세부 정보를 조회할 수 있는 권한list
: 리소스 목록을 조회할 수 있는 권한update
: 기존 리소스 내용 전체를 수정할 수 있는 권한patch
: 기존 리소스 내용을 부분적으로 수정할 수 있는 권한delete
: 특정 리소스를 삭제할 수 있는 권한deletecollection
: 여러 리소스를 한 번에 삭제하는 권한. 일괄 삭제할 때 사용watch
: 리소스의 변경 사항을 실시간으로 모니터링 할 수 있는 권한impersonate
: 다른 사용자 혹은 계정으로 가장하는 권한. 다른 사용자 계정으로 API 요청을 수행할 수 있음
이처럼, 역할 기반 접근 제어를 적용할 경우 클러스터 관리자는 리소스에 대한 접근 권한을 관리할 수 있다. 이를 통해 다양한 사용자와 서비스 계정 간 접근 권한을 분리하여, 권한이 부여되지 않은 사용자가 민감한 데이터를 볼 수 없게끔 보호할 수 있다.
Secret
시크릿은 암호화된 정보, 토큰, 비밀번호와 같은 중요한 데이터를 안전하게 저장하기 위한 리소스다.
시크릿 정보는 컨테이너 이미지에 포함되지 않으며, 애플리케이션을 실행하는 동안 환경 변수 또는 볼륨 마운트를 통해 컨테이너에 전달된다.
시크릿은 일반적으로 Base64
로 인코딩된 Key-Value
쌍으로 정의된다. 인코딩된 시크릿은 컨테이너 런타임시 복호화 되어 사용되므로, 시크릿을 사용하는 애플리케이션은 별도로 복호화 로직을 구현할 필요가 없다.
시크릿은 Pod와 독립적으로 생성되며, 민감 정보를 etcd
저장소에 보관하다가 Pod가 해당 정보를 필요로 할 때 컨테이너에 제공해준다.
아래는 간단한 시크릿 사용 예제다.
$echo -n "admin" | base64
YWRtaW4=
$echo -n "1234" | base64
MTIzNA==
시크릿에 정보를 저장하기 위해서는 우선 평문이 아닌 Base64
로 인코딩한 문자열을 필드로 넣어줘야 한다.
apiVersion: v1
kind: Secret
metadata:
name: secret-sa-sample
type: Opaque
data:
USERNAME: YWRtaW4=
PASSWORD: MTIzNA==
시크릿은 타입은 크게 네 가지가 존재한다.
Opaque
: 일반적인 용도의 시크릿.ConfigMaps
와 동일한 목적으로 사용할 수 있으며, 민감한 데이터를 컨테이너에 전달하는 용도로도 사용할 수 있다dockerconfigjson
: 도커 이미지 저장소 인증 정보. private 레지스트리에 접근하기 위한 인증 절차를 치룰 때 사용하는 용도tls
: TLS 인증서를 시크릿으로 관리할 수 있도록 한다. TLS 인증서를 시크릿을 통해 저장하면, Pod나 Service 같은 리소스에서 TLS 인증서를 가져다 통신을 암호화 할 수 있다service-account-token
: Service Account에 대한 인증 정보를 담은 토큰을 시크릿으로 생성
시크릿은 인코딩-디코딩 방식을 통해 암복호화가 이루어지기 때문에 완벽하게 데이터를 보호하는 방법이라고 볼 수 없다.
그렇기 때문에 역할 기반 접근 제어를 통해 권한이 있는 사용자만 시크릿 정보를 조회할 수 있도록 제한하거나, 별도의 암호화 플러그인과 KMS를 통해 시크릿을 완전히 암호화 하는 것을 권장한다.
Reference
'Infra' 카테고리의 다른 글
메모리 영역 (0) | 2025.02.06 |
---|---|
[k8s] NodePort, LoadBalancer, Ingress (2) | 2024.11.12 |
[k8s] Kubernetes Network (0) | 2024.11.08 |
[Traefik] K8s Traefik 설치, External-IP 설정 및 HTTPS 적용 방법 (5) | 2024.09.22 |
[k8s] K8s Storage with OpenEBS (0) | 2024.08.02 |