CIS 강화 가이드
RKE2는 기본적으로 강화된 보안 기능을 갖도록 설계되어 있으며, 수정 없이 대부분의 Kubernetes CIS 제어 테스트를 통과한다. 하지만 몇 가지 주목할 만한 예외 사항이 있으며, 이러한 경우에는 CIS 벤치마크를 완벽하게 통과하기 위해 수동 개입이 필요하다.
- RKE2는 호스트 운영 체제를 수정하지 않는다. 따라서 운영자인 호스트 수준에서 몇 가지 수정을 해야한다.
- CIS의 네트워크 정책 및 파드 보안 표준(RKE2 v1.25 이전 버전에서는 Pod 보안 정책(PSP))에 대한 특정 제어 기능은 클러스터의 기능을 제한할 수 있다. RKE2에서 이러한 설정을 구성하도록 하려면 해당 옵션을 선택해야한다. 이러한 요구 사항을 충족하도록 하려면 RKE2 버전에 따라 플래그
profile: cis, cis-1.23를 설정하여 RKE2를 시작할 수 있습니다.
1. 호스트 서버 준수 요구사항
1.1 커널 매개변수 설정
보안에 필요한 커널 설정을 적용해야 한다. RKE2가 설정 파일은 만들어주지만, 적용은 직접 해야 한다.
- 방법
- RKE2 설치 방식(RPM 또는 타르볼)에 따라 지정된 경로의
rke2-cis-sysctl.conf 파일을 이용하여 커널 매개변수를 설정한다
RKE2가 RPM, YUM, DNF에 의해 설치된 경우
1
2
| sudo cp -f /usr/share/rke2/rke2-cis-sysctl.conf /etc/sysctl.d/60-rke2-cis.conf
sudo systemctl restart systemd-sysctl
|
RKE2가 tarball을 통해 설치된 경우
1
2
| sudo cp -f /usr/local/share/rke2/rke2-cis-sysctl.conf /etc/sysctl.d/60-rke2-cis.conf
sudo systemctl restart systemd-sysctl
|
systemd-sysctl.service및/또는 /etc/sysctl.d디렉터리가 없는 경우, 부팅 시 다음 명령을 실행하여 sysctl이 적용되도록해야한다.
1
| sudo sysctl -p /usr/local/share/rke2/rke2-cis-sysctl.conf
|
- 주의
- 이미 운영 중인 클러스터에서 이 작업을 하면 네트워크에 문제가 생길 수 있으니, 처음 설치할 때만 한다.
1.2 etcd 전용 사용자 생성
보안을 위해 데이터 저장소인 etcd를 루트(root) 권한이 아닌 별도의 etcd 계정으로 실행한다.
- 아래 명령어로
etcd 유저와 그룹을 만든다.
1
| sudo useradd -r -c "etcd user" -s /sbin/nologin -M etcd -U
|
2. RKE2 설정 파일 작성
/etc/rancher/rke2/config.yaml 파일을 만들고 아래 내용을 넣는다.
위 설정이 하는 일은
- 위에서 말한 호스트 요구사항이 충족 되었는지 확인한다.
- 네트워크 정책(Network Policies)를 자동으로 적용해 불필요한 통신을 차단한다.
- 파일 권한을 더 엄격하게 제한한다.
- 파드 보안
- 제한(Restricted) 모드를 강제하여 위험한 컨테이너 실행을 막는다.
3. 운영자가 직접 관리해야할 보안 사항
3.1 기본 서비스 계정(Service Account) 제한
쿠버네티스의 default 서비스 계정은 토큰을 자동으로 가지고 있는데 이게 해킹에 악용될 수 있다.
- 아래처럼 모든 네임스페이스의 default 계정에서
automountServiceAccountToken: false 설정을 해줘야 한다.
1
2
3
4
5
| apiVersion: v1
kind: ServiceAccount
metadata:
name: default
automountServiceAccountToken: false
|
아래의 account_update.sh 파일로 적용 가능
sudo chmod +x account_update.sh
1
2
3
4
5
6
| #!/bin/bash -e
for namespace in $(kubectl get namespaces -A -o=jsonpath="{.items[*]['metadata.name']}"); do
echo -n "Patching namespace $namespace - "
kubectl patch serviceaccount default -n ${namespace} -p "$(cat account_update.yaml)"
done
|
3.2 API 서버 감사(Audit) 로그 설정
RKE2는 로그를 남길 준비는 해주지만 무엇을 기록할지는 사용자가 정해야 한다.
- 파일 위치
/etc/rancher/rke2/audit-policy.yaml
1
2
3
4
5
6
| apiVersion: audit.k8s.io/v1
kind: Policy
metadata:
creationTimestamp: null
rules:
- level: None
|
- 기본값은
None(기록 안 함)으로 되어 있다. 이를 Metadata 등으로 수정하고 RKE2를 재시작해야 실제 로그가 남기 시작한다.
1
| sudo systemctl restart rke2-server.service
|
Pod Security Standard
쿠버네티스 클러스터에서 “어떤 파드는 허용하고, 어떤 파드는 막을 것인가?”를 결정하는 것은 보안의 핵심이다. RKE2는 최신 쿠버네티스 표준에 맞춰 Pod Security Admission(PSA)을 기본적으로 지원하며, 이를 통해 클러스터 전체의 보안 수준을 선언적으로 관리한다.
1. PSS의 세 가지 보안 레벨
쿠버네티스 표준(PSS)은 보안 엄격도에 따라 세 가지 레벨을 정의한다.
| 레벨 | 설명 | 특징 |
|---|
| Privileged | 제한 없음. 가장 완화된 수준. | 시스템 파드, CNI, 로그 수집기 등 높은 권한이 필요한 경우 사용. |
| Baseline | 최소한의 제한 | 일반적인 애플리케이션용. 알려진 권한 상승(Privilege Escalation)만 방지. |
| Restricted | 최고 수준의 제한 | 보안 베스트 프랙티스 준수. CIS 벤치마크 통과를 위한 필수 설정. |
2. RKE2에서의 작동방식
RKE2는 시작 시 자동으로 PSA 설정파일을 생성하며 실행 프로파일에 따라 내용이 달라진다.
- 설정 파일 위치
/etc/rancher/rke2/rke2-pss.yaml
- kube-apiserver는 위 경로의 파일을
--admission-control-config-file 플래그로 자동 참조한다.
2.1 CIS 모드
- 프로파일 :
profile: cis, profile: cis-1.23 - 적용 모드 :
restricted - 예외 네임스페이스 :
kube-system, cis-operator-system, tigera-operator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1beta1
kind: PodSecurityConfiguration
defaults:
enforce: "restricted" # 위반 시 파드 거부
enforce-version: "latest"
audit: "restricted" # 위반 시 감사 로그 기록
audit-version: "latest"
warn: "restricted" # 위반 시 경고 출력
warn-version: "latest"
exemptions: # 아래 네임스페이스는 제한 없이 허용
usernames: []
runtimeClasses: []
namespaces: [kube-system, cis-operator-system, tigera-operator]
|
참고로 기본 PSA 설정을 변경하고 싶다면 config.yaml에 아래 항목 추가한다.
pod-security-admission-config-file: /path/to/custom-psa-config.yaml2.2 Non-CIS 모드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1beta1
kind: PodSecurityConfiguration
defaults:
enforce: "privileged"
enforce-version: "latest"
exemptions:
usernames: []
runtimeClasses: []
namespaces: []
|
테스트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1beta1
kind: PodSecurityConfiguration
defaults:
enforce: "privileged"
enforce-version: "latest"
exemptions:
usernames: []
runtimeClasses: []
namespaces: []
|
- 테스트 파드 yaml 작성 (
privileged-pod.yaml)
1
2
3
4
5
6
7
8
9
10
11
| apiVersion: v1
kind: Pod
metadata:
name: privileged-pod
namespace: default
spec:
containers:
- name: nginx
image: nginx
securityContext:
privileged: true # restricted 모드에서 거부됨
|
1
| kubectl create namespace psa-test
|
1
2
3
4
| kubectl label namespace psa-test \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted
|
1
| kubectl apply -f privileged-pod.yaml -n psa-test
|
restricted-pod.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| apiVersion: v1
kind: Pod
metadata:
name: restricted-pod
namespace: default
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: nginx
image: nginx
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
|
1
| kubectl apply -f restricted-pod.yaml -n psa-test
|
SELinux
SELinux란?
리눅스의 보안 강화 기능으로, CentOS/RHEL 7 이상에서는 기본으로 활성화되어 있다. RKE2는 SELinux가 켜진 시스템과 호환가능하다.
rke2-selinux 패키지 설치 후 RKE2 시작 전에 노드 재부팅이 필요할 수 있다. 설치했는데도 audit 로그에 거부(denial) 메시지가 뜬다면 재부팅 하자.
커스텀 컨텍스트 레이블
RKE2 컨트롤 플레인은 여러 경로에 접근해야 한다. 과도한 권한(spc_t) 없이 보안을 유지하기 위해 RKE2 전용 SELinux 레이블 2가지를 사용한다.
rke2_service_db_t : 읽기 / 쓰기/var/lib/rancher/rke2/server/db (etcd)
rke2_service_t : 읽기 전용/var/lib/rancher/rke2/server/tls (인증서)
이 레이블은 RKE2 컨트롤 플레인 static pod에만 적용된다.
설정 방법
/etc/rancher/rke2/config.yaml 에서 아래의 값 설정
또는 환경 변수로
RPM으로 설치 했다면 기본으로 SELinux가 활성화된다. tarball로 설치 했다면 위 설정을 직접 추가해야 한다.
시크릿 암호화 설정(Secrets Encryption)
RKE2는 저장된 비밀 정보를 암호화하는 기능을 지원하며 다음 작업을 자동으로 수행한다.
- AES-CBC키를 생성한다
- 생성된 키를 사용하여 암호화 구성 파일을 생성한다
- 구성 정보를 암호화 공급자 구성으로 K8s API 서버에 전달한다.
생성된 암호화 config 파일
RKE2가 기본 aescbc 프로바이더를 사용하여 생성하는 암호화 설정 파일의 예
/var/lib/rancher/rke2/server/cred/encryption-config.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| {
"kind": "EncryptionConfiguration",
"apiVersion": "apiserver.config.k8s.io/v1",
"resources": [
{
"resources": [
"secrets"
],
"providers": [
{
"aescbc": {
"keys": [
{
"name": "aescbckey",
"secret": "xxxxxxxxxxxxxxxxxxx"
}
]
}
},
{
"identity": {}
}
]
}
]
}
|
시크릿 암호화 도구를 이용한 암호화 키 로테이션
RKE2는 서브커맨드 secrets-encrypt를 내포하고있다. 이 서브커맨드는 아래와 같은 기능을 수행할 수 있다.
- 새로운 암호키 추가
- 암호키 삭제 및 로테이션
- 시크릿 재암호화
암호키 순환
1
| rke2 secrets-encrypt rotate-keys
|
- 재암호화가 완료될때까지 대기 및 서버로그 확인
1
| rke2 secrets-encrypt status
|
1
| systemctl restart rke2-server.service
|
암호 키 순환 (클래식)
1
| rke2 secrets-encrypt prepare
|
1
2
3
4
5
6
| # Get the kube-apiserver container ID
export CONTAINER_RUNTIME_ENDPOINT="unix:///var/run/k3s/containerd/containerd.sock"
crictl ps --name kube-apiserver
# Stop the pod
crictl stop <CONTAINER_ID>
|
1
| rke2 secrets-encrypt rotate
|
kube-apiserver 파드 재시작
재 암호화
1
| rke2 secrets-encrypt reencrypt
|
비밀 암호화 상태
secrets-encrypt status는 노드에서 진행중인 시크릿 암호화의 상태에대한 정보를 표시한다.
1
| rke2 secrets-encrypt status
|
Encryption Status: 노드에서 비밀 정보 암호화가 비활성화되었는지 활성화되었는지 여부를 표시Current Rotation Stage : 노드의 현재 회전 단계를 나타낸다.startpreparereencrypt_requestreencrypt_activereencrypt_finished
Server Encryption Hashes- 고가용성(HA) 클러스터에 유용하며 모든 서버가 로컬 파일과 관련하여 동일한 상태에 있는지 여부를 나타낸다. 이를 통해 다음 단계로 진행하기 전에 서버를 재시작해야 하는지 여부를 확인할 수 있다.
- 아래와같이 HA 예시에서 노드 1과 노드 2의 해시 값이 서로 다른 것은 현재 암호화 구성이 동일하지 않음을 의미한다. 서버를 재시작하면 구성이 동기화된다.
1
2
3
4
5
6
| Server Encryption Hashes: hash does not match between node-1 and node-2
Active Key Type Name
------ -------- ----
* AES-CBC aescbckey-2021-12-10T22:54:38Z
AES-CBC aescbckey
|