Linux 사용자 네임스페이스에서는 루트가 아닌 사용자로서 /tmp/foo
자신에게 마운트를 바인딩합니다. 그게 다야.
/tmp/foo
그런 다음 읽기 전용으로 다시 설치해 보았습니다 . 또는 를 /tmp
사용하여 nosuid
마운트 한 경우 nodev
다시 마운트가 실패합니다. 그렇지 않으면 다시 마운트가 성공합니다.
재설치가 성공하지 못하게 nosuid
하거나 방해하는 요인이 있습니까 ? nodev
이 동작이 어딘가에 문서화되어 있습니까? 바인드 설치 및 재설치가 모두 성공하거나 둘 다 실패할 것으로 예상하기 때문에 혼란스럽습니다.
바인드 설치 및 재설치를 재현하는 코드는 다음과 같습니다.
#define _GNU_SOURCE /* unshare */
#include <errno.h> /* errno */
#include <sched.h> /* unshare */
#include <stdio.h> /* printf */
#include <string.h> /* strerror */
#include <sys/mount.h> /* mount */
#include <unistd.h> /* getuid */
int main() {
printf ( "getuid %d\n", getuid() );
int rv = unshare ( CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUSER );
printf ( "unshare %2d %s\n", rv, strerror(errno) );
rv = mount ( "/tmp/foo", "/tmp/foo", 0, MS_BIND | MS_REC, 0 ),
printf ( "mount %2d %s\n", rv, strerror(errno) );
rv = mount ( "/tmp/foo", "/tmp/foo", 0,
MS_BIND | MS_REMOUNT | MS_RDONLY, 0 ),
printf ( "remount %2d %s\n", rv, strerror(errno) );
return 0;
}
예제 출력:
$ mkdir -p /tmp/foo
$ mount | grep /tmp
tmpfs on /tmp type tmpfs (rw,nosuid,nodev,relatime,inode64)
$ gcc test.c && ./a.out
getuid 1000
unshare 0 No error information
mount 0 No error information
remount -1 Operation not permitted
$ uname -a
Linux hostname 5.12.12_1 #1 SMP 1624132767 x86_64 GNU/Linux
그러나 /tmp
마운트할 때 nosuid
둘 다 없으면 nodev
아래와 같이 바인드 마운트와 다시 마운트가 모두 성공합니다.
$ mkdir -p /tmp/foo
$ mount | grep /tmp
tmpfs on /tmp type tmpfs (rw,relatime,inode64)
$ gcc test.c && ./a.out
getuid 1000
unshare 0 No error information
mount 0 No error information
remount 0 No error information
답변1
나는 답을 찾았습니다.
아래 발췌된 커널 소스 코드에서 볼 수 있듯이 NODEV, NOSUID, NOEXEC 및/또는 ATIME 플래그가 이미 설정된 경우 두 번째 호출에서 해당 플래그를 유지(즉, 계속 설정)해야 합니다 mount()
.
fs/namespace.c
Linux 커널 소스 코드 에서 :
/*
* Handle reconfiguration of the mountpoint only without alteration of the
* superblock it refers to. This is triggered by specifying MS_REMOUNT|MS_BIND
* to mount(2).
*/
static int do_reconfigure_mnt(struct path *path, unsigned int mnt_flags)
{
struct super_block *sb = path->mnt->mnt_sb;
struct mount *mnt = real_mount(path->mnt);
int ret;
if (!check_mnt(mnt))
return -EINVAL;
if (path->dentry != mnt->mnt.mnt_root)
return -EINVAL;
if (!can_change_locked_flags(mnt, mnt_flags))
return -EPERM;
down_write(&sb->s_umount);
ret = change_mount_ro_state(mnt, mnt_flags);
if (ret == 0)
set_mount_attributes(mnt, mnt_flags);
up_write(&sb->s_umount);
mnt_warn_timestamp_expiry(path, &mnt->mnt);
return ret;
}
static bool can_change_locked_flags(struct mount *mnt, unsigned int mnt_flags)
{
unsigned int fl = mnt->mnt.mnt_flags;
if ((fl & MNT_LOCK_READONLY) &&
!(mnt_flags & MNT_READONLY))
return false;
if ((fl & MNT_LOCK_NODEV) &&
!(mnt_flags & MNT_NODEV))
return false;
if ((fl & MNT_LOCK_NOSUID) &&
!(mnt_flags & MNT_NOSUID))
return false;
if ((fl & MNT_LOCK_NOEXEC) &&
!(mnt_flags & MNT_NOEXEC))
return false;
if ((fl & MNT_LOCK_ATIME) &&
((fl & MNT_ATIME_MASK) != (mnt_flags & MNT_ATIME_MASK)))
return false;
return true;
}