Linuxユーザーの名前空間は、rootではなくユーザーとして/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フラグがすでに設定されている場合は、2番目の呼び出しでそのフラグを維持(つまり設定を継続)する必要があります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;
}