1. 왜 User Namespace가 필요한가

컨테이너는 격리되어 있다고 하지만, 기본적으로 컨테이너 내부의 root 사용자는 host 관점에서도 위험한 의미를 가질 수 있다. 컨테이너 탈출 취약점이나 runtime 취약점이 발생했을 때, 컨테이너 내부 root가 host에서 높은 권한을 얻는 상황을 줄이기 위해 Linux는 user namespace라는 격리 기능을 제공한다.

Kubernetes의 user namespace는 Kubernetes namespace와 다른 개념이다. Kubernetes namespace는 리소스를 논리적으로 나누는 Kubernetes 기능이고, Linux user namespace는 컨테이너 내부 UID/GID와 host UID/GID를 분리하는 Linux 커널 기능이다. Kubernetes 공식 문서도 user namespace가 컨테이너 안의 사용자와 host 사용자를 격리하며, 컨테이너 내부 root가 host에서는 다른 non-root 사용자로 매핑될 수 있다고 설명한다.

쉽게 말하면, user namespace를 쓰면 컨테이너 내부에서는 여전히 root(UID 0)처럼 보일 수 있지만, host에서는 전혀 다른 높은 UID로 보인다.

 

그래서 컨테이너 안에서 root 권한이 필요하더라도, host에서는 root 권한을 갖지 않게 만들 수 있다. 이게 user namespace의 핵심 보안 효과다.

 

2. Kubernetes에서는 어떻게 활성화하는가

Kubernetes에서는 Pod spec에 아래 설정을 넣어 user namespace를 사용할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: userns-test
spec:
  hostUsers: false
  containers:
  - name: shell
    image: debian
    command: ["sleep", "infinity"]

hostUsers: false의 의미는 Pod가 host의 user namespace를 공유하지 않고, Pod 전용 user namespace를 사용한다는 뜻이다. Kubernetes task 문서도 Pod에서 user namespace를 사용하려면 .spec.hostUsers를 false로 설정한다고 안내한다.

실제로 테스트 Pod 안에서 아래 명령을 실행하면 UID 매핑을 확인할 수 있다.

이 의미는 다음과 같다.

컨테이너 내부 UID 0
→ host 기준 UID 371458048부터 시작하는 65536개 UID range로 매핑

즉, 컨테이너 내부 root가 host root가 아니라 별도 UID range로 remap된 것이다.

3. AKS에서 필요한 조건

AKS에서는 user namespace를 사용하려면 Kubernetes 버전뿐 아니라 node OS 조건도 중요하다. Microsoft AKS 문서는 AKS workload에서 user namespace, AppArmor, seccomp 같은 Linux 보안 기능을 사용할 수 있다고 설명하며, user namespace 사용 조건으로 지원되는 node OS 구성을 안내한다.

이번 테스트에서는 기존 officeaddin nodepool이 Kubernetes v1.33.10이었지만, node OS가 Ubuntu 22.04였기 때문에 hostUsers: false Pod 생성 시 아래 오류가 발생했다.

Failed to create pod sandbox:
can't set spec.hostUsers: false, runtime does not support user namespaces

이후 Ubuntu 24.04 기반 nodepool을 새로 생성한 뒤 동일한 hostUsers: false Pod를 실행하자 정상적으로 Running 되었고, /proc/self/uid_map에서도 UID remap이 확인되었다.

정리하면

Ubuntu 22.04 nodepool
→ user namespace 테스트 실패

Ubuntu 24.04 nodepool
→ hostUsers: false 정상 동작
→ UID remap 확인

 

 

4. seccomp를 풀어야 하는가?

이번 테스트에서 hostUsers: false 자체는 seccomp를 풀지 않고도 동작했다. Pod spec에 seccompProfile: Unconfined를 넣지 않았고, privileged 설정도 하지 않았다.

다만 per-skill 격리를 위해 별도 network namespace를 생성하는 테스트에서는 결과가 나뉘었다.

 

기본 권한 상태

kubectl exec userns-netns-test -- /usr/bin/unshare --net -- echo NETNS_OK

 

결과

unshare: unshare failed: Operation not permitted

 

SYS_ADMIN capability를 추가하고, seccomp는 RuntimeDefault로 유지한 상태

kubectl exec userns-netns-cap-test -- /usr/bin/unshare --net -- echo NETNS_OK

 

결과

NETNS_OK

 

따라서 이번 검증 기준으로는 다음과 같이 정리할 수 있다.

hostUsers: false user namespace
→ seccomp Unconfined 불필요

unshare --net 기반 netns 생성
→ 기본 권한으로는 실패
→ seccomp RuntimeDefault + SYS_ADMIN capability 조합에서는 성공

 

즉, seccomp를 풀 필요는 없어 보이지만, per-skill netns 생성을 위해서는 SYS_ADMIN capability가 필요했다.

5. idmapped mount란 무엇인가

user namespace를 쓰면 컨테이너 내부 UID/GID와 host UID/GID가 달라진다. 문제는 볼륨이다. 컨테이너 안에서는 UID 0으로 보이는 프로세스가 host에서는 높은 UID로 보이기 때문에, PV/PVC 같은 볼륨에 접근할 때 UID/GID 해석이 맞지 않을 수 있다.

