장치 노드의 바인드 마운트가 tmpfs 루트 디렉터리의 EACCES와 함께 중단되는 이유는 무엇입니까?

장치 노드의 바인드 마운트가 tmpfs 루트 디렉터리의 EACCES와 함께 중단되는 이유는 무엇입니까?

컨테이너/샌드박스를 설정하는 일반적인 시나리오는 새 tmpfs에 최소한의 장치 노드 세트를 생성하는 것입니다(호스트를 노출하는 것과 반대 /dev). 제가 아는 유일한(권한이 없는) 방법은 바인드 마운트하는 것입니다. 당신이 원하는 것. 내가 (내부적으로 unshare -mc --keep-caps) 사용하는 명령은 다음과 같습니다.

mkdir /tmp/x
mount -t tmpfs none /tmp/x
touch /tmp/x/null
mount -o bind /dev/null /tmp/x/null

마운트를 의 상단으로 이동할 계획입니다 /dev. 그러나 이동을 수행하기 전에도 실행하면 echo > /tmp/x/null"권한 거부" 오류( EACCES)가 발생합니다.

하지만 다르게 실행하면 다음과 같습니다.

mkdir /tmp/x/y
touch /tmp/x/y/null
mount -o bind /dev/null /tmp/x/y/null
echo > /tmp/x/y/null

예상대로 쓰기가 성공합니다. 나는 이것을 꽤 많이 시도했지만 근본 원인이나 왜 이런 일이 발생해야하는지 찾을 수 없습니다. 이 문제는 바인드 마운트된 노드를 하위 디렉토리에 배치하고 해당 심볼릭 링크를 새 파일 시스템이 될 최상위 레벨에 배치하여 해결할 수 있지만 /dev꼭 필요한 것은 아닌 것 같습니다.

어떻게 되어가나요? 이에 대한 합리적인 설명이 있습니까? 아니면 일부 액세스 제어 논리에 문제가 있습니까?

답변1

음, 이것은 세 가지 메커니즘이 함께 결합된 결과인 매우 흥미로운 효과인 것 같습니다.

첫 번째 (사소한) 요점은 무언가를 파일로 리디렉션할 때 쉘이 파일이 O_CREAT아직 존재하지 않는 경우 생성되도록 보장하는 옵션을 사용하여 대상 파일을 연다는 것입니다.

두 번째로 고려해야 할 점은 마운트 지점이지만 /tmp/x일반 디렉터리라는 점입니다. 옵션 없이 마운트한다고 가정하면 마운트 지점에 대한 권한이 자동으로 변경되어 전역적으로 쓰기 가능해지고 고정 비트( , 이는 일반적인 권한 집합 이므로 합리적인 기본값처럼 느껴짐)를 갖게 됩니다. 가능합니다 (귀하의 상황에 따라 다름 ).tmpfs/tmp/x/ytmpfs1777/tmp/tmp/x/y0755umask

마지막으로 세 번째 퍼즐 조각은 사용자 네임스페이스가 설정되는 방식입니다. 즉, unshare(1)호스트 사용자의 UID/GID가 새 네임스페이스의 동일한 UID/GID에 매핑되도록 지시하는 것입니다. 이것은오직새 네임스페이스에 매핑되므로 상위/하위 네임스페이스 간에 다른 UID를 변환하려고 하면 호출되는 결과가 발생합니다.오버플로 UID, 기본값은 65534—user 입니다 nobody( user_namespaces(7)섹션 참조 Unmapped user and group IDs). 이는 /dev/null하위 사용자 네임스페이스가 내부적으로 소유하는 바인드 마운트를 만듭니다( nobody하위 사용자 네임스페이스에는 호스트 사용자에 대한 매핑이 없기 때문입니다).root

$ ls -l /dev/null
crw-rw-rw- 1 nobody nobody 1, 3 Nov 25 21:54 /dev/null

