首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >mount必须是root才能运行带选项参数命令是真的吗?

mount必须是root才能运行带选项参数命令是真的吗?

原创
作者头像
Uncle匠
修改2025-05-30 10:48:20
修改2025-05-30 10:48:20
23600
代码可运行
举报
运行总次数:0
代码可运行

背景介绍

之前为了最小化进程权限,主导设计了DAC进程降权方案并在全业务线进行推广,对所有进程的进行了降权,解决了所有进程以root运行的乱象,对于个别需要特权操作的进程通过启动时赋予capibilities能力解决。此方案运行良好很长一段时间,最近有业务线反馈说需要使用mount,但是配置capibilities为ALL都没有用,必须使用root启动。当时我把震惊了,之前针对capibilities做了深入研究分析,只有root进程能执行而配置了capibilities的普通进程的不能执行的操作是有,但非常少,而mount需要root才能执行让我震惊,决定花个把小时分析一把。

现象复现

在ubuntu简单复现一下错误现象,先用root成功运行

代码语言:shell
复制
# 生成镜像并格式化(普通用户可操作)
dd if=/dev/zero of=test.img bs=1M count=100
mkfs.ext4 test.img

# 挂载时需使用 sudo(关键步骤)
sudo mkdir -p /mnt/image
sudo mount -o loop test.img /mnt/image # "-o loop" 需 root 权限

# 卸载时同样需要 sudo
sudo umount /mnt/image

再用非root报错

代码语言:shell
复制
mount -o loop test.img /mnt/image
错误输出如下
mount: only root can use "--options" option

给进程赋予所有capibilities再看

代码语言:shell
复制
cp /usr/bin/mount .
sudo setcap all=ep ./mount
getcap ./mount
输出: ./mount =ep #表示所有cap设置成功,可用交互终端bash替换mount运行来验证,cat /proc/pid/status
mount -o loop test.img /mnt/image  #再次运行
输出: mount: only root can use "--options" option #还是不行

以上从直接运行mount命令看,确实只有root才能运行带选项mount命令,但真的是这样吗?

初步分析

用strace分析一下有无sudo运行的差异

未使用sudo跟踪系统调用如下

代码语言:shell
复制
strace mount -o loop test.img /mnt/image
输出:
...
getuid()                                = 1000
geteuid()                               = 1000
getuid()                                = 1000
geteuid()                               = 1000
write(2, "mount: ", 7mount: )                  = 7
write(2, "only root can use \"--options\" op"..., 36only root can use "--options" option) = 36
...

使用sudo跟踪系统调用如下

代码语言:shell
复制
sudo strace mount -o loop test.img /mnt/image
输出:
...
geteuid()                               = 0
getegid()                               = 0
getuid()                                = 0
getgid()                                = 0
access("/run/mount/utab", R_OK|W_OK)    = 0
mount("/dev/loop15", "/mnt/image", "ext4", 0, NULL) = 0
...

对比两个系统调用,很明显,未使用sudo根本没有运行到mount这个系统调用就报错了,而且结合前面有getuid和geteuid的调用,猜想是在用户态程序判断当前不是root,直接就拒绝往下运行了。

猜想验证

查看内核源码mount系统调用需要CAP_SYS_ADMIN能力

代码语言:c
代码运行次数:0
运行
复制
 // fs/namespace.c 文件搜
 if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))  

写个代码验证一下。

步骤 1:检查可用的循环设备

代码语言:shell
复制
# 查看当前已使用的循环设备
losetup -a

# 找到第一个可用的循环设备(如 /dev/loop15)
losetup -f
输出:/dev/loop15

步骤 2:将 test.img 绑定到循环设备

代码语言:shell
复制
# 将 test.img 绑定到 /dev/loop15(需要 root 权限)
sudo losetup /dev/loop15 test.img

步骤 3:用带cap的程序挂载循环设备到目标目录

代码语言:c
代码运行次数:0
运行
复制
// 创建挂载点(如 /mnt/image)
// sudo mkdir -p /mnt/image

// 用程序挂载 /dev/loop15(文件系统类型为 ext4)
// sudo mount /dev/loop15 /mnt/image -t ext4  如下程序类似实现这个替代吗命令

