[CKA] Container Security Context
Container Security Context의 핵심 개념과 구성 방법, 실습 풀이를 정리합니다.
Pre-requisite - Security in Docker
docker에 설치된 호스트에는 다수의 운영 체제 프로세스나 deamon, ssh 서버 등으로 실행되는 프로세스의 집합을 가지고 있습니다. 또한 Docker는 가상머신과 달리 호스트로부터 완전히 격리되어 있지 않습니다. 따라서** 컨테이너와 호스트가 같은 커널을 공유한다는 특징이 있습니다.**
대부분의 컨테이너는 리눅스에서 namespace를 이용해 격리된다. 컨테이너에 의해 실행되는 모든 프로세스는 호스트 자체에서 실행되지만 고유의 네임스페이스에서 실행된다고 이해할 수 있습니다.
고유의 네임스페이스에 있어 고유의 프로세스만 볼 수 있습니다. 바깥이나 다른 네임스페이스에서는 볼 수 없습니다. sleep 3600 명령어를 수행하는 docker conatainer 내에서 ps aux 명령어로 docker contaner 내의 프로세스를 나열하면 PID 1로 sleep 3600 프로세스를 볼 수 있습니다.
호스트에서 ps aux 명령어로 호스트 내의 프로세스를 나열하면 sleep 3600를 실행 중인 다른 PID를 확인할 수 있는데, 이는 프로세스가 다른 네임스페이스에서 다른 PID를 가질 수 있기 때문입니다. 이러한 방식을 통해 docker가 시스템 내에서 컨테이너를 격리하게 됩니다.
별도 USER나 --user를 지정하지 않으면 컨테이너의 주 프로세스는 기본적으로 UID 0으로 실행됩니다. 이 설명은 호스트의 다른 프로세스까지 루트로 실행된다는 의미가 아닙니다.
컨테이너 내의 프로세스가 루트 사용자로 실행되길 원하지 않는다면 docker run 명령 안에서 ‘—user’ 옵션을 사용해 사용자를 설정하고 새 사용자 ID를 명시할 수 있습니다. 명시된 USER ID로 프로세스가 실행되는 것을 확인할 수 있습니다.
유저 보안을 적용하는 또 다른 방법은 Dockerfile에서 USER를 지정하는 것입니다.
docker는 컨테이너 내 루트 사용자의 능력을 제한하는 보안 기능을 제공하고 있습니다. **컨테이너 안의 루트 사용자는 호스트의 루트 사용자와는 다른데, **docker는 이것을 구현하기 위해 리눅스 기능을 사용합니다.
루트 사용자는 시스템에서 가장 강력한 사용자이다. 루트 사용자는 뭐든 할 수 있고 루트 사용자에 의한 프로세스도 마찬가지로 시스템의 제한 없이 접근할 수 있다. ‘/usr/include/linux/capability.h’에서 루트 사용자가 할 수 있는 기능의 리스트를 확인할 수 있다.
**기본값으로 docker는 컨테이너의 기능을 제한하고 있습니다. **추가 capability가 필요하면 docker run --cap-add <capability>를 사용합니다.
제거하려는 capability는 --cap-drop <capability>로 지정합니다.
호스트에 대한 광범위한 접근 권한을 주는 privileged 모드는 --privileged로 설정하며 필요한 경우에만 제한적으로 사용해야 합니다.
Security Contexts
앞선 내용에서 확인해봤을 때 docker 컨테이너를 실행할 때 보안 표준 집합을 정의할 옵션이 있으며, 이를
쿠버네티스에서도 설정할 수 있습니다. 쿠버네티스에서는 컨테이너를 파드에 넣어 보관합니다. 그래서 컨테이너 수준이나 파드 수준에서 보안 설정을 선택할 수 있게 됩니다.
파드 수준으로 설정하면 파드 내 모든 컨테이너에 설정값이 전달됩니다.
파드 정의 파일에서 securityContext 영역을 통해 파드 수준의 보안 설정을 할 수 있다. runAsUser 필드로 파드 사용자 ID를 설정한다.
**컨테이너 수준에서 보안을 설정하려면 containers 영역에 securityContext 영역을 추가하면 된다. **그리고 capabilities 영역에 추가할 기능 목록을 명시하면 됩니다. capabilities 영역은 컨테이너 수준에서만 지정 가능합니다.
SecurityContext의 핵심 목적은 컨테이너나 Pod의 권한과 접근 제어를 정의하는 것입니다.
runAsUser: 1000의 의미는:
- 컨테이너 내의 프로세스가 UID 1000으로 실행됨
- 별도로 사용자를 생성할 필요 없음 (이미 존재하는 UID를 사용)
주요 보안 설정: - 사용자 ID (runAsUser)
- 그룹 ID (runAsGroup)
- 파일시스템 권한 (fsGroup)
- 리눅스 Capabilities
- SELinux 옵션
- Privileged 모드 여부
- SecurityContext는 Pod 레벨과 컨테이너 레벨에서 설정 가능
- capabilities는 컨테이너 레벨에서만 설정 가능
- 새로운 사용자를 생성하지 않고, 기존 UID를 지정해서 사용
# Ubuntu 이미지의 경우 일반적으로:
UID 0 = root
UID 1-999 = 시스템 계정들
UID 1000+ = 일반 사용자
kubectl exec ubuntu-sleeper -- whoami
파드 정의 파일에서 securityContext 영역을 통해 파드 수준의 보안 설정
apiVersion: v1
kind: Pod
metadata:
name: ubuntu-sleeper
namespace: default
spec:
securityContext:
runAsUser: 1010
containers:
- command:
- sleep
- "4800"
image: ubuntu
name: ubuntu-sleeper
Root 사용자와 SYS_TIME capability 설정
capabilities는 Pod 레벨이 아닌 컨테이너 레벨에서 설정해야합니다.
- runAsUser: 1000은 모든 컨테이너에 적용
- capabilities는 해당 컨테이너에만 적용
- 만약 컨테이너에서 다른 runAsUser를 지정하면 컨테이너 값이 우선
apiVersion: v1
kind: Pod
metadata:
name: ubuntu-sleeper
namespace: default
spec:
containers:
- command:
- sleep
- "4800"
image: ubuntu
name: ubuntu-sleeper
securityContext: # 컨테이너 레벨로 이동
capabilities:
add: ["SYS_TIME"]
apiVersion: v1
kind: Pod
metadata:
name: ubuntu-sleeper
namespace: default
spec:
containers:
- command:
- sleep
- "4800"
image: ubuntu
name: ubuntu-sleeper
securityContext: # 컨테이너 레벨로 이동
capabilities:
add: ["SYS_TIME", "NET_ADMIN"]