모든 사실을 종합해 보면 다음과 같은 결론에 도달합니다. 파일이 전역적으로 쓰기 가능한 고정 디렉터리에 있고 파일이 포함된 디렉터리의 소유자가 아닌 다른 사람이 소유하고 있는 동안 echo > /tmp/x/null옵션을 사용하여 기존 파일을 열려고 시도 했습니다.O_CREATnobody

이제 openat(2)한 단어씩 주의 깊게 읽어 보십시오.

유럽 ​​항공 우주 센터

O_CREAT가 지정되고 protected_fifos 또는 protected_regular sysctl이 활성화되고 파일이 존재하며 FIFO 또는 일반 파일이고 파일 소유자가 현재 사용자도 아니고 포함 디렉터리의 소유자도 아니며 포함 디렉터리는 모두 월드 또는 일반 파일입니다. 그룹 쓰기 가능 및 점도. 자세한 내용은 proc(5)의 /proc/sys/fs/protected_fifos 및 /proc/sys/fs/protected_regular에 대한 설명을 참조하세요.

정말 좋지 않나요? 이것은 우리의 상황과 거의 비슷해 보입니다...맨 페이지에서 다음과 같이 설명한다는 사실을 제외하면오직일반 파일과 FIFO에 대해 알려주세요.아무것도 없다장치 노드 정보.

알았어 한번 살펴보자실제로 이를 구현하는 코드. 본질적으로 먼저 성공해야 하는 예외 조건(첫 번째 조건)을 확인한 다음 고정 디렉터리가 전역적으로 쓰기 가능한 경우 다른 조건(두 번째 , 첫 번째 조건) if에 대한 액세스를 거부한다는 것을 알 수 있습니다 .if

static int may_create_in_sticky(umode_t dir_mode, kuid_t dir_uid,
        struct inode * const inode)
{
  if ((!sysctl_protected_fifos && S_ISFIFO(inode->i_mode)) ||
      (!sysctl_protected_regular && S_ISREG(inode->i_mode)) ||
      likely(!(dir_mode & S_ISVTX)) ||
      uid_eq(inode->i_uid, dir_uid) ||
      uid_eq(current_fsuid(), inode->i_uid))
    return 0;

  if (likely(dir_mode & 0002) ||
      (dir_mode & 0020 &&
       ((sysctl_protected_fifos >= 2 && S_ISFIFO(inode->i_mode)) ||
        (sysctl_protected_regular >= 2 && S_ISREG(inode->i_mode))))) {
    const char *operation = S_ISFIFO(inode->i_mode) ?
          "sticky_create_fifo" :
          "sticky_create_regular";
    audit_log_path_denied(AUDIT_ANOM_CREAT, operation);
    return -EACCES;
  }
  return 0;
}

따라서 대상 파일이 문자 장치(일반 파일이나 FIFO가 아님)인 경우 O_CREAT커널은 전역적으로 쓰기 가능한 고정 디렉터리에 있을 때 파일 열기를 여전히 거부합니다.

원인을 올바르게 찾았다는 것을 증명하기 위해 다음 조건 중 하나에서 문제가 사라지는지 확인할 수 있습니다.

  • 설치 — 이 tmpfs작업은-o mode=777아니요마운트 지점에 고정 비트가 있도록 만드세요.
  • /tmp/x/null으로 열지 O_WRONLY만 옵션은 없습니다(이를 테스트하려면 O_CREAT호출하는 프로그램을 작성하고 컴파일하고 실행하여 각 호출의 반환 값을 확인하세요).open("/tmp/x/null", O_WRONLY | O_CREAT)open("/tmp/x/null", O_WRONLY)strace -e trace=openat

이 동작을 커널 버그로 간주해야 하는지는 확실하지 않지만 문서에서는 이에 대한 내용을 openat(2)다루지 않는 것 같습니다.모두시스템이 호출되는 상황실제로실패하고 표시됩니다 EACCES.

관련 정보