사용자 네임스페이스
사용자 네임스페이스 (User Namespaces)
기능 상태: Kubernetes v1.36부터 Stable. 이것은 쿠버네티스의 안정적인 기능이며 v1.36부터 그랬어요. 처음에는 v1.28 릴리스에서 사용할 수 있었어요. 더 이상 이 기능이나 동작을 비활성화하거나 거부할 수 없어요(잠겨 있음). 관련 기능 게이트인 UserNamespacesSupport에 값을 명시적으로 설정하면 쿠버네티스는 이를 무시하지만 오류는 보고하지 않아요.
이 페이지는 쿠버네티스 파드에서 사용자 네임스페이스가 어떻게 사용되는지 설명해요. 사용자 네임스페이스는 컨테이너 안에서 실행되는 사용자를 호스트의 사용자로부터 격리해요.
컨테이너에서 root로 실행되는 프로세스는 호스트에서 다른(비root) 사용자로 실행될 수 있어요. 다시 말해 그 프로세스는 사용자 네임스페이스 안의 연산에는 완전한 권한을 가지지만, 네임스페이스 밖의 연산에는 권한이 없어요.
이 기능을 사용하면 손상된 컨테이너가 호스트나 같은 노드의 다른 파드에 할 수 있는 피해를 줄일 수 있어요. 사용자 네임스페이스가 활성화되면 악용할 수 없었던 HIGH 또는 CRITICAL로 평가된 보안 취약점이 여러 개 있어요. 사용자 네임스페이스가 향후 일부 취약점도 완화할 것으로 기대돼요.
참고: 이 섹션은 쿠버네티스에 필요한 기능을 제공하는 서드파티 프로젝트를 링크해요. 쿠버네티스 프로젝트 저자들은 이 프로젝트들에 책임을 지지 않으며, 이 목록은 알파벳순으로 나열돼 있어요. 이 목록에 프로젝트를 추가하려면 변경 사항을 제출하기 전에 콘텐츠 가이드를 읽으세요. 자세한 내용 보기.
출처: 문서
본문
시작하기 전에 (Before you begin)
이것은 Linux 전용 기능이며, 사용되는 파일시스템에 idmap 마운트 지원이 필요해요. 즉 다음을 의미해요.
- 노드에서
/var/lib/kubelet/pods/에 사용하는 파일시스템(또는 이를 위해 구성한 커스텀 디렉터리)에 idmap 마운트 지원이 필요해요. - 파드 볼륨에 사용되는 모든 파일시스템이 idmap 마운트를 지원해야 해요.
실무적으로는 최소 Linux 6.3이 필요해요. tmpfs가 그 버전에서 idmap 마운트를 지원하기 시작했기 때문이에요. 여러 쿠버네티스 기능이 tmpfs를 사용하므로 보통 필요해요(기본으로 마운트되는 서비스어카운트 토큰이 tmpfs를 사용하고, Secrets도 tmpfs를 사용하는 등).
Linux 6.3에서 idmap 마운트를 지원하는 인기 있는 파일시스템은 btrfs, ext4, xfs, fat, tmpfs, overlayfs가 있어요.
또한 컨테이너 런타임과 그 기반 OCI 런타임이 사용자 네임스페이스를 지원해야 해요. 다음 OCI 런타임이 지원을 제공해요.
- crun 버전 1.9 이상 (1.13+ 권장)
- runc 버전 1.2 이상
사용자 네임스페이스를 쿠버네티스와 함께 사용하려면 CRI 컨테이너 런타임도 사용해야 쿠버네티스 파드에서 이 기능을 사용할 수 있어요.
- containerd: 버전 2.0(이상)이 컨테이너용 사용자 네임스페이스를 지원해요.
- CRI-O: 버전 1.25(이상)이 컨테이너용 사용자 네임스페이스를 지원해요.
cri-dockerd의 사용자 네임스페이스 지원 상태는 GitHub의 이슈에서 확인할 수 있어요.
소개 (Introduction)
사용자 네임스페이스는 컨테이너의 사용자를 호스트의 다른 사용자로 매핑할 수 있게 하는 Linux 기능이에요. 또한 사용자 네임스페이스에서 파드에 부여된 capabilities는 네임스페이스 안에서만 유효하고 밖에서는 무효해요.
파드는 pod.spec.hostUsers 필드를 false로 설정해 사용자 네임스페이스 사용을 선택할 수 있어요.
kubelet은 파드가 매핑될 호스트 UID/GID를 선택하며, 같은 노드의 두 파드가 같은 매핑을 사용하지 않도록 보장하는 방식으로 선택해요.
pod.spec의 runAsUser, runAsGroup, fsGroup 등의 필드는 항상 컨테이너 안의 사용자를 가리켜요. 이 사용자들은 볼륨 마운트(pod.spec.volumes에 지정)에 사용되므로, 호스트 UID/GID는 파드가 마운트할 수 있는 볼륨의 읽기/쓰기에 어떤 영향도 주지 않아요. 즉 파드가 마운트한 볼륨에서 생성/읽기되는 inode는 사용자 네임스페이스를 사용하지 않는 파드와 동일해요.
이렇게 하면 파드는 (볼륨의 파일 소유권에 영향을 주지 않고) 사용자 네임스페이스를 쉽게 활성화/비활성화할 수 있고, 컨테이너 안에 적절한 사용자(RunAsUser, RunAsGroup, fsGroup 등)를 설정하기만 하면 사용자 네임스페이스가 없는 다른 파드와 볼륨을 공유할 수도 있어요. 이는 파드가 마운트할 수 있는 모든 볼륨에 적용되며, hostPath(파드가 hostPath 볼륨 마운트를 허용받은 경우)도 포함해요.
이 기능이 활성화될 때 유효한 UID/GID는 기본적으로 0-65535 범위예요. 이는 파일과 프로세스(runAsUser, runAsGroup 등)에 적용돼요.
이 범위 밖의 UID/GID를 사용하는 파일은 오버플로 ID(보통 65534, /proc/sys/kernel/overflowuid와 /proc/sys/kernel/overflowgid에 구성)에 속한 것으로 보여요. 하지만 65534 사용자/그룹으로 실행하더라도 그 파일을 수정할 수는 없어요.
0-65535 범위가 구성 노브(knob)로 확장되면 앞선 제한 사항이 확장된 범위에 적용돼요.
root로 실행돼야 하지만 다른 호스트 네임스페이스나 리소스에 접근하지 않는 대부분의 애플리케이션은, 사용자 네임스페이스가 활성화되어도 별다른 변경 없이 계속 잘 실행될 거예요.
파드를 위한 사용자 네임스페이스 이해하기 (Understanding user namespaces for pods)
기본 구성의 여러 컨테이너 런타임(예: Docker Engine, containerd, CRI-O)은 격리를 위해 Linux 네임스페이스를 사용해요. 다른 기술도 존재하며 그 런타임들과 함께 사용할 수 있어요(예: Kata Containers는 Linux 네임스페이스 대신 VM을 사용). 이 페이지는 격리에 Linux 네임스페이스를 사용하는 컨테이너 런타임에 적용돼요.
파드를 만들 때 기본적으로 격리를 위해 여러 새 네임스페이스가 사용돼요. 컨테이너의 네트워크를 격리하는 네트워크 네임스페이스, 프로세스 보기를 격리하는 PID 네임스페이스 등이 있죠. 사용자 네임스페이스가 사용되면 컨테이너의 사용자를 노드의 사용자로부터 격리해요.
즉 컨테이너가 root로 실행되고 호스트의 비root 사용자에 매핑될 수 있다는 뜻이에요. 컨테이너 안에서 프로세스는 자신이 root로 실행 중이라고 생각해요(그래서 apt, dnf 같은 도구가 잘 동작). 하지만 실제로 프로세스는 호스트에서 권한이 없어요. 예를 들어 호스트에서 ps aux를 실행해 컨테이너 프로세스가 어떤 사용자로 실행 중인지 확인하면 검증할 수 있어요. ps가 보여주는 사용자는 컨테이너 안에서 id 명령을 실행했을 때 보이는 사용자와 같지 않아요.
이 추상화는 예를 들어 컨테이너가 호스트로 탈출(escape)하는 경우에 일어날 수 있는 일을 제한해요. 컨테이너가 호스트에서 권한 없는 사용자로 실행되고 있으므로, 호스트에 할 수 있는 일이 제한돼요.
또한 각 파드의 사용자가 호스트의 서로 겹치지 않는 사용자에 매핑되므로, 다른 파드에 할 수 있는 일도 제한돼요.
파드에 부여된 capabilities도 파드 사용자 네임스페이스로 제한되며 그 밖에서는 대부분 무효이고, 일부는 완전히 무효예요. 두 예를 들어 볼게요.
CAP_SYS_MODULE은 사용자 네임스페이스를 사용하는 파드에 부여돼도 아무 효과가 없어요. 파드는 커널 모듈을 로드할 수 없어요.CAP_SYS_ADMIN은 파드의 사용자 네임스페이스로 제한되고 그 밖에서는 무효예요.
사용자 네임스페이스를 사용하지 않으면 root로 실행되는 컨테이너는 컨테이너 탈출의 경우 노드에서 root 권한을 가져요. 그리고 컨테이너에 일부 capability가 부여되면 그 capabilities도 호스트에서 유효해요. 사용자 네임스페이스를 사용하면 이러한 것 중 어느 것도 사실이 아니에요.
사용자 네임스페이스가 사용될 때 무엇이 바뀌는지 더 자세히 알고 싶다면 man 7 user_namespaces를 보세요.
사용자 네임스페이스를 지원하도록 노드 설정하기 (Set up a node to support user namespaces)
기본적으로 kubelet은 호스트의 파일과 프로세스가 이 범위 안의 UID/GID를 사용한다고 가정하여(대부분의 Linux 배포판에서 표준임) 파드에 0-65535 범위 위의 UID/GID를 할당해요. 이 방식은 호스트와 파드의 UID/GID 사이의 어떤 겹침도 방지해요.
겹침을 피하는 것은 CVE-2021-25741 같은 취약점의 영향을 완화하는 데 중요해요. 이 취약점에서 파드는 호스트의 임의 파일을 잠재적으로 읽을 수 있어요. 파드와 호스트의 UID/GID가 겹치지 않으면 파드가 할 수 있는 일이 제한돼요. 파드 UID/GID가 호스트의 파일 소유자/그룹과 일치하지 않기 때문이에요.
kubelet은 파드를 위한 사용자 ID와 그룹 ID의 커스텀 범위를 사용할 수 있어요. 커스텀 범위를 구성하려면 노드가 다음을 가져야 해요.
- 시스템에
kubelet사용자가 있어야 해요 (여기서 다른 사용자 이름을 쓸 수 없어요). getsubids바이너리(shadow-utils의 일부)가 설치되고 kubelet 바이너리의PATH에 있어야 해요.kubelet사용자에 대한 subordinate UID/GID 구성이 있어야 해요(man 5 subuid와man 5 subgid참고).
이 설정은 UID/GID 범위 구성을 수집할 뿐 kubelet을 실행하는 사용자를 변경하지 않아요.
kubelet 사용자에 할당하는 subordinate ID 범위에 대해 몇 가지 제약을 따라야 해요.
- Pod의 UID 범위를 시작하는 subordinate 사용자 ID는 65536의 배수여야 하고 65536보다 크거나 같아야 해요. 즉 Pod에 0-65535 범위의 어떤 ID도 사용할 수 없어요. kubelet은 실수로 안전하지 않은 구성을 만들기 어렵게 하기 위해 이 제한을 부과해요.
- subordinate ID 개수는 65536의 배수여야 해요.
- subordinate ID 개수는 최소
65536 x <maxPods>여야 해요. 여기서<maxPods>는 노드에서 실행할 수 있는 최대 파드 수예요. - 사용자 ID와 그룹 ID 모두에 같은 범위를 할당해야 해요. 다른 사용자가 그룹 ID 범위와 정렬되지 않는 사용자 ID 범위를 가져도 상관없어요.
- 할당된 범위 중 어떤 것도 다른 할당과 겹치면 안 돼요.
- subordinate 구성은 한 줄만이어야 해요. 즉 여러 범위를 가질 수 없어요.
예를 들어 /etc/subuid와 /etc/subgid를 모두 kubelet 사용자에 대해 다음 항목으로 정의할 수 있어요.
# 형식은
# name:firstID:count of IDs
# 여기서
# - firstID는 65536 (가능한 최소값)
# - count of IDs는 110 * 65536
# (110은 노드의 기본 파드 수 제한)
kubelet:65536:7208960
노드를 재구성할 때의 참고 사항 (Note if you reconfigure a node)
첨언해서, 사용자 네임스페이스로 파드를 실행 중인 기존 노드에서 위 구성을 적용하려 한다면 몇 가지 중요한 참고 사항이 있어요.
구성은 노드에서 사용자 네임스페이스를 사용하는 파드가 실행되지 않을 때 변경해야 해요. 사용자 네임스페이스를 가진 파드를 실행 중인 노드에서 변경할 때는, 구성을 적용하고 kubelet을 재시작하기 전에 먼저 노드를 drain해야 해요. 노드를 drain할 때 DaemonSet 파드나 unschedulable 테인트를 허용하는 다른 파드는 퇴거되지 않는다는 점을 명심하세요.
사용자 네임스페이스를 사용하는 파드가 실행 중이면 안 되는 이유는, 그 파드들이 새 구성 범위 밖일 수 있는 임의의 범위를 사용할 수 있기 때문이에요. kubelet은 노드의 기존 파드에 대해 새 구성을 충족할 수 없으면 시작에 실패해요.
파드 각각의 ID 개수 (ID count for each of Pods)
Kubernetes v1.33부터 각 파드의 ID 개수를 KubeletConfiguration에서 설정할 수 있어요.
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
userNamespaces:
idsPerPod: 1048576
idsPerPod(uint32) 값은 65536의 배수여야 해요. 기본값은 65536이에요. 이 값은 이 KubeletConfiguration으로 kubelet이 시작된 후 생성된 컨테이너에만 적용돼요. 실행 중인 컨테이너는 이 구성의 영향을 받지 않아요.
v1.33 이전의 쿠버네티스에서는 각 파드의 ID 개수가 65536으로 하드코딩돼 있었어요.
Pod 보안 승인 검사와의 통합 (Integration with Pod security admission checks)
사용자 네임스페이스를 활성화하는 Linux Pod의 경우, 쿠버네티스는 Pod Security Standards의 적용을 통제된 방식으로 완화해요.
사용자 네임스페이스를 사용하는 Pod를 만들면, Baseline 또는 Restricted 파드 보안 표준을 강제하는 컨텍스트에서도 다음 필드는 제약되지 않아요. 이 동작은 보안 문제를 나타내지 않아요. 사용자 네임스페이스가 있는 Pod 안의 root는 실제로 컨테이너 안의 사용자를 의미하며, 호스트의 권한 있는 사용자에게 매핑되지 않기 때문이에요. 그러한 상황에서 Pod에 대해 확인되지 않는 필드 목록은 다음과 같아요.
spec.securityContext.runAsNonRootspec.containers[*].securityContext.runAsNonRootspec.initContainers[*].securityContext.runAsNonRootspec.ephemeralContainers[*].securityContext.runAsNonRootspec.securityContext.runAsUserspec.containers[*].securityContext.runAsUserspec.initContainers[*].securityContext.runAsUserspec.ephemeralContainers[*].securityContext.runAsUser
또한 파드가 Baseline 파드 보안 표준을 가진 컨텍스트에 있다면 다음 필드의 검증도 비슷하게 완화돼요.
spec.containers[*].securityContext.procMountspec.initContainers[*].securityContext.procMountspec.ephemeralContainers[*].securityContext.procMount
Restricted 파드 보안 표준에서는 파드가 여전히 기본 또는 빈 ProcMount만 사용해야 해요.
제한 사항 (Limitations)
파드에 사용자 네임스페이스를 사용할 때 다른 호스트 네임스페이스를 사용하는 것은 허용되지 않아요. 특히 hostUsers: false를 설정하면 다음 중 어떤 것도 설정할 수 없어요.
hostNetwork: truehostIPC: truehostPID: true
어떤 컨테이너도 volumeDevices(원시 블록 볼륨, 예: /dev/sda)를 사용할 수 없어요. 이는 파드 스펙의 모든 컨테이너 배열을 포함해요.
containersinitContainersephemeralContainers
파일시스템 지원 (Filesystem support)
사용자 네임스페이스를 사용하는 파드는 파일시스템이 idmap 마운트를 지원해야 해요. 일부 파일시스템은 idmap 마운트를 지원하지 않아 사용자 네임스페이스와 함께 사용할 수 없어요. 그런 경우 다음 이벤트가 생성돼요. 경고 세부 사항은 사용 중인 컨테이너 런타임에 따라 달라진다는 점을 알아두세요.
Warning Failed 1s kubelet Error: failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: failed to fulfil mount request: failed to set MOUNT_ATTR_IDMAP on ${your mount path} invalid argument (maybe the filesystem used doesn't support idmap mounts on this kernel?): unknown
NFS 볼륨은 Linux NFS 클라이언트가 아직 idmap 마운트를 지원하지 않기 때문에 사용자 네임스페이스 파드에 마운트될 수 없어요. 지원되는 파일시스템의 현재 목록은 Linux 커널의 mount_setattr(2) man page를 참고하세요.
메트릭과 관찰성 (Metrics and observability)
kubelet은 사용자 네임스페이스에 특화된 두 가지 prometheus 메트릭을 내보내요.
started_user_namespaced_pods_total: 생성이 시도된 사용자 네임스페이스 파드 수를 추적하는 카운터started_user_namespaced_pods_errors_total: 사용자 네임스페이스 파드 생성 오류 수를 추적하는 카운터
다음 단계 (What's next)
- 파드와 함께 사용자 네임스페이스 사용하기를 확인해 보세요.