이를 해결하기 위해 Linux에는 idmapped mount라는 기능이 있다.

idmapped mount는 파일 자체의 실제 소유권을 바꾸지 않고, 특정 mount 지점에서만 UID/GID를 다르게 매핑해서 보이게 하는 커널 기능이다. Linux kernel 문서는 idmapped mount를 설명하면서, filesystem ownership과 mount ownership을 구분해야 한다고 설명한다.

https://docs.kernel.org/filesystems/idmappings.html

 

쉽게 말하면 다음과 같다.

실제 파일시스템 기준:
파일 owner = UID 371458048

컨테이너 내부 mount 기준:
파일 owner = UID 0처럼 보이게 매핑

이 방식의 장점은 chown을 하지 않아도 된다는 점이다. 볼륨 전체의 소유권을 매번 바꾸지 않고, mount 단위에서만 UID/GID를 다르게 해석할 수 있다.

Kubernetes user namespace 관련 문서와 블로그에서도, Pod가 user namespace를 사용할 때 볼륨을 함께 쓰려면 파일시스템의 idmapped mount 지원 여부가 중요하다고 설명한다. Kubernetes v1.33 user namespace 블로그는 user namespace가 활성화되면 Pod가 opt-in할 수 있고, 볼륨 지원과 관련해 파일시스템별 idmap mount 지원 여부가 중요하다고 설명한다.

6. 왜 Azure Files에서 문제가 발생했는가

이번 테스트에서 가장 중요한 결과는 Azure Files PVC와 hostUsers: false 조합이었다.

테스트 결과는 다음과 같았다.

hostUsers 없음 + Azure Files PVC
→ 정상 Running

hostUsers: false + emptyDir
→ 정상 Running, read/write 성공

hostUsers: false + Azure Files PVC
→ RunContainerError

실패한 에러는 다음과 같았다.

failed to set MOUNT_ATTR_IDMAP on .../agent-workspace-pv-pwc-dev/mount:
invalid argument
maybe the filesystem used doesn't support idmap mounts on this kernel?

 

이 에러는 container runtime이 Azure Files PVC mount 경로에 MOUNT_ATTR_IDMAP을 적용하려 했지만, 해당 파일시스템이 idmapped mount를 지원하지 않아 실패했다는 의미로 해석할 수 있다.

Azure Files는 Linux에서 SMB kernel client를 통해 마운트된다. Microsoft 문서도 Linux에서 Azure Files SMB 공유를 SMB kernel client를 사용해 마운트한다고 설명한다.

따라서 이번 케이스는 “Azure Files 서비스 장애”라기보다는 다음과 같이 보는 것이 정확하다.

hostUsers: false 사용
→ Pod UID/GID remap 발생
→ PV/PVC 볼륨에도 idmapped mount 필요
→ Azure Files/SMB 계열 볼륨에 MOUNT_ATTR_IDMAP 적용 실패
→ 컨테이너 시작 실패

즉, user namespace 자체는 정상이고, Azure Files PVC 자체도 정상이다. 하지만 hostUsers: false Pod에 Azure Files PVC를 직접 마운트하는 조합이 현재 호환되지 않는 것이다.

7. 이번 테스트 결과 요약

이번 AKS 테스트 결과를 정리하면 다음과 같다.

1. Ubuntu 24.04 기반 nodepool 생성
   → 성공

2. hostUsers: false Pod 실행
   → 성공

3. /proc/self/uid_map 확인
   → UID remap 확인

4. seccomp Unconfined 없이 실행
   → 성공

5. unshare --net 테스트
   → 기본 권한: Operation not permitted
   → SYS_ADMIN capability 추가 + RuntimeDefault seccomp: NETNS_OK

6. hostUsers: false + emptyDir read/write
   → 성공

7. hostUsers 없음 + Azure Files PVC
   → 성공

8. hostUsers: false + Azure Files PVC
   → 실패
   → MOUNT_ATTR_IDMAP invalid argument

 

8. 결론

Kubernetes user namespace는 컨테이너 내부 root를 host root와 분리하여 보안성을 높이는 기능이다. hostUsers: false를 사용하면 Pod는 host user namespace를 공유하지 않고, 컨테이너 내부 UID/GID를 host의 별도 UID/GID range로 remap한다.

이번 AKS 테스트에서 Ubuntu 24.04 nodepool에서는 user namespace가 정상 동작했고, seccomp를 Unconfined로 풀지 않아도 UID remap이 확인되었다. 또한 SYS_ADMIN capability를 추가하면 unshare --net을 통한 network namespace 생성도 가능했다.

다만 hostUsers: false Pod에 Azure Files PVC를 직접 마운트하는 경우, Azure Files/SMB 계열 파일시스템이 idmapped mount를 지원하지 않아 MOUNT_ATTR_IDMAP invalid argument 오류로 컨테이너 시작이 실패했다.

따라서 현재 구조에서는 sandbox Pod는 user namespace + emptyDir 기반으로 격리하고, Azure Files 접근은 Main Pod에서 담당하는 방식이 가장 현실적인 적용안이다.