프로세스는 자신이 하위 네임스페이스에 있는지 어떻게 감지합니까?

프로세스는 자신이 하위 네임스페이스에 있는지 어떻게 감지합니까?

이 주제를 조사한 결과 Github에서 다음 코드를 발견했습니다.

// HasNamespace determines if a container is using a particular namespace or the
// host namespace.
// The device number of an unnamespaced /proc/1/ns/{ns} is 4 and anything else is
// higher.
// Only works from inside a container.

https://github.com/genuinetools/amicontained/blob/568b0d35e60cb2bfc228ecade8b0ba62c49a906a/vendor/github.com/jessfraz/bpfd/proc/proc.go#L461

그러나 이 의견은 다음에서 알 수 있듯이 오래된 것입니다.

$ docker run -ti --rm --pid host debian
root@e29ab2d7176b:/# stat --format="%d" /proc/self/ns/net
58
root@e29ab2d7176b:/# stat --format="%d" /proc/self/ns/pid
58

해당 설명이 정확하다면 결과는 stat --format="%d" /proc/self/ns/pid4가 되어야 합니다.

프로세스가 하위 네임스페이스에 있는지 어떻게 감지합니까?

답변1

얼마 전 이 주제를 직접 조사하면서 제가 발견한 내용을 공유할 수 있습니다. 확실히 권위 있거나 철저한 것은 아니지만 도움이 될 수 있습니다.

기술적 설명

부인 성명

나 자신은 내가 설명하려는 방법을 실제로 구현한 적이 없습니다. 이는 제가 얼마 전에 참여했던 사용자 지정 컨테이너화 프로젝트의 가능성일 뿐이었지만 네임스페이스 감지를 완전히 포기하기로 결정했습니다. 관심이 있으시면 계속 읽어보시기 바랍니다.


이는 커널에서 관찰되는 동작을 활용하여 초기 네임스페이스를 감지하는 방법입니다. (하지만이러한 동작은 공식 API가 아닙니다.). 이 방법은 가장 일반적이고 합리적인 설정에서 작동하지만 항상 그런 것은 아닙니다.

"스타트업" 네임스페이스

에서 시작하다커널 v3.8최신 안정 버전으로v5.11(현재 v5.12-rc),초기의IPC, UTS, 사용자, PID, cgroup 및 시간 네임스페이스에는 항상 아래와 같이 특정 하드코딩된 ID가 있습니다. 그러므로 우리는 다음과 같이 안전하게 가정할 수 있습니다.그것들네임스페이스 유형 고정 ID보다 큰 네임스페이스 ID는 하위 네임스페이스로 간주될 수 있습니다.

IPC    = 0xEFFFFFFF
UTS    = 0xEFFFFFFE
USER   = 0xEFFFFFFD
PID    = 0xEFFFFFFC
CGROUP = 0xEFFFFFFB
TIME   = 0xEFFFFFFA

위 목록은 v5.12-rc6 소스 코드에서 가져온 것이지만, 값은 v3.8에서 전혀 존재하지 않았던 네임스페이스("cgroup")를 제외하고는 v3.8부터 항상 동일했습니다. v4.6에 추가되었으며, v5에서는 6).

값이 아래쪽으로 "성장"함에 따라 이러한 초기 네임스페이스가 어떻게 (수년에 걸쳐) 추가되는지 확인하세요. 대신 모든 하위 네임스페이스는 순차적(요청 시) 값을 갖습니다.위쪽으로 자라다0xF0000000.

그래서,초기 네임스페이스의 설정을 재정의하는 상위 항목이 없는 프로세스의 경우, 이러한 고정 값은 이러한 네임스페이스의 "탐지 작업"을 매우 깔끔하게 해결할 수 있습니다.

그러나 이러한 값은별말씀을요사용자 공간에 노출된 공식 API의 일부(커널 공간 AFAICT도 아님)따라서 향후에는 변경될 수 있습니다..

커널 개발자는 이를 모두 동적으로 만들거나 무작위로 만들 수도 있습니다. 실제로 마운트 및 네트워크 네임스페이스가 이 목록에 없다는 것을 알 수 있습니다.모두네트워크 및 마운트 네임스페이스,포함하다원본은 이미 완전히 동적이었고언제나0xF0000000하위 네임스페이스와 마찬가지로 ID로 시작하세요. 따라서 가장 우호적인 조건에서도 마운트 및 네트워크 초기 네임스페이스에 대해 몇 가지 경험적 방법을 수행해야 합니다.

마운트 네임스페이스

