쿠버네티스 배포판 — 직접 세운다 · OpenShift 로 옮길 때: 임의 UID 와 SCC · 이론
OpenShift 의 임의 UID 와 SCC — 이미지가 먼저 준비돼야 한다
한 줄 요약
OpenShift 는 컨테이너를 이미지의 USER 가 아니라 프로젝트마다 할당된 임의 UID 로 돌리고 그 사용자를 root 그룹(GID 0)에 넣으므로, 쓰는 디렉터리를 root 그룹 소유·그룹 쓰기 가능으로 만든 이미지만 그대로 옮겨 갑니다.
왜 이게 필요했나
일반 쿠버네티스에서는 이미지에 USER 가 없으면 root 로, 있으면 그 사용자로 돕니다. 그래서 많은 이미지가 "root 가 만든 디렉터리에 root 가 쓴다" 는 가정 위에 서 있습니다. OpenShift 는 이 가정을 받아 주지 않습니다. [OCP 4.21 이미지 작성 지침](https://docs.redhat.com/en/documentation/openshift_container_platform/4.21/html/images/creating-images)은 기본으로 임의 할당된 UID 로 컨테이너를 돌린다고 밝히고, 그 이유를 컨테이너 엔진 취약점으로 프로세스가 탈출하더라도 호스트에서 높은 권한을 얻지 못하게 하려는 것이라고 설명합니다.
UID 가 매번 달라질 수 있으니 이미지 작성자는 "어떤 사용자로 돌지" 를 미리 알 수 없습니다. 그래서 지침은 소유자 대신 그룹을 기준으로 삼습니다. 컨테이너 사용자는 항상 root 그룹의 구성원이므로, 쓰는 디렉터리와 파일을 root 그룹 소유로 두고 그룹에 읽기·쓰기(실행 파일이면 실행) 권한을 주면 어떤 UID 가 오든 됩니다. 지침의 예가 chgrp -R 0 <디렉터리> && chmod -R g=u <디렉터리> 입니다. 또 이 사용자는 특권이 없으니 1024 아래 포트를 열 수 없습니다.
한 가지 먼저 밝혀 둡니다. OpenShift 는 단일 노드 설치에도 최소 8 vCPU·16GB RAM·120GB 저장소가 필요하다고 문서에 적혀 있어, 8GiB 인 우리 실습 VM 에서는 돌릴 수 없습니다. 이 글의 OpenShift 동작은 문서에서 확인한 것이고, 다음 실습은 k3s 에서 같은 조건을 손으로 만들어 증상을 재현합니다.
어떻게 동작하나
UID 를 고르는 것은 SCC(Security Context Constraints)입니다. [OCP 4.19 SCC 문서](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/authentication_and_authorization/managing-pod-security-policies)에 따르면 인증된 사용자가 기본으로 쓰는 SCC 는 restricted-v2 이고, 이런 성질을 가집니다.
runAsUser MustRunAsRange 범위를 네임스페이스 주석에서 가져온다seLinuxContext MustRunAs MCS 레벨도 네임스페이스 주석에서fsGroup MustRunAs supplemental-groups 주석, 없으면 uid-range 로capabilities ALL 을 떨어뜨림 NET_BIND_SERVICE 만 명시적으로 더할 수 있다seccomp runtime/defaultallowPrivilegeEscalation 설정하지 않거나 false 여야 한다범위는 프로젝트(네임스페이스)의 openshift.io/sa.scc.uid-range 주석이고 <시작>/<길이> 블록 하나를 받습니다. 파드가 runAsUser 를 요청하지 않으면 범위의 최솟값이 기본값이 됩니다. 판에 따른 차이도 있습니다. 4.19 문서에서는 restricted-v2 가 기본이지만, 4.20·[4.21 SCC 문서](https://docs.redhat.com/en/documentation/openshift_container_platform/4.21/html/authentication_and_authorization/managing-pod-security-policies)에는 사용자 네임스페이스(hostUsers: false)를 강제하는 restricted-v3 가 새 설치의 기본으로 추가됐습니다. 어느 쪽이든 범위의 UID 로 돈다는 점은 같습니다.
SCC 와 별개로 Pod Security Admission 도 돕니다. [4.21 파드 보안 어드미션 문서](https://docs.redhat.com/en/documentation/openshift_container_platform/4.21/html/authentication_and_authorization/understanding-and-managing-pod-security-admission)는 둘을 독립된 두 장치로 설명합니다. 전역으로는 privileged 를 강제하고 restricted 는 경고·감사에만 쓰며, 네임스페이스의 경고·감사 라벨은 서비스 계정이 쓸 수 있는 SCC 에 맞춰 자동으로 동기화됩니다. 워크로드는 둘을 모두 통과해야 합니다.
현장에서 만나는 모습
k3s VM 에서 restricted-v2 가 채울 값(uid 1000680000, gid 0, fsGroup)을 직접 넣고 흔한 레거시 이미지(root 가 만든 /app/data, 권한 755)를 돌린 결과입니다(실측).
id: uid=1000680000 gid=0(root) groups=0(root),1000680000whoami: whoami: unknown uid 1000680000HOME=/startup failed: cannot write /app/data/started/app/run.sh: line 6: can't create /app/data/started: Permission denied여기서 흔히 시도하는 우회책 셋을 같은 VM 에서 재 봤습니다(실측).
- fsGroup 만 주기: 위 파드에 이미 fsGroup 이 있었지만 이미지 안
/app/data는 그대로 root 755 였습니다. fsGroup 은 파드에 붙는 볼륨의 그룹을 바꾸는 장치입니다. - emptyDir 을 덮어 마운트: 그룹 1000680000, 권한 2777 로 만들어져 쓰기는 됐습니다. 대신 파드가 사라지면 데이터도 사라지고, 이미지가 그 경로에 넣어 둔 파일은 가려집니다.
- root initContainer 로 chown:
violates PodSecurity "restricted:latest": runAsUser=0으로 만들어지지도 않았습니다.
이미지를 chgrp -R 0 /app && chmod -R g=u /app 로 고치자 범위의 첫 UID 와 마지막 UID(1000689999) 모두에서 떴습니다. 이때 한 번 더 걸렸습니다. 스크립트 파일에 실행 비트가 없던 첫 빌드는 exec: "/app/run.sh": permission denied 로 RunContainerError 가 났습니다 — 지침이 실행 파일에 그룹 실행 권한을 요구하는 이유입니다.
whoami 실패와 HOME=/ 은 containerd 에서 본 모습입니다. OpenShift 문서는 CRI-O 가 임의 UID 를 컨테이너의 /etc/passwd 에 넣어 줄 수 있다고 설명하므로, OpenShift 에서는 이름 조회가 될 수 있습니다. 다만 4.21 문서는 같은 자리에서 이미지에 /etc/passwd 가 들어 있으면 CRI-O 가 그 주입에 실패해 실행 중인 UID 를 해석하지 못할 수 있다고도 적고, 그 파일의 권한을 열어 주는 방법은 위험하다고 경고합니다. 그러니 "우리 k3s 에서 whoami 가 안 된다" 를 OpenShift 의 증상으로 옮겨 적으면 안 됩니다. 반면 HOME 에 캐시를 쓰는 도구는 어디서든 HOME 을 쓰기 가능한 경로로 돌려 두는 편이 안전합니다.
실무에서 진짜 중요한 것
- 옮기기 전에 이미지를 임의 UID 로 돌려 봅니다. 일반 클러스터에서도 runAsUser 를 큰 값으로, runAsGroup 을 0 으로 주면 OpenShift 에서 날 권한 문제의 대부분이 미리 드러납니다. 범위 안의 서로 다른 두 UID 로 돌려 보는 것이 확실합니다.
- 고치는 곳은 이미지입니다. 특정 UID 로 chown 하는 대신 root 그룹과 g=u 를 쓰고, USER 는 숫자로 적습니다(문서는 S2I 이미지에 숫자 USER 가 없으면 빌드가 실패한다고 설명합니다).
- anyuid 같은 넓은 SCC 를 주는 것은 마지막 수단입니다. 문서는 기본 SCC 를 고치지 말라고 경고하고, 넓은 SCC 는 임의 UID 가 막으려던 위험을 되살립니다.
- 읽기 전용 루트를 함께 켜면 이미지가 어디에 쓰는지 드러납니다. 쓰는 경로마다 볼륨을 주는 목록이 곧 운영 문서가 됩니다.
다음 실습에서 할 것
k3s VM 에 OpenShift 프로젝트를 흉내 낸 네임스페이스를 만들고, 레거시 이미지가 임의 UID 에서 권한 거부로 죽는 것을 기록합니다. 신원 조사, buildah 로 이미지 고치기와 k3s 로 옮기기, 두 UID 로 띄우기, fsGroup·emptyDir·root initContainer 비교, 읽기 전용 루트까지 해 본 뒤 보고서로 정리합니다.