[CKS] Minimize Microservice Vulnerabilities (1)
쿠버네티스 Security Context와 Admission Controllers를 통해 Pod 수준에서 사용자 권한을 제어하고, API 요청 검증 및 보안 정책을 강화하는 방법을 살펴봅니다.
개요
CKS 자격증을 위해 아래 내용을 정리했습니다.
Security Context
Docker 컨테이너 실행시 특정 사용자 ID를 설정하여 보안을 강화할 수 있습니다
docker run --user=1001 ubuntu sleep 3600
docker run --cap-add MAC_ADMIN ubuntu
Kubernetes에서도 유사한 기능을 제공함과 동시에 Pod 및 컨테이너 레벨에서 적용도 가능합니다.
Pod 수준에서 Security Context를 구성하면 해당 Pod 내의 모든 컨테이너에 자동으로 전파됩니다.
다만 Container 수준의 Security Context가 적용된 경우 컨테이너 구성이 우선 적용됩니다.
apiVersion: v1
kind: Pod
metadata:
name: web-pod
spec:
containers:
- name: ubuntu
image: ubuntu
command: ["sleep", "3600"]
securityContext:
runAsUser: 1000
capabilities:
add: ["MAC_ADMIN"]
UID 자체에 특별한 의미는 없지만 잘 기억해두는 것이 좋습니다.
- UID 0: root 사용자 (최고 권한)
- UID 1-999: 시스템 계정용으로 예약 (데몬, 서비스 등)
- UID 1000 이상: 일반 사용자 계정
pod가 어떤 유저를 사용하는지는 exec 로 접근하여 확인합니다.
k exec ubuntu-sleeper -- whoami
root
spec:
securityContext: # Pod 레벨
runAsUser: 1010
containers:
- name: ubuntu
Admission Controllers
Admission Controllers는 API 요청이 수행되기 전 유효성을 검사, 변경, 거부하는 컴포넌트입니다.
Validating,Mutating,Both
흐름도는 아래와 같습니다.
kubectl 요청 → API Server → [Authentication(인증)] → [Authorization(인가)] → [Admission Controllers] → etcd 저장
Kubernetes Request Life Cycle
kubectl로API Server에 요청했을 때KubeConfig파일에 필요한 인증서가 제공됩니다.
cat ~/.kube/config
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVUx...
- 인증 후 요청에 대한 권한 부여
역할 기반 접근제어(RBAC)를 사용하여 요청한 작업이 수행 가능한지 검토합니다.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["list", "get", "create", "update", "delete"]
특정 이름으로만 파드를 생성하게 제어 가능합니다.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["create"]
resourceNames: ["blue", "orange"]
Admission Controllers의 역할
default 인증 및 권한 부여하는 과정 외 들어오는 요청을 추가적인 유효성 검사, 수정을 하는 시나리오가 있습니다.
- 이미지가 승인된 내부 Registry에서만 가져오게 구성
latest이미지 태그를 못 사용하도록 강제- 컨테이너가 루트 사용자 권한으로 실행되는 경우 거부
- 컨테이너의 보안 컨텍스트를 수정하거나 특정 메타데이터 레이블을 적용
예시 Pod에서 RBAC은 복잡한 구성 요소 검사와 수정을 하지 않습니다. Admission Controllers는 etcd에 도달하기 전에 이러한 요소를 검토, 수정, 거부하여 추가적인 보안 계층을 제공합니다.
apiVersion: v1
kind: Pod
metadata:
name: web-pod
spec:
containers:
- name: ubuntu
image: ubuntu:latest
command: ["sleep", "3600"]
securityContext:
runAsUser: 0
capabilities:
add: ["MAC_ADMIN"]
Built-In Admission Controllers
API Server에 기본으로 포함되어 있는 Admission Controller들입니다. 별도 설치 없이 바로 사용 가능합니다.
주요 Built-In Admission Controllers는 아래 역할을 수행합니다.
| 이름 | 역할 |
|---|---|
NamespaceLifecycle |
삭제 중인 namespace에 리소스 생성 방지 |
LimitRanger |
namespace의 기본 리소스 limit/request 적용 |
ServiceAccount |
기본 ServiceAccount 자동 할당 |
DefaultStorageClass |
PVC에 기본 StorageClass 자동 지정 |
ResourceQuota |
namespace 리소스 쿼터 검증 |
PodSecurity |
Pod 보안 표준 적용 (PSA) |
NodeRestriction |
kubelet이 자기 노드만 수정 가능하도록 제한 |
AlwaysPullImages |
이미지 항상 pull 강제 |
Namespace-Related Admission Controllers
Namespace Exists Admission Controller
존재하지 않는 Namespace에 Pod를 생성하려고 하면 Admission controllers가 확인 후 요청을 거부합니다.
- API Server는 사용자를 인증하고 권한을 인가합니다.
- Namespace-Related Admission Controllers는 "blue" 네임스페이스를 사용할 수 있는지 확인합니다.
- 해당 네임스페이스가 존재하지 않으므로 요청이 거부됩니다.
Namespace Auto-Provision Admission Controller같은 Controller는 네임스페이스에 존재하지 않는다면 자동으로 생성할 수 있습니다.
Configuring Admission Controllers
기본적인 built-in Admission Controller는 30개 정도 존재합니다. (버전에 따라 차이가 존재) 그 중에서 일부만 활성화되어있습니다.
추가 Admission Controllers를 활성화 하려면 --enable-admission-plugins 플래그를 업데이트합니다.
ExecStart=/usr/local/bin/kube-apiserver \\
--advertise-address=${INTERNAL_IP} \\
--allow-privileged=true \\
--apiserver-count=3 \\
--authorization-mode=Node,RBAC \\
--bind-address=0.0.0.0 \\
--enable-swagger-ui=true \\
--etcd-servers=https://127.0.0.1:2379 \\
--event-ttl=1h \\
--runtime-config=api/all \\
--service-cluster-ip-range=10.32.0.0/24 \\
--service-node-port-range=30000-32767 \\
--v=2 \\
--enable-admission-plugins=NodeRestriction,NamespaceAutoProvision
API Server가 실행될 때 Manifest를 다음과 같이 구성 가능합니다.
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: kube-apiserver
namespace: kube-system
spec:
containers:
- name: kube-apiserver
image: k8s.gcr.io/kube-apiserver-amd64:v1.11.3
command:
- kube-apiserver
- --authorization-mode=Node,RBAC
- --advertise-address=172.17.0.107
- --allow-privileged=true
- --enable-bootstrap-token-auth=true
- --enable-admission-plugins=NodeRestriction,NamespaceAutoProvision
비활성화 하려는 경우 --disable-admission-plugins 플래그를 활용합니다.
위 예시에서 Namespace Exists Admission Controller 는 삭제되어 NamespaceLifecycle 대체되었습니다. 해당 Admission Controller는 존재하지 않는 네임스페이스를 대상으로 하는 요청을 거부 + kube-system, kube-public 같은 중요 네임스페이스를 보호합니다.
아래 명령어를 통해 기본 admission controller를 확인할 수 있습니다.
kubectl exec -it kube-apiserver-controlplane -n kube-system -- kube-apiserver -h | grep 'enable-admission-plugins'
추가 Admission Controller 생성하기
# 1. kube-apiserver 매니페스트 수정
vi /etc/kubernetes/manifests/kube-apiserver.yaml
# 2. 라인 찾아서 수정하기
- --enable-admission-plugins=NodeRestriction,NamespaceAutoProvision
ps -ef | grep kube-apiserver | grep admission-plugins
# 실행 중인 Admission Controller와 disable된 admission controller 확인 가능
kube-apiserver --advertise-address=192.168.117.57 --allow-privileged=true --authorization-mode=Node,RBAC --client-ca-file=/etc/kubernetes/pki/ca.crt --enable-admission-plugins=NodeRestriction,NamespaceAutoProvision --disable-admission-plugins=DefaultStorageClass --enable-bootstrap-token-auth=true --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key --etcd-servers=https://127.0.0.1:2379 --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key --requestheader-allowed-names=front-proxy-client --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --secure-port=6443 --service-account-issuer=https://kubernetes.default.svc.cluster.local --service-account-key-file=/etc/kubernetes/pki/sa.pub --service-account-signing-key-file=/etc/kubernetes/pki/sa.key --service-cluster-ip-range=172.20.0.0/16 --tls-cert-file=/etc/kubernetes/pki/apiserver.crt --tls-private-key-file=/etc/kubernetes/pki/apiserver.key