지금까지의 경험에 따르면 초기 마운트 네임스페이스 ID는 항상 첫 번째 동적 값( 0xF0000000)을 가져옵니다. 이는 아마도 초기 PID 네임스페이스가 공통 proc파일 시스템을 인스턴스화하여 첫 번째 마운트 네임스페이스도 가져오기 때문일 것입니다. 그럼에도 불구하고 초기 마운트 네임스페이스의 ID는 상당히 예측 가능한 것으로 보이며 실제로는 동적 범위 내에서도 고정되어 있습니다.

네트워크 네임스페이스

반면에 구성 변경이 동적으로 생성된 inode 번호 시퀀스에 영향을 미치는 경우 초기 네트워크 네임스페이스 ID는 동일한 운영 체제를 사용하는 동일한 컴퓨터의 이전 부팅과 훨씬 다른 값을 획득할 수 있습니다. 따라서 초기 네트워크 네임스페이스를 탐지하는 것이 실제 복권이 될 수 있습니다. 당신은 종종 "승리"할 수 있지만, 그러기 위해서는 일반적으로 제정신인 환경에서는 사실이지만 항상 사실이 아닐 수도 있는 몇 가지 사항을 가정해야 합니다.

첫 번째 네트워크 네임스페이스는 시스템이 시작된 이후 첫 번째 네트워크 작업을 요청하는 프로세스의 결과로 인스턴스화됩니다(일반적으로 PID 1). 따라서 /proc/net/디렉토리를 사용할 수 있게 되고 그 안에 파일/디렉토리가 생성되며, 각각은 네임스페이스 ID에 사용된 것과 동일한(동적) 값에서 할당된 고유한 inode 번호를 갖습니다. 공교롭게도(이 글을 쓰는 시점까지의 내 경험에 따르면) 거기서 생성된 첫 번째 이름은 디렉토리입니다 stat. 따라서 이 디렉토리는마지막생성된 inode 번호직전네트워크 네임스페이스의 인스턴스화. 따라서 네트워크 네임스페이스의 자체 ID는 /proc/net/statinode 번호 + 1입니다.

물론 /proc/net/stat, 디렉토리는 실제로 모든 프로세스에서 볼 수 있듯이 "네임스페이스" 이름 자체이며 반드시 다음을 참조하지는 않습니다.초기의네트워크 네임스페이스. 프로세스가 해당 디렉터리에 액세스하면 초기 네트워크 네임스페이스를 참조합니다.초기 네임스페이스에서는(즉, 컨테이너화되지 않은 프로세스) 컨테이너화된 환경에서는 초기 네트워크 네임스페이스보다는 프로세스가 속한 프라이빗 네트워크 네임스페이스를 참조할 가능성이 더 높습니다.

묻다:그렇다면 프로세스는 자신의 네트워크 네임스페이스가 실제로 첫 번째 네트워크 네임스페이스인지 여부를 일반적으로 어떻게 추측하려고 할까요?

ㅏ:열거로재귀적으로모두 표시됨PID가 아닌inode 번호 가 발견될 /proc때까지 해당 디렉터리의 파일/디렉터리에서 inode 번호를 찾습니다.0xF0000001첫 번째구멍적어도2inode 번호가 누락되었습니다.

pid가 아닌 많은 파일/디렉토리는 /proc(irq 통계 등) 커널의 핵심 기능과 관련되어 있기 때문에 (지금까지) 모든 PID 네임스페이스에 공통적입니다. inode 번호의 구멍은 다음과 같아야 합니다.적어도2가까운하나는 /proc/net/stat초기 네트워크 네임스페이스용으로 생성된 디렉터리용이고 다른 하나는 초기 네트워크 네임스페이스 자체용입니다(둘 사이의 원자 할당도 가정). 첫 번째 홀에서는 이렇게회의초기 네트워크 네임스페이스의 ID입니다. 해당 ID(취약성)를 프로세스 자체(또는 기타) 네트워크 네임스페이스의 ID와 비교하면 (가장 일반적인 경우) 마침내 모든 설정이 완료됩니다.