// mount_demo.c
#include <sys/mount.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    // 挂载 /dev/loop15 到 /mnt/image (ext4)
    if (mount("/dev/loop15", "/mnt/image", "ext4", 0, NULL) == 0) {
        printf("挂载成功!\n");
    } else {
        perror("挂载失败");
        return 1;
    }
    return 0;
}
//编译程序
//gcc mount_demo.c -o mount_demo
//未设置权限直接运行
//$./mount_demo
//输出:挂载失败: Operation not permitted
//给程序添加 CAP_SYS_ADMIN 能力(需要 root 权限)
//sudo setcap CAP_SYS_ADMIN=ep mount_demo
//getcap mount_demo
//输出:mount_demo = cap_sys_admin+ep
//再运行 ./mount_demo 
//输出:挂载成功!

从以上输出可以看出使用CAP_SYS_ADMIN能力直接调用moutn接口运行成功了。而且mount没权限提示的是“Operation not permitted”错误。

步骤 4:验证挂载

代码语言:shell
复制
# 查看挂载内容
ls /mnt/image
输出:lost+found

步骤 5:卸载与清理

完成后需依次卸载挂载点和循环设备:

代码语言:shell
复制
# 卸载挂载点
sudo umount /mnt/image
# 断开循环设备与 test.img 的绑定
# 如下也可以非root用CAP_SYS_ADMIN,CAP_DAC_OVERRIDE两个cap来解决
sudo losetup -d /dev/loop15

深入源码分析佐证

mount.c

代码语言:c
代码运行次数:0
运行
复制
//...
	while ((c = getopt_long(argc, argv, "aBcfFhilL:Mno:O:rRsU:vVwt:T:",
					longopts, NULL)) != -1) {

		/* only few options are allowed for non-root users */
		if (mnt_context_is_restricted(cxt) && //如果是非root
		    !strchr("hlLUVvrist", c) && //非这些选项
		    c != MOUNT_OPT_TARGET &&
		    c != MOUNT_OPT_SOURCE)
			exit_non_root(option_to_longopt(c, longopts)); //输出非root提示

//...

static void __attribute__((__noreturn__)) exit_non_root(const char *option)
{
	const uid_t ruid = getuid();
	const uid_t euid = geteuid();

	if (ruid == 0 && euid != 0) {
		/* user is root, but setuid to non-root */
		if (option)
			errx(MOUNT_EX_USAGE, _("only root can use \"--%s\" option "
					 "(effective UID is %u)"),
					option, euid);
		errx(MOUNT_EX_USAGE, _("only root can do that "
				 "(effective UID is %u)"), euid);
	}
	if (option)
		errx(MOUNT_EX_USAGE, _("only root can use \"--%s\" option"), option);
	errx(MOUNT_EX_USAGE, _("only root can do that"));
}

mnt_context_is_restricted & mnt_new_context

代码语言:c
代码运行次数:0
运行
复制
/**
 * mnt_context_is_restricted:
 * @cxt: mount context
 *
 * Returns: 0 for an unrestricted mount (user is root), or 1 for non-root mounts
 */
int mnt_context_is_restricted(struct libmnt_context *cxt)
{
	return cxt->restricted; //返回这个成员变量,从函数注释可以看到是root判断
}


/**
 * mnt_new_context:
 *
 * Returns: newly allocated mount context
 */
struct libmnt_context *mnt_new_context(void)
{
	struct libmnt_context *cxt;
	uid_t ruid;

	cxt = calloc(1, sizeof(*cxt));
	if (!cxt)
		return NULL;

	ruid = getuid();

	mnt_context_reset_status(cxt);

	cxt->ns_orig.fd = -1;
	cxt->ns_tgt.fd = -1;
	cxt->ns_cur = &cxt->ns_orig;

	cxt->map_linux = mnt_get_builtin_optmap(MNT_LINUX_MAP);
	cxt->map_userspace = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);

	INIT_LIST_HEAD(&cxt->hooksets_hooks);
	INIT_LIST_HEAD(&cxt->hooksets_datas);

	/* if we're really root and aren't running setuid */
    //这边根据root来设置
	cxt->restricted = (uid_t) 0 == ruid && !is_privileged_execution() ? 0 : 1; 

	cxt->noautofs = 0;

	DBG(CXT, ul_debugobj(cxt, "----> allocate %s",
				cxt->restricted ? "[RESTRICTED]" : ""));

	return cxt;
}

结论

到此,结论很明显,mount系统调用只需要CAP_SYS_ADMIN能力就可以了,而linux系统自带的mount命令工具自作聪明,在用户态程序中做了一些不合适的判断,只要检测到非root就退出了,未能继续往下执行了。因此,只要重写mount程序,或者自己写程序直接调用即可。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景介绍
  • 现象复现
  • 初步分析
  • 猜想验证
  • 深入源码分析佐证
  • 结论
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档