Skip to content
isdnetworks
Go back

설치 전에 재는 uid

(수정됨)

호스트에 mysql 과 redis-cli 같은 명령줄 도구를 몇 개 깔려고 했는데 docker exec 로 컨테이너에 들어가 명령을 치는 것이 번거로웠기 때문이다. 깔기 전에 위험 평가를 했고 그 평가가 실제로 필요했다.

Table of contents

Open Table of contents

배경 — 겹치는 데이터 디렉터리

컨테이너 몇 개가 호스트의 /var/lib/mysql 과 /var/lib/redis 를 -v 로 직접 마운트해서 데이터 저장에 쓰고 있었다. 호스트에 같은 패키지를 깔면 그 패키지도 관례상 같은 경로를 데이터 디렉터리로 잡는다.

배포판이 달라도 이런 경로는 대체로 같기 때문에 겹칠 가능성이 높았다. 겹치는 경로를 두 주체가 서로 모른 채로 쓰는 것이 이 상황의 위험이었다.

상황 — 세 곳이 같은 식별자를 쓰는 상태

패키지를 깔면 mysql 서비스 사용자가 만들어지고 그 사용자가 데이터 디렉터리를 소유하게 된다. ls -ln /var/lib/mysql 과 getent passwd 로 지금 상태를 보니 디렉터리 소유자 번호를 호스트의 다른 시스템 데몬이 이미 점유하고 있었다.

docker exec 로 본 컨테이너 안 mysqld 프로세스의 id -u 도 같은 번호였고 디렉터리는 그 번호에 700 으로 권한이 좁게 걸려 있었다. 세 곳이 같은 식별자에 물려 있는 상태였다.

원인 — 설치가 소유권을 바꾸는 경로

useradd(8) 매뉴얼은 --system 계정의 번호를 SYS_UID_MIN 과 SYS_UID_MAX 사이에서 고르되 이미 쓰인 값은 피한다고 설명하므로 그 번호가 차 있으면 다른 번호를 받게 된다. 설치 스크립트가 데이터 디렉터리를 새 사용자 소유로 chown 하면 컨테이너 프로세스가 자기 데이터에 접근을 잃는다.

권한이 좁게 걸려 있어서 그룹이나 기타 사용자로는 읽지도 못하므로 컨테이너가 다음 쓰기 시점에 죽는다. 설치 직후가 아니라 나중에 죽는다는 점이 이 사고를 찾기 어렵게 만든다.

검증 — 파일 상태와 프로세스 응답

실제로 apt install 로 깔고 나서 다시 보니 소유권이 그대로였고 서비스 사용자도 안 만들어졌으며 systemctl status 로 본 서비스는 비활성 상태였다. 디렉터리가 이미 있고 번호가 차 있어서 설치 스크립트가 넘어간 것으로 보인다.

그렇다고 안 건드렸으니 괜찮다로 끝내지 않고 컨테이너에 SELECT 1 과 PING 을 직접 보내 응답을 확인했다. 파일 상태가 안 바뀐 것과 프로세스가 정상인 것은 다른 사실이라 둘 다 봐야 했다.

결론 — 클라이언트만 두는 원칙

이번에는 괜찮았지만 다른 패키지였으면 소유권 변경이 돌았을 수도 있다. 두 가지 방법이 있었는데 서버 패키지째 깔고 서비스만 비활성으로 두는 것과 mysql-client 처럼 클라이언트 패키지만 두는 것이다. 뒤쪽을 골랐다. 이유는 서버 패키지의 설치 스크립트가 데이터 디렉터리를 만지는 경로를 아예 밟지 않아서다. 대신 호스트에서 서버 쪽 도구를 쓰고 싶을 때마다 docker exec 로 돌아가야 하는 것을 감수했다.

서버가 뜨면 같은 데이터 디렉터리를 두 프로세스가 쓰게 되어 확실히 깨진다. 패키지가 없는 도구는 실행 파일을 직접 풀거나 인터페이스를 직접 호출하는 것으로 대체했는데 목적이 도구 설치가 아니라 호스트에서 명령을 치는 것이었기 때문이다.

정리


Share this post on:

Previous Post
인코딩해서 통째로 넘긴 스크립트
Next Post
환경 이름은 코드에 있는 걸 쓴다