그러나 일반적인 경우에도 pid가 아닌 이름에 의존한다는 것은 명백합니다.언제나에서 볼 수 있음모두PID 네임스페이스그리고네임스페이스의 ID와 동일한 번호를 공유합니다.그리고(거의) 완벽한 순서로그리고inode 번호는 /proc/net/*네임스페이스의 자체 ID와 함께 자동으로 할당됩니다. 이 모든 가정가능한현재는 사실이지만 이 동작은 공식 API가 아니기 때문에 앞으로는 더 이상 그렇지 않을 것 같습니다.

또한 이것이 얼마나 까다로운지 더 자세히 지적하기 위해 아래에서 본 내용을 참고하세요 /proc.언제나프로세스의 PID 네임스페이스설치됨저것특정한 /proc디렉토리이므로 반드시 프로세스의 PID 네임스페이스일 필요는 없습니다.읽다/proc디렉토리. 정상적인 실행에서는 /proc"설치자"와 "독자" 사이의 차이가 /proc발생할 가능성이 거의 없지만 여전히 완전히 가능하며 일관성 없는 분석으로 쉽게 이어질 수 있습니다.


몇 가지 독단적인 고려 사항

초기를 제외하고사용자감지가 매우 쉬운 네임스페이스1 공식 API의 일부인 네임스페이스 감지는 이를 지원하는 실제적이고 포괄적인 API가 없기 때문에 (가능한 경우) 해결하기 위해 많은 노력이 필요한 문제입니다. 그리고 의도적). 몇년전에 몇개 있었는데ioctl(2)작업이 네임스페이스 목록에 추가되었습니다., 하지만 여전히 매우 제한적이며 이를 사용하는 방법(심지어 말도 안되는 방법)을 이해할 수 없습니다.명확한테스트 목적.

실제로 PID 네임스페이스를 감지하는 몇 가지 간단한 트릭이 있지만 공식 API도 아닙니다. systemd사람들의 예를 보세요최근에 논의됨그들을 위한 도구. 분명히 그들은 또한 장치 번호가 "3 또는 4"인 것을 조사했지만 proc그다지 많이 보유하지 않는다는 것을 알았기 때문에 그 아이디어를 포기했습니다(아무리 흔하더라도 "맑은 날" 조건에서만 유지되었을 수도 있음). 그들은 또한 항상 커널 스레드가 존재하는 PID 2를 탐색했습니다. [kthreadd]이는 초기 PID 네임스페이스의 확실한 신호이지만 마운트를 사용하면 해당 검사를 완전히 위반 proc하게 되므로 해당 아이디어도 포기했습니다.hidepid=[12]

네임스페이스 감지의 근본적인 문제는 네임스페이스가 본질적으로 임의적이며 다른 네임스페이스로 완전히 대체될 수 있다는 것입니다. 커널에는 모든 네임스페이스 유형에 대해 소위 "초기" 네임스페이스가 있지만 첫 번째 PID 1 프로세스(또는 그 안에 있는 프로세스 ) 는 시작하기 전에 단순히 -ing을 수행하여 이를 다른 프로세스로 재정의함으로써 이 작업을 수행 initramfs하도록 선택할 수 있습니다. unshare(2)프로세스. 분명히, 이 (그렇지 않은) 가상 조건에서 초기 네임스페이스를 탐지하기 위한 탐색은 유용한 의미를 잃습니다. 관련된 것은 "호스트" 네임스페이스이기 때문입니다. 네임스페이스입니다.없는운영 체제(예:기본PID 1 init프로세스)작업일단 부팅되면 이러한 "호스트" 네임스페이스도 이미어린이커널에 관한 한 네임스페이스입니다. init프로세스가 실제로 항상 초기 네임스페이스를 덮어쓴다는 말은 아니지만 원칙적으로는 가능합니다. 이는 네임스페이스 감지 도구를 손상시키기에 충분합니다.

내가 보기엔 대부분의 실제 사용 사례에서는 그렇지 않은 것 같습니다.진짜관심이 있다평상복네임스페이스. UTS, IPC, cgroup, time 네임스페이스에는 전혀 관심이 없을 것이 거의 확실하며 아마도 user 및 PID 네임스페이스에도 관심이 없을 수도 있습니다. 그렇다면 데이터 및 연결 액세스와 관련된 마운트 및 네트워크 네임스페이스에만 관심이 있을 것입니다. PID 네임스페이스는 사용자 네임스페이스보다 훨씬 더 많이 검색되기 때문에 자주 검색됩니다.힌트더 넓은 의미의 컨테이너와 "더 넓은" 컨테이너는 흥미로운 마운트와 네트워크 네임스페이스를 제공합니다. 불행하게도 후자는 찾기가 가장 어렵습니다. 이것이 아마도 감지 도구가 느슨하게 바인딩되었지만 마운트/네트워크 네임스페이스 사이에 좋은 관계를 설정하기를 희망하면서 PID 네임스페이스를 찾는 것을 선호하는 이유일 것입니다.

전체적으로 이러한 모든 "ifs", "buts" 및 주의 사항과 함께 문제는 "초기 또는 하위" 네임스페이스를 검색하려는 노력이 그만한 가치가 있는지 여부입니다. 나는 일반적으로 그렇지 않다고 말하고 싶습니다. 전혀 감지하지 않거나 잘 정의된 특정 사용 사례에 대해 "호스트" 네임스페이스에 대한 좁은 정의만 감지하는 것이 더 나을 것입니다.

화타이


/proc/self/uid_map1. 파일을 읽고 보고서가 0 0 4294967295정확한지 확인하세요.

관